From 458736d71e6f4215ba37bf82f6d0e2b8a5b96e10 Mon Sep 17 00:00:00 2001 From: ymq Date: Fri, 11 Sep 2026 16:18:58 +0800 Subject: [PATCH] =?UTF-8?q?fix(agent):=20=E7=BB=AD=E8=B7=91=E9=97=A8?= =?UTF-8?q?=E7=A6=81=E2=80=94=E2=80=94=E5=B7=B2=E6=9C=89=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=8E=A8=E8=BF=9B(tool=5Fcall=5Fcount>0)=E6=97=B6=E7=BA=AF?= =?UTF-8?q?=E6=96=87=E6=9C=AC=E7=9F=AD=E5=9B=9E=E5=A4=8D(<400=E5=AD=97)?= =?UTF-8?q?=E8=A7=86=E4=B8=BA=E4=B8=AD=E9=80=94=E6=84=8F=E5=9B=BE=E5=8F=A5?= =?UTF-8?q?=E8=80=8C=E9=9D=9E=E6=9C=80=E7=BB=88=E7=AD=94=E6=A1=88,?= =?UTF-8?q?=E6=B3=A8=E5=85=A5=E7=BB=AD=E8=B7=91=E6=8C=87=E4=BB=A4=E7=BB=A7?= =?UTF-8?q?=E7=BB=AD=E5=BE=AA=E7=8E=AF(=E4=B8=8A=E9=99=902=E6=AC=A1?= =?UTF-8?q?=E9=98=B2=E6=AD=BB=E5=BE=AA=E7=8E=AF);=E6=A0=B9=E6=B2=BB?= =?UTF-8?q?=E5=95=86=E6=9C=BA=E4=BA=A7=E7=BA=BF=E5=AE=9E=E6=B5=8B=E8=BF=9E?= =?UTF-8?q?=E7=BB=AD4=E4=BC=9A=E8=AF=9D=E5=9C=A87~11=E6=AC=A1=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=E8=B0=83=E7=94=A8=E5=90=8E=E6=A8=A1=E5=9E=8B=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E6=84=8F=E5=9B=BE=E5=8F=A5=E6=8F=90=E5=89=8D=E7=BB=93?= =?UTF-8?q?=E6=9D=9Fturn=E2=86=92=E5=BE=AA=E7=8E=AF=E8=AF=AF=E5=88=A4?= =?UTF-8?q?=E7=BB=88=E6=AD=A2=E2=86=92=E6=8A=A5=E5=91=8A/=E6=80=BB?= =?UTF-8?q?=E7=BB=93=E4=BB=8E=E6=9C=AA=E4=BA=A7=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pipeline_service/agent_loop_v2.py | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/pipeline_service/agent_loop_v2.py b/pipeline_service/agent_loop_v2.py index 6bc4e3f..550c2db 100644 --- a/pipeline_service/agent_loop_v2.py +++ b/pipeline_service/agent_loop_v2.py @@ -99,6 +99,15 @@ class AgentExecutor: #(实测上游抖动窗口内 utility 调用连烧 15 分钟才放弃,会话全程无响应)。 _UTILITY_TIMEOUT = 60 + # 续跑门禁(2026-09-11 商机产线实测):模型输出纯文本「意图句」就结束 turn、 + # 不调工具 → 循环误判为最终答案终止会话,用户拿到半截话(实测连续 4 个会话 + # 在 tool_call_count 7~11 时这样夭折,报告/总结从未产出)。 + # 门禁条件:已有工作推进(tool_call_count>0) 且回复短于 _CONTINUE_GATE_MIN_CHARS + #(真结论/报告远长于此)且催促未达上限 → 注入续跑指令继续循环; + # 达上限后尊重模型终止(防死循环)。 + _CONTINUE_GATE_MIN_CHARS = 400 + _CONTINUE_GATE_MAX = 2 + def __init__( self, config, # AgentConfig (from pipeline_core) @@ -175,6 +184,7 @@ class AgentExecutor: self._started_at = time.time() self._turn_count = 0 self._tool_call_count = 0 + self._continue_nudges = 0 # Step 0: 初始化(解析 pipeline_id / skill_loader 等,slash 命令也需要) await self._init_components() @@ -422,6 +432,30 @@ class AgentExecutor: # (历史版本会在 tool_call_count==0 时用硬编码关键词强制路由, # 那会剥夺 LLM 诚实降级/能力自省的能力,把 agent 变成路由器。) final_reply = act.get("message", "") + + # 续跑门禁(2026-09-11):已有工作推进但回复是短意图句 + #("我再拉一次数据…")= 模型提前结束 turn 而非真结论。 + # 注入续跑指令继续循环,不让会话夭折在半截话上。 + if (self._tool_call_count > 0 + and len(final_reply) < self._CONTINUE_GATE_MIN_CHARS + and self._continue_nudges < self._CONTINUE_GATE_MAX): + self._continue_nudges += 1 + logger.warning( + f"run: 续跑门禁 turn={turn+1} 短回复({len(final_reply)}字)" + f" tool_calls={self._tool_call_count} 催促{self._continue_nudges}次") + yield json.dumps({ + "type": "progress", + "message": "检测到未完成的中途回复,继续推进任务…\n", + }, ensure_ascii=False) + "\n" + self._msgs.append({"role": "assistant", "content": final_reply}) + self._msgs.append({"role": "user", "content": ( + "【续跑指令】你刚才的回复是中途进度句,不是最终答案," + "任务尚未完成(用户要求的产出还没交付)。" + "请继续执行:要么调用工具推进下一步," + "要么直接输出完整的最终结论/报告(含全部要求的分析项)。" + "不要只说下一步打算做什么。")}) + continue + yield json.dumps({ "type": "reply", "message": final_reply, }, ensure_ascii=False) + "\n"