先看现象。工具定义加上 strict:true 之后,模型经常只回一句文字,tool_calls 是空的,同一段 prompt 把 strict 去掉就稳定触发调用,参数也填得对。

原因不止一个,得分开看。schema 里 required 与 properties 不完全对应,工具被标成不可用。也可能是参数描述过长或含特殊字符,模型判断自己无法满足约束。

不容易想到的是,tool_choice 留在 auto,模型有权选择不调用工具。

还有一种情况,strict 下默认值被移除,模型缺少填参的参照。

发一次最小请求看工具是否被服务端接受

curl -s https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"北京天气"}],"tools":[{"type":"function","function":{"name":"get_weather","strict":true,"parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false}}}]}' | jq '.choices[0].message'

再往下把 tool_choice 强制指向这个函数

curl -s https://api.openai.com/v1/chat/completions -H "Authorization: Bearer $OPENAI_API_KEY" -H "Content-Type: application/json" -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"北京天气"}],"tool_choice":{"type":"function","function":{"name":"get_weather"}},"tools":[{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"],"additionalProperties":false}}}]}' | jq '.choices[0].message.tool_calls'

接着核对 required 与 properties 是否一一对应

python -c "import json;p=json.load(open('tool.json'))['function']['parameters'];print(sorted(p['required'])==sorted(p['properties']))"

剩下的就是把参数描述压短后重试

python -c "import json;d=json.load(open('tool.json'));d['function']['parameters']['properties']['city']['description']='城市名';json.dump(d,open('tool2.json','w'),ensure_ascii=False)"

恢复的判定其实很直接。强制 tool_choice 时每次都能拿到 tool_calls,arguments 是合法 JSON 且包含必填字段,改回 auto 后调用率也回到八成以上。

strict 是把双刃剑,约束越紧模型越倾向不调用,先用强制指定验证 schema 本身没问题,再放开选择权看调用率。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189