工具搜索
当会话中附加了许多 MCP 服务器或非核心插件工具时,它们的 JSON Schema 会在每一轮对话中占用上下文窗口的相当大一部分——即使其中只有少数几个与用户的实际请求相关。
工具搜索(Tool Search) 是 Hermes 针对该问题提供的可选渐进式披露层。激活后,模型可见的工具数组中的 MCP 和插件工具将被三个桥接工具取代,模型会按需加载每个特定工具的 Schema。
构成 Hermes 核心能力集的工具(terminal、read_file、write_file、patch、search_files、todo、memory、browser_*、web_search、web_extract、clarify、execute_code、delegate_task、session_search、send_message 以及 _HERMES_CORE_TOOLS 中的其余工具)始终直接加载。只有 MCP 工具和非核心插件工具才有资格被延迟加载。
工作原理
当工具搜索在某一轮对话中激活时,模型会看到三个新工具来替代被延迟加载的工具:
tool_search(query, limit?) — search the deferred-tool catalog
tool_describe(name) — load the full schema for one tool
tool_call(name, arguments) — invoke a deferred tool
典型的交互流程如下:
Model: tool_search("create a github issue")
→ { matches: [{ name: "mcp_github_create_issue", ... }, ...] }
Model: tool_describe("mcp_github_create_issue")
→ { parameters: { type: "object", properties: { ... } } }
Model: tool_call("mcp_github_create_issue", { title: "...", body: "..." })
→ { ok: true, issue_number: 42 }
当模型调用 tool_call 时,Hermes 会解包桥接层,并像模型直接调用底层工具一样分派该工具。预工具调用钩子、防护栏、审批提示和后工具调用钩子都针对真实工具名称运行,而不是针对 tool_call。CLI 和网关中的活动反馈也会进行解包,因此你看到的是底层工具,而非桥接工具。
何时激活?
默认情况下,工具搜索以 auto 模式运行:仅当可延迟加载的工具 Schema 将占用当前模型上下文窗口至少 10% 时才会激活。低于该阈值时,工具数组的组装是纯透传的,不会产生任何开销。
每次构建工具数组时都会重新评估此决策,因此:
- 仅有少量 MCP 工具且使用长上下文模型的会话永远不会激活工具搜索。
- 附加了许多 MCP 服务器(通常超过 15 个工具)的会话开始激活它。
- 在会话中途移除 MCP 服务器后,下一次组装时会正确恢复为直接暴露模式。
配置
tools:
tool_search:
enabled: auto # auto (default), on, or off
threshold_pct: 10 # percentage of context — only used in auto mode
search_default_limit: 5
max_search_limit: 20
| 键 | 默认值 | 含义 |
|---|---|---|
enabled | auto | auto 在超过阈值时激活;on 只要存在至少一个可延迟加载的工具就始终激活;off 完全禁用。 |
threshold_pct | 10 | auto 模式启动时的上下文长度百分比。范围 0–100。 |
search_default_limit | 5 | 当模型调用 tool_search 而未指定 limit 时返回的结果数量。 |
max_search_limit | 20 | 模型可以通过 limit 请求的硬性上限。范围 1–50。 |
你也可以切换传统的布尔值形式:
tools:
tool_search: true # equivalent to {enabled: auto}
何时不使用
工具搜索用固定的每轮 Token 成本(三个桥接工具 Schema,约 300 个 Token)以及至少一次额外的往返交互(搜索 → 描述 → 调用)来换取延迟加载 Schema 所节省的空间。当你拥有大量工具但每轮只使用少数几个时,这是一个明显的优势;但当你的工具总数很少时,这反而会成为开销。
auto 默认值会自动为你处理这种情况。如果你无条件设置 enabled: on,预计在小型工具集上每轮对话会有轻微的额外成本。
无法避免的权衡
这些权衡源于提示缓存完整性不变量——它们是任何渐进式披露设计固有的,并非此实现特有:
- 冷启动工具需要额外的一次往返交互。 模型首次需要某个延迟加载的工具时,需要花费一到两次额外的模型调用来查找并加载其 Schema。静态侧节省的 Token 是真实的,但部分成本会在运行时偿还。
- 延迟加载的 Schema 无法享受缓存收益。 加载后的
tool_describe结果会进入对话历史(因此在后续轮次中确实会被缓存),但它永远无法受益于系统提示前缀缓存。 - 依赖于模型质量。 工具搜索假设模型能够为其想要的工具编写合理的搜索查询。较小的模型在这方面表现较差;Anthropic 发布的数据(Opus 4 在使用与不使用工具搜索时的准确率从 49% 提升到 74%)显示了其优势,但也表明仍有约 26 个百分点的准确率损失源于检索失败。
- 工具集编辑会使缓存失效。 在会话中途添加或移除工具会改变桥接工具的描述(其中包括延迟加载工具的数量)和目录,从而导致提示缓存失效。这与任何工具集编辑面临的权衡相同。
实现细节
- 检索: 对分词后的工具名称 + 描述 + 参数名称执行 BM25 算法。当 BM25 未返回任何正分数命中结果时,回退到对工具名称的字面子串匹配,从而防止零 IDF 退化情况(例如,在目录中每个工具名称都包含 "github" 的情况下搜索
"github")。 - 目录在多轮对话中无状态。 每次组装时都会从当前的工具定义列表重新构建目录——不使用基于会话键的
Map。这避免了一类 bug,即存储的目录与实时工具注册表不同步。 - 目录的作用域限定于会话的工具集。
tool_search、tool_describe和tool_call只能查看和调用会话实际被授予权限的工具。限制为工具集子集的子代理(subagent)、看板工作器(kanban worker)或网关节点(gateway session)无法使用该桥接来发现或调用该子集之外的工具——延迟目录是会话自身启用/禁用工具集的可延迟切片,而非整个进程注册表。 - 无 JS 沙箱。 Hermes 使用更简单的“结构化工具”模式(将搜索/描述/调用作为普通函数)。其他一些实现提供的 JS 沙箱“代码模式”具有较大的攻击面;我们跳过它。
另请参阅
tools/tool_search.py— 实现代码tests/tools/test_tool_search.py— 回归测试套件- 原始实现 PR 中的
openclaw-tool-search-reportPDF,其中包含塑造该设计的研究内容