Files
2026-05-16 17:16:51 +08:00

19 KiB
Raw Permalink Blame History

title, draft, tags
title draft tags
Tool Calling 与 Acting让模型学会做事 false
Toolformer
ReAct
Tool-Calling
Acting
大语言模型

Tool Calling 与 Acting让模型学会做事


一、从"说话"到"做事"

1.1 语言模型的本质限制

语言模型的能力

给定输入 $x$,生成文本输出 $y$

P_\theta(y|x) = \prod_{t=1}^T P_\theta(y_t | x, y_{<t}) \tag{1.1}

限制

  1. 无法访问实时信息:不知道当前时间、天气、股价
  2. 无法执行操作:不能搜索网页、发送邮件、执行代码
  3. 无法与环境交互:只能处理文本,无法改变外部世界

1.2 工具调用的核心思想

核心洞察

让语言模型不仅能"说话",还能"调用工具"——通过函数/API 调用执行操作,获取外部信息或改变外部状态。

形式化

将语言模型扩展为:

P_\theta(y|x, \mathcal{T}) = \prod_{t=1}^T P_\theta(y_t | x, y_{<t}, \mathcal{T}) \tag{1.2}

其中 \mathcal{T} 是可用工具的集合。

工具调用示例

用户:今天北京天气如何?

模型决定调用工具:
[TOOL_CALL] get_weather(location="北京")
[TOOL_RESULT] 晴25°C

模型基于结果生成回答:
"今天北京天气晴朗气温25摄氏度。"

二、Toolformer模型自主学习工具调用

2.1 核心思想

ToolformerSchick et al., 2023的核心是让模型自己决定何时调用哪些工具,以及如何调用。

关键创新

  1. 工具选择:模型自己决定是否需要调用工具
  2. 工具调用:模型生成符合 API 格式的调用
  3. 结果融合:将工具返回结果融入下一步生成

2.2 Toolformer 的工具定义

定义(工具)

每个工具 t \in \mathcal{T} 由以下部分组成:

t = (name, description, parameters, return\_type) \tag{2.1}

示例:计算器工具

name: "calculator"
description: "计算数学表达式的值"
parameters: {
    "expression": "str"  # 要计算的表达式
}
return_type: "float"  # 返回计算结果

2.3 Toolformer 的数据生成

核心优势:自监督自动构建

Toolformer 的关键创新在于无标注自监督自动构造工具调用样本——并非简单人工插入调用语句,而是让模型自行判断何地插入工具调用并验证结果,形成低成本大规模数据集。

Algorithm: Toolformer Data Generation

输入:语言模型 $\pi_0$,工具集 $\mathcal{T}$,工具调用数据集 \mathcal{D}_{\text{tool}}

步骤 1API 相关采样

for each sample (x, y) in D:
    # 判断是否适合插入工具调用
    if should_insert_tool(x, y):
        y' = insert_tool_calls(x, y, T)  # 在适当位置插入工具调用

步骤 2工具调用格式

在自然语言中插入特殊格式的调用:

这导致 45 + 23 = [TOOL_CALL] calculator("45 + 23") [TOOL_RESULT] 68 [TOOL_CALL_END]
所以结果是 68。

2.4 工具调用的训练目标

Toolformer 的损失函数

在每个 token 位置 $t$,计算:

\mathcal{L}_t = -\log \pi_\theta(y_t | x, y_{<t}, \text{context}) \tag{2.2}

但只对以下 token 计算梯度:

  • 自然语言 token
  • 工具调用 tokenAPI 名、参数)
  • 不包含工具结果的 token

梯度阻断的三大 Token 类别

Token 类型 示例 梯度计算
自然语言 Token 普通文本生成 回传梯度
工具调用格式 Token [TOOL_CALL], calculator, "45+23" 回传梯度(模型自主生成)
外部工具返回结果 Token [TOOL_RESULT] 68 冻结梯度(外部系统生成)

梯度阻断的核心原因工具返回结果由外部系统如计算器、搜索API生成不属于模型自主生成内容若对其回传梯度会导致模型学习外部系统的输出分布破坏训练目标。

原因:工具结果是外部提供的,不应该从中学习。

2.5 工具选择的决策

何时调用工具?

模型通过 prompt 学习何时调用工具:

当你需要执行计算、查询信息或访问外部数据时,
使用以下格式调用工具:

[TOOL_CALL] tool_name("param1", "param2")
[TOOL_RESULT] result
[TOOL_CALL_END]

工业主流 JSON Function Calling 格式OpenAI 生态标准):

{
  "name": "get_weather",
  "arguments": {
    "location": "北京",
    "unit": "celsius"
  }
}

优势

  • 结构化程度高,参数类型安全校验
  • 企业级落地首选方案
  • 规避自定义标签解析歧义

零样本/少样本工具调用提示工程

无需 SFT 微调,仅靠 Prompt 即可实现工具调用:

可用工具:
{
  "name": "calculator",
  "description": "计算数学表达式",
  "parameters": {
    "expression": {"type": "string", "description": "要计算的表达式"}
  }
}

示例调用:
{
  "name": "calculator",
  "arguments": {"expression": "45 + 23"}
}

请遵循上述格式调用工具。

决策的数学表示

P(\text{call tool} | x) = \text{softmax}(W \cdot h_T) \tag{2.3}

其中 h_T 是最后一层的隐藏状态。


三、ReAct推理与行动的交织

3.1 核心思想

ReActYao et al., 2023的核心是将**推理Reasoning行动Acting**交错起来。

ReAct 的核心洞察

人类智能是推理和行动的交织:边想边做,边做边想。

与 Toolformer 的区别

方法 工具调用 推理方式
Toolformer 模型自主决定 工具调用穿插在生成中
ReAct 每个 step 都显式 Thought → Action → Observation 循环

3.2 ReAct 的轨迹格式

ReAct 的轨迹由多个步骤组成:

输入x

Step 1: Thought: 模型思考下一步该做什么
        Action: 执行的操作(如 search, calculate
        Observation: 操作返回的结果

Step 2: Thought: 基于观察继续思考
        Action: 继续操作
        Observation: 继续观察

... (重复直到得到答案)

关键特性:双向交互与动态纠错

ReAct 支持环境反馈反向修正推理逻辑——可推翻之前错误思路重新规划,而非一条道走到黑。

Step 1: Thought: 我认为应该先搜索"北京天气"
        Action: search("北京天气")
        Observation: 结果显示"晴25°C"

Step 2: Thought: 等等,用户问的是"明天"北京的天气,
           而搜索结果显示的是"今天"的。
           需要重新搜索"北京明天天气"
        Action: search("北京明天天气")
        Observation: 结果显示"多云22°C"

形式化表示

s_t 是第 t 步的状态,a_t 是行动,o_t 是观察。

\text{Traj} = (x, s_0) \xrightarrow{a_0} o_0 \xrightarrow{a_1} o_1 \xrightarrow{a_2} \cdots \tag{3.1}

3.3 ReAct 的数学框架

定义(状态)

状态 s_t 包含:

  • 原始输入 x
  • 之前的思考 h_{<t} = (a_0, o_0, \ldots, a_{t-1}, o_{t-1})
  • 当前任务描述

定义(行动空间)

\mathcal{A} = \mathcal{A}_{\text{search}} \cup \mathcal{A}_{\text{calc}} \cup \mathcal{A}_{\text{finish}} \cup \mathcal{A}_{\text{custom}}

其中:

  • $\mathcal{A}_{\text{search}}$:检索类行动(如搜索网页、查询数据库)
  • $\mathcal{A}_{\text{calc}}$:计算类行动(如执行数学运算、代码运行)
  • $\mathcal{A}_{\text{finish}}$:终止行动(给出最终答案)
  • $\mathcal{A}_{\text{custom}}$:自定义业务行动

定义(策略)

\pi(a_t | s_t) = P_\theta(a_t | s_t) \tag{3.2}

3.4 ReAct 的推理循环

Algorithm: ReAct

输入:问题 x可用工具集 T
输出:最终答案 y

初始化s_0 = (x, empty_context)

for step t = 1 to MAX_STEPS:
    # 1. 思考Thought
    thought_t = pi_thought(s_{t-1})
    # thought_t 是对当前状态的推理

    # 2. 行动Action
    action_t = pi_action(s_{t-1}, thought_t)
    # action_t 可以是 search/calculate/finish

    # 3. 执行并观察Observation
    if action_t.type == "search":
        obs_t = search(action_t.query)
    elif action_t.type == "calculate":
        obs_t = calculate(action_t.expression)
    elif action_t.type == "finish":
        return action_t.answer

    # 4. 更新状态
    s_t = \text{Concat}\big(s_{t-1},\; \text{Thought}_t,\; \text{Action}_t,\; \text{Observation}_t\big)

3.5 ReAct vs CoT vs Toolformer

对比

方法 推理结构 工具调用 反馈利用
CoT 线性推理链
Toolformer 工具穿插在文本中 自动插入 通过工具结果
ReAct Thought→Action→Obs 循环 显式决策 每步都有观察

各自适用场景

  • CoT:数学推理、逻辑推导(不需要外部信息)
  • Toolformer:需要调用工具但不希望干扰生成流程
  • ReAct:需要与环境交互、多步信息获取

四、工具调用的训练方法

4.1 监督微调方法

步骤

  1. 数据收集:收集包含工具调用的轨迹数据
  2. 格式化为对话:将轨迹格式化为对话格式
  3. SFT 训练:在格式化数据上做监督微调

数据格式

[USER] x
[ASSISTANT] thought: ...
         action: search("query")
[SYSTEM] observation: ...
[ASSISTANT] thought: ...
         action: finish("answer")

4.2 工具选择的训练

方法 1分类头判别式

在语言模型上添加一个二分类头,判断是否调用工具:

P(\text{call tool} | x) = \sigma(W \cdot h_T + b) \tag{4.1}

方法 2工具 embedding生成式

为每个工具学习一个 embedding在生成时通过 attention 选择工具。

判别式 vs 生成式工具选择

方式 核心思想 适用场景
判别式(分类头) 二分类头判断是否调用工具 简单二选一场景
生成式(直接生成) 直接生成工具名与参数(主流) 复杂多工具选择

4.3 API 调用的格式学习

学习 API 调用格式

模型需要学会:

  1. 生成正确的函数名
  2. 生成正确的参数JSON 格式)
  3. 解析返回结果

格式约束

使用特殊的 token 标记来约束格式:

[TOOL_CALL] calculator("45 + 23") [TOOL_RESULT] 68 [TOOL_CALL_END]

4.4 训练中的问题

问题 1工具描述的利用

模型需要理解工具的 description 才能正确调用。

解决方案

将工具描述作为输入的一部分:

可用工具:
- calculator: 计算数学表达式
- search: 搜索网页

请在需要时调用工具。

问题 2工具结果的幻觉

模型可能伪造工具结果(没有调用工具但生成假结果)。

解决方案

严格使用真实工具结果替换模型输出中的 [TOOL_RESULT] 部分。

工具调用三类典型幻觉

幻觉类型 描述 治理方案
函数名幻觉 写错工具名称(如 get_whether 而非 get_weather Schema 校验 + 相似度匹配限制
参数幻觉 入参格式/内容错误(如字符串未加引号) 严格 JSON Schema 验证
结果伪造幻觉 未调用工具,凭空编造返回结果 强制真实结果替换 + 调用日志审计

高阶工具调用模式

  1. 并行工具调用:同时发起多个独立工具请求(如同时搜索多个关键词)
  2. 嵌套工具调用:一个工具的输出作为另一个工具的输入
  3. 条件分支调用:根据中间结果选择不同工具分支

示例:并行调用

# 同时查询天气和股价
[TOOL_CALL] get_weather(location="北京")
[TOOL_CALL] get_stock(symbol="AAPL")

示例:嵌套调用

# search 结果作为 calculate 输入
result = search("北京的人口")
calculate(f"population * 2 = {result}")

五、多工具协作与规划

5.1 多工具调用序列

问题:一个任务可能需要多个工具。

示例

任务:查找今天北京演唱会票价

Step 1: search("北京演唱会今天")
         → 获取演出名称和场馆

Step 2: calculate("场馆到最近地铁站的距离")
         → 需要先 search 地铁站信息

Step 3: 整合信息给出回答

多步依赖

后一步的工具可能依赖于前一步的结果。

5.2 工具调用图

定义(工具依赖图)

\mathcal{G} = (V, E) V = \{t_1, t_2, \ldots, t_n\} \quad \text{工具集合} E = \{(t_i, t_j) | t_j \text{的输入依赖于 } t_i \text{的输出}\} \tag{5.1}

有向无环图DAG:工具调度使用 DAG 结构规避循环调用死锁。若存在环路,拓扑排序失效,需检测并终止。

拓扑排序

按照依赖关系排序工具调用:

t_{\pi(1)}, t_{\pi(2)}, \ldots, t_{\pi(n)} \quad \text{满足依赖顺序} \tag{5.2}

5.3 规划与重规划

静态规划:事先规划好完整调用序列

动态规划:根据每步结果调整后续调用

Algorithm: Dynamic Replanning

if last_action.failed:
    # 根据失败原因调整计划
    new_plan = replan(current_state, failure_reason)
else if last_observation.is_satisfactory:
    # 继续原计划
    pass

六、实际应用中的挑战

6.1 工具调用的可靠性

问题:模型可能调用错误的工具或参数。

解决方案

  1. 参数验证:对工具参数做 schema 验证
  2. 错误处理:工具调用失败时重新尝试或换工具
  3. 置信度阈值:低于置信度阈值时不调用

6.2 工具数量与选择

问题:当工具数量很大时(如 100+),如何选择?

解决方案

  1. 分层选择:先选类别,再选具体工具
  2. 语义匹配:用文本相似度匹配工具描述
  3. 工具 embedding:学习工具的向量表示

6.3 循环与终止判断

问题:如何知道任务完成了?

终止条件

  1. 显式终止:调用了 finish 类型的工具
  2. 隐式终止:连续几步没有新信息
  3. 步数限制:达到最大步数
  4. 自反思终止:模型自我判断当前信息已足够回答,主动停止交互

自反思终止策略示例

Step t: Thought: 我已经获取了足够的信息来回答用户的问题。
               北京今天天气晴朗气温25°C与用户询问相符。
               不需要进一步搜索。
        Action: finish("北京今天天气晴朗气温25摄氏度。")

6.4 工具结果的处理

长结果处理

工具可能返回很长的结果(如搜索返回 10 条结果)。

解决方案

  1. 截断:限制结果长度
  2. 摘要:对结果做摘要
  3. 选择性使用:只使用最相关的部分

七、数学公式速查

7.1 工具调用公式

扩展语言模型

P_\theta(y|x, \mathcal{T}) = \prod_t P_\theta(y_t | x, y_{<t}, \mathcal{T}) \tag{7.1}

工具选择

P(\text{call tool} | x) = \sigma(W \cdot h_T + b) \tag{7.2}

7.2 ReAct 公式

状态更新

s_t = \text{Concat}\big(s_{t-1},\; \text{Thought}_t,\; \text{Action}_t,\; \text{Observation}_t\big) \tag{7.3}

策略

\pi(a_t | s_t) = P_\theta(a_t | s_t) \tag{7.4}

八、总结

Toolformer vs ReAct

方面 Toolformer ReAct
工具调用方式 自动插入在文本中 显式 Thought→Action→Obs 循环
推理方式 工具调用穿插在生成 推理与行动交替
反馈利用 通过工具结果 每步都有观察
适用场景 需要工具辅助生成 需要交互探索

核心洞察

Tool Calling 将语言模型从"纯文本生成器"扩展为"能够操纵外部世界的智能体"。ReAct 则进一步将推理和行动交织,使得模型能够在与环境交互中不断调整策略。

关键挑战

  1. 工具选择的准确性
  2. 工具调用的可靠性
  3. 多步规划的效率
  4. 循环检测与终止

8.1 主流智能体框架适配

LangChain 工具调用封装

from langchain.agents import AgentExecutor, Tool
from langchain.llms import OpenAI

tools = [
    Tool(name="calculator", func=calculate, description="计算数学表达式"),
    Tool(name="search", func=search_web, description="搜索网页")
]

agent = initialize_agent(tools, llm, agent_type="react")
agent.run("北京今天天气如何?")

LlamaIndex 工具调用封装

from llama_index import download_loader
from llama_index.agent import ReActAgent

agent = ReActAgent.from_tools(tools, verbose=True)
response = agent.query("计算 45 + 23 的结果")

AgentScope 工具调用封装

import agentscope
from agentscope.agents import ReActAgent

agent = ReActAgent(config={
    "tools": [get_weather, calculator],
    "model": "gpt-4"
})

8.2 工程实战落地方案

调用异常处理

异常类型 处理策略
超时 重试3次间隔2s/4s/8s指数退避
参数错误 自动纠错(如类型转换)+ 回退到自然语言回答
工具不可用 降级方案:直接基于已有信息回答,或提示用户

多轮交互上下文优化

问题用户进行20轮对话后Context 溢出风险

解决方案:
1. 摘要压缩每隔5轮用 small LLM 压缩历史
2. 滑动窗口:只保留最近 N 轮对话
3. 重要性筛选:删除与当前任务无关的历史

微调实战配比

数据类型 推荐比例 作用
工具调用轨迹数据 30% 专精工具调用能力
自然对话数据 70% 保持通用对话能力

冷热方案选型

场景 推荐方案 说明
快速上线验证 少样本Prompt 无需训练,快速迭代
深度业务适配 SFT微调 专属工具调用范式,效果更优
复杂多工具场景 ReAct + DAG规划 推理+行动+环境观察闭环

延伸阅读

  1. Schick et al., "Toolformer: Language Models Can Teach Themselves to Use Tools" (ICLR 2023)
  2. Yao et al., "ReAct: Synergizing Reasoning and Acting in Language Models" (ICLR 2023)
  3. Liu et al., "Tool Learning with Foundation Models" (2023)
  4. Zhou et al., "InternLM: A Multilingual Language Model with Progressively Enhanced Capabilities" (2023)
  5. Patil et al., "Gorilla: Large Language Models Connect with Tools" (2023)