报错是这么冒出来的。Claude 3.7 用 tool_choice 指定 record 工具想强制结构化输出,返回 200 但 stop_reason 是 tool_use,content 里 input 是空对象,业务侧取字段直接 KeyError,改提示词也没用。

中间走过弯路,把「工具描述与字段描述互相矛盾」当成主因,改完现象没变。

原因不止一个,得分开看。schema 里没有 required,模型可以合法返回空对象。另一种情况是max_tokens 偏小,参数还没写完就被截断。

另外,thinking 预算占用了输出额度。

第一件事是看 stop_reason 与 usage 判断是否被截断

python -c "import json;d=json.load(open('resp.json'));print(d['stop_reason'],d['usage'])"

给 schema 补 required 与字段说明

python -c "import json;s=json.load(open('tool.json'));s['input_schema']['required']=['title','amount'];json.dump(s,open('tool2.json','w'))"

再往下把 max_tokens 提到 2048 再试

python -c "import json;d=json.load(open('req.json'));d['max_tokens']=2048;json.dump(d,open('req2.json','w'))"

空对象时走一次带示例的重试

python retry_with_example.py --case empty_input --out retry.json

这类改动我都写进变更记录,过两周再出问题时能对上号。

我习惯把每一步的执行时间和结果记在一边,后面复盘省事。

别只看报错消失,验证要多做一步。input 里稳定出现 title 与 amount,stop_reason 为 tool_use 且没有再出现空对象。

强制工具不等于强制填字段,required 和足够的输出预算缺一不可,两者都补上才稳。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189