2.2 三大原语:用「谁决定调用」重新理解工具、资源、提示词


2.2 三大原语:用「谁决定调用」重新理解工具、资源、提示词

本节摘要:第 1 章你写过 @mcp.tool@mcp.resource@mcp.prompt 三个装饰器,但还没真正理解「为什么是这三类」。本节给出全书最重要的一个分类视角——三大原语的划分不是功能分类,而是控制权分类。工具、资源、提示词的本质差别,不在「它们做什么」,而在「谁决定调用它们」:工具由模型决定(类比 POST,有副作用)、资源由应用决定(类比 GET,只读)、提示词由用户决定(命名消息模板)。这个视角一旦建立,「该用哪个原语」就不再纠结,而是一个基于控制权归属的清晰判断。

一、划分依据:不是「做什么」,是「谁决定」

很多人的第一反应是按「功能」分类——「工具是用来计算的,资源是用来读文件的,提示词是用来生成模板的」。这是错的视角,会把设计动机彻底掩盖。

正确的视角是问一个问题:这个能力,由谁来决定「现在要用它」?

原语 谁决定调用 类比 有副作用吗
工具(Tool) 模型决定 POST 通常有(写库、调 API、发邮件)
资源(Resource) 应用(宿主)决定 GET 没有(只读数据)
提示词(Prompt) 用户决定 保存的查询 没有直接副作用(注入消息)

这个「控制权归属」的划分,是 MCP 协议设计的核心洞见。它解决了一个真实问题:不同的能力,天然应该由不同角色触发,硬塞进同一类会出乱子。

二、工具:模型决定,类比 POST

工具是模型主动决定调用的能力。模型在推理时判断「我需要算两个数的和」,于是调用 add 工具。

为什么类比 POST?因为工具通常有副作用——写数据库、调外部 API、发消息、改文件状态。每一次调用都改变世界,所以必须由「理解当前任务」的模型来判断「现在该不该调」。

模型推理中 │ ▼ 「我需要查这个用户的订单」 模型决定:调用 get_orders(user_id="u123") │ ▼ 客户端转发到服务端 服务端执行,返回订单列表 │ ▼ 结果回传模型 模型继续:「根据这些订单,我建议……」

关键特征:模型在每一轮推理里,自主决定调不调、调哪个、传什么参数。这就是为什么工具需要详细的 Schema(第 4 章)——Schema 是模型做这个决策的依据。

三、资源:应用决定,类比 GET

资源是宿主应用决定加载的能力。宿主判断「这个对话需要这些上下文」,于是把资源内容加载进模型的上下文。

为什么类比 GET?因为资源是只读数据,没有副作用。它不改变世界,只是把信息喂给模型。比如一份配置文件、一份用户资料、一个 API 的响应。

宿主准备调用模型前 │ ▼ 「这次对话需要项目配置」 宿主决定:加载 config://app 这个资源 │ ▼ 客户端发读取请求到服务端 服务端返回配置内容(JSON/文本) │ ▼ 宿主把内容塞进模型上下文 模型带着这份上下文开始推理

关键差别:资源是「在模型说话前」就被加载的,它扩充模型的「背景知识」;而工具是「模型说话过程中」被调用的,它执行动作。这个时序差别是工具与资源最本质的分界。

💡 技巧:遇到「这个能力该做成工具还是资源」纠结时,问一句:「它有副作用吗?」 有→工具;没有→资源。再问一句:「它该由模型主动调,还是该预先加载进上下文?」 模型主动调→工具;预先加载→资源。两个问题答完,归属就清楚了。

四、提示词:用户决定,类比保存的查询

提示词是用户主动触发的命名消息模板。用户在界面上选「总结这段代码」,宿主就调用对应的 summarize 提示词,把它返回的文本当作用户消息注入对话。

为什么类比「保存的查询」?因为提示词就是预先写好的、带参数的消息模板——用户不用每次手敲一长段指令,选个名字、填个参数就行。

用户在界面上 │ ▼ 选「summarize」,填参数 text="..." 宿主调用 prompts/get summarize │ ▼ 客户端转发到服务端 服务端渲染模板,返回消息文本 │ ▼ 宿主把文本当「用户消息」注入对话 模型看到用户说「总结这段代码:...」,开始推理

关键差别:提示词由用户主动触发,它产生的是一条「用户消息」(不是工具结果、不是资源内容)。用户不点,它就不会跑——这与工具(模型自动调)、资源(应用自动加载)形成第三种控制权归属。

五、三个原语的时序对照

把三者放在一次完整对话的时间线上,控制权差别更清晰:

时间 ──► 用户开对话 │ ├─ 用户选「summarize」提示词 ◄── 提示词:用户决定 │ │ │ ▼ 渲染成用户消息注入 │ ├─ 宿主预加载 config://app ◄── 资源:应用决定 │ │ │ ▼ 内容塞进模型上下文 │ 模型开始推理 │ ├─ 模型决定调 get_data 工具 ◄── 工具:模型决定 │ │ │ ▼ 执行,结果回传 │ 模型继续推理,生成回复

注意三种「决定」发生在不同时间点、由不同角色发出——这就是控制权划分的物理体现。

六、为什么这个划分重要

你可能会想:「我把所有能力都做成工具不行吗?」能,但会很难用。考虑一个反面例子:把「项目配置」做成工具,而不是资源。

错误做法:配置做成工具 模型每次推理都要决定「要不要调 config 工具」 → 浪费一轮调用 → 模型可能忘记调,导致带错上下文 → 配置是只读的,做成「有副作用的工具」语义错乱 正确做法:配置做成资源 宿主在对话开始时一次性加载 → 模型始终带着正确上下文 → 不消耗调用轮次 → 「只读 GET」语义清晰

这个对比说明了为什么三原语不能合成一个:不同的控制权归属,对应不同的使用模式与性能特征。把它们分开,让每个能力都归到最合适的触发方,是 MCP 协议工程设计的精华。

第 5 章会用一整节讲「为什么不直接用工具」这个追问,本节先建立这个直觉。

本节要点回顾

  1. 三大原语的划分不是功能分类,是控制权分类——按「谁决定调用」划分。
  2. 工具由模型决定(类比 POST,有副作用),需要详细 Schema 支撑模型决策。
  3. 资源由应用决定(类比 GET,只读),在模型说话前预先加载进上下文。
  4. 提示词由用户决定(类比保存的查询),产生一条用户消息注入对话。
  5. 三者发生在不同时间点、由不同角色触发,这是控制权划分的物理体现。
  6. 「都做成工具」不可取——会让只读数据消耗调用轮次、语义错乱、模型可能漏调。
  7. 纠结归属时问两句:有副作用吗?该模型主动调还是预先加载?

控制权划分清楚了,下一节我们看服务端内部是怎么组织的——全书最重要的架构判断:两层服务端模型


作者与出处
原作者: 灏天文库
来源:modelcontextprotocol
许可证:MIT
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: 灏天文库 转发
评论区 (0)
U