fix(agent): 文本兜底解析裸多行JSON tool_call——LLM输出无围栏连续动作对象时误当交付件正文deliver

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不回归)
This commit is contained in:
ymq 2026-09-15 17:56:41 +08:00
parent 715be0b387
commit 66387c4210

View File

@ -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}