跳到主要内容

工具 — 怎么找到它、怎么调它、怎么把结果送回模型

这一章讲三件事: 一个工具送到客户端手里时长什么样;模型怎么表示「我要用它」; 以及跑完之后结果怎么送回模型才算完整。

它在全书链条里的位置: 这是全书的主干。 第 06 章的两类东西、第 07 章的反向能力、第 09 章的规模问题, 全部是这一章那个四步循环的变体或它的后果。

1. 三类东西,同一个套路

这一节先给一张地图,省得你把后面两章当成三套新东西来学。

服务器能提供的东西一共三类,书里管它们叫原语—— 协议规定好的那几种基本零件,不能再往下拆:

这一类干什么用这本书在哪一章讲
工具让模型动手改变外面的东西本章
只读数据让模型看到它本来看不到的东西第 06 章
写好的话术让用户挑一段现成的话发给模型第 06 章

三类的调法完全一样,而且只有两步:先要一张清单,再挑一个用1

① 要清单 tools/list resources/list prompts/list
② 挑一个用 tools/call resources/read prompts/get
↑ ↑ ↑
本章讲 第 06 章 第 06 章

图说:三列的形状一模一样,只是方法名不同。
学会第一列,另外两列只需要记住动词换成了什么。

所以本章其实是在教一套通用动作。 后面遇到新的一类东西,你要问的第一个问题永远是: 「它的清单方法叫什么、它的使用方法叫什么。」

2. 列清单:一个工具在客户端手里长什么样

这一节是主走查的第 1 步。

本章主走查的输入:用户在聊天框里打了一句「12 乘以 30 是多少」。 连着的是第 04 章那台计算器服务器。下面出现的具体字串和数值, 除了四个工具名来自书里,其余是我们按规范写的示例。

客户端向服务器要一张清单,拿回来四条,每条长这样:

里面有什么这条的值干什么用
名字multiply_two_numbers之后调用时靠它指认
说明「把两个数相乘」模型就是靠这句话决定要不要用它
参数表a(数字,必填)、b(数字,必填)告诉模型该填什么
其他附注(这台没填)比如「这个工具会改东西,别乱试」

书里列的属性名是 namedescriptioninputSchemaannotations2

中间那个 inputSchema 就是表里那一行「参数表」—— 一份写清「这个工具收什么参数」的表:每个参数叫什么、是什么类型、哪些必填。 这份写法本身也不是 MCP 发明的,它有自己的名字叫 JSON Schema(JSON 数据的格式说明书)。

参数表里那个「类型」只有几种可填,最常见的是三种:数字、真假、 以及字符串——一串连在一起的文字,比如一个文件路径、一个人名。 这台计算器的 ab 都是数字,所以模型填 1230,不会填 "十二"

为什么它必须存在? 因为模型不会读你的源码。 它能看到的只有这张表和上面那句说明——说明写得含糊,它就挑错;参数表写得不全,它就填错。 (这一点会在第 09 章变成一个真正的麻烦。)

书里对工具本身的定义很朴素:一个大语言模型可以在需要时决定去调用的、确定性的函数; 函数里想干什么都行,从算数到查天气3它并不神秘,神秘的只是「谁来决定调它」。

3. 模型怎么表示「我要用这个工具」

这一节是主走查的第 2 步,也是最容易被误解的一步。

宿主应用把那四条工具描述,连同用户那句「12 乘以 30 是多少」,一起发给模型。 这是第一次调用模型。

模型回过来的东西不是答案,是一个停顿信号:

模型的回复:
stop_reason = "tool_use" ← 这个字段就是停顿信号
内容里带着一块:
name = "multiply_two_numbers"
input = { "a": 12, "b": 30 }
id = "toolu_01ABC…" ← 这次请求的编号,等下要原样带回来

图说:模型没有执行任何东西。它只是写下了「我要用哪个、参数是什么」,
然后把球踢回给宿主应用。

书里对这一格的说法是:如果回复的 stop_reasontool_use, 那就说明模型正在等你把它请求的那个工具的结果回给它4

两个细节值得记住:

第一,模型可能一次要求调好几个工具。 所以宿主那一侧要把回复里所有工具请求 挑出来做成一个列表,挨个跑5。我们这个例子只有一个。

第二,那个 id 必须原样带回去。 因为一次要好几个的时候, 结果得靠它认领是哪一个的

拿到这块之后,宿主应用去调服务器:方法名 tools/call, 参数是 multiply_two_numbers{a: 12, b: 30}这是全程唯一一次调服务器。

4. 调完拿回来的不是一段文字,而是一串块

这一节是主走查的第 3 步。

服务器回过来的东西,不是一个字符串,是一个列表6。列表里的每一项叫一个内容块—— 一小段带类型标签的内容:标签说明这是什么,后面跟着这一块的正文。

我们这次拿到的是:

[ { type: "text", text: "360" } ]

图说:只有一块,类型是文字,正文是「360」——正好是 12 × 30。

但客户端必须准备好认领别的类型,因为工具想返回什么都行。

先立一个量词,后面几章要反复用它算账。 字节就是计算机存东西时用的那个计量单位:一个英文字母占 1 个字节, 一个汉字通常占 3 个。 一张 300 KB 的图片,大约就是 30 万个字节。

块的类型正文放在哪长什么样
文字text一段字符串,直接能用
图片data一坨 base64(把二进制改写成普通字母数字的一种办法,下面细说)
音频data同上,也是 base64

base64 需要当场解释一句:它是把二进制数据(图片、音频这种) 改写成一串普通字母数字的办法,这样就能塞进只能装文字的地方。 代价是体积会涨正好三分之一——一张 300 KB 的图片写成 base64 是 400 KB7。 所以能用地址指过去的时候,别把整坨内容塞进来。

除了这三种,还有两种块认不出来——它们跟第 06 章要讲的那类只读数据有关。 等第 06 章讲完,我们会回头把这两种补上。 在此之前,你的客户端遇到它们 应当至少打一条警告,而不是当作空结果丢掉。

一条容易忽略的设计选择: 书里在这里插了一句提醒—— 在客户端里把这些类型处理掉,未必是好事。 你要想清楚:用你这个客户端的人,想不想自己决定怎么处理图片和音频?8 处理得越早,别人能做的选择越少。

5. 把结果送回模型,再问第二遍

这一节是主走查的第 4 步。工具跑完了,但活还没完。

一个常见的错误是:拿到「360」就直接打给用户。那样用户看到的是一个孤零零的数字, 既没有单位也没有解释;而且如果模型本来打算调两个工具、再把结果合起来算, 你就把它的活干砸了。

正确做法是:把结果送回模型,再问一遍。这是第二次调用模型。

书里写明这一次必须带三条消息9:

第几条角色内容少了它会怎样
用户「12 乘以 30 是多少」模型不知道最初要干什么
助手模型上一轮那块工具请求(原样)模型看不懂自己刚才要过什么
用户工具结果:tool_use_id = 那个 toolu_01ABC…,内容 = 360模型不知道结果是什么、是哪一次的

第 ② 条最容易漏。 它看起来是多余的——结果都给它了,还要它自己那句话干什么? 但模型每一轮都是从零读起的:你不把它上一轮说的话原样贴回去, 它就不知道 360 是谁的结果。

模型收到这三条,回:「12 乘以 30 等于 360。」到这里这一圈结束。

整条走查回看一遍

用户:「12 乘以 30 是多少」

├─ ① 要清单 ────→ 服务器回 4 个工具(加减乘除)

├─ ② 第一次调模型(四条工具描述 + 用户那句话)
│ ← 回:stop_reason=tool_use,multiply_two_numbers(a=12, b=30)

├─ ③ 调服务器一次 ──→ 回:[ {type:"text", text:"360"} ]

└─ ④ 第二次调模型(三条消息)
← 回:「12 乘以 30 等于 360。」

图说:两次模型,一次服务器。第 ① 步通常在程序启动时做一次就够,
所以每一轮对话的固定开销是「两次模型」。

书里本来准备为这条流程画一张时序图,正文里留着作者自己的待办: 「☐ Todo - 画一张 App、Model、Server 的时序图」10图没有画出来。

6. 谁来决定用不用工具:那个开关有四挡

这一节澄清一个常见误解:把工具给了模型,它就一定会用吗?不一定,而且你能控制。

模型厂商的接口上有一个开关,四挡11:

挡位意思什么时候用
自动(默认)模型自己看着办,用不用、用哪个都由它绝大多数情况
至少用一个必须用工具,但用哪个由它挑你确定这轮一定要动手
指定这一个必须用你点名的那一个你已经知道该干什么,只是要它填参数
不许用这一轮一个都不许用你只想让它说话

这个开关有一件事必须记清楚:它是模型厂商的,不是 MCP 的。 书里在这一节明说,例子用的是 Anthropic 的接口,而且有些模型根本没有这个位置—— 那种情况下你只能把工具的名字、说明和参数表写进发给模型的那段话里12

这句话在第 09 章会变成一整节:同一份工具描述,喂给不同厂商的模型要换不同的形状。

7. 结构化返回:后来补上的第二条出口

这一节讲一个书里没有、但现在你一定会遇到的东西。

回看第 4 节:工具返回的是给人看的文字。这在聊天场景没问题, 但如果接工具结果的不是人、是另一段程序呢? 那段程序就得自己去把「360」这样的一串字拆开、认出哪一部分是什么,或者更糟——去拆一整段自然语言。

规范后来补了第二条出口:工具除了那串给人看的块,还可以额外交一份 按约定格式写好的数据,叫结构化返回;而工具描述里也可以多写一份输出格式说明, 说明这份数据长什么样——和参数表是同一套写法,只是描述的是出去的那一头13。 同一次调用两份都给,谁需要谁取。

它真正改变的是什么: 有了这条出口,把一串工具串起来自动跑才成立—— 上一个工具的输出可以直接喂给下一个工具,中间不必让模型当翻译。 这正是第 09 章最后那条路(让模型写代码去调工具)的前提。

8. 边界与一笔欠账:工具一多,模型就挑不准

这一节是本章唯一的坏消息,而且它是书里自己给的。

书里在这一章插了一条警告,措辞相当重:

随着你往智能体上加工具,工具调用和整体表现会「急剧下降」; 典型原因是挑不准该用哪个工具,而挑不准又有两个来源: 工具说明之间含糊或重叠,以及说明和参数表加起来信息量太大,模型处理不准14

注意它给的是两个不同的病因,治法也不同:

病因长什么样该怎么治
说明重叠两台服务器都有一个叫「搜索」的工具,说明也差不多改名、改说明、按相关度排序
总量太大五台服务器一百多个工具,说明书比用户那句话长几十倍别一次全塞给模型

这两条治法都在第 09 章。 本章先把账记下: 工具越多越强,这个直觉是错的。

这一章还有什么是书里没有的

内容来源
三类东西的两步套路、工具的属性、内容块的类型、stop_reason、三条消息、四挡开关、性能警告书里有
结构化返回与输出格式说明官方规范(第 7 节,脚注 12)
那张时序图书里留着待办,没有画10

9. 可带走的

  1. 三类东西共用一个套路:先要清单,再挑一个用——学会工具这一套,另外两类只换方法名;
  2. 一条工具描述里有四样:名字、说明、参数表、附注;模型全靠说明和参数表做判断;
  3. 参数表(inputSchema)的写法是现成的 JSON Schema:每个参数叫什么、什么类型、必不必填;
  4. 模型不执行工具,它只回一个停顿信号(stop_reason = tool_use)加一块「我要调 X、参数 Y」;
  5. 那块里的 id 必须原样带回去——一次要好几个工具时,靠它认领谁是谁的结果;
  6. 工具返回的是一串块,不是一个字符串:文字、图片、音频各有类型,图片音频是 base64(体积涨约三分之一);
  7. 跑完必须把结果送回模型再问第二遍,而且要带三条消息;最容易漏的是模型自己上一轮那条;
  8. 一圈的固定开销是两次模型、一次服务器;
  9. 用不用工具有个四挡开关,但它是模型厂商的,不是 MCP 的;有的模型根本没有这个位置;
  10. 工具一多,选择准确率会急剧下降——两个病因(说明重叠、总量太大),两种治法都在第 09 章。

10. 原文地图

主题原书章原文位置
两步套路、清单与使用的方法名Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:7(搜「discovery and use」)
三类原语Example: A Simple Host Applicationtext/06-fm-example-a-simple-host-application.txt:234(搜「The three MCP primitives are tools」)
工具的定义Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:37(搜「a deterministic function that an LLM can」)
工具的属性Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:47(搜「name, description, inputSchema, annotations」)
内容块的类型、base64Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:94(搜「TextContent」) · :99(搜「base64-encoded string in the data property」)
返回的是列表不是字符串Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:111(搜「return lists of」)
要不要在客户端处理类型Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:120(搜「consider your user」)
停顿信号Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:289(搜「then we know the model is waiting」)
送回模型的三条消息Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:301(搜「we send a list of messages back to the model」)
四挡开关Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:280(搜「setting tool_choice to」) · :285(搜「to prevent the model from using any available tools」)
有的模型要把工具写进那段话里Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:181(搜「require building a list」)
工具一多性能下降Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:171(搜「Performance tends to drop dramatically」)
时序图待办Interacting with MCP Server Capabilitiestext/08-fm-interacting-with-mcp-server-capabilities.txt:190(搜「sequence diagram」)

Footnotes

  1. 出处:「Interacting with MCP Server Capabilities」第 7 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:7,搜「discovery and use」)。原文的规律写得很整齐:清单一律是 <原语>/list,使用一律是 <原语>/<动词>,动词随原语而变;Python 工具包里则包成 list_<原语>s()<动词>_<原语>()

  2. 出处:「Interacting with MCP Server Capabilities」第 47 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:47,搜「name, description, inputSchema, annotations」)。补充(不在书里):官方规范里一条工具还可以带 title(给人看的显示名)和 outputSchema(输出格式说明),后者见本章第 7 节。来源:MCP 官方规范工具页 https://modelcontextprotocol.io/specification/2026-07-28/server/tools(查阅于 2026-08-25)。

  3. 出处:「Interacting with MCP Server Capabilities」第 37 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:37,搜「a deterministic function that an LLM can」)。原文还说工具是服务器提供得最多的一类,理由是智能体式的活儿常常需要工具来补足模型本身的能力。

  4. 出处:「Interacting with MCP Server Capabilities」第 289 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:289,搜「then we know the model is waiting」)。代码里那一行判断见第 233 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:233,搜「stop_reason」)。

  5. 出处:「Interacting with MCP Server Capabilities」第 291 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:291,搜「more than one tool call」)。原文的做法是:先过滤出所有工具请求做成一个列表,再逐个调用,每调完一个就往回传的消息里追加一块结果。

  6. 出处:「Interacting with MCP Server Capabilities」第 111 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:111,搜「return lists of」)。四种块类型的逐条说明见第 94 段起(text/08-fm-interacting-with-mcp-server-capabilities.txt:94,搜「TextContent」)。

  7. 补充(不在书里,来自通用知识):base64 的换算规则是每 3 个字节编成 4 个字符,所以体积正好涨三分之一(4 ÷ 3 ≈ 1.33):300 KB × 4 ÷ 3 = 400 KB。这个 3 换 4 的规则出自 base64 这套编码本身的定义(RFC 4648 第 4 节),不是 MCP 定的;书里只说了图片和音频用 base64,没有给过体积的账。 第 06 章那笔「3 MB 的运行记录嵌进来实际是 4 MB」用的是同一条规则。

  8. 出处:「Interacting with MCP Server Capabilities」第 120 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:120,搜「consider your user」)。原文的原话是:对某些用法来说,在客户端里就把这些类型处理掉可能并不可取——做客户端的时候要替你的用户想一想,他们会不会想自己掌控不同内容类型的处理方式。

  9. 出处:「Interacting with MCP Server Capabilities」第 301 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:301,搜「we send a list of messages back to the model」)。原文列的三条依次是:用户消息、带着原始工具请求内容的助手消息、以及前面拼好的整份工具结果列表。

  10. 出处:「Interacting with MCP Server Capabilities」第 190 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:190,搜「sequence diagram」)。作者的待办原文写着要画一张包含 App、Model、Server 三方的时序图来解释上面这条流程——图没有出现在这一版里,所以本章那张流程图是我们照文字重建的。 2

  11. 出处:「Interacting with MCP Server Capabilities」第 280 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:280,搜「setting tool_choice to」)与第 285 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:285,搜「to prevent the model from using any available tools」)。四挡的原文取值依次是 autoany、指定工具名加类型 tool、以及 none

  12. 出处:「Interacting with MCP Server Capabilities」第 181 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:181,搜「require building a list」)。原文说很多支持工具调用的接口有一个可选的 tools 参数,但另一些模型要求你把工具的名字、说明和格式说明书拼进系统提示词里;本书的例子选的是 Anthropic 的接口,因为它有这个参数。

  13. 补充(不在书里):官方规范里,一次工具调用的结果除了给人看的 content 块列表,还可以带一个 structuredContent 字段,里面是一份任意 JSON 值;工具定义里对应的 outputSchema 说明这份值长什么样。规范同时提醒:客户端必须outputSchema 校验这份值。来源:MCP 官方规范工具页 https://modelcontextprotocol.io/specification/2026-07-28/server/tools(查阅于 2026-08-25)。

  14. 出处:「Interacting with MCP Server Capabilities」第 171 段(text/08-fm-interacting-with-mcp-server-capabilities.txt:171,搜「Performance tends to drop dramatically」)。原文用的词是 dramatically(急剧),两个病因分别是「工具说明含糊或重叠」和「说明与接口加总的信息量太大,模型没法准确处理」。书里只提出问题,没有给解法——解法在本组拆解的第 09 章,取自官方的客户端最佳实践文档。