0%

大模型 Function Calling:底层原理与训练机制

背景

“Function Calling 的原理是什么?”,很多人的理解是这样的:

“模型读懂了用户的意图,判断出需要调用工具,然后决定生成一段调用指令……”

听起来很合理?但实际上,这些词——读懂判断决定——都是危险的词

为什么危险?因为它们暗示模型有某种”理解”和”决策”能力,仿佛模型真的在”思考”。但大模型的本质从来没变过:Next Token Prediction(预测下一个词)

Function Calling 并没有引入什么神秘的”推理机制”,它只是让模型学会了在特定情况下切换输出格式——从自然语言切换成结构化的 JSON。

今天我们就来拆解 Function Calling 的底层原理,看看模型到底是怎么”学会”调用工具的。


一、核心原理:没有魔法,还是预测

先说结论:Function Calling 没有引入任何新的推理机制,底层仍然是 Next Token Prediction。

1.1 模型到底在做什么?

无论是否启用 Function Calling,模型的计算过程都是一样的:

1
输入 → Transformer 计算 → 在整个词表上做概率分布 → 采样下一个 Token

区别只在于:启用 Function Calling 后,模型的词表里多了几个特殊 Token,训练数据里多了”工具调用”的样本

1.2 一个形象的比喻

想象你训练了一只鹦鹉,它会模仿人类说话:

  • 普通模式:你教它说”你好”、”谢谢”、”再见”,它学会在合适场合说这些话
  • Function Calling 模式:你额外教它一套”暗号”,比如听到”几点了”就说 [CALL:get_time]

鹦鹉并不知道”几点了”是什么意思,也不知道 get_time 真的会获取时间。它只是学会了:听到 X,就说 Y

Function Calling 的本质也是这样:模型学会了在特定上下文下,输出特定格式的文本——只是这个文本恰好是 JSON 格式的工具调用指令。


二、两阶段训练:如何教会模型”调用工具”

模型是怎么学会 Function Calling 的?主要通过两个阶段的训练。

2.1 SFT 阶段(监督微调)

这个阶段,模型通过大量标注样本,学会三件事:

学习内容 训练样本示例
什么时候切换输出模式 用户问”今天天气怎么样?” → 模型输出 [tool_call] 开始标签
怎么写调用指令 [tool_call]{"name": "get_weather", "args": {"city": "北京"}}[tool_call_end]
怎么处理工具返回结果 工具返回 {"temp": 25, "condition": "晴"} → 模型输出”今天北京天气晴朗,气温25度”

训练数据的结构大致如下:

1
2
3
4
5
6
7
8
{
"messages": [
{"role": "user", "content": "今天北京天气怎么样?"},
{"role": "assistant", "content": "[tool_call]{\"name\": \"get_weather\", \"args\": {\"city\": \"北京\"}}[/tool_call]"},
{"role": "tool", "content": "{\"temp\": 25, \"condition\": \"晴\"}"},
{"role": "assistant", "content": "今天北京天气晴朗,气温25度。"}
]
}

模型通过大量这样的样本,学会了一个模式匹配

1
用户问题涉及"可调用的工具" → 输出 [tool_call] → JSON 格式的调用指令

2.2 RL 阶段(强化学习)

SFT 阶段学会了”基本操作”,但还有很多边界情况需要精调:

边界情况 RL 阶段的优化
该调工具时没调 增加奖励,强化正确行为
不该调工具时调了 减少奖励,抑制错误行为
参数填错 调整参数生成的准确性
调用错工具 优化工具选择的准确性

这个阶段的核心是:让模型学会”分寸感”——知道什么时候该调用,什么时候不该调用。


三、特殊 Token 的作用:给外部系统的信号

Function Calling 的关键设计之一,是引入了特殊 Token

3.1 什么是特殊 Token?

特殊 Token 是专门添加到词表中的”标记”,比如:

1
2
3
[tool_call]     → 工具调用开始
[/tool_call] → 工具调用结束
[tool_result] → 工具返回结果

这些 Token 不是普通的文本,而是专门设计的信号

3.2 为什么需要特殊 Token?

如果没有特殊 Token 有了特殊 Token
模型输出 get_weather(北京) 模型输出 [tool_call]{"name":"get_weather"}[/tool_call]
外部系统需要解析自然语言 外部系统只需检测 [tool_call] 标签
解析容易出错、歧义 解析简单、确定性高

特殊 Token 的本质作用:给外部系统一个确定性的信号。

当模型输出 [tool_call] 时,外部系统就知道:接下来要截获这段文本,解析 JSON,执行调用,然后把结果返回给模型。

3.3 不同厂商的特殊 Token 设计

厂商 开始标签 结束标签 特点
OpenAI <function_call> </function_call> XML 风格
Claude <tool_use> </tool_use> XML 风格
某些国产模型 [TOOL_CALL] [/TOOL_CALL] 方括号风格

虽然形式不同,但核心逻辑一样:给外部系统一个明确的”截获点”


四、推理过程:概率如何决定输出

让我们看看模型在推理时到底发生了什么。

4.1 整个词表上的概率分布

模型在生成每个 Token 时,会在整个词表上计算概率分布。假设词表有 5 万个 Token,模型会计算每个 Token 的”可能性”:

1
2
3
4
5
6
7
8
9
用户输入: "今天北京天气怎么样?"

Token 概率分布(简化示例):
- "今天" : 0.15
- "北京" : 0.08
- "天气" : 0.12
- [tool_call] : 0.45 ← 最高!
- "我" : 0.05
- ...

在这个例子中,[tool_call] 的概率最高,所以模型会输出它。

4.2 上下文如何影响概率

上下文会强烈影响概率分布

1
2
3
4
5
6
7
8
上下文 1: 用户问"今天北京天气怎么样?" + 工具列表包含 get_weather
→ [tool_call] 概率飙升 → 输出工具调用

上下文 2: 用户问"今天北京天气怎么样?" + 工具列表为空
→ "今天" 概率最高 → 输出自然语言回答

上下文 3: 用户问"你好"
→ [tool_call] 概率很低 → 输出自然语言

这就是为什么工具描述很重要:工具描述会被注入到上下文中,”暗示”模型在什么情况下应该调用什么工具。

4.3 概率飙升的机制

可以把这个过程类比为:

1
2
3
4
模型就像一个"概率调节器":
- 工具描述 = "暗示信号"
- 用户问题 = "触发条件"
- 当两者匹配 → [tool_call] 的概率被"推高"

模型并没有”决定”调用工具,只是概率计算的结果恰好是 [tool_call]


五、外部执行:模型只生成,不执行

这是一个关键概念:模型本身不执行函数,只生成文本。

5.1 完整的执行流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌─────────────────────────────────────────────────────────────┐
│ 用户提问 │
│ "今天北京天气怎么样?" │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 大模型生成 │
│ 输出: [tool_call]{"name":"get_weather"}[/tool_call]│
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 外部系统截获 │
│ 检测到 [tool_call] → 解析 JSON → 执行函数 │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 函数返回结果 │
│ {"temp": 25, "condition": "晴"} │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ 结果注入回上下文 │
│ 模型看到工具结果 → 生成最终回答:"今天北京天气晴朗..." │
└─────────────────────────────────────────────────────────────┘

5.2 为什么设计成外部执行?

如果模型内部执行 外部执行的设计
模型需要”知道”所有工具的实现细节 模型只需”知道”工具的描述和参数格式
新增工具需要重新训练模型 新增工具只需更新描述,无需重训练
安全风险:模型可能执行危险操作 外部系统可以做权限控制、审计
无法处理实时数据 外部系统可以查询最新数据

外部执行的核心优势:模型只负责”说”,外部系统负责”做”。


六、四种失败模式

Function Calling 不是万能的,模型会犯错。常见的失败模式有四种:

6.1 调用错工具

1
2
3
4
5
用户: "帮我查一下明天的股票行情"
工具列表: [get_weather, send_email, search_web]

模型输出: [tool_call]{"name": "get_weather", "args": {...}}[/tool_call]
↑ 应该调用 search_web,却调用了 get_weather

原因:工具描述不够清晰,或者模型对工具功能的理解有偏差。

6.2 参数填错

1
2
3
4
5
用户: "帮我订一张明天从北京到上海的机票"
工具: book_ticket(from_city, to_city, date)

模型输出: [tool_call]{"name": "book_ticket", "args": {"from_city": "上海", "to_city": "北京"}}
↑ 起点终点搞反了

原因:模型对参数的”填充”是基于上下文的语义理解,而不是精确的逻辑推理。

6.3 该调工具时没调

1
2
3
4
用户: "现在几点了?"
工具列表: [get_current_time]

模型输出: "现在是2024年..." ← 编造了一个答案,没有调用工具

原因:工具描述不够突出,或者模型”自信”地认为自己知道答案。

6.4 不该调工具时调了

1
2
3
4
5
用户: "讲一个关于时间的故事"
工具列表: [get_current_time]

模型输出: [tool_call]{"name": "get_current_time"}[/tool_call]
↑ 用户只是想听故事,不需要真的查时间

原因:模型”过度积极”,看到”时间”相关词汇就触发工具调用。

6.5 失败模式总结

失败模式 典型原因 缓解方法
调用错工具 工具描述不清晰 优化工具描述,增加示例
参数填错 语义理解偏差 参数校验,增加约束
该调没调 模型过度自信 提示词引导,RL 优化
不该调调了 过度积极触发 工具描述加”适用场景”

七、不同厂商的差异

虽然 Function Calling 的核心原理相同,但不同厂商在实现上有差异:

7.1 特殊 Token 设计

厂商 设计风格 特点
OpenAI XML 风格标签 可读性好,易于解析
Claude XML 风格标签 支持多工具并行调用
部分国产模型 方括号风格 与系统提示词更统一

7.2 工具描述注入方式

方式 说明 优点 缺点
System Prompt 注入 工具描述放在 System Prompt 灵活,易于调试 占用上下文
特殊字段注入 API 有专门的 tools 字段 结构清晰,便于管理 需要解析
混合方式 部分在 System Prompt,部分在 API 兼顾灵活和结构 可能冲突

7.3 并行调用支持

1
2
3
4
5
6
7
8
9
10
11
12
13
# OpenAI:支持并行调用多个工具
response = client.chat.completions.create(
model="gpt-4",
messages=[...],
tools=[
{"type": "function", "function": {"name": "get_weather"}},
{"type": "function", "function": {"name": "get_stock"}}
]
)
# 模型可以一次性输出多个工具调用

# 部分模型:只支持串行调用
# 需要先调用一个工具,返回结果后,再决定是否调用下一个

7.4 强制/禁止调用控制

控制方式 说明
tool_choice: "auto" 模型自动决定是否调用工具(默认)
tool_choice: "required" 强制模型必须调用某个工具
tool_choice: "none" 禁止模型调用工具
tool_choice: {"name": "xxx"} 强制调用指定的工具

八、总结:理解 Function Calling 的五个要点

最后,用五个要点总结 Function Calling 的底层原理:

要点一:没有新机制

Function Calling 没有引入新的推理机制,底层仍然是 Next Token Prediction。

模型只是学会了在特定上下文下,输出特定格式的文本——只是这个文本恰好是 JSON 格式的工具调用指令。

要点二:两阶段训练

1
2
SFT 阶段:教会模型"基本操作"(何时调用、怎么写、怎么处理结果)
RL 阶段:精调"分寸感"(边界情况的处理)

要点三:特殊 Token 是信号

特殊 Token 不是给模型”理解”的,而是给外部系统截获的确定性信号

要点四:外部执行

模型只生成文本,不执行函数。 外部系统负责:截获指令 → 执行调用 → 返回结果。

要点五:概率决定一切

模型没有”决定”调用工具,只是概率计算的结果恰好是 [tool_call]。上下文(包括工具描述)会影响概率分布,从而影响输出。


写在最后

理解 Function Calling 的底层原理,更重要的是:

  1. 知道它的局限性:模型会犯错,需要外部校验
  2. 知道怎么优化:工具描述、提示词设计、参数校验
  3. 知道怎么调试:当模型调用错误时,从概率角度分析原因

“Function Calling 的底层原理是 Next Token Prediction,没有引入新的推理机制。模型通过 SFT 和 RL 两阶段训练,学会了在特定上下文下输出特殊 Token 标记的 JSON 格式调用指令。特殊 Token 是给外部系统的信号,外部系统负责截获、执行和返回结果。”

这个回答,比”模型读懂了意图、判断需要调用工具、决定生成调用指令”要准确得多。


参考资源