Kimi 的 tool_choice 怎么控制模型调不调工具
数据截至 2026-08,价格与限额以各官网为准。本文只讲计费与接入机制,不列具体价格数字。
一句话结论:在 Kimi API 里声明了 tools 之后,模型默认自己判断这一轮要不要调用工具,tool_choice 就是把这个判断权从模型手里拿回来的开关。它有四种写法——auto(不传就是它)、required(本轮至少调一个工具)、none(禁止产生任何 tool_calls)、以及传一个函数对象强制调用指定的那一个。它是请求级参数,每次请求独立生效,官方明确说明改动它不会破坏前缀缓存,所以完全可以按请求粒度来回切。真正会绊住人的不是语法,而是两条限制:一是并非所有 Kimi 模型都支持 required,二是「强制调用指定函数」这个写法与思考开启不兼容,会直接返回 400——而按官方说明,kimi-k3 与 kimi-k2.7-code 的思考本来就关不掉。
先搞清楚 tool_choice 管的是哪一段
工具调用这件事,在 Kimi API 里被拆成了两个动作:你通过 tools 参数把可用工具的 JSON Schema 交给模型,模型决定「这一轮调不调、调哪个、参数填什么」,然后由你的应用去真正执行。官方文档在工具调用那一章说得很直白:Kimi 大模型不会替你执行工具,它只负责生成调用参数,执行是你的应用自己的事。
tool_choice 管的只是中间那半句——调不调。它管不到工具执行,也管不到模型把参数填成什么样。参数质量靠的是 function.description 和 parameters 里每个字段的 description 写得够不够清楚,官方在示例注释里反复强调这一点:函数介绍里要写清具体作用以及在什么场合需要使用,参数的 description 是为了让模型更好地生成参数。所以如果你的问题是「模型老是选错工具」,先去改 description,tool_choice 治不了这个病;如果你的问题是「模型该查数据库的时候凭记忆瞎答」,那才轮到 tool_choice 上场。
顺带记一个容易被忽略的命名约束:官方示例的注释里写明,函数名称请使用英文大小写字母、数字加上减号和下划线。名字里带中文或空格,是在给自己找麻烦。
三个枚举值,分别对应三种截然不同的工作流
官方文档给出的 tool_choice 可用枚举是 auto、none、required 三个,另外还可以传函数对象(下一节单独说)。
auto 是默认值,不传 tool_choice 时就等同于传了 auto。模型根据上下文自行决定是否调用工具,官方的定位是「适合常规对话」。绝大多数场景不用动它。
required 让模型在本轮必须至少调用一个工具。 官方给的适用场景写得很具体:当工作流必须走工具链路时使用,例如强制检索、强制查询数据库,不允许模型凭记忆直接作答。这里有一个使用前提,文档专门提醒过——用 required 时请确保请求中声明了可调用的工具。逻辑上也说得通:你要求它必须调一个,却一个都没给它,那就是自相矛盾的请求。
none 是反方向的开关:禁止工具调用。 官方描述的场景是「请求只需要纯文本回复、不希望模型误触发工具」。设成 none 之后,模型会直接输出文本,不产生任何 tool_calls;文档在同一句里还写明,这样做同时降低延迟与 token 消耗。
这第三种取值的价值容易被低估。很多 Agent 的做法是在整个会话里一直挂着一套工具定义,哪怕某一轮明显只是在跟用户确认「你是要 A 还是 B」。这种轮次把 tool_choice 设成 none,既避免了模型手滑触发一次没必要的工具调用,也省掉了一轮往返。注意 none 只是禁止模型调用,tools 里的定义仍然在请求里、仍然要占 token——官方在注意事项里明确写了,tools 参数中的内容也会被计算在总 Tokens 中,请确保 tools 与 messages 的 Tokens 总数不超过模型的上下文窗口大小。想真正省掉这部分开销,得从工具定义本身下手,那是动态加载工具要解决的问题。
强制调用指定的那一个工具:传函数对象
除了三个枚举值,tool_choice 还接受一个函数对象,形如把 type 写成 function、再在 function 里给出 name,指向你想强制调用的那个工具名。这是比 required 更硬的约束:required 只保证「至少调一个」,模型仍然可以在你给的一堆工具里自己挑;传函数对象则是把「挑哪个」也定死了。
这个写法有一条明确的兼容性限制,也是本篇最值得记住的一句话——官方文档写明:指定函数调用当前与思考开启不兼容,思考开启时传入会返回 400 错误,错误信息是 tool_choice 'specified' is incompatible with thinking enabled。
看到这条报错不用怀疑自己的 JSON 写错了,它就是一条能力边界。而这条边界具体卡死在哪些模型上,官方文档在另外几页里写得很清楚,值得一起读。
模型参数参考页里对 kimi-k2.7-code 的描述是:它始终开启思考、不可禁用,传入 {"type": "disabled"} 会报错,且 Preserved Thinking 始终开启,因此调用时无需传入 thinking 参数。K3 那边的说法同样是「始终启用思考,并开启 Preserved Thinking」,K3 快速开始页与调试排查页的常见问答里还各问了一遍「Kimi K3 的思维链怎么关」,答复都是目前关不了、K3 始终开启思考模式,只能把 reasoning_effort 设成 low 来降低推理强度。
把这两页和上面那条 400 放在一起,结论其实不需要你自己发请求去试:在 kimi-k3 与 kimi-k2.7-code 上,思考本身就关不掉,那么「传函数对象强制指定某一个工具」这条路在这两个模型上是走不通的。真要用这个写法,得落在能显式关掉思考的那一侧——kimi-k2.6 的 thinking 参数接受 {"type": "enabled"}(默认)、{"type": "disabled"} 以及 {"type": "enabled", "keep": "all"} 三种配置,把它设为 disabled 之后才谈得上强制指定函数。需要说明的是,模型参数参考页的 tool_choice 那一行只列了各模型对 auto / none / required 三个枚举值的支持范围,没有单独交代函数对象写法的逐模型支持情况,所以这里能确定的是「思考关不掉就一定用不了」,反过来并不构成官方对某个模型的支持承诺。各模型思考行为的更多差异见思考模型的调用差异,模型迭代较快,以官方文档当前版本为准。
不是每个 Kimi 模型都支持 required
这条限制不会静默降级:模型不支持时,请求直接报错。
官方的模型参数参考页把各模型对 tool_choice 的支持差异单独列了一节,结论是:kimi-k3 支持 auto / none / required 三档;kimi-k2.6 与 kimi-k2.7-code 不支持 required,传入会报错。同一页的常见问答里又把这个问题单独问了一遍并给出同样的答复:这两个模型不支持 required,该档位仅 kimi-k3 支持。同一件事在一页里写两处,抄参数的时候别只看表格。
对应到实践上有两层含义。第一,如果你的代码是从别的平台迁过来的,或者是在一套多模型网关里跑,那么「换个 model 字段就能跑」这个假设在 tool_choice: required 这里不成立,切模型时要连带检查这个参数。第二,如果你把 required 写进了通用的请求构造函数里,一旦有人把模型切到不支持的那一档,报错会出现在一个跟工具调用八竿子打不着的调用链上,排查起来很费劲。稳妥的做法是把「当前模型是否支持 required」做成配置,而不是硬编码。
Kimi K3 的定价说明页里把「工具调用约束(tool_choice)」和动态加载工具一起列为 K3 新增的 API 能力,这也从侧面解释了为什么老一些的模型上会缺这一档。以官方文档为准,各模型的支持范围可能随版本变化。
required 只解决「调不调」,不解决「调完之后」
这一节要单独拎出来说:required 保证的只是模型这一轮会产出 tool_calls,从拿到 tool_calls 到把执行结果喂回去,中间还有一整段得你自己写对的逻辑。
模型决定调用工具时,返回里的 finish_reason 是 tool_calls,官方说可以直接用它判断当前回复是不是一次工具调用。此时 message.content 通常为空,因为模型还在执行 tool_calls、尚未生成面向用户的回复。但文档的注意事项里补了一句很实用的话:finish_reason=tool_calls 时 message.content 偶尔不为空,那通常是模型在解释需要调用哪些工具、为什么调用。所以你的解析代码别写成「content 为空就是工具调用」,会漏判。
tool_calls 是个列表,模型可以一次性选择多个工具进行调用,可以是多个不同的工具,也可以是相同工具用不同参数调用。每个元素带一个模型生成的唯一 id,用 function.name 表明工具函数名称,参数放在 function.arguments 里,是被序列化过的合法 JSON Object,拿到之后需要反序列化才能用;type 目前固定是 function。
接下来这一段,官方文档专门把会报错的情况逐条列了出来,值得对着自己的消息拼装代码看一遍。官方要求:当模型生成了 tool_calls 时,请确保每一个 tool_call 都有对应的 role=tool 的 message,并且这条 message 设置了正确的 tool_call_id。文档明确列了两种会出错的情况——role=tool 的消息数量与 tool_calls 数量不一致会导致错误,tool_call_id 与 tool_calls 里的 id 对不上也会导致错误。
如果你遇到 tool_call_id not found,官方给的排查方向只有一个:很可能是你没把 API 返回的那条 role=assistant 消息加回 messages 列表。正确的消息序列里,role=tool 的消息前面必须有一条完整包含 tool_calls 字段及其值的 assistant 消息。官方推荐的做法是每次收到返回值后直接执行一次 append,把返回的 choice.message 原封不动地放进消息列表,别自己重新拼一个精简版——文档特意用了「原封不动」这个说法。
流式输出下还有几条额外规则:建议改用 delta.tool_calls 字段是否存在来判断是不是工具调用,因为 finish_reason 要到最后一个数据块才出现;流式过程中会先输出 delta.content、再输出 delta.tool_calls,所以必须等 content 输出完才能识别工具调用;首个数据块会给出 tool_call.id 与 function.name,后续数据块只输出 function.arguments,需要你自己拼接;一次返回多个工具调用时,靠额外的 index 字段区分,别拼串了。
另外提一句历史包袱:官方说明 function_call 是 tool_calls 的子集,由于 OpenAI 已将 function_call、functions 等参数标记为已废弃,Kimi 的 API 也不再支持 function_call,请用 tool_calls 代替。老教程里的写法直接照抄会踩空。
工具很多的时候:首轮 required,之后恢复 auto
tool_choice 单独看只是个开关,官方给的组合用法才是它真正的价值所在。
在 K3 的工具调用最佳实践里,官方描述的场景是:当 Agent 可用的工具达到几十上百个时,不要把所有工具定义一次性放进请求——它们会占掉大量上下文,还会让模型更容易选错工具。给出的编排方式是,会话开始时顶层 tools 里只声明一个由你后端实现的 search_tools 工具(外加少量每轮都可能用到的核心工具),让模型先检索候选工具,再按需注入完整定义。
tool_choice 在这套流程里承担的角色很具体:模型可以选择不调用任何工具、直接凭记忆作答,为了确保它先检索再回答,首轮请求设置 tool_choice: "required" 强制它调用 search_tools,检索完成后,后续请求把 tool_choice 恢复为 auto。整套流程官方总结成五步:顶层只放搜索工具和少量核心工具、首轮用 required 强制检索、按检索结果用 system 消息动态插入工具定义、模型在后续生成里直接调用已加载的工具、会话开始前确定推理强度配置。
这个「一开始收紧、之后放开」的节奏,比全程 required 或者全程 auto 都更合理,值得直接抄进自己的 Agent 编排里。
它跟前缀缓存的关系:可以放心切
按请求切换参数,最让人担心的就是把缓存切没了。这件事官方在两个地方给了同一个答复。
工具调用约束页的注意事项里写:是否设置 tool_choice 不会破坏前缀缓存,可以放心按请求粒度调整该参数。K3 最佳实践页里又说了一遍:修改 tool_choice 不会破坏前缀缓存,可以按请求粒度调整。
对比一下别的参数,差异就出来了。模型参数参考页在讲推理强度时写的是:切换档位会破坏前缀缓存命中,建议在会话开始前确定 effort 档位,避免中途切换;K3 最佳实践页「按任务复杂度确定推理强度」那一节则同时交代了动态工具声明的影响——在 messages 末尾追加动态工具声明不会影响已有前缀的缓存,删除或修改之前的工具声明可能影响变更位置之后的缓存命中,而修改 tool_choice 不会破坏前缀缓存。三个参数摆在一起,tool_choice 是唯一一个可以随手改、不必担心缓存的。
原因也不难理解:前缀缓存匹配的是消息前缀本身,而 tool_choice 是个请求级的控制参数,不进入被缓存的前缀。所以在这套编排里,该收紧就收紧、该放开就放开,缓存这一头不用替它操心。官方文档还提到命中前缀缓存有一个最小 prompt token 门槛,低于门槛的请求不会被缓存而是被丢弃——具体门槛数值以官方上下文缓存文档为准,这属于缓存计费机制那一层要算的账,跟 tool_choice 无关。
最后:三个必须自己兜住的边界
第一,别在不支持的模型上传 required。这不是降级,是报错,而且报错点离问题源很远。切模型时把这个参数一起过一遍。
第二,别把「强制调用指定函数」和思考开启混在一起用。官方明确写了不兼容,会返回 400 并带上 incompatible with thinking enabled 的提示。而按官方对模型的说明,kimi-k3 与 kimi-k2.7-code 的思考是始终开启、关不掉的,所以这个写法只能落在支持 thinking: {"type": "disabled"} 的模型上。选型阶段就该把这件事定下来,而不是等 400 打脸。
第三,别以为设了 required 就万事大吉。required 保证的只是模型这一轮会产出 tool_calls,后面每个 tool_call 是否都补上了对应的 role=tool 消息、tool_call_id 有没有对上、assistant 消息有没有原样回传,全都要你自己保证。这几件事出错的表现是请求直接失败,而不是回答质量下降,所以第一次接工具调用的时候,把这段消息拼装逻辑单独写个测试比什么都值。
准备动手的话,可以先把Kimi API 的接入步骤跑通,再回来按本文的顺序加工具调用约束——先默认 auto 跑起来,确认工具执行循环没问题,再考虑要不要用 required 把某几轮锁死。