引言
LLM 本身不会查数据库、不会读文件、不会创建工单,也不会真的调用接口。它能做的是根据上下文生成文本。
Function Calling / Tool Calling 的核心,就是让模型用结构化格式表达:
|
|
它把模型从“只能回答”扩展成“可以请求外部系统执行动作”的能力。
一个最简单的例子:
|
|
所以 Tool Calling 不是模型真的拥有了外部能力,而是建立了一条受控链路:
|
|
从文本生成到动作请求
传统 LLM 调用只有输入和输出:
|
|
Tool Calling 增加了一个中间分支:
|
|
当模型判断需要外部信息或外部动作时,它不会直接回答,而是生成一个工具调用请求。
这个请求通常包含:
- 工具名称
- 参数 JSON
- 调用 ID
- 可能的并行调用列表
Host 应用收到工具调用后,执行真实函数,再把结果作为新的上下文发回模型。
Tool 的三要素
一个工具通常由三部分组成。
|
|
Name
Name 是工具的唯一标识。模型会根据名称判断工具用途。
好的命名应该语义明确:
|
|
名称越模糊,模型越容易选错。
Description
Description 告诉模型“什么时候用这个工具”。
很多工具描述只写“查询订单”,这不够。更好的描述是:
|
|
对模型来说,负面边界和正面用途同样重要。
Parameters
Parameters 通常用 JSON Schema 描述。
它约束:
- 参数有哪些字段
- 字段类型是什么
- 哪些字段必填
- 枚举值有哪些
- 嵌套结构如何组织
Schema 的价值不是让模型“绝对不会出错”,而是显著降低参数生成的不确定性,并给应用层提供校验依据。
调用链路
一次完整 Tool Calling 通常包含六步。
|
|
第一步:用户请求
用户提出任务:
|
|
这句话本身没有外部信息,模型如果直接回答就只能猜。
第二步:注入工具表
应用把可用工具描述发给模型:
|
|
工具表是模型决策的重要上下文。工具越多,选择难度越大,所以工具设计要克制。
第三步:模型生成 Tool Call
模型输出结构化调用:
|
|
注意,这一步还没有执行任何外部动作。模型只是生成了一个“动作请求”。
第四步:应用执行工具
应用层拿到工具名和参数后:
- 校验工具是否允许调用
- 校验参数是否符合 schema
- 检查权限
- 执行真实函数或 API
- 捕获异常
- 标准化返回结果
例如:
|
|
第五步:结果回填
工具结果会作为新的消息放回模型上下文。
模型看到:
|
|
这一步非常关键。工具结果不是最终答案,而是模型继续推理的证据。
第六步:最终回答
模型基于工具结果回答用户:
|
|
模型如何选择工具
模型选择工具不是传统代码里的 if/else,而是基于上下文概率生成。
它会综合判断:
- 用户意图
- 工具名称
- 工具描述
- 参数 schema
- 对话历史
- 系统指令
- 是否允许直接回答
如果工具描述模糊,模型就可能:
- 不该调用时调用
- 该调用时不调用
- 调错工具
- 参数填错
- 过度调用多个工具
工具选择的关键影响因素
| 因素 | 影响 |
|---|---|
| 工具名称 | 决定第一印象 |
| description | 决定适用边界 |
| schema 严格度 | 决定参数稳定性 |
| 示例 | 帮助模型学习调用模式 |
| 工具数量 | 越多越难选 |
| 上下文位置 | 越接近当前任务越容易被关注 |
工具设计本质上是一种面向模型的 API 设计。
Function Calling 与 Tool Calling 的关系
Function Calling 是早期更常见的叫法,强调“模型输出函数名和参数”。
Tool Calling 是更广义的说法。Tool 不一定是一个普通函数,也可以是:
- 查询数据库
- 调用 HTTP API
- 读写文件
- 启动浏览器
- 运行测试
- 调用另一个 Agent
- 访问 MCP Server
可以简单理解:
|
|
在工程上,两者的核心链路一致:模型生成结构化调用,应用执行,再把结果回填。
Tool Calling 与 MCP 的区别
Tool Calling 和 MCP 容易混淆。
| 维度 | Tool Calling | MCP |
|---|---|---|
| 关注点 | 模型如何请求调用工具 | 工具如何标准化接入 Host |
| 所属层 | 模型 API / Agent Runtime | 应用协议 |
| 工具来源 | 应用代码传入 | MCP Server 动态提供 |
| 协议 | 通常由模型 API 定义 | JSON-RPC + 传输层 |
| 目标 | 让模型表达动作意图 | 让外部能力可被发现和调用 |
二者经常组合使用:
|
|
MCP 是工具接入标准,Tool Calling 是模型调用工具的表达机制。
并行工具调用
有些模型支持一次输出多个工具调用。
例如用户问:
|
|
模型可以一次生成:
|
|
应用层并行执行三个工具,再把结果一起回填。
并行调用适合:
- 多个独立查询
- 多文件读取
- 多数据源检索
- 多服务状态检查
不适合:
- 后一步依赖前一步结果
- 有写操作
- 多个工具可能修改同一资源
- 需要严格顺序的任务
并行能降低延迟,但也会增加调度复杂度和错误处理成本。
错误恢复
工具调用一定会失败。
常见失败类型:
| 类型 | 示例 |
|---|---|
| 参数错误 | 缺少 order_id |
| 权限错误 | token 没有写权限 |
| 外部失败 | API 超时、数据库不可用 |
| 结果为空 | 查不到订单 |
| 业务冲突 | 订单已取消,不能退款 |
| 安全拦截 | 试图读取敏感文件 |
好的工具返回应该让模型知道怎么恢复:
|
|
差的错误返回:
|
|
模型无法基于这种信息做有效下一步。
恢复策略
| 场景 | 策略 |
|---|---|
| 参数缺失 | 让模型补参数或追问用户 |
| 临时超时 | 自动重试,限制次数 |
| 权限不足 | 告知用户需要授权 |
| 数据不存在 | 明确说明未找到 |
| 高风险操作 | 请求用户确认 |
| 工具不可用 | 降级为解释性回答 |
Agent 的可靠性很大程度取决于工具错误设计。
安全边界
Tool Calling 最大的风险是:模型生成的不是普通文本,而是可能改变外部系统的动作请求。
安全边界必须由应用层保证,不能相信模型“会自觉”。
模型不能直接执行
模型只生成调用意图:
|
|
是否执行,必须由应用层决定。
应用层要检查:
- 这个工具是否允许当前用户调用
- 参数是否在允许范围内
- 是否需要二次确认
- 是否有审计日志
- 是否可以回滚
工具结果不可信
工具返回的数据也可能包含攻击内容。
例如网页工具返回:
|
|
这类内容必须被视为外部数据,而不是系统指令。
防护原则:
- 明确系统指令优先级
- 给工具结果加来源标记
- 高风险工具必须确认
- 不把敏感工具暴露给不可信上下文
- 对网页、文档、Issue 评论做 prompt injection 防护
工具权限最小化
不要暴露万能工具:
|
|
这些工具过于灵活,也过于危险。
更好的方式是收窄能力:
|
|
能力越具体,越容易控制。
工程设计原则
工具数量要少
不要一次给模型几十个工具。工具越多,选择空间越大,误调用概率越高。
可以按任务动态选择工具:
|
|
Schema 要严格
能用枚举就不用自由字符串。
|
|
Schema 越严格,参数越稳定。
返回要结构化
工具返回不要只给自然语言。
|
|
结构化结果更容易被模型使用,也更容易被系统记录和评估。
高风险工具要可审计
写操作必须记录:
- 谁触发
- 模型输入
- 工具名
- 参数
- 执行结果
- 时间
- 是否人工确认
没有审计日志,就不要让 Agent 做高风险动作。
评估指标
Tool Calling 的评估不能只看最终回答。
调用指标
| 指标 | 含义 |
|---|---|
| Tool Selection Accuracy | 工具选择准确率 |
| Argument Accuracy | 参数准确率 |
| Call Necessity | 是否该调用工具 |
| Over-call Rate | 过度调用率 |
| Under-call Rate | 漏调用率 |
执行指标
| 指标 | 含义 |
|---|---|
| Tool Success Rate | 工具执行成功率 |
| Retry Rate | 重试比例 |
| Recovery Success Rate | 失败恢复成功率 |
| Permission Denial Rate | 权限拒绝率 |
| Latency | 工具调用耗时 |
安全指标
| 指标 | 含义 |
|---|---|
| Unsafe Call Block Rate | 危险调用拦截率 |
| Sensitive Data Exposure | 敏感数据泄露次数 |
| Confirmation Coverage | 高风险操作确认覆盖率 |
| Injection Resistance | 对工具结果注入的抵抗能力 |
如果没有这些指标,Tool Calling 很容易看起来“能用”,但生产环境不可靠。
一个完整例子
用户说:
|
|
可用工具:
|
|
理想流程:
|
|
工具结果:
|
|
最终回答:
|
|
这个回答的可靠性来自工具结果,而不是模型猜测。
常见反模式
把工具当搜索框
工具不是“让模型随便查一下”。每个工具都应该有明确输入、输出和边界。
暴露万能函数
万能函数让模型自由度太大,也让安全边界变模糊。
忽略参数校验
模型生成 JSON 不代表 JSON 一定可信。应用层必须校验。
把工具结果直接当最终答案
工具结果是证据,不是回答。模型需要结合用户问题解释结果。
没有失败路径
只设计成功调用,不设计失败恢复,Agent 一上线就会脆。
小结
Function Calling / Tool Calling 的本质是:
|
|
它让 LLM 从“文本生成器”变成“可连接外部系统的决策者”,但同时也引入了权限、安全、错误恢复和评估问题。
一个可靠的 Tool Calling 系统,关键不在于工具数量多,而在于:
- 工具边界清楚
- schema 足够严格
- 执行链路受控
- 结果结构化
- 错误可恢复
- 高风险动作可审计
工具调用做得好,Agent 才能从“会说”走向“会做”。