这个坑值得记一笔。run 进入 requires_action 之后,客户端把工具结果写成 [{tool_call_id:…,output:{…}}] 提交,接口返回 400,原文 Invalid tool_outputs,提示 output 必须是字符串。
刚开始盯着「提交走的是 update run,而正确端点是 submit_tool_outputs」,折腾了半个多小时,排掉了。
把日志和配置对完,能解释这个现象的基本就这几处。output 字段只接受字符串,结构化结果要自己序列化后再放进去;tool_call_id 取的是函数名而不是调用返回的 call id;一次只提交了部分 tool_calls,服务端认为结果没交全。
先把 required_action 的完整结构打出来
curl -s https://api.openai.com/v1/threads/thr_abc/runs/run_abc -H "Authorization: Bearer $OPENAI_API_KEY" -H "OpenAI-Beta: assistants=v2" | jq '.required_action.submit_tool_outputs'
然后把对象结果序列化成字符串再提交
python -c "import json;print(json.dumps({'temp':21},ensure_ascii=False))"
再往下用 submit_tool_outputs 端点提交全部调用结果
curl https://api.openai.com/v1/threads/thr_abc/runs/run_abc/submit_tool_outputs -H "Authorization: Bearer $OPENAI_API_KEY" -H "OpenAI-Beta: assistants=v2" -H "Content-Type: application/json" -d '{"tool_outputs":[{"tool_call_id":"call_1","output":"{\"temp\":21}"}]}'
提交后继续轮询确认状态推进
curl -s https://api.openai.com/v1/threads/thr_abc/runs/run_abc -H "Authorization: Bearer $OPENAI_API_KEY" -H "OpenAI-Beta: assistants=v2" | jq -r .status
别只看报错消失,验证要多做一步。提交返回 200,run 状态从 requires_action 变成 queued 再到 completed,最终消息里能用上工具返回的数值。
工具结果是字符串协议,不是 JSON 协议,把对象直接塞进去时服务端只做类型校验,报错文案不会告诉你该序列化。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。