5.2 为什么不直接用工具:资源的设计动机


文档摘要

5.2 为什么不直接用工具:资源的设计动机 本节摘要:本节回答一个每位初学者都会问的问题——「资源能做的,工具也能做,为什么不把所有东西都做成工具?」 答案不在功能(工具确实能返回数据),而在控制权、时序与成本。资源由应用预加载、不消耗调用轮次、只读语义清晰;工具由模型运行时调用、每次消耗一轮、带副作用语义。把只读数据硬塞进工具,会让模型浪费轮次、可能漏调、语义错乱。本节用一个反面案例讲透这个取舍,让你之后遇到「该用工具还是资源」时不再纠结。 一、问题的提出 先承认一个事实:从「能不能返回数据」的角度,工具完全可以替代资源。下面两个写法,都能让模型拿到配置: 写法二能跑,模型也能调它拿到配置。那为什么还要资源?这是本节要讲透的。 二、差别一:控制权归属 最根本的差别在第 2.

5.2 为什么不直接用工具:资源的设计动机

本节摘要:本节回答一个每位初学者都会问的问题——「资源能做的,工具也能做,为什么不把所有东西都做成工具?」 答案不在功能(工具确实能返回数据),而在控制权、时序与成本。资源由应用预加载、不消耗调用轮次、只读语义清晰;工具由模型运行时调用、每次消耗一轮、带副作用语义。把只读数据硬塞进工具,会让模型浪费轮次、可能漏调、语义错乱。本节用一个反面案例讲透这个取舍,让你之后遇到「该用工具还是资源」时不再纠结。

一、问题的提出

先承认一个事实:从「能不能返回数据」的角度,工具完全可以替代资源。下面两个写法,都能让模型拿到配置:

# 写法一:配置做成资源 @mcp.resource("config://app") def get_config() -> dict: return {"theme": "dark"} # 写法二:配置做成工具 @mcp.tool() def get_config() -> dict: """Get app configuration.""" return {"theme": "dark"}

写法二能跑,模型也能调它拿到配置。那为什么还要资源?这是本节要讲透的。

二、差别一:控制权归属

最根本的差别在第 2.2 节讲过——谁决定调用:

原语 谁决定 时序
资源 应用(宿主) 模型说话预加载
工具 模型 模型推理主动调

这个差别对「配置该用什么」有直接影响:

配置做成资源(正确) 宿主在对话开始时一次性加载 config://app → 模型从一开始就带着配置上下文 → 每次推理都基于正确配置 配置做成工具(错误) 模型每次推理都要决定「要不要调 get_config 工具」 → 模型可能忘记调(它不知道需要配置) → 模型可能在不同轮次调多次(浪费) → 配置进上下文的时机不确定

控制权归属错了,行为就不可控。配置这种「该预先准备好」的东西,交给「会忘记、会浪费」的模型决定,就是错配。

三、差别二:消耗调用轮次

工具调用消耗一轮对话——模型发出调用、等结果、再继续。这意味着:

配置做成工具 模型第 1 轮:决定调 get_config 工具返回配置 模型第 2 轮:基于配置真正回答用户 → 多消耗 1 轮(token 成本、延迟) 配置做成资源 宿主预加载配置(不消耗模型轮次) 模型第 1 轮:直接基于配置回答 → 不多消耗轮次

对单个工具可能看不出差别,但真实场景里一个对话可能涉及多份上下文(配置、用户资料、项目说明)。全做成工具,每次对话都多消耗好几轮;做成资源,预加载一次,模型直接用。

💡 技巧:把「模型每轮都很贵」记住。每多一轮工具调用,就是多一次模型推理、多一份 token 成本、多一段延迟。资源把「准备上下文」这件事从模型手里拿走,交给宿主一次性做完,省的是真金白银。

四、差别三:语义清晰

第三层差别是语义——资源是只读的,工具通常有副作用。把只读数据做成工具,语义就错乱了:

资源的语义:GET 「我读这份配置」→ 纯查询,不改世界 → 宿主可放心预加载,无副作用顾虑 工具的语义:POST(通常) 「我调这个工具」→ 可能有副作用(写库、调 API) → 模型调前要权衡,宿主加载前要确认

配置是只读的,做成资源,语义清晰;做成工具,模型与宿主都要把它当「可能有副作用」对待,徒增谨慎与开销

五、一个反面案例:全做成工具的混乱

把所有能力都做成工具,会怎样?看一个反面场景:

场景:用户问「根据我的配置,推荐一个方案」 全工具化的服务端: - get_config() (配置,只读) - get_user_profile() (用户资料,只读) - get_project_doc() (项目说明,只读) - recommend() (推荐,真正干活) 模型推理过程: 第 1 轮:我要不要先调 get_config?嗯,要。 第 2 轮:拿到配置了,要不要调 get_user_profile?要。 第 3 轮:拿到资料了,要不要调 get_project_doc?要。 第 4 轮:终于调 recommend。 → 4 轮才到正题,且每轮都可能出错(忘记调、调错顺序)

而把只读的做成资源:

资源化 + 工具化的服务端: - 资源:config://app、user://me、doc://project - 工具:recommend() 模型推理过程: 宿主预加载三份资源(不消耗模型轮次) 第 1 轮:模型直接基于完整上下文调 recommend。 → 1 轮到正题,上下文完整

这个对比说明,资源不是「工具的重复」,而是把不该消耗模型轮次的东西挪出去,让模型专注于「真正需要它决策的事」。

六、判断准则:三问决定归属

把上面的分析浓缩成三个判断问题,遇到「该用工具还是资源」时依次问:

问题一:这个能力有副作用吗? 有 → 工具 无 → 继续问二 问题二:它该由模型主动调,还是预先加载? 模型主动调 → 工具 预先加载 → 资源 问题三:它每次调用结果一样吗(幂等只读)? 一样(只读数据) → 资源 不一样(有状态变化) → 工具

三个问题答完,归属基本清楚。举个例子:

  • 「查用户余额」:无副作用、可预加载、只读 → 资源(或工具,边界情况)
  • 「转账」:有副作用 → 工具
  • 「项目配置」:无副作用、该预加载、只读 → 资源
  • 「按关键词搜索」:无副作用,但每次参数不同、该模型主动调 → 工具

⚠️ 注意:有些能力在边界——「查余额」既是只读(像资源),又可能需要参数动态调(像工具)。这种边界情况,优先看「谁该决定调用」:模型根据对话决定→工具;应用预加载→资源。拿不准时,工具更通用(模型能自己决定),资源更高效(不消耗轮次)。

本节要点回顾

  1. 从「能不能返回数据」看,工具可替代资源——但这是错的视角。
  2. 差别一:控制权——资源由应用预加载、工具由模型运行时调,归属错了行为不可控。
  3. 差别二:消耗轮次——工具每调一次消耗一轮,资源预加载不消耗,省 token 与延迟。
  4. 差别三:语义——资源是 GET(只读),工具通常 POST(有副作用),错配语义徒增谨慎。
  5. 全工具化的混乱:模型要花多轮「决定调只读工具」,可能忘记、调错顺序。
  6. 资源把不该消耗模型轮次的东西挪出去,让模型专注真正需要决策的事。
  7. 三问决定归属:有副作用吗?谁决定调用?是否幂等只读?

资源的设计动机清楚了,下一节转向提示词——用户控制的第三类原语。


发布者: 作者: 灏天文库 转发
评论区 (0)
U