当前位置: 首页 > news >正文

Claude Code 的工具延迟加载机制

AI Agent 的工具调用依赖模型理解每个工具的参数 schema。传统做法是在 system prompt 中列出所有工具的完整定义——名称、描述、参数类型、参数说明。当工具数量在 10-20 个以内时,这没有问题。

但当工具数量增长到 50+,schema 的 token 成本就不可忽视了。Claude Code 的内置工具(如 Bash、Read、Edit)每个 schema 约 70-150 token,MCP 工具的 schema 通常更复杂。一个典型的 MCP 场景:用户配置了 Slack、GitHub、Jira、Notion、Linear 五个服务器,每个提供 10-15 个工具,总计 50-75 个。全部内联发送会消耗大量 token——在 200K 上下文窗口中占比可观。

更关键的是,大多数工具在一次对话中不会被用到。用户可能只用了 Slack 的发送消息功能,但 GitHub、Jira、Notion、Linear 的所有工具 schema 都白白占据了上下文空间。

Claude Code 通过defer_loading(延迟加载)机制解决了这个问题。先看 Claude Code 是怎么把工具传给大模型的,再看延迟加载如何在这个流程中发挥作用。


工具如何传给大模型

每次调用 Claude API 时,工具列表作为tools参数传入anthropic.beta.messages.create()。这个参数是一个数组,每个元素包含工具的名称、描述和参数 schema:

constresult=awaitanthropic.beta.messages.create({model:options.model,messages:messagesForAPI,system,tools:allTools,// 工具列表在这里传入tool_choice:options.toolChoice,max_tokens:maxOutputTokens,stream:true,});

每个工具对象在传入 API 前,会经过转换,取出名称、描述和输入 schema,组装成 API 需要的格式。最终allTools中每个元素的结构类似:

{"name":"Bash","description":"Executes a given bash command and returns its output.","input_schema":{"type":"object","properties":{"command":{"type":"string","description":"The command to execute"}},"required":["command"]}}

这就是工具传入大模型的基本流程。


defer_loading 的核心机制

延迟判定:谁被延迟,谁不被延迟

工具组装时,每个工具都会经过一个判定函数:

functionisDeferredTool(tool:Tool):boolean{// alwaysLoad 优先——MCP 工具可以通过 _meta 设置此标志if(tool.alwaysLoad===true)returnfalse;// MCP 工具默认延迟if(tool.isMcp===true)returntrue;// ToolSearch 自身不能被延迟——模型需要它来加载其他工具if(tool.name===TOOL_SEARCH_TOOL_NAME)returnfalse;// 内置工具通过 shouldDefer 显式标记returntool.shouldDefer===true;}

判定规则:

  • MCP 工具默认被延迟——这是延迟加载存在的主要原因
  • alwaysLoad: true的 MCP 工具跳过延迟——适用于高频使用的工具
  • ToolSearchTool自身永远不延迟——它是模型发现其他工具的唯一入口
  • 内置工具默认不延迟——shouldDefer字段默认为false,除非工具显式标记

defer_loading 如何生效

判定结果通过willDefer()函数传入toolToAPISchema(),后者在工具的 API schema 上加上defer_loading: true标记:

consttoolSchemas=awaitPromise.all(filteredTools.map(tool=>toolToAPISchema(tool,{deferLoading:willDefer(tool),}),),);

toolToAPISchema()的核心逻辑:

functiontoolToAPISchema(tool,options){constschema={name:base.name,description:base.description,input_schema:base.input_schema,};if(options.deferLoading){schema.defer_loading=true;}returnschema;}

defer_loading: true的工具在 API 侧的行为不同:模型只能看到工具的名称和描述,看不到完整的参数 schema。这正是延迟加载的核心——通过一个 API 字段,控制工具 schema 的可见性。

延迟后的工具在 prompt 中长什么样

被延迟的工具不会从工具池中移除,但发送给 API 时只携带名称,不携带完整的 inputSchema。

模型在 system prompt 中看到的是一个<available-deferred-tools>区域,每个工具只有一行:

<available-deferred-tools>mcp__slack__send_message — send a message to a Slack channel or user mcp__slack__list_channels — list available Slack channels mcp__github__create_issue — create a new GitHub issue mcp__jira__search_issues — searchforJira issues using JQL</available-deferred-tools>

模型知道这些工具存在,也知道它们大概做什么,但不知道它们需要什么参数。这意味着模型无法直接调用它们——它必须先获取完整的 schema。

ToolSearchTool:按需加载的桥梁

ToolSearchTool是整个延迟加载机制的关键枢纽。它是唯一一个始终加载的工具,负责在模型需要时加载其他工具的完整 schema。

它支持两种查询方式:

  1. 精确查询"select:Read,Edit,Grep"—— 按名称直接获取
  2. 关键词搜索"notebook jupyter"—— 匹配工具描述,返回最相关的工具

搜索结果以tool_reference块的形式返回。每个匹配的工具包含完整的 JSONSchema 定义:

{"type":"tool_reference","tool":{"name":"mcp__slack__send_message","description":"send a message to a Slack channel or user","input_schema":{"type":"object","properties":{"channel":{"type":"string","description":"Channel ID or name"},"text":{"type":"string","description":"Message text"}},"required":["channel","text"]}}}

API 层会自动将tool_reference展开为完整的工具定义。展开后,工具就像从未被延迟过一样可以正常调用。

完整流程


模型缓存:defer_loading 的隐性问题

defer_loading 解决了上下文窗口膨胀的问题,但它还意外地解决了另一个更隐蔽的问题。

模型的 prompt cache 以 system prompt 的前缀匹配为基础。如果工具列表在两次请求之间发生变化——比如 MCP 服务器重连后工具数量变了——prompt 就会变化,缓存就会失效。缓存失效意味着更多的 token 需要重新计算,API 调用成本和延迟都会大量上升。

针对这个问题,claude 模型 API 支持传入 defer_loading 的工具:它们不参与 KV Cache 的 key 计算。API 侧会将 defer_loading 的工具从 prompt 中剥离,因此它们不影响实际的缓存 key。即使 MCP 工具集合在会话中不断变化,已有的缓存依然有效。

为什么要将工具 Schema 传给模型 API 接口,而不是通过 Prompt 让模型生成工具调用?

这里有一个容易混淆的点:Schema 是给 API 用的,不是给模型看的。

如果只是让模型"知道有哪些工具、怎么调用",prompt 确实够了,但 Schema 的作用不止于此:

  • API 层的结构化输出:Claude API 的tools参数会改变模型的输出格式。传入工具后,模型返回的不是自由文本,而是结构化的tool_use块——包含工具名和 JSON 格式的输入参数。这是 API 级别的能力,不是 prompt 能模拟的。
  • 输入校验:API 侧会根据 Schema 校验模型的输入——类型是否正确、必填字段是否缺失、是否符合 enum 约束。校验失败时 API 会自动修正或拒绝,而不是把错误参数传给工具执行层。
  • 可靠性保证:没有 Schema,模型可能幻觉出不存在的参数、用错类型、生成格式错误的 JSON。有了 Schema,这些错误在 API 层就被拦截了。

所以 Schema 和 Prompt 解决的是不同层面的问题:Prompt 告诉模型"有什么、做什么",Schema 告诉 API"怎么验证、怎么格式化"。

如果模型不支持呢

defer_loading 需要模型侧配合——它是一个 API 级别的特性,不是所有模型都能处理。Claude Code 在构建 API 请求前会检查当前模型是否支持:

consttoolSearchEnabled=isToolSearchEnabled(options.model,...)

不支持的模型无法使用 defer_loading,所有工具 schema 只能内联发送。支持的模型还需要在请求头中附加 beta 标志。

这就引出了一个实际问题:延迟行为应该一刀切吗?Claude Code 提供了三种 ToolSearchMode 策略:

模式行为适用场景
tst(默认)始终延迟 MCP 工具和 shouldDefer 工具MCP 工具较多的场景
tst-auto仅当延迟工具的 token 超过上下文窗口 10% 时启用MCP 工具较少时自动内联
standard不延迟,所有工具 schema 内联发送模型不支持或工具数量很少

tst-auto是一个自适应策略:MCP 工具较少时内联发送(省去 ToolSearch 的额外 round trip),数量增长到一定程度自动切换到延迟模式。standard则是完全不延迟——当模型 不支持 defer_loading 时,这是唯一的选项。

如果模型没有 defer_loading API,要怎么实现工具延迟加载?

可以,思路是用一个通用的代理工具做中转。

注册一个CallTool,它的 Schema 只有两个字段:tool_nameargs。所有动态工具都不直接注册到 API,而是通过CallTool间接调用。模型先通过ToolSearch获取目标工具的完整参数定义,然后把参数填进CallToolargs字段,由CallTool转发给实际工具执行。

模型 → ToolSearch("slack send_message")→ 获取参数 Schema 模型 → CallTool({tool_name:"slack_send_message", args:{channel:"#general", text:"hello"}})→ 内部路由到实际工具执行

这个方案虽然可行,但有一个明显的弊端:失去了 API 层的 Schema 强校验CallToolargs是一个通用的 JSON 对象,API 无法校验里面的结构是否符合目标工具的定义。模型生成的参数是否正确,完全依赖模型自身的能力 —— 类型错误、缺少必填字段、幻觉出不存在的参数,都不会在 API 层被拦截。

相比之下,defer_loading的方案让 API 在工具被调用时拥有完整的 Schema 定义,校验是自动的,当然其问题就在于延迟加载工具需要重复计算缓存了,各有利弊。


MCP 工具:延迟加载的主要受益者

MCP 工具是 defer_loading 存在的核心原因。MCP 工具与内置工具有一个根本区别:数量不可预测

内置工具是 Claude Code 开发团队控制的,数量稳定在 40 个左右,schema 经过优化,token 占用可控,且部分为异步加载。

MCP 工具则来自外部服务器——用户可以配置任意数量的 MCP 服务器,每个服务器可以提供任意数量的工具。

returnresult.tools.map((tool):Tool=>({...MCPTool,// 骨架模板name:`mcp__${serverName}__${tool.name}`,// 命名空间隔离isMcp:true,// 标记为 MCP → 默认延迟alwaysLoad:tool._meta?.['anthropic/alwaysLoad']===true,inputJSONSchema:tool.inputSchema,// 直接使用 JSON Schemaasynccall(args,context,...){constconnectedClient=awaitensureConnectedClient(client)returnawaitcallMCPTool(connectedClient,tool.name,args)},}))

isMcp: true标记让这些工具在isDeferredTool()判定中默认返回true

总结

Claude Code 的 defer_loading 机制解决了工具数量增长时的上下文窗口膨胀问题:

  1. MCP 工具默认被延迟 —— 只发送名称到 API,不发送完整 schema
  2. 模型通过 ToolSearchTool 按需发现工具 —— 精确查询或关键词搜索
  3. 搜索结果返回tool_reference块 —— API 自动展开为完整工具定义
  4. 展开后的工具可以正常调用 —— 对模型来说没有区别
  5. defer_loading 的工具不参与 KV Cache 计算 —— 工具变化不会导致缓存失效

这套机制让 Claude Code 能够支持几乎无限数量的 MCP 工具,同时保持上下文窗口的高效利用。

如果你觉得这篇文章有帮助,欢迎点赞、收藏,也可以关注我。

http://www.zskr.cn/news/1489015.html

相关文章:

  • 三阳路空调维修|三阳路空调移机|三阳路空调加氟|三阳路空调回收 高性价比宅到家快速上门 - 武汉宅到家
  • 任何商业行为都要 问这几个问题 ,凭什么轮到你
  • 天赐范式第67天:三分子悬赏令·最终版声明——如果天赐范式没有与之相对应的工程,那我筛选出来的悬赏分子又算什么呢?
  • GEO优化公司避坑指南:2026五强靠谱服务商最新全解析 - GEO优化
  • W55RP20-EVB-MKR 模块 MicroPython 实战 (11):HTTP 协议与 OneNET 平台数据上云
  • 100、安全机制:地理围栏与限高限速
  • NOVELLUS SYSTEMS YSC-BSA01038PLOS / 02-294832-00
  • 串口空闲中断使能到串口空闲中断建立需要多长时间
  • 2026年 IGBT模块、功率模块、可控硅、二极管、整流桥、晶闸管厂家推荐排行榜:高性能与稳定品质之选 - 品牌发掘
  • 革命性微信聊天记录永久保存与智能分析工具:掌握你的数字记忆主权
  • Bilibili 视频合集时长计算最新脚本
  • 思维链 ,Anthropic Mythos模型的 Looped Transformer架构解析,claud为什么厉害性能优越的研究
  • C语言容器中数据的高效串行化和解串--下位机的C语言处理(1)
  • AI Agent 学习指南
  • AMHS自动物料搬运系统实战
  • 2026 泉州防水补漏服务商口碑测评榜单|全屋渗漏维修机构优选指南 - 宅安选房屋修缮
  • 深度测评 | 怎么判断一家竹笋干供应商是否专业?只看产品种类多不多吗?
  • 汽车以太网PHY芯片TJA1102A硬件配置、寄存器驱动与睡眠唤醒实战指南
  • 如何配置WarcraftHelper解决魔兽争霸III现代系统兼容性问题
  • 2026年杭州企业家必读:GEO优化公司选型实战指南与五大厂商深度横评 - 品牌报告
  • JoyAI-Echo:长音视频生成的真正难点,不是把视频做长,而是让世界不断线
  • uni-app扫码工具包:前后置摄像头自由切换,一套代码跑通H5、小程序和App
  • 2026 惠州市外墙渗水维修|屋顶漏水|地下室防水本地 7 家公司推荐|惠州漏水维修附近上门,卫生间 / 外墙 / 屋顶 / 阳台 / 飘窗防水补漏权威测评(全惠州同城服务) - 吉林同城获客
  • 2026年 HC420/780DP高强钢厂家推荐榜:汽车结构件与轻量化选材的精准之选 - 品牌发掘
  • 期末论文扎堆不用慌,百考通AI帮你高效搞定课业写作难题
  • 2026年 2,4-二甲基苯酚厂家推荐排行榜:工业级/医药级中间体,高纯度与稳定供货实力品牌深度解析 - 品牌发掘
  • 2026年卡板厂家推荐榜单:实木/熏蒸/出口/免检/胶合/欧标/美标/IPPC卡板,多场景坚固承重之选 - 品牌发掘
  • LGTV Companion终极指南:5步实现智能电视与电脑的完美同步
  • 把 PyTorch 的 Autograd 压进 280 行 C++:从 Dual Number 到一台 Kahn 拓扑排序引擎
  • ArduPilot自动驾驶系统核心技术架构深度解析