用 tool_choice 强制只调一个工具:在一把聚合 key 上哪些后端真的照做、哪些只是建议
tool_choice 在聚合 API 上并非处处等价:Claude 与 GPT 系通常能可靠遵循强制单工具约束,部分开源模型后端只把约束当建议。本文解释差异来源、给出逐后端实测方法与提示词兜底方案,并说明如何就在同一把 key 下做选择。
为什么同一个 tool_choice 在不同后端表现不同
在聚合站上,你写的是同一份 OpenAI 兼容请求体,但请求最终落在不同厂商的推理栈上。tool_choice 的语义在 OpenAI 协议里有明确定义:"auto"、"none"、"required"(或 "any")、以及指定具体函数 {"type":"function","function":{"name":"..."}}。不同厂商对这一字段的实现深度并不一致:
- 有的后端在解码阶段就做约束,强制输出符合指定工具的 JSON schema;
- 有的只在提示层面注入一条指令,模型“通常”会照做,但可以被上下文带偏;
- 有的只对
required生效,对“指定具体某个函数”退化为auto。
所以在聚合场景下,把 tool_choice 当作硬保证是危险的。更准确的看法是:它是一个意图声明,不同后端的兑现程度不同。
分档看待:硬约束、软约束、仅提示
不必记住逐模型结论,记住分档更实用:
- 硬约束档:指定具体函数后,后端保证只返回该工具的调用,schema 不合规会被拒绝或修正。Claude 与 GPT 系一般落在这一档,适合对正确性要求高的流程。
- 软约束档:模型倾向调用指定工具,但在多轮上下文、工具数量多、工具描述重叠时可能改调别的工具,或干脆输出纯文本。部分 DeepSeek、Qwen、GLM 部署在这一档。
- 仅提示档:
tool_choice被忽略或部分忽略,模型按自身偏好决定是否调用。Kimi 等部分后端在特定版本下更接近这一档。
这些分档会随厂商版本更新变化,不要把它当固定事实,而要当作“先验证、再依赖”的假设。
动手验证:一份最小对照实验
同一个请求体发给不同模型,比较返回:
- 构造两个语义相近的工具(例如
searchdocs与searchweb),问题故意两边都能答; - 分别发送
toolchoice: "auto"、"required"、指定searchdocs; - 记录三点:是否返回
tool_calls、调用的是哪个工具、arguments是否严格符合 schema; - 每个配置重复若干次(温度非零时结果会抖动,单次结果不足以下结论);
- 再加一轮“干扰上下文”(历史消息里大量提及另一个工具),看约束是否被破坏。
判据很直接:如果指定具体函数后仍出现别的工具名或纯文本回答,这个后端对你就是软约束。
兜底方案:不依赖后端守规矩
当后端只把 tool_choice 当建议时,用工程手段补强:
- 收敛工具集:一次请求只挂当前任务需要的工具,别把十几个工具全塞进去。工具越少,误选概率越低;
- 强化系统提示:明确写出“本轮必须调用
xxx,不得调用其他工具,不得直接回答”; - 服务端校验:拿到响应后检查
tool_calls[0].function.name与 schema,不符就重试一次或降级到硬约束后端; - 用
required兜底:当“必须调工具但调哪个可放宽”时,required比指定函数更常被后端正确实现; - 结构化输出辅助:若任务本质是抽取而非工具调用,考虑用 JSON 模式替代 tool 调用,减少一层不确定性;
- 路由分流:关键路径固定走硬约束后端,探索性、低风险任务走成本更低的后端。
在一把聚合 key 下做选择
聚合站的价值正在于:上面这些实验和分流,都只需要一个 API key、一套 SDK 配置,改 model 字段即可切换后端,不必为每家单独开户、单独配额度。你可以写一个薄封装:按任务类型选模型,按上面判据校验工具调用,失败则回退到硬约束档。
计费上也要把这个因素算进去:使用方按官方价 ×1.3 扣费,贡献 key 按官方价 ×1.1(优质 ×1.2)返 USDC。也就是说,重试和降级都产生实际成本,把“哪些后端真的守约束”测清楚,本身就是在省钱。充值走 Base 上的 USDC,无 KYC,适合需要快速做多后端对照的团队。
小结
tool_choice 是意图而不是契约。把后端按硬约束、软约束、仅提示三档看待,先用最小对照实验验证,再用服务端校验和路由分流兜底。这样即使某个后端只把约束当建议,你的流程也不会因此崩掉。