Chapter 04

工具设计 + 前沿生态

前三章讲清了机制:协议、循环、终止。这一章转向工程——怎么设计让模型真正用得好的工具——再把镜头拉到 2025–2026 的前沿生态。

本章你将建立的 schema

  • 工具的 description 是模型读的 prompt,是工具性能的最高杠杆
  • 命名 / 参数 / 返回值 / 错误 / 数量 的设计取舍
  • 设计工具本质是 prompt engineering(poka-yoke、token 经济)
  • 前沿:MCP(M×N→M+N)、模型写代码调工具、Tool Search、computer use、Responses API

前三章把工具调用拆到了机制层:协议长什么样、循环怎么转、模型在哪里停机、谁来决定终止。机制讲清之后,剩下的问题是工程问题——同样一套协议,为什么有的工具模型用得又准又省,有的工具模型反复选错、填错参数、卡死在循环里?差距几乎全在工具设计上。这一章先讲设计的五个维度,再讲设计背后的取舍,最后把镜头拉到 2025–2026 真正在动的前沿生态。

4.1description 是最高杠杆

工具的 description 不是文档,是写给模型读的 prompt——它决定模型选不选这个工具、填不填对参数。

一个工具定义里有 name、description、参数 schema 三部分。直觉上 schema 最重要——它定义了数据结构。但 Anthropic 在《Writing effective tools for agents》里把工具 description 称为决定工具性能的「by far the most important factor」(影响最大的单一因素)。原因在下一段,先看怎么写。

一条合格的 description 至少 3–4 句,覆盖四件事:

  • 做什么——这个工具的功能边界。
  • 何时用、何时不要用——这是最容易漏、也最值钱的一句。模型靠它在多个相似工具之间做选择。
  • 每个参数的含义与格式——不只是类型,还有取值约定(绝对路径还是相对路径?时间戳还是 ISO 字符串?)。
  • 边界——这个工具不返回什么、不处理什么情况。

一个实用的标尺:把 description 当成给一位刚入职、对系统一无所知的新同事写的上手说明。新同事看完能不能不追问就正确使用?模型的处境与此完全相同——它对工具背后的实现一无所知,全部判断依据就是这几句文本。

底层机制(呼应 02 章「模型怎么生成一个调用」):模型内部没有任何「语义分发」逻辑。它不会理解「这个工具是查数据库的」然后路由过去。选哪个工具、每个参数填什么,全部是在 name + description 这段文本上做的下一 token 预测。description 含糊,等价于给预测过程喂了噪声,会直接降低模型选对工具、填对参数的概率。这条因果链解释了一个反直觉的现象:改 description 往往比换一个更强的模型,更能提升工具调用的准确率——因为瓶颈不在模型的智力,在它读到的那段文本。

洞察 · 设计即 prompt engineering

一个工具定义里,代码量最大的常是 input_schema,但影响模型行为最大的是 description。这把「工具设计」从一个接口设计问题,变成了一个 prompt 设计问题——优化的对象不是数据结构,是模型读到的那段自然语言。

4.2命名与参数

命名是 description 之外的第二高杠杆,因为 name 同样进入模型的选择预测。

清晰、不重叠的名字。 参数叫 user_id,不要叫 user——后者到底是用户名字?整个用户对象?还是 id?模型读到 user 时只能猜,猜错就填错。工具之间也一样:两个职责重叠或名字含糊的工具(比如 get_data 和 fetch_info)会让模型在选择时摇摆,准确率直接下滑。

参数设计。 两条经验:

  • 取值有限时,用 enum 限定优于自由字符串。status: enum["active","archived"] 让模型不可能填出第三种值;status: string 则把校验责任推回给模型的自由发挥。
  • 必填和可选要分清,并在 schema 的 required 里如实声明。模型靠它判断哪些参数必须凑齐才能发起调用。

Poka-yoke(防错设计)。 这个词来自制造业,指「让错误根本无法发生」的设计——插头做成只能正插的形状,就不可能插反。工具设计里同样适用:改变参数的形状,让一整类错误在结构上不可能出现。Anthropic 给过一个真实案例:某个文件工具原本接受相对路径,模型经常因为搞不清当前工作目录而拼出错误路径;把参数改成要求绝对路径后,这一整类路径相关的错误「直接消失」。注意这里的关键——它不是靠在 description 里多写一句「请注意路径」实现的,而是靠参数形状的改变实现的。这是 prompt 改不出来的那种修复。

想一想

给模型 50 个工具,和给它 5 个精心设计的工具,哪种工具选择准确率更高?

展开答案

通常是 5 个。工具越多,定义之间越容易出现职责重叠,模型越难在它们之间选准;而且每个定义都要占 context,50 个工具的定义会把上下文撑得又长又吵,反过来拖累模型对全局的判断。少而高杠杆 > 一堆薄包装——这条结论会贯穿后面的 §4.5 和 §4.7。

4.3返回值设计

工具的返回值会原样进入 context,成为模型下一轮预测的输入。所以返回什么、返回多少,既影响模型后续的判断质量,也直接决定 token 成本。

返回高信号、人类可读的字段。 优先返回有语义的标识(name、可读的状态文字),而不是低层 id(uuid、mime_type 这类机器标识)。原因:模型后续若要引用某条结果,语义化的标识(「张三的订单」)比一串 uuid 更不容易被记错、编错——低层 id 会诱发检索幻觉,模型容易凭空拼出一个格式正确但不存在的 id。

给一个详略开关。 用类似 response_format: enum["concise","detailed"] 的参数,让模型按需取详略。Anthropic 的实测数据:同一条结果,concise 模式能从 206 token 压到 72 token,约为原来的三分之一。在一个多轮、多次调用的 agent 里,这种压缩会逐轮累积。

分页 / 过滤 / 截断要有合理默认。 一个返回列表的工具,默认就该分页或截断,而不是一次吐回上万条。这里要把返回内容的量和质放在同等位置:返回内容会原样进入 context,撑大的不只是这一轮,而是之后每一轮——因为每轮都要把含这段结果的完整历史重发给模型(呼应 03 章讲的 re-prefill 成本)。一次臃肿的返回,成本会被乘上剩余的轮数。

4.4错误返回(呼应 03.4)

工具会失败——参数缺了、取值非法、下游超时。错误怎么返回给模型,直接决定 agent loop 是能自我纠正继续走、还是当场卡死。

正确做法:返回结构化的错误标记 + 一句模型可读的信息,讲清两件事——缺了什么、合法取值是什么。在 Anthropic 的协议里,这通过给 tool_result 标记 is_error 并在其内容里写明原因来实现。模型读到「status 取值非法,合法值为 active / archived」这种信息,下一轮就能自己改对参数重试。

错误做法:把原始的 stack trace 直接塞回去。一串 Python traceback 对模型几乎是噪声——它定位不了你的代码行号,只会被这段长文本干扰,既浪费 token 又提高跑偏概率。错误信息的受众是模型,不是看日志的工程师,要按这个受众来写。

4.5工具数量与消歧

§4.1 的预测题已经给了结论:少而高杠杆。这里讲怎么落地。

把工作流合并成高层工具。 与其暴露一堆底层端点让模型自己编排,不如提供一个已经把流程封好的高层工具。一个 schedule_event(内部自己查空闲、再建日程)好过让模型先调 find_availability 再调 create_event——后者把编排负担推给了模型,每一步都是一次会出错、会卡住的调用。同理,search_contacts(带查询条件、只返回匹配项)好过 list_contacts(把全部联系人塞进 context)。

工具多了用 namespace 切分。 当工具确实多(接了多个外部系统),给名字加前缀来消歧:asana_search / jira_search 而不是两个都叫 search。前缀既减少模型的选择歧义,也让人读对话日志时一眼看清调的是哪个系统。

这两条都在对抗同一个东西——工具定义的膨胀。当工具数量大到连 namespace 也压不住时,前沿已经有了「按需加载工具」的方案,不再一次性把所有定义塞进 context(见 §4.7 的 Tool Search Tool)。

4.6备选方案权衡表(为什么这样设计)

很多设计选择,只有看到它「放弃了什么」才真正讲得通。前三章的两个核心机制——JSON Schema 接口、回合制循环——都是在一组备选里选出来的,各自带着代价。

表 4.1 · 协议层的两个关键取舍
关键决策 被放弃的备选 为什么放弃 选中方案的代价
工具接口用 JSON Schema 自由文本 / 自定义 DSL 自由文本要靠正则抽取、易碎;DSL 每种都要专门写解析器,无法通用 失去自由文本的表达力,换来通用、可校验、且模型在预训练里见得多的格式
用回合制循环执行 让模型直接调函数 模型是无状态文本预测器,副作用 / 鉴权 / 执行不可信代码必须发生在模型之外 模型每停机一次就交还控制,每一步都要把变长的历史重发一遍(re-prefill 成本)

这两个取舍就是前三章机制的「为什么」:选 JSON Schema 解释了 02 章看到的协议形状——为什么调用长成那种结构化的样子;选回合制循环解释了 03 章的停机与 re-prefill——为什么模型会停、为什么多轮 agent 的 token 会随轮数累积。下一节的前沿生态,本质上就是在这两个代价上做文章。

4.7前沿生态(截至 2026-06)

工具调用的核心协议已经稳定——JSON Schema 接口 + tool_use / tool_result 循环,三大厂商通用,2023 年就定型了。真正在动的是它周围的生态。下面四块按「解决什么问题」来讲,每个论断都带日期。

2022 ReAct 2023 function calling 2024-10 computer use 2024-11 MCP 2025-03 Responses API 2025-11 代码调工具 / Tool Search 2025-12 MCP→Linux Found.
图 4.3工具调用生态的时间线(截至 2026-06)。注意:协议核心 2023 年就稳了,2024 下半年起真正在动的是「标准化(MCP)」和「让模型写代码而不是吐 JSON」这两条线。

MCP(Model Context Protocol):把 M×N 集成压成 M+N

问题:有 M 个 agent 应用、N 个工具 / 数据源。过去每个应用要接每个工具,得两两对接——这是 M×N 的集成爆炸。每加一个数据源,所有应用都要再写一遍接入代码。

MCP(Model Context Protocol,模型上下文协议)定义了一套开放标准:工具 / 数据源只要实现一次 MCP server,任何 MCP client(agent 应用)就都能接入。集成量从 M×N 降到 M+N——M 个应用各实现一次 client,N 个工具各实现一次 server,再没有两两组合。

协议模型分三部分:client(在 agent 应用一侧)、server(在工具 / 数据源一侧)、transport(两者之间的传输层)。传输现在有两种:stdio(标准输入输出,用于本地进程)和 Streamable HTTP(用于网络,2025-03 起取代了旧的 HTTP+SSE 传输)。

时间线:Anthropic 于 2024-11 提出 MCP;2025 年被三大厂商相继采纳——OpenAI(3 月)、Google(4 月)、微软(5 月);2025-12 捐给 Linux Foundation,成为厂商中立的标准。一句话意义:工具接一次,所有支持 MCP 的 agent 都能用。

没有 MCP:M×N App1 App2 App3 工具A 工具B 工具C 有 MCP:M+N App1 App2 App3 MCP ServerA ServerB ServerC
图 4.1MCP 把两两对接的集成爆炸,收敛成「都接到一个标准」。注意:左边连线数随 M×N 膨胀,右边只随 M+N 增长——这就是它在 2024-11 出现后被各家迅速采纳的原因。

模型写代码调工具:从「吐 JSON」到「写代码」

Anthropic 于 2025-11 推出 Code execution / Programmatic Tool Calling(编程式工具调用)。转变是这样的:传统方式下,模型每用一次工具,就要吐出一个 JSON 调用、round-trip(往返)过模型一次、拿回结果、再吐下一个——每一步都穿过模型。Programmatic Tool Calling 让模型改为写一段代码,在沙箱里把多个工具串起来调用,中间数据留在沙箱里、不进 context,只把最终结果带回模型。

省的是 token 和延迟:中间那些一次性的、模型根本不需要看的数据(比如取了 1000 条记录只为算一个总和),不再逐条穿过 context。这正是「前沿正从吐 JSON 转向写代码」的核心转向——也正是它要消掉 03 章那个 re-prefill 成本:少一次 round-trip,就少一次把完整历史重发给模型。

吐 JSON:每步过模型 模型 工具1 工具2 工具3 中间结果都进 context 写代码:沙箱里串起来 模型 工具1 工具2 工具3 只回最终结果 spacer
图 4.2两种调工具方式的数据路径。注意:上面每个中间结果都要穿过模型的 context(贵、慢);下面把多步留在沙箱里,只把最终结果带回——这就是 2025-11「模型写代码调工具」省 token 的来源。

Tool Search Tool:工具定义按需加载

同期(2025-11,beta):Tool Search Tool 直接修 §4.5 末尾说的「工具定义膨胀」。做法是不再把所有工具定义一次性塞进 context,而是先给模型一个「搜工具」的元工具,让它根据当前任务搜出需要的工具再加载。Anthropic 称这能省下约 85% 的工具定义 token——对一个接了几十上百个工具的 agent,这是把上下文从一开始就吃满,变成用多少加载多少。

computer use:工具调用去操作屏幕

Anthropic 自 2024-10 起推的方向:把「截图 → 给出点击坐标 / 键盘动作」这件事也封装成工具调用,让模型直接操作图形界面。本质仍然是 02、03 章那个 tool_use 循环,没有新协议——只是工具的语义变成了「看屏幕」(返回截图)和「动鼠标 / 敲键盘」(接受坐标和按键)。它把工具调用的边界从「调 API」扩展到了「操作任意有界面的软件」。

OpenAI Responses API + 被取代清单

OpenAI 于 2025-03 推出 Responses API,作为 Chat Completions 与 Assistants API 的后继接口,内置了 web_search / file_search / code_interpreter / computer use / 远程 MCP 等能力。学习时要避开几处已被取代的旧写法:

  • 旧的函数调用参数:functions / function_call → 改用 tools / tool_calls。
  • Assistants API(2025-08 宣布弃用、2026-08-26 停用)→ 迁移到 Responses API。
  • MCP 的 HTTP+SSE 传输 → 改用 Streamable HTTP。

合上页面,用自己的话回答下面三题。卡壳的地方就是这一章真正没读进去的地方——回到对应小节重看,再展开答案对照。

§本章 self-check

  1. 一个工具调用准确率低,你只能改一处,最先改哪里?为什么是它?
  2. MCP 解决的核心问题用一句话说是什么?「M×N → M+N」具体指什么?
  3. (设计层)「模型写代码调工具」相比「每步吐 JSON」,省的到底是什么?联系 03 章的 re-prefill 成本回答。
展开参考答案

1. 先改 description(含命名)。因为模型没有语义分发,选哪个工具、填什么参数全是在 name + description 文本上做下一 token 预测;description 是「by far the most important factor」,改它往往比换更强的模型更有效——瓶颈通常在模型读到的文本,不在模型的智力。

2. 一句话:把 agent 应用与工具 / 数据源的集成方式标准化。「M×N → M+N」指——过去 M 个应用要分别对接 N 个工具,组合数是 M×N;MCP 让每个工具实现一次 server、每个应用实现一次 client,组合消失,集成量降到 M+N(M 个 client + N 个 server)。

3. 省的是中间结果穿过模型的次数与 token。每步吐 JSON 时,每次工具往返都把结果塞进 context,且下一轮要把含全部历史的请求重发一遍(re-prefill)。写代码则把多步留在沙箱、中间数据不进 context,只回最终结果——少了多次 round-trip,就少了多次 re-prefill 的累积成本。

进阶挑战 · 刚好够不着

重新设计一个糟糕的工具

有个工具 list_users,返回每个用户的全部字段(含 uuid、mime_type、内部状态码)。从命名、参数、返回值、错误处理四个角度重新设计它,并说明每处改动为模型省了什么。

提示(卡住再展开)

想想:默认是否该分页 / 过滤?返回里哪些低层 id 可以删?要不要 response_format 开关?字段名是否语义化到模型不会认错?