照着 OpenAI 的写法把工具返回的 JSON 当成 assistant 消息发回去,claude-3-5-sonnet 返回 400,原文 unexpected tool_use_id found in tool_result blocks,SDK 版本 anthropic 0.40。
一开始怀疑是「同一轮多个工具调用要一次性全部回填,不能只回一个」,查了一圈,方向不对。
把日志和配置对完,能解释这个现象的基本就这几处。Anthropic 的工具结果要用 user 角色携带 tool_result 块。少数情况下是tool_use_id 必须与上一轮 tool_use 块逐一对应,写错或漏一个都报错。
不容易想到的是,结果块 is_error 没置位,失败被当成成功文本喂回去。
先把上一轮 tool_use 的 id 全列出来核对
python -c "import json;d=json.load(open('resp.json'));print([b['id'] for b in d['content'] if b['type']=='tool_use'])"
随后按 id 组织 user 角色的 tool_result 数组重发
curl -sS https://api.anthropic.com/v1/messages -H "x-api-key: $ANTHROPIC_API_KEY" -H "anthropic-version: 2023-06-01" -d @tool_followup.json
接着执行失败时把 is_error 置真并写清原因
python -c "import json;print(json.dumps({'type':'tool_result','tool_use_id':'toolu_01','is_error':True,'content':'timeout'}))"
剩下的就是检查消息序列里没有 assistant 携带 tool_result
grep -c 'tool_result' history.json
别只看报错消失,验证要多做一步。续轮请求返回 200,stop_reason 回到 end_turn,历史里每个 tool_use_id 都能找到唯一对应的 tool_result 块。
这里还有个坑。把工具结果当用户发言是语义上的正确做法,角色写反会让模型把结果当成自己说过的话,行为会变得很奇怪。如果同一现象隔几天又出现,说明根因没找到,别当偶发处理。
