4.10 工具搜索与延迟加载
并非所有 60+ 工具都会在每次 API 调用时进入模型的上下文窗口(context window)。ToolSearchTool 与 API 侧的 defer_loading(延迟加载)配合工作——Claude Code 工具定义里的 shouldDefer 字段对应序列化到请求中的 defer_loading 标记。这里要澄清一个常见误解:defer_loading 决定的是"什么进入上下文",而不是"请求里发送什么":
- 请求的
tools数组始终包含所有工具的完整定义,包括延迟加载的那些——API 需要它们在服务端执行工具搜索,并在模型发现工具时把tool_reference块展开成完整定义 defer_loading: false(默认):工具立即进入模型上下文defer_loading: true:工具只有在模型通过ToolSearch发现之后才进入上下文- 工具的
searchHint字段提供搜索提示 - 两种搜索变体(
regex与bm25)都会匹配工具名称、描述、参数名和参数描述
延迟加载的收益在于:减少进入上下文(从而进入系统提示词前缀)的工具数量,压缩每一轮实际处理的 token,同时让前缀保持稳定以提升 prompt cache 命中率。
searchHint 字段
每个可延迟加载的工具都可以定义 searchHint 字符串,用于提高工具被发现的概率。例如,一个 Jupyter Notebook 编辑工具可能设置 searchHint: "notebook jupyter ipynb cell"。当模型调用 ToolSearch 时,搜索算法同时匹配工具名称、描述、参数名、参数描述以及 searchHint。
ToolSearchTool 查询语法
ToolSearchTool 支持三种查询模式:
| 语法 | 说明 | 示例 |
|---|---|---|
"select:Name1,Name2" | 精确选择——按名称直接加载指定工具 | "select:Read,Edit,Grep" |
"关键词1 关键词2" | 关键词搜索——返回最匹配的 N 个工具 | "notebook jupyter" |
"+前缀 关键词" | 名称前缀约束——要求工具名包含前缀,再按关键词排序 | "+slack send" |
select: 模式最常用,当模型已经知道需要哪个工具时直接按名称加载,零搜索开销。关键词模式适用于探索性场景,如"我需要一个处理数据库的工具"。
对提示词缓存的影响
defer_loading 决定的是"什么进入上下文窗口",而不是"请求里发送什么"——这正是它缓存收益的来源。既然每次请求的 tools 数组始终包含全部工具(包括延迟的),工具集本身并不构成缓存变动因素;真正参与 prompt cache 缓存键的是系统提示词前缀(system-prompt prefix)。
API 在服务端把延迟工具排除在系统提示词前缀之外,整个机制分三步:
- 对话开始时:前缀只包含非延迟工具的完整定义,形成一段稳定的缓存前缀
- 模型发现延迟工具时:API 在对话历史中就地追加一个
tool_reference块,并在传给模型前把它展开为完整工具定义 - 前缀从未被改动:因此 prompt cache 全程命中——动态加载工具不会破坏缓存
这意味着你可以用一小撮"始终加载"的工具(保持缓存命中)开启对话,让模型按需发现更多工具,而每一轮都能维持同样的缓存命中。
与 strict mode 的配合:严格工具调用(strict tool use)的语法约束是从完整工具集构建的,与哪些工具被延迟无关——所以 defer_loading 与 strict mode 组合使用时不需要重新编译语法(grammar),prompt cache 与 grammar cache 都能在动态加载工具时被保留。
使用建议(来自官方文档):
- 绝不要给工具搜索工具本身设置
defer_loading: true——否则模型将无法通过搜索发现任何其他工具 - 保持 3-5 个最常用的工具为非延迟,让模型无需先搜索即可直接调用
- 由于搜索覆盖工具名称、描述、参数名和参数描述,写
searchHint与工具描述时应尽量覆盖这些维度
⚠️ 兼容性提示:并非所有模型厂商都支持延迟加载
defer_loading与工具搜索是 API 服务端实现的能力,Anthropic API 原生支持,但第三方网关、本地模型、其他厂商的兼容层未必实现这一机制。如果所使用的提供商不支持该功能,建议关闭延迟加载(将所有工具设为非延迟),避免不必要的副作用。