From 66387c42100ef8419a664307d0fbd8de9b07aa6e Mon Sep 17 00:00:00 2001 From: ymq Date: Tue, 15 Sep 2026 17:56:41 +0800 Subject: [PATCH] =?UTF-8?q?fix(agent):=20=E6=96=87=E6=9C=AC=E5=85=9C?= =?UTF-8?q?=E5=BA=95=E8=A7=A3=E6=9E=90=E8=A3=B8=E5=A4=9A=E8=A1=8CJSON=20to?= =?UTF-8?q?ol=5Fcall=E2=80=94=E2=80=94LLM=E8=BE=93=E5=87=BA=E6=97=A0?= =?UTF-8?q?=E5=9B=B4=E6=A0=8F=E8=BF=9E=E7=BB=AD=E5=8A=A8=E4=BD=9C=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E6=97=B6=E8=AF=AF=E5=BD=93=E4=BA=A4=E4=BB=98=E4=BB=B6?= =?UTF-8?q?=E6=AD=A3=E6=96=87deliver?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pbls M1a第四轮实测:角色LLM在长上下文里输出连续多行裸{"action":"tool_call",...}(无```围栏),整体json.loads失败+围栏扫描无命中→兜底把tool_call文本当result deliver→交付件content是JSON文本、files_json只剩快照,QC必退且退回理由失真(实际代码/git提交都真实落地了)。修:_parse_agent_action加raw_decode逐段扫描取第一个动作对象。本地单测6/6(多对象/单deliver/纯文本/围栏优先/坏前缀跳过/DSML不回归) --- pipeline_service/agent_loop.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pipeline_service/agent_loop.py b/pipeline_service/agent_loop.py index 69cc10e..fd8bc37 100644 --- a/pipeline_service/agent_loop.py +++ b/pipeline_service/agent_loop.py @@ -2032,6 +2032,28 @@ def _parse_agent_action(raw): if isinstance(d, dict) and 'tool' in d: return {"action": "tool_call", "tool": d.get("tool", ""), "params": d.get("params") or {}} + # 裸 JSON 动作对象扫描(2026-09-15 pbls M1a 实测):LLM 连续输出多个无围栏的 + # {"action":"tool_call",...} 对象(一行一个)——整体 json.loads 失败(多对象拼接)、 + # 围栏扫描无命中 → 兜底把 tool_call 文本当交付件正文 deliver → 交付件 content + # 就是 JSON 文本、files_json 只有快照文件,QC 必退且退回理由失真。 + # raw_decode 逐个扫描,取第一个可解析的动作对象。 + _dec = json.JSONDecoder() + _idx = 0 + while True: + _b = raw.find('{', _idx) + if _b < 0: + break + try: + _d, _end = _dec.raw_decode(raw[_b:]) + except (json.JSONDecodeError, ValueError): + _idx = _b + 1 + continue + if isinstance(_d, dict) and 'action' in _d: + return _d + if isinstance(_d, dict) and 'tool' in _d: + return {"action": "tool_call", "tool": _d.get("tool", ""), "params": _d.get("params") or {}} + _idx = _b + max(_end, 1) + return {"action": "deliver", "result": raw}