fix(aibox): Batch N 波次1~2——第八轮审计 T 系列速赢与并发正确性修复
波次1 速赢批(7 项): - T8 ProcessManager watermark 取号与回显记账原子化(entry.lock 改 RLock) - T9 LLM 非 200 响应抛出前 close,流式路径清 _current_response - T13 _sanitize_log_msg 增加裸 sk- key 脱敏,异常构造点统一过脱敏 - T12 流式判定改共享 saw_complete 标志(排空后 empty() 恒 False 死逻辑), 超长截断前移堵住非流式直发绕过 N8 输出预算 - T16/T17 三处 Popen 显式 encoding=utf-8 + errors=replace - T18 detach/log_file/force 统一过 _coerce_bool(字符串 "false" 误判) 波次2 并发正确性批(6 项): - T4 registry 输出队列入口快照,不再向共享工具实例传播(跨会话串扰) - T5 executor 单槽 _current_process 改令牌多槽注册表,_terminate_tree 显式传 (proc, pgid),finally 只注销自己槽位(防并发误杀进程组) - T6 FailoverClient 冷却键改 id(client),与 P6 去重键语义一致 - T7 stream_chat 拆普通方法+内部生成器,消除生成器延迟副作用清除取消标志 - T15 工具结果统一 TOOL_MAX_RESULT_LENGTH 截断(两条分支同口径) - T24 非流式 execute() timeout 下限夹取 1s(该路径无 3600s 兜底) 测试:532 passed / 4 failed(平台环境项)/ 1 skipped,零回归 审计报告补记 §23 第八轮审计 + §24 Batch N 波次1~2 修复记录
This commit is contained in:
1 parent
3e860c16e0
commit
5894e64287
8 files changed
+583
-110
No files matched your search
+25
-13
@@ -1376,13 +1376,20 @@ class DeepSeekChat:
|
||||
# 队列是否为空),闭包长期持有 output_callback(含 websocket 引用)
|
||||
# 阻碍 GC,常驻服务下线程与内存持续累积。
|
||||
drain_stop = threading.Event()
|
||||
# T12:drain 线程与工具调用后的排空循环并发消费同一队列,complete
|
||||
# 标记可能被任一方取走——用可变容器共享,两路见到都置位,供
|
||||
# has_stream_output 判定流式工具(流式已逐行转发,勿重复整发结果)
|
||||
saw_complete = [False]
|
||||
|
||||
def _drain_queue(q=output_queue, cb=output_callback, stop_event=drain_stop):
|
||||
def _drain_queue(q=output_queue, cb=output_callback, stop_event=drain_stop,
|
||||
flag=saw_complete):
|
||||
"""持续排空 output_queue 并转发 line 输出(长时间工具执行期间防 WS 空闲断线)"""
|
||||
while not stop_event.is_set():
|
||||
try:
|
||||
msg_type, content = q.get(timeout=0.1)
|
||||
if cb and msg_type == "line":
|
||||
if msg_type == "complete":
|
||||
flag[0] = True
|
||||
elif cb and msg_type == "line":
|
||||
cb("line", str(content) if content is not None else "")
|
||||
except queue.Empty:
|
||||
continue
|
||||
@@ -1423,6 +1430,9 @@ class DeepSeekChat:
|
||||
)
|
||||
self.logger.warning(f"编排者误调执行面工具 {tool_name},已拦截并引导改用 create_subagent")
|
||||
else:
|
||||
# T12:每个工具调用前重置流式标志(drain 线程与下方排空
|
||||
# 循环并发消费,任一方见到 complete 都会置位)
|
||||
saw_complete[0] = False
|
||||
try:
|
||||
result_raw = self.tool_registry.execute_tool(tool_name, **args)
|
||||
# 排空队列中剩余标记(complete 标记供 has_stream_output 判定、
|
||||
@@ -1430,7 +1440,9 @@ class DeepSeekChat:
|
||||
while not output_queue.empty():
|
||||
try:
|
||||
msg_type, content = output_queue.get_nowait()
|
||||
if output_callback and msg_type == "line":
|
||||
if msg_type == "complete":
|
||||
saw_complete[0] = True
|
||||
elif output_callback and msg_type == "line":
|
||||
output_callback("line", str(content) if content is not None else "")
|
||||
except queue.Empty:
|
||||
break
|
||||
@@ -1448,20 +1460,20 @@ class DeepSeekChat:
|
||||
# 安全转换并清理结果
|
||||
result = self._sanitize_tool_result(result_raw)
|
||||
|
||||
# drain 线程持续转发 line 输出,这里通过 complete 标记判断是否流式工具
|
||||
has_stream_output = not output_queue.empty()
|
||||
# T12:流式工具的 line 输出已由 drain 线程逐行转发(registry 侧
|
||||
# N8 预算已生效),勿再把完整 result 整发——此前排空后判定
|
||||
# output_queue.empty() 恒为 False,导致重复推送 + 超长结果绕过预算
|
||||
has_stream_output = saw_complete[0]
|
||||
|
||||
# 普通(非流式)工具没有队列输出,直接发送结果
|
||||
# 长度预算前置:超长结果先替换为提示,再决定是否推送,
|
||||
# 避免全量 result 经非流式直发路径绕过 N8 输出限制(T12)
|
||||
if len(result) > MAX_RESULT_LENGTH:
|
||||
result = f"⚠️ 工具调用结果长度超过 {MAX_RESULT_LENGTH} 字符,已丢弃。请使用更精确的查询获取所需信息。"
|
||||
|
||||
# 普通(非流式)工具没有队列输出,直接发送(已截断的)结果
|
||||
if not has_stream_output and output_callback and result:
|
||||
output_callback("line", result + "\n")
|
||||
|
||||
# 检查结果大小,如果超过阈值,不保留截断内容,仅返回提示信息
|
||||
if len(result) > MAX_RESULT_LENGTH:
|
||||
truncated_note = f"⚠️ 工具调用结果长度超过 {MAX_RESULT_LENGTH} 字符,已丢弃。请使用更精确的查询获取所需信息。"
|
||||
result = truncated_note
|
||||
if output_callback:
|
||||
output_callback("line", truncated_note + "\n")
|
||||
|
||||
# 结束调用工具时发送换行
|
||||
if output_callback:
|
||||
output_callback("line", "\n\n")
|
||||
|
||||
Reference in new issue
Block a user