这类问题我又碰上了一次。用 boto3 调 Bedrock 的 Converse 接口做工具调用,把执行结果按 OpenAI 的格式回填后返回 400,原文是 messages.2.content.0.toolResult.toolUseId: field required。

第一反应是「一个 toolUse 对应一个 toolResult,不能合并」,验证下来不成立。

排查方向其实有限,先把可能性排一排。Converse 的工具结果必须放在 toolResult 块里;toolUseId 要用模型返回的原值,不能自己生成;content 结构里混入了纯文本块导致校验失败。

动手时先打印模型返回的 toolUse 块结构

python -c "import json;print(json.load(open('resp.json'))['output']['message']['content'])"

随后按原 id 组装 toolResult 再请求

python -c "import json;print(json.dumps({'role':'user','content':[{'toolResult':{'toolUseId':'tu_1','content':[{'text':'ok'}]}}]}))"

然后用 boto3 发一次最小会话验证结构

python converse_demo.py --model anthropic.claude-3-5-sonnet-20241022-v2:0 --tool weather

校验每个 toolUse 都有对应结果

python check_converse.py --messages msgs.json

中途换过一次思路,把范围缩到最小的那一层,反而快了。

中间停过一次,把已经改的东西回滚干净,再重新来一遍。

怎么算真的好了,看的是同一批现象。第二轮请求返回 200,模型基于工具结果给出最终回答,id 与请求中的 toolUse 一一对应。

Bedrock 的消息结构和其他家差异很大,照抄 OpenAI 的 tool 消息一定失败,回填前先对齐官方结构。监控比日志更早给出信号,事后回看经常能提前半小时发现异常。

作者 9823189