Skip to content

第 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。看个例子:

python
{
    "name": "search_papers",
    "description": "在 arxiv 上搜索学术论文。当用户想找论文、查研究进展时使用。",
    "input_schema": {
        "type": "object",
        "properties": {
            "query": {
                "type": "string",
                "description": "搜索关键词,英文"
            },
            "max_results": {
                "type": "integer",
                "description": "返回结果数量,默认 10",
                "default": 10
            }
        },
        "required": ["query"]
    }
}

三个关键字段:

  1. name:工具名,要短、动词开头、表意清晰。search_paperspaper_search 好。
  2. description:最重要!LLM 决定何时用这个工具,全靠这段描述。要写清"什么场景下用"。例子里写了"当用户想找论文、查研究进展时使用"。
  3. 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 blockClaude 说"我要调这个工具"
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_searchweb_fetchcode_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_data

LLM 看 name 决定调不调,名字差一点效果差很多。

3. description 写"什么时候用",不只写"是什么"

❌ "搜索 arxiv 论文。"
✅ "在 arxiv 上搜索学术论文。当用户想找论文、查研究进展、或需要引用文献时使用。"

加"什么时候用"能让 LLM 在对的时机调对的工具。

4. 错误要返回给 LLM,不要吞

python
# ❌ 错误吞掉
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 里告诉模型:

text
你有一个工具 get_current_time。
如果你需要调用它,请输出:
FUNCTION get_current_time

当用户问"现在几点",模型输出 FUNCTION get_current_time,开发者的程序用正则匹配这个标记,然后执行真实函数,把结果放回 context,再让模型生成最终回答。

完整链路

text
用户提问
-> 模型判断需要工具
-> 模型输出 "FUNCTION get_current_time"
-> 应用程序解析字符串
-> 应用程序执行真实函数
-> 函数结果返回
-> 应用程序把结果加入对话历史
-> 模型基于工具结果生成最终回答

这个史前协议揭示了一个关键事实:模型本身不执行函数

很多人误以为"模型调用工具"--更准确的说法是模型请求调用工具,真正执行函数的是应用程序。完整职责划分:

角色职责
模型判断是否需要工具,生成工具调用请求
客户端/框架解析请求,调用真实函数,把结果放回 context
应用代码执行真实函数,处理错误,管理多轮调用

这个划分在现代 Function Calling API 里被封装起来了--框架自动处理解析、调用、回传。但理解早期协议的工程价值在于:它揭穿了"模型调用工具"的魔法--工具调用不是模型的能力,是模型 + 应用程序的协议。

现代工具语法(本章 4.2 节的 tools 参数)只是把这个协议标准化了:

text
早期:prompt 约定 "输出 FUNCTION xxx" -> 正则解析
现代:tools 参数传 schema -> 模型返回 tool_use block

底层逻辑完全一样,只是从"手写协议"升级到"标准协议"。

Toolformer:模型学会自己调工具

Toolformer 论文(Schick et al., 2023)比 Function Calling 更早,但它证明了模型能学会何时调工具

Toolformer 的训练:

  1. 给模型含 [TOOL] 占位符的文本
  2. 模型预测占位符位置和参数
  3. 执行工具,插回文本
  4. 筛选"有工具结果"比"无工具结果"更好的样本
  5. 微调

Toolformer 的工程意义:工具调用的"何时调"和"调什么"可以是模型自己学的,不是工程师写死的。这条结论支撑了本章"工具 description 决定何时用"的设计--description 写得好,模型能学会在对的时机调对的工具。

Anthropic Tool Use 的字段差异

Anthropic 在 2024 年 3 月 Claude 3 发布时同步上线 Tool Use。和 OpenAI 的字段差异:

OpenAIAnthropic
工具列表字段toolstools
单工具包装{"type": "function", "function": {...}}直接 {"name": ..., "input_schema": ...}
参数 schemaparametersinput_schema
调用返回tool_callstool_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——怎么让工具跨平台复用,写一次到处用。

基于 CC BY-SA 4.0 发布