上一话跑通了 Function Calling 协议,但留了个更根本的问题没收尾:
模型凭什么知道该从 tools 菜单里挑 get_weather、又凭什么把“北京”填进 city?
不是提示词写得好。协议只是给了模型一个“填在哪里”的位置,真正让模型知道“填什么”的,是它的训练。这一话就追这件事:模型是怎么学会“提出一次工具调用”的。
先给结论,后面再拆:工具调用不是模型的内置能力,而是训练出来的“输出倾向”。训练改变的是它生成某些 token 序列的概率,不是给它装了一个真的会退款的函数。
一、模型学的,仍然是下一个 token
模型里没有一个叫“退款”的函数。它有的,只是“给定上文,预测下一个词”的能力。所谓“会调工具”,是它学会了:当上下文里出现“退款意图 + 工具定义”时,下一个该输出的,是 refund_order 和 order_id 这几个 token。
训练样本把它要学的样子,直接摆出来:
上下文:订单 O-100 已支付,用户要求退款;可用工具有 refund_order(order_id)
目标:refund_order({"order_id":"O-100"})
反复见这样的样本,模型参数里就长出一套倾向:“退款 + 已支付”这类上下文,会抬高 refund_order 的概率;订单号 O-100,会抬高被填进 order_id 的概率。
注意,训练集还得有反例,否则模型会养成坏习惯:
- 用户只问配送时间 → 直接回答,不调工具;
- 订单状态不明 → 先追问,不调工具。
少了这些负样本,模型就会变成“看见什么都想调工具”。
关键洞察:模型只是提出了候选调用。它没有因此获得订单事实、权限,也没有执行权。 会“说”要干什么,和“真的干了什么”,是两码事。
二、这条“倾向”,是靠什么训练出来的?
问题来了:这些“上下文 → 正确调用”的样本,是怎么来的?人工一条条标注太贵,学界给了几条公开的路线。
Toolformer 解决的是“数据怎么规模化造出来”。它的做法很有意思——让模型自己当标注员:
- 在一段普通文本里,让模型猜哪些位置可能需要调 API,采样出一堆候选调用;
- 真的去执行这些调用,拿到结果;
- 关键一步:只保留那些“拿到结果后,模型预测后续文字更准了”的调用——具体说,就是比较“有结果”和“没结果”两种情况下,模型对后面 token 的预测损失,损失降得够多才留下;
- 用过滤后的样本去微调模型。
一句话:模型自己提出调用、自己执行、再自己判断“这次调用到底有没有用”,有用的才拿来当训练样本。 全程不需要人工标注“这里该不该调工具”。Toolformer
Gorilla 解决的是另一个问题:API 会变。哪怕模型学过 get_weather(city),文档也可能改成 get_weather(location, unit)。Gorilla 的做法是微调 + 文档检索结合:训练给模型调用能力,检索把“当前最新的接口契约”带进上下文,让它跟得上变化。Gorilla
CoT 常被顺带提起,但它其实是个对照,不是工具调用训练:它研究的是“在提示里给推理示例能不能改变输出”。它能改变当前这一轮的输出,但不改模型参数——和 Toolformer/Gorilla 那种真正动参数的训练,是两回事。CoT
三、一件必须分清的事:哪些证据能信,哪些不能
讲到这里,得划一条诚实的边界。关于“模型为什么能调工具”,能拿到的证据分三类,它们不能互相冒充:
| 证据类型 | 能说明什么 | 不能说明什么 |
|---|---|---|
| 论文公开的方法(Toolformer/Gorilla) | “存在这些训练工具调用的公开方法” | 不代表某个商业模型就用了它 |
| 厂商披露(OpenAI 公告) | 0613 模型经过微调、Structured Outputs 靠训练+约束解码 | 只限定到那几家、那段时间,不能外推 |
| 黑盒 API 能观察到的 | 输入什么、输出什么(比如 description 改了就选错工具) | 看不到训练语料、训练阶段、奖励方法 |
最关键的一条是第三类:你从 API 拿到的结果再稳定,也反推不出厂商到底是怎么训练的。 训练语料长什么样、分几个阶段、有没有用 RLHF——这些是黑盒,除非厂商自己说,否则只能标“未知”。
所以别犯那个错:看到 tool_calls 稳定输出,就说“这个模型用了 Toolformer”。稳定输出能证明“它训练得很好”,证明不了“它是怎么训练的”。
四、Prompt-only 和 Native tools,差的不只是“格式”
上一话讲了 Native tools 的协议形态,这里补一层它和训练/推理的关系。
Prompt-only:在 system prompt 里约定“需要退款时输出 JSON”。模型把 JSON 塞进普通 content 里,Runtime 还得从正文里把命令抠出来——这里混着自然语言、Markdown、可能还有好几个 JSON。
Native tools:在 API 的 tools 字段传 schema,模型用独立的 tool_calls 字段返回。Runtime 直接拿到工具名、参数和调用 id,不用猜哪里是命令。
这个差异不是“格式好看点”,而是责任归属变了:Prompt-only 把“保证输出长得像 JSON”的责任甩给了提示词和你的解析器;Native tools 把这层责任接了过去,一部分靠训练(让模型更会按 schema 输出),一部分靠推理阶段的约束解码(生成时卡住,只允许符合 schema 的 token 出来)。
OpenAI 官方也明确说过:Function Calling 的可靠性,一部分来自模型的微调,一部分来自 Structured Outputs 的 constrained decoding。Structured Outputs
这里有个真实的坑能说明这层差异。我们 Java 版最初只写了“需要时输出 JSON”,没说清 name 和 arguments.order_id 的形状。结果 Native tools 模式仍能返回结构化调用,Prompt-only 模式却解析不出来。把提示词补成明确的 JSON 模板后,Java 的 8 个场景才全过。这不是“Java 不适合做 Agent”,而是Prompt-only 把输出协议的一部分责任,留在了你的提示词和解析器上——而 Native tools 帮你卸掉了一部分。
五、动手看一次:description 才是“选哪个”的开关
我们用一套虚构订单做实验,Python 和 Java 21 各跑一遍。Runtime 先注入已验证的订单状态,模型只做一次工具选择,程序只记录候选调用,绝不执行退款或取消。
四种场景,正确行为是这样:
| 场景 | 正确行为 |
|---|---|
| 已支付 O-100,要求退款 | refund_order(O-100) |
| 未支付 O-200,要求取消 | cancel_order(O-200) |
| 无订单号和状态 | 追问,不调用 |
| 无退款/取消意图 | 自然语言回答,不调用 |
基线下两种语言都过。然后做一个故障注入:只把 refund_order 和 cancel_order 的 description 对调,其余全不变——工具名、模型、用户请求、参数 schema 都一样。
结果很干净:已支付退款稳定变成 cancel_order(O-100),未支付取消稳定变成 refund_order(O-200),而订单号仍然是对的。恢复 description 后,回归全过。
这个实验说明两件事:
- 工具选择(选哪个)靠的是 description——模型是读描述来判断“这个工具是干嘛的”,描述写错了,它就选错。
- 参数提取(填什么)可以和工具选择分开看——订单号还是从用户消息和 Runtime 注入的上下文里来的,两个工具的参数 schema 没变,所以订单号没错。
但这能证明什么、不能证明什么,得说清楚:它证明了“description 影响路由”这个可观察行为,证明不了模型用了哪种训练法。description 写对了,也不代表参数永远填对——Runtime 照样得校验。
六、所以,能信到什么程度
绕了一圈,回到开头那个问题:模型为什么能生成工具调用?
因为它被训练成了这样——训练样本让它形成了“意图 + 工具定义 → 结构化调用”的输出倾向。Toolformer、Gorilla 这些公开方法,展示了这条倾向可以怎么规模化地训练出来;厂商还叠加了微调和约束解码,让输出更稳。
但“会提请求”不等于“可靠”。训练让模型更会“说”要干什么,不会让它“变聪明”——它照样可能选错工具、编造业务参数、请求一个无权执行的动作。tool_calls 永远是候选,不是命令。 谁来判断“能不能执行、怎么执行”,是下一篇的事。
参考资料:
- Wei et al., Chain-of-Thought Prompting Elicits Reasoning in Large Language Models
- Schick et al., Toolformer: Language Models Can Teach Themselves to Use Tools
- Patil et al., Gorilla: Large Language Model Connected with Massive APIs
- OpenAI, Function calling and other API updates
- OpenAI, Introducing Structured Outputs in the API

留言
欢迎分享你的想法。评论提交后会在审核通过后显示。