Commit Graph

1 Commits

Author SHA1 Message Date
caoqianming e1d5bf6788 feat(loop): 畸形 tool_call arguments 就地抢救(salvage)省一次非流式重试(bump 0.58.24)
失败聚集面板同形态跨 write/edit/run_python/shell 复发(err=Expecting value char 0):
char-0 是 provider 在 wire 上把别处正文字节错标成该 tool_call 的 arguments delta 粘的
垃圾前缀,尾部真 JSON 完好并跑到串尾(定层已证 provider-wire 脏、拼接层无辜)。

命中畸形后先试就地抢救,而非直接整轮丢弃 + 降级非流式重 roll:
- core/salvage.py::salvage_tool_arguments(raw, allowed_keys) 纯函数,左→右扫每个 '{'
  试 json.loads(raw[i:]),第一个 parse-to-end 成功 + 非空 dict + 顶层 key ⊆ 该工具
  schema 参数名的即采纳。双护栏:parse-to-end 挡尾部截断的半个 JSON,key 白名单挡前缀
  里自洽的旁支 JSON。工具无关——allowed_keys 每工具从 executor.schemas() 现取。
- AgentLoop._try_salvage_response 全有或全无:本轮所有畸形都能抠出才就地改写 .arguments
  当轮继续,任一抠不出则一个都不动、原样走既有 _log_malformed_args + 非流式重试(零回退)。
- 抢救成功记 usage_events kind=tool_salvaged(record_salvaged_tool_call,cost 0),与
  tool_malformed 成对;toolfail 精确匹配 tool_malformed/run_error,salvaged 不进失败面板。

纯函数单列 core/salvage.py(不 import litellm)以便 tests/test_salvage.py 无依赖本机可跑
(loop.py 顶层 import litellm 本机导入期卡死);14 用例含 write/edit/run_python/shell 四工具
真前缀 + 截断/多键/旁支/尾残渣/空{}/跨工具白名单隔离等边界。盲区:provider-wire 抖动本机
复现不出、/verify 端到端跑不了,靠单测 + 生产灰度观察 tool_salvaged/tool_malformed 比。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:20:38 +08:00