严重度:中–高(条件触发:文本解析回退路径;触发后向 LLM API 发 400)
标签建议:bug
现象
doctoragent/model/agent.py 在 _dispatch_tool_calls 里生成 assistant 消息的 tool_calls[].id 与后续 tool 消息的 tool_call_id 用了不同的计数器时机:
agent.py:1469(入口,早于任何执行):"id": tc.get("id") or f"call_{self._tool_calls_used}_{idx}" —— 取的是自增前的 self._tool_calls_used。
agent.py:1512(_run_group 内,每个工具执行后):self._tool_calls_used += 1。
agent.py:1537(生成 tool 消息,执行之后):call_id = tc.get("id") or f"call_{self._tool_calls_used}_{idx}" —— 取的是自增后的 self._tool_calls_used。
当走文本解析回退路径(_parse_tool_calls 产出的 tc 没有 id 字段)时,assistant 侧 id 用自增前的值、tool 侧 id 用自增后的值,两者不一致。下一轮 _call_llm_with_tools 把这组消息发给 OpenAI / 兼容接口时,tool_call_id 与 assistant 的 tool_calls[].id 对不上 → API 返回 400(tool_call_id does not match any tool_calls)。
证据
复现(来源 /tmp/repro_tool_call_id.py):2 个工具调用场景下
assistant tool_calls ids : ['call_0_0', 'call_0_1']
tool messages ids : ['call_2_0', 'call_2_1']
MATCH: False
(自增前 N=0 生成 assistant id;执行 2 次后 N=2 生成 tool id,差了调用数。)
影响
- 仅在文本解析回退路径(
tc 无 id)触发;结构化 tool_calls(LLM 正常返回带 id)不受影响。
- 一旦触发,多轮工具调用直接 400 失败,agent 无法继续基于工具结果推理。
建议修复
tool 消息的 tool_call_id 必须与 assistant 侧使用的 id 同源:要么在入口处为无 id 的 tc 预先分配并缓存 id(用同一变量生成两侧),要么在 1537 行用与 1469 行完全相同的取值逻辑(同一 self._tool_calls_used 快照)。确保同一次 dispatch 里 assistant 与 tool 消息的 id 逐一对齐。
关联
与 #2(同文件 _estimate_tokens 已修)相邻;属 agent 工具编排的契约正确性。
现象
doctoragent/model/agent.py在_dispatch_tool_calls里生成 assistant 消息的tool_calls[].id与后续 tool 消息的tool_call_id用了不同的计数器时机:agent.py:1469(入口,早于任何执行):"id": tc.get("id") or f"call_{self._tool_calls_used}_{idx}"—— 取的是自增前的self._tool_calls_used。agent.py:1512(_run_group内,每个工具执行后):self._tool_calls_used += 1。agent.py:1537(生成 tool 消息,执行之后):call_id = tc.get("id") or f"call_{self._tool_calls_used}_{idx}"—— 取的是自增后的self._tool_calls_used。当走文本解析回退路径(
_parse_tool_calls产出的tc没有id字段)时,assistant 侧 id 用自增前的值、tool 侧 id 用自增后的值,两者不一致。下一轮_call_llm_with_tools把这组消息发给 OpenAI / 兼容接口时,tool_call_id与 assistant 的tool_calls[].id对不上 → API 返回 400(tool_call_iddoes not match anytool_calls)。证据
复现(来源
/tmp/repro_tool_call_id.py):2 个工具调用场景下(自增前 N=0 生成 assistant id;执行 2 次后 N=2 生成 tool id,差了调用数。)
影响
tc无id)触发;结构化tool_calls(LLM 正常返回带 id)不受影响。建议修复
tool 消息的
tool_call_id必须与 assistant 侧使用的 id 同源:要么在入口处为无 id 的tc预先分配并缓存 id(用同一变量生成两侧),要么在 1537 行用与 1469 行完全相同的取值逻辑(同一self._tool_calls_used快照)。确保同一次 dispatch 里 assistant 与 tool 消息的 id 逐一对齐。关联
与 #2(同文件
_estimate_tokens已修)相邻;属 agent 工具编排的契约正确性。