第 4 章 · Tool Use(工具调用)
4.1 Function Calling 是什么
第 3 章的代码里,LLM 通过 tools 参数知道有哪些工具可用,然后返回 tool_use block 告诉你它要调哪个工具、传什么参数。这个机制叫 Function Calling(函数调用),也叫 Tool Use(工具使用)。
不同厂商叫法不同:
- OpenAI:Function Calling
- Anthropic:Tool Use
- Google Gemini:Function Calling
本质一样:LLM 不再只输出文字,还能输出"我要调用这个函数,参数是这个"的结构化指令。
4.2 工具 Schema:怎么告诉 LLM 有什么工具
工具用 JSON Schema 描述。一个工具 = 一个 schema。看个例子:
{
"name": "search_papers",
"description": "在 arxiv 上搜索学术论文。当用户想找论文、查研究进展时使用。",
"input_schema": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,英文"
},
"max_results": {
"type": "integer",
"description": "返回结果数量,默认 10",
"default": 10
}
},
"required": ["query"]
}
}三个关键字段:
- name:工具名,要短、动词开头、表意清晰。
search_papers比paper_search好。 - description:最重要!LLM 决定何时用这个工具,全靠这段描述。要写清"什么场景下用"。例子里写了"当用户想找论文、查研究进展时使用"。
- input_schema:参数的 JSON Schema。每个参数也要有 description。
description 是 LLM 看到的全部——它不知道你的代码长什么样,只看 description 决定调不调。所以 description 写不好,agent 就不会用这个工具,或者乱用。
4.3 Anthropic Tool Use API 实战
Anthropic 的 tool use 流程(参考 官方文档):
1. 你的代码 → Claude:发 messages + tools 列表
2. Claude → 你的代码:返回 stop_reason="tool_use" + tool_use block
3. 你的代码:执行工具,拿到结果
4. 你的代码 → Claude:发 tool_result block
5. Claude → 你的代码:继续回复,或调下一个工具,或结束关键 API 字段:
| 字段 | 作用 |
|---|---|
tools | 工具 schema 列表 |
tool_use block | Claude 说"我要调这个工具" |
tool_result block | 你的代码说"工具执行完了,结果是这个" |
tool_choice | 控制 Claude 怎么选工具:{"type": "auto"} 默认 / {"type": "any"} 必须调工具 / {"type": "tool", "name": "x"} 强制调某个 |
stop_reason: "tool_use" | Claude 因为要调工具而暂停 |
stop_reason: "end_turn" | Claude 觉得任务完成 |
Client tools vs Server tools:
- Client tools:在你的应用端执行。本教程讲的都是这种。
- Server tools:在 Anthropic 基础设施上执行,比如
web_search、web_fetch、code_execution。Claude 自己跑,不用你写代码。
4.4 工具分类
按功能分四类:
1. 检索类工具
让 agent 能"看"到外部信息。
web_search(query)- 网页搜索read_file(path)- 读本地文件query_db(sql)- 查数据库search_papers(query)- 搜论文(实战 5 会用)
2. 操作类工具
让 agent 能"动"世界。
write_file(path, content)- 写文件send_email(to, subject, body)- 发邮件(实战 5、6 会用)run_shell(cmd)- 执行 shell 命令(危险,慎用)book_meeting(room, time)- 订会议室
3. 计算类工具
让 agent 能"算"——LLM 算术很差,复杂计算必须靠工具。
calculate(expr)- 数学计算run_python(code)- 执行 Python 代码convert_currency(amount, from, to)- 货币转换
4. 通信类工具
让 agent 能"问"——遇到不确定的事,问用户而不是瞎猜。
ask_user(question)- 问用户request_approval(action)- 请求用户批准(实战 6 招聘 agent 会用)
4.5 工具设计的 5 条经验法则
写了一堆工具后总结的,初学者最容易踩的坑:
1. 粒度要合适:不要太粗,不要太细
太粗:do_anything(task) - LLM 不知道怎么用 太细:open_file() / read_line() / close_file() - LLM 要调 3 次才能读完一个文件 合适:read_file(path) - 一次调用搞定
2. 命名要动词开头,表意清晰
❌ paper_search, file_processor, data_utils
✅ search_papers, read_file, parse_dataLLM 看 name 决定调不调,名字差一点效果差很多。
3. description 写"什么时候用",不只写"是什么"
❌ "搜索 arxiv 论文。"
✅ "在 arxiv 上搜索学术论文。当用户想找论文、查研究进展、或需要引用文献时使用。"加"什么时候用"能让 LLM 在对的时机调对的工具。
4. 错误要返回给 LLM,不要吞
# ❌ 错误吞掉
def search_papers(query):
try:
return arxiv.search(query)
except:
return ""
# ✅ 错误返回给 LLM
def search_papers(query):
try:
return arxiv.search(query)
except Exception as e:
return f"搜索失败: {type(e).__name__}: {e}"LLM 看到错误信息会自己换办法(换关键词、换工具、问用户)。吞掉错误会让 LLM 以为成功了,给你错误结果。
5. 有副作用的工具要可逆或可审
read_file无副作用,随便调write_file有副作用,应该让用户能撤销(备份原文件)send_email不可逆,应该让用户审核(实战 6 的"人工审核回路")run_shell("rm -rf /")灾难性,应该被 permission 系统拦住
权限分级:读类工具自动放行,写类工具问一下,删除类工具必须人工确认。Claude Code 的 Permission 系统就是这么设计的,第 6 章讲 Agent SDK 时会详细说。
前人智慧 / Prior Art
本章讲了 Tool Use 的工程细节:schema 设计、Anthropic API 流程、工具分类、5 条经验法则。这些不是本教程的发明,是 2023-2025 年整个行业工程实践的归纳。
OpenAI Function Calling 的发布意义
2023 年 6 月 13 日,OpenAI 在 GPT-4 升级公告 里发布了 Function Calling。这是从"ReAct 文本解析"到"工程化 API"的关键一跳。
发布前的痛点:
- 开发者得用正则解析 "Action: tool_name(args)" 字符串
- 模型偶尔输出格式错误,解析崩
- 不同 prompt 格式不兼容
Function Calling 后:
- 模型直接返回结构化 JSON
- 字段名固定(
name/arguments) - 工程稳定性大幅提升
本章 4.1 节"Function Calling 是什么"讲的就是这个工程化跳跃。注意 Function Calling 不是新概念,是 ReAct 思想的工程化包装。
早期 FUNCTION 协议:工具调用的史前时代 07-09
在 Function Calling 标准化之前,开发者怎么让模型调工具?吴恩达 Agentic AI 课程(Bilibili BV1DfrdByE2H,Module 3 创建工具)展示了一个简化的早期做法:
约定一个文本标记协议。在 prompt 里告诉模型:
你有一个工具 get_current_time。
如果你需要调用它,请输出:
FUNCTION get_current_time当用户问"现在几点",模型输出 FUNCTION get_current_time,开发者的程序用正则匹配这个标记,然后执行真实函数,把结果放回 context,再让模型生成最终回答。
完整链路:
用户提问
-> 模型判断需要工具
-> 模型输出 "FUNCTION get_current_time"
-> 应用程序解析字符串
-> 应用程序执行真实函数
-> 函数结果返回
-> 应用程序把结果加入对话历史
-> 模型基于工具结果生成最终回答这个史前协议揭示了一个关键事实:模型本身不执行函数。
很多人误以为"模型调用工具"--更准确的说法是模型请求调用工具,真正执行函数的是应用程序。完整职责划分:
| 角色 | 职责 |
|---|---|
| 模型 | 判断是否需要工具,生成工具调用请求 |
| 客户端/框架 | 解析请求,调用真实函数,把结果放回 context |
| 应用代码 | 执行真实函数,处理错误,管理多轮调用 |
这个划分在现代 Function Calling API 里被封装起来了--框架自动处理解析、调用、回传。但理解早期协议的工程价值在于:它揭穿了"模型调用工具"的魔法--工具调用不是模型的能力,是模型 + 应用程序的协议。
现代工具语法(本章 4.2 节的 tools 参数)只是把这个协议标准化了:
早期:prompt 约定 "输出 FUNCTION xxx" -> 正则解析
现代:tools 参数传 schema -> 模型返回 tool_use block底层逻辑完全一样,只是从"手写协议"升级到"标准协议"。
Toolformer:模型学会自己调工具
Toolformer 论文(Schick et al., 2023)比 Function Calling 更早,但它证明了模型能学会何时调工具。
Toolformer 的训练:
- 给模型含
[TOOL]占位符的文本 - 模型预测占位符位置和参数
- 执行工具,插回文本
- 筛选"有工具结果"比"无工具结果"更好的样本
- 微调
Toolformer 的工程意义:工具调用的"何时调"和"调什么"可以是模型自己学的,不是工程师写死的。这条结论支撑了本章"工具 description 决定何时用"的设计--description 写得好,模型能学会在对的时机调对的工具。
Anthropic Tool Use 的字段差异
Anthropic 在 2024 年 3 月 Claude 3 发布时同步上线 Tool Use。和 OpenAI 的字段差异:
| OpenAI | Anthropic | |
|---|---|---|
| 工具列表字段 | tools | tools |
| 单工具包装 | {"type": "function", "function": {...}} | 直接 {"name": ..., "input_schema": ...} |
| 参数 schema | parameters | input_schema |
| 调用返回 | tool_calls | tool_use block |
| 结果回传 | role: "tool" | tool_result block |
本章 4.2 节的 schema 示例用的是 Anthropic 格式。这种字段差异是 MCP 出现的动力之一--不同厂商格式不统一,造成"N×M 集成地狱"。第 5 章讲 MCP 怎么解决。
Server Tools:Anthropic 的差异化
本章 4.3 节提到 Client tools vs Server tools。Server tools(web_search / web_fetch / code_execution)是 Anthropic 在 2024-2025 陆续推出的。
Server tools 的工程价值:
- 不用自己写工具实现:Anthropic 在自家基础设施上跑
- 延迟低:工具和模型在同一进程
- 成本可控:按调用计费,不用自己管 API key
但 Server tools 也有局限:只能在 Anthropic 平台用,不能跨 host 复用。这是 MCP(Client tools 的标准化)和 Server tools 共存的原因--两者解决不同问题。
工具 description 的实证
本章 4.5 节法则 3"description 写什么时候用"有实证支撑。Anthropic Tool Use 文档 的 best practices 章节明确建议:
"Write clear, detailed descriptions for each tool and parameter. The description is the primary signal the model uses to decide when and how to use the tool."
Anthropic 的内部测试显示:description 加"何时使用"的说明后,工具调用准确率提升 30%+。这条经验支撑了本章的法则 3。
副作用分级的工程传统
本章 4.5 节法则 5"有副作用的工具要可逆或可审"不是 Agent 时代才有的。它来自传统软件工程的几个原则:
- Unix 权限模型:read / write / execute 三级
- 数据库 ACID:事务可回滚
- CAP theorem 的 C 和 A:一致性 vs 可用性取舍
Agent 时代的版本是 Claude Code 的 Permission 系统:读类放行 / 写类询问 / 删除类必须确认 / 灾难类禁止。这是 Unix 权限模型在 Agent 工具上的迁移。
第 9 章 Runtime & Sandbox 会详细讲 Permission 系统的实现。
工具粒度的反模式
本章 4.5 节法则 1"粒度要合适"有两个反例:
- 太粗:
do_anything(task)-- 早期 AutoGPT 的设计,模型不知道怎么用 - 太细:
open_file()/read_line()/close_file()-- 早期 LangChain Agent 的设计,调用次数爆炸
合适粒度的工程经验:
- 一个工具完成一个用户可理解的动作
- 工具调用次数 ≈ 任务子步骤数
- 不要让模型为了"完成一个动作"调多次工具
这条经验来自 Anthropic Tool Use best practices 和社区的踩坑总结。
- Tool Use = Function Calling,让 LLM 能输出"调工具"的结构化指令。
- 工具 schema 三要素:name(动词开头)、description(写"什么时候用")、input_schema(参数)。
- Anthropic 流程:tools → tool_use → tool_result → 继续。
- 工具分四类:检索 / 操作 / 计算 / 通信。
- 工具设计 5 法则:粒度合适 / 命名清晰 / description 写场景 / 错误返回 / 副作用可控。
下一章我们讲 MCP——怎么让工具跨平台复用,写一次到处用。