跳到主要内容

采样 — 服务器反过来求你的模型帮忙

这一章讲三件事: 协议里最反直觉的一个设计——消息方向倒过来; 这个倒转解决了什么真问题(服务器的「生成力」从哪来); 以及两端各自怎么实现。 到这一章,第 02 章埋的那张牌(「sampling 第 08 章讲」)兑现。

1. 先看别扭之处:方向倒了

前五章建立的世界观是:客户端发请求,服务器干活。这一章把它倒过来。

场景是书里的电商后台:运营录了一个新商品「tomato」,只填了名字和关键词 (red、vegetable、delicious)。商品描述谁来写? 写描述是生成任务,要模型; 可模型在客户端那边(第 06 章的分工),服务器这边只有数据1

MCP 的答案叫采样(sampling)——服务器在处理一次请求的途中, 反过来向客户端发一个请求:「这段内容,请你的模型帮我生成一下」2。 (这个名字起得绕。它借的是字典里「取样分析」的意思—— 服务器把「样本」发给客户端分析。它和大语言模型调温度** (控制回答随机程度的旋钮——越高越天马行空、越低越保守)时的「采样」是两个概念,别混**; 后者是「按可能性清单随机挑下一个词」,见《这就是 ChatGPT》拆解3。)

正常方向: 客户端 ──tools/call──────────► 服务器
采样方向: 客户端 ◄──sampling/createMessage── 服务器(干到一半,回头求助)
客户端 ──生成结果─────────────► 服务器(接着把活干完)

图说:一次工具调用中途,消息方向倒转一次,再倒回来。
这就是本章主走查的轨道:create_product 工具的两进两出。

2. 为什么这样设计:三层理由,一层比一层硬

第一层(书明说):模型在客户端那边。 服务器常常只是个数据系统, 根本接不到模型;而客户端(VS Code、Claude Desktop)天生带模型2

第二层(书明说):服务器由此甩掉了三件事。 自己的模型密钥、模型账单、选模型的责任—— 都不需要了。服务器只出「提示词和建议」,客户端用自己的模型完成4

第三层(规范强调,书也点了):用户在场。 采样请求对客户端来说只是建议—— 建议用什么模型、什么语气、多长;客户端应该把它摆给用户看, 用户可以接受、可以改了再发。这叫人在回路(human in the loop)—— 自动流程里刻意留一个「人看一眼再放行」的环节5。 第 04 章讲过批准工具调用是同一道闸,这里闸更宽: 服务器发来的每一句话,理论上都该经用户过目后才变成模型的输入5

3. 请求里能装什么:全是建议,没有命令

看一个真实的采样请求(书里 tomato 例子的完整形态)6:

{ "jsonrpc": "2.0", "id": 1, "method": "sampling/createMessage",
"params": {
"messages": [{ "role": "user", "content": { "type": "text",
"text": "Write a compelling description of this product: tomato,
here's some keywords: red, vegetable, fresh" } }],
"modelPreferences": {
"hints": [{ "name": "claude-3-sonnet" }],
"intelligencePriority": 0.8, "speedPriority": 0.5 },
"systemPrompt": "You're a professional writing assistant and
tends to want to write descriptions in a poetic way",
"maxTokens": 100 } }

逐字段拆67:

字段管什么性质
messages喂给模型的对话内容(这里装着商品名和关键词)主料
modelPreferences.hints建议用哪个模型(名字只是「提示」)建议
intelligencePriority / speedPriority / costPriority0–1,聪明/快/省钱各要几分建议
systemPrompt系统提示词——摆在用户消息之前、给模型定「人设和规矩」的那段话;这里让它「写得诗意」建议
maxTokens最多生成多少个词元(token——模型切分文字的最小单位,英文里常常几个字母算一个,100 个词元大约六七十个英文单词)预算

客户端的回包则带着实际用了什么:哪个模型、生成的内容、 以及 stopReason(为什么停了:endTurn 是正常说完)8建议与实际可以不一致——请求说想要 claude,回答完全可以用别的模型,这不算违约8

4. 主走查:一个番茄的两次越境

跟着书里的 create_product 例子走全程。

第 ① 步,入境(正常方向)。 用户在 VS Code 的 agent 模式里说 「create product called tomato with keywords red and vegetable and delicious」。 宿主把这句话解析成工具调用,批准之后,create_product 工具收到 { product_name: "tomato", keywords: "red, vegetable, delicious" }9

第 ② 步,出境(方向倒转)。 工具干到一半——商品建了,描述空着—— 服务器这一侧发出采样请求。代码上只有一调10:

const response = await server.server.createMessage({
messages: [{ role: "user", content: { type: "text", text: prompt } }],
maxTokens: 500, systemPrompt: "You're an assistant"
});

注意那个双层 server.server:外层是高层 McpServer(第 03 章), 内层是它包着的低层 Server(第 05 章)——采样属于低层能力, 所以从外层身上「再进一层」才拿得到10。这一行调用的效果就是第 3 节 那条 sampling/createMessage 消息飞向了客户端。

第 ③ 步,客户端这一侧加工。 VS Code 收到请求,弹给用户 (你要先在服务器的设置里用 Configure Model Access 圈定允许采样用哪些模型9); 用户放行后,VS Code 用自己的模型生成,把结果回给服务器。

第 ④ 步,回境(方向倒回)。 工具拿到 response.content.text, 填进商品的 description,然后作为工具的正常响应交差10。 书里这次真实生成的描述长这样(节录)9:

Introducing our Red Garden Medley—a vibrant selection of the freshest, most delicious red vegetables…(一段煞有介事的番茄营销文案)

一次工具调用,消息越境两次: 人话进来,采样出去,文案回来,结果交差。 作者看完只问了一句:「这样的番茄,你不想买吗?」9

自写客户端的版本(不用 VS Code 时)也走同一轨道,只是第 ③ 步换成自己的代码: 客户端创建时声明 capabilities: { sampling: {} }(不声明,服务器就不该发采样), 再挂一个处理器11:

client.setRequestHandler(CreateMessageRequestSchema, async (request) => {
let prompt = request.params.messages[0].content.text; // 取出服务器的话
let llmResponse = await callLLM(prompt, "You are a helpful assistant…");
return { model: "gpt-4o-mini", role: "assistant",
content: { type: "text", text: llmResponse } };
});

书里这套代码跑 paprika(红椒)那次,产出的商品描述有完整日志可查—— 从 sampling/createMessage 进来到成品写回,全程留痕12

5. 三个典型用法与一个新协议动向

书给了三个配得上课的场景,各自说明采样的一种用法13:

  • 博客摘要/标签:用户交草稿(人会写的部分),服务器存稿,采样求客户端出标签(模型擅长的部分);
  • 电商文案:即主走查;
  • 游戏 NPC 对话:服务器把角色设定(「600 岁的吸血鬼,爱抱怨城堡电费」) 当系统提示发过去,客户端的模型扮演这个角色回答——NPC 从此不说背好的台词13

还有一个书外的动向必须交代:我们书架的规范拆解显示,2026-07-28 版规范 把「服务器主动向客户端发请求」整体改造成了「多轮往返请求(MRTR)」模式—— sampling/createMessage 这类服务器反向请求,必须走这个新模式, 老的书里这种发法属于 2025 年代形态;概念与字段没变,信封换了14

更重的一刀:同一版规范把采样(sampling)与 roots 标记为已废弃(SEP-2577)—— 过渡窗口内旧实现仍可用,但新实现不应再采用:服务器要 AI 能力,改为直接对接 LLM 厂商 API;roots(服务器问客户端「哪些目录相关」的能力)改用工具参数、 资源 URI 或服务器配置来传。三个反向能力里只有征询(elicitation)仍然活跃14。 所以这一章读的是「2025 年代的一个好设计」,照它写新功能前务必先查现行规范。

6. 作者的判断与证据

有证据的:

  • 采样请求/响应的字段、四方角色(用户/服务器/客户端/模型)、 「只是建议」的定位,书里给了完整消息示例5678;
  • server.server.createMessagecapabilities.samplingCreateMessageRequestSchema 的用法与运行日志都在书里101112

作者的判断:

  • 「应该让用户看到并可以修改采样请求」——书里措辞是 should(应当), 属于强烈建议而非强制5;我们姊妹书架上《AI Agents with MCP》的拆解 在这一点上说得更硬:要敏感信息必须走不经过客户端的路15;
  • 用 VS Code 当采样客户端做演示,是便利性选择——也再次暴露了作者的工具链立场(见总纲)。

判断(我们的,不是书里的): 采样是 MCP 里最容易被读漏、却最改变架构想象力的一个能力。 它意味着「服务器」可以不智能而提供智能功能——文案、摘要、翻译、扮演, 全都外包给调用方的模型。对 SaaS(software as a service——把软件做成在线服务、 按月订阅收钱的那种生意)做 MCP 服务器的人,这是一句话的商业模式: 你的服务器出数据和流程,客户的模型出智能和账单。 如果错,会错在: 现实中客户端对采样的支持参差——很多宿主默认不开、或只许指定模型; 而且把服务器文本交给用户模型之前没有过滤的话,等于让服务器间接「指挥」用户的模型, 提示注入风险跟着来(第 11 章的测试与治理会回到这个问题)。

7. 边界与局限

  • 书里客户端示例的 callLLM(直接返回拼接字符串),真实接入要自己写11;
  • 「人在回路」在自写客户端里全靠自觉——书里代码直接自动回答了,没实现「给用户看一眼」11;
  • 采样的嵌套与循环(采样过程中服务器又发采样)书里完全没提;
  • 书里 mcp.json 示例又一次用了 python 命令、日志里是 server.py——改编残留,忽略即可;
  • 模型与采样请求的内容审查(服务器发来的提示词是否恶意)本书未展开,只在安全章泛泛带过。

8. 可带走的

  1. 采样 = 服务器反向请求客户端的模型;方向倒转是它全部的本质;
  2. 触发点通常在工具执行中途:干到一半发现要生成内容;
  3. 请求字段全是建议(模型、优先级、系统提示、预算),实际用什么由客户端定;
  4. 服务器这一侧:server.server.createMessage(...)(双层 server,低层能力);
  5. 客户端这一侧:声明 capabilities.sampling + setRequestHandler(CreateMessageRequestSchema, …);
  6. 人在回路不是可选项的设计意图:请求应摆给用户,可改可拒;
  7. 一句话记商业价值:服务器出数据和流程,客户端出模型和账单;
  8. 写新代码前查协议版本:采样在 2026-07-28 版已废弃(SEP-2577),新功能不该建在它上面——服务器要 AI 能力就直连 LLM 厂商 API;维护旧实现时,这类请求改走 MRTR 模式14

9. 原文地图

主题原书章原文位置
采样定义(字典义)Delegating Tasks with Samplingtext/11-fm-delegating-tasks-with-sampling.txt:21(搜「taking samples of something」)
服务器为什么需要同上text/11-fm-delegating-tasks-with-sampling.txt:37(搜「the client is the one with the LLM」)
四方角色同上text/11-fm-delegating-tasks-with-sampling.txt:83(搜「human in the loop」)
请求是建议同上text/11-fm-delegating-tasks-with-sampling.txt:101(搜「recommendation for what to do」)
请求消息全文同上text/11-fm-delegating-tasks-with-sampling.txt:212(搜「sampling/createMessage」)
响应消息同上text/11-fm-delegating-tasks-with-sampling.txt:293(搜「stopReason」)
三场景同上text/11-fm-delegating-tasks-with-sampling.txt:131(搜「Writing a blog post」)· :167(搜「Mystery game」)
create_product 实现同上text/11-fm-delegating-tasks-with-sampling.txt:362(搜「create_product」)
VS Code 流程同上text/11-fm-delegating-tasks-with-sampling.txt:494(搜「create product called tomato」)· :531(搜「Red Garden Medley」)
客户端实现同上text/11-fm-delegating-tasks-with-sampling.txt:605(搜「CreateMessageRequestSchema」)
paprika 日志同上text/11-fm-delegating-tasks-with-sampling.txt:647(搜「Sampling request」)
ch2 的采样概念段Explaining the Model Context Protocoltext/04-fm-explaining-the-model-context-protocol.txt:1923(搜「I don't know how to do this」)

Footnotes

  1. 出处:「Delegating Tasks with Sampling」第 35-37 段(text/11-fm-delegating-tasks-with-sampling.txt:37,搜「the client is the one with the LLM」)。

  2. 出处:「Delegating Tasks with Sampling」第 27-31 段(text/11-fm-delegating-tasks-with-sampling.txt:27,搜「sending a sampling request」);概念首见「Explaining the Model Context Protocol」第 1913-1945 段(text/04-fm-explaining-the-model-context-protocol.txt:1923,搜「I don't know how to do this」)。 2

  3. 补充(不在书里,依据我们的 book 书架):大语言模型按温度从可能性清单里挑词的「采样」, 《这就是 ChatGPT》拆解第一章有完整解释。依据: book=what-is-chatgpt-doing §01-one-word-at-a-time 事实=该章讲温度如何控制从清单里挑词的随机程度。

  4. 出处:「Delegating Tasks with Sampling」第 61-65 段(text/11-fm-delegating-tasks-with-sampling.txt:61,搜「delegate some problems to the client」)。

  5. 出处:「Explaining the Model Context Protocol」第 2350-2356 段(text/04-fm-explaining-the-model-context-protocol.txt:2354,搜「human in the loop」)与「Delegating Tasks with Sampling」第 99-103 段(text/11-fm-delegating-tasks-with-sampling.txt:101,搜「recommendation for what to do」)。 2 3 4

  6. 出处:「Delegating Tasks with Sampling」第 209-237 段(text/11-fm-delegating-tasks-with-sampling.txt:212,搜「sampling/createMessage」)。 2 3

  7. 出处:「Explaining the Model Context Protocol」第 1963-1993 段(text/04-fm-explaining-the-model-context-protocol.txt:1979,搜「modelPreferences」)。 2

  8. 出处:「Delegating Tasks with Sampling」第 281-305 段(text/11-fm-delegating-tasks-with-sampling.txt:293,搜「stopReason」);「Explaining the Model Context Protocol」第 2025-2039 段(text/04-fm-explaining-the-model-context-protocol.txt:2031,搜「doesn't have to be the same」)。 2 3

  9. 出处:「Delegating Tasks with Sampling」第 443-541 段(text/11-fm-delegating-tasks-with-sampling.txt:474,搜「Configure Model Access」;:531,搜「Red Garden Medley」)。 2 3 4

  10. 出处:「Delegating Tasks with Sampling」第 349-441 段(text/11-fm-delegating-tasks-with-sampling.txt:375,搜「server.server.createMessage」)。 2 3 4

  11. 出处:「Delegating Tasks with Sampling」第 558-605 段(text/11-fm-delegating-tasks-with-sampling.txt:558,搜「"capabilities"」;:605,搜「CreateMessageRequestSchema」)。 2 3 4

  12. 出处:「Delegating Tasks with Sampling」第 644-667 段(text/11-fm-delegating-tasks-with-sampling.txt:647,搜「Sampling request」)。 2

  13. 出处:「Delegating Tasks with Sampling」第 127-192 段(text/11-fm-delegating-tasks-with-sampling.txt:131,搜「Writing a blog post」)与第 743-753 段(text/11-fm-delegating-tasks-with-sampling.txt:747,搜「600 year old vampire」)。 2

  14. 补充(不在书里,依据我们的 protocol 书架):2026-07-28 版引入多轮往返请求(MRTR)模式,规范用 MUST 要求 roots/list、sampling/createMessage、elicitation/create 这类请求走它,服务器主动发请求的旧做法不再支持;同一版还把 Sampling 与 Roots 标记为已废弃(SEP-2577,过渡窗口内仍可用、新实现不应采用),只剩 Elicitation 活跃。 依据: shelf=ai-protocol-reference/mcp-spec#04-mrtr-and-client-features.md 事实=该章讲 MRTR 如何把「服务器反向请求」做成无状态,并在能力表里标注 Sampling/Roots 已废弃、Elicitation 活跃。 2 3

  15. 补充(不在书里,依据我们的 book 书架):《AI Agents with MCP》拆解在征询一节总结了规范对敏感信息的硬性要求。 依据: book=ai-agents-with-mcp §07-client-side-capabilities 事实=该章讲采样与征询在客户端这一侧的能力与安全约束。