title: Tool Search sidebar_position: 95
工具搜索功能
当一个会话中关联了大量的MCP服务器或非核心插件工具时,它们的JSON架构可能会在每一轮对话中占用相当大的上下文窗口空间——即便其中只有少数工具与用户实际提出的问题相关。
- 工具搜索功能正是Hermes为解决这一问题而推出的可选渐进式披露机制。启用该功能后,模型可见的工具数组中的MCP工具和插件工具会被三个桥接工具替代,模型会按需加载每个具体工具的架构。
-
::info 内置Hermes工具永不延迟加载 构成Hermes核心功能集的工具(如
terminal、read_file、write_file、patch、search_files、todo、memory、browser_*、web_search、web_extract、clarify、execute_code、delegate_task、session_search以及_HERMES_CORE_TOOLS中的其他工具)始终会直接加载。只有MCP工具和非核心插件工具才支持延迟加载。 -
::
工作原理
当某轮对话启用工具搜索功能时,模型会看到三个新的工具取代了那些被延迟加载的工具:
tool_search(queries, limit?) search the deferred-tool catalog (one or more queries)
tool_describe(names) load the full schemas for one or more tools
tool_call(calls) invoke deferred tools; `calls` is an array of {name, arguments}
每次调用时,calls 参数仅接受一个条目;单个本地调用对应的数组长度为 1。仅有 connectors__ 名称可以批量处理,混合类型或包含多个本地调用的批量请求将被拒绝。
典型的交互流程如下:
Model: tool_search(["create a github issue", "send a slack message"])
→ { results: [ { query: "create a github issue",
matches: ["mcp_github_create_issue", ...] },
{ query: "send a slack message",
matches: ["mcp_slack_post_message", ...] } ],
tools: { mcp_github_create_issue: { description: "...",
required: ["title"], ... },
mcp_slack_post_message: { ... } } }
Model: tool_describe(["mcp_github_create_issue", "mcp_slack_post_message"])
→ { tools: { mcp_github_create_issue: { parameters: { ... } },
mcp_slack_post_message: { parameters: { ... } } } }
Model: tool_call({ calls: [{ name: "mcp_github_create_issue",
arguments: { title: "...", body: "..." } }] })
→ { ok: true, issue_number: 42 }
在 tool_search 调用中,每个查询都会针对同一个目录独立进行搜索(每个查询均适用 limit 限制);每个查询组仅包含工具名称,而共享的 tools 映射则一次性存储所有匹配工具的描述及所需参数名称。系统会对查询词进行词干提取处理,因此“issues”这一查询会匹配到“create_issue”。对于没有返回任何匹配结果的查询组,系统还会提供 available_sources 摘要,列出已连接的服务器信息,从而避免将词汇匹配失败误认为是功能缺失。
tool_describe 会在单次调用中解析所有请求的工具名称;对于未知的工具名称,系统会将其标记为 not_found,而不会影响其余查询的处理。
当模型调用 tool_call 时,Hermes 会解除桥接封装,直接将请求转发给底层的工具,其效果就如同模型直接调用了该工具一样。所有在工具调用前、调用过程中及调用后的钩子函数、限制规则、审批提示等,都会基于真实的工具名称来执行,而非 tool_call 本身。CLI 和网关中的操作日志也会显示底层工具的名称,而非桥接层信息。
它何时会被激活?
工具搜索采用分层披露机制:只要存在任何可延迟调用的(MCP/插件类型的)工具,就会启用桥接功能。随着目录规模的扩大,变化的是目录中可见内容的多少,而非架构是否采用延迟调用方式。
| 等级 | 条件 | 模型所看到的内容 |
|---|---|---|
| 0 | 无 MCP/插件工具 | 所有工具均处于可用状态,无桥接机制,采用直接传递方式。 |
| 1 | 延迟加载的工具列表在预算范围内 | 包含桥接机制以及每个延迟加载工具的技能式清单(包含名称和简短描述;若超出预算则仅显示名称)。此降级机制是按服务器独立生效的:当某个资源占用较大的服务器(如 Cloudflare)与资源占用较小的服务器(如 Linear)一同连接时,小型服务器仍会保留针对每个工具的详细列表,而仅大型服务器会简化为概要行。 |
| 2 | 即使仅显示名称,每台服务器上的单个工具列表也超出预算(例如,仅 Cloudflare 的 API 就有约 3,300 个工具,其名称对应的 token 数量约为 32,000) | 仅保留桥接机制,并为每台服务器提供一行概要信息(服务器名称 + 工具数量),这样模型就能知道哪些域名是可访问的;各个具体工具则只能通过 tool_search 功能来查找。 |
列表预算的计算公式为 min(上下文总 token 数的 threshold_pct%,listing_max_tokens)。
每当构建工具数组时,都会重新评估这一条件,因此如果在会话进行过程中添加或移除 MCP 服务器,下一次数据整合时该会话的等级将会发生变化。
配置
tools:
tool_search:
enabled: auto # auto (default), on, or off
threshold_pct: 5 # listing budget as a percentage of context
search_default_limit: 5
max_search_limit: 25
listing: auto # embed a grouped name+description catalog manifest
listing_max_tokens: 4000
| 键值 | 默认值 | 含义 |
|---|---|---|
enabled | auto | auto/on:只要存在至少一个可延迟的工具,就会启用该功能;off:完全禁用(所有工具均会立即处理)。目前 auto 是 on 的别名——它被保留用于未来的一种模式,即当模式匹配上下文时直接内联架构定义,仅在不匹配时才进行延迟处理。若希望升级后仍保持当前行为,建议明确指定 on 或 off。 |
threshold_pct | 5 | 以活跃模型上下文长度的百分比形式表示的列表预算值。范围为 0–100。 |
search_default_limit | 5 | 当模型在调用 tool_search 时未指定 limit 参数时,每次查询返回的匹配结果数量。 |
max_search_limit | 25 | 模型通过 limit 参数可请求的最大匹配结果数量(每次查询限)。范围为 1–50。 |
listing | auto | 在 tool_search 接口描述中嵌入每个可延迟工具的技能卡片式信息(包括工具名称及其描述的第一句话,长度不超过 60 个字符,按 MCP 服务器分组)。auto 模式会在预算允许的情况下嵌入完整信息;若预算不足,则依次仅显示工具名称或二级服务器摘要;而 on/off 模式则会强制以某种特定方式显示信息。 |
listing_max_tokens | 4000 | 无论上下文大小如何,嵌入的列表信息的绝对上限。范围为 200–60000。当工具目录过于庞大时,系统会降级为仅显示工具名称或各服务器的摘要,但用户仍可通过搜索查看完整的架构定义。 |
每次调用的数组长度上限属于内部安全限制,并非配置项。若超过此限制,调用将会返回错误,以便模型可以尝试使用更小的数据批次重新调用。
为何需要显示这些信息
若没有该功能,延迟加载的能力将不可见——实际测试表明,模型会使用可见的核心工具(如在终端中运行 gh 而非查找延迟加载的 GitHub 工具),或者直接声明某能力并不存在,而非调用 tool_search。该机制通过技能模式来处理各类工具:所有能力始终可以通过名称被检索到,而完整的参数结构则仍保持延迟加载状态。如果模型在列表中看到了确切的工具名称,它就可以跳过 tool_search 直接调用 tool_describe,从而省去一次往返查询的步骤。
您也可以切换为传统的布尔值格式:
tools:
tool_search: true # equivalent to {enabled: auto}
连接器(远程工具)
当您登录 Nous Portal 后,该桥接层还会访问连接器——即由托管工具网关提供的远程工具。这些连接器永远不会在本地进行注册:tool_search 会将每个查询发送至网关,再将网关返回的结果作为文档添加到本地目录中(标记为 source: "connectors",文件名为 connectors__<connector>__<tool>),并且对两者应用相同的 BM25 排序算法及相同的稀有词规则。因此,limit 参数会限制整个结果集的大小,而那些能够回答查询的连接器工具,即便与某些本地工具存在单词重合,也不会被这些本地工具挤占展示位置。网关调用的时长上限为 30 秒;若网关响应缓慢或不可用,则仅显示本地搜索结果。《tool_describe函数用于从网关获取连接器的结构信息,而tool_call函数则会按输入顺序将每个连接器条目作为独立的网关请求发送出去(如果网关无法通过常规名称识别某个工具,会尝试使用其原始名称再次发起请求,因此一个条目可能需要两次请求)。如果某个连接器同时存在GMAIL_X形式和原始名称X两种形式,系统会将两者合并为connectors__gmail__X,实际执行的是 GMAIL_X`;搜索系统会保留这个有效版本,舍弃另一个版本,并记录警告信息。最终,所有结果会按原始批次顺序重新排列,并附带重新计算后的出现频次。
tools:
connectors:
enabled: true # false — never touch connector routes; the bridge
# behaves exactly as if the feature didn't exist
当用户已退出登录(或网关未为该账户提供连接器服务)时,上述所有功能都将不可用:本地搜索行为将完全遵循本页其余部分的描述,模型不会显示任何错误信息。
如果某个连接器调用需要关联的账户而您尚未完成关联,系统会返回CONNECTION_REQUIRED错误,并附上相应的关联链接。manage_connections工具(其可用条件与连接器桥接功能相同)可用于列出所有连接器及其连接状态、启动授权流程,并等待用户完成操作;而解除账户关联则需由用户在门户界面中自行操作。
tool_call支持批量调用:calls参数为一个包含 {name, arguments} 键值对数组的集合(单次调用对应一个长度为1的数组)。批次中的每个连接器请求将依次作为独立的网关请求发送;对于本地延迟型工具,每次tool_call仅处理一个请求。在发送请求之前,系统会先逐个处理审批流程,若在多个请求之间输入/stop指令,则未启动的请求将不会被发送(其对应时隙的状态将为INTERRUPTED)。
何时不应使用此功能
工具搜索功能虽然通过延迟加载机制节省了资源,但为此需要付出每轮对话固定的令牌成本(包括三种桥接工具模式及目录列表展示),并且在处理冷启动工具时还需额外进行至少一次“描述→调用”的往返操作。在基础套餐层级下,目录列表仍会显示所有功能,因此通常无需额外的发现流程——模型可直接进入tool_describe阶段。实际测试表明,目录列表模式在保证任务成功率的同时,其成本还低于单纯的桥接模式。
如果您希望为小型工具集恢复旧的“始终即时响应”模式,可设置 enabled: off。
无法避免的权衡问题
这些问题源于提示词缓存的一致性要求——它们是任何渐进式披露设计所固有的,并非仅限于当前实现方式:
- 延迟调用的工具需多进行一次往返通信。 当模型首次需要使用延迟调用的工具时,它需要额外进行一到两次模型调用来查找并加载该工具的架构信息。虽然静态层面确实能节省令牌,但其中一部分成本会在运行时被抵消。
- 延迟加载的架构无法享受缓存优势。 已加载的
tool_describe返回结果会被记录到对话历史中(因此会在后续轮次中被缓存),但它无法利用系统提示词缓存的前缀功能。 - 延迟加载的架构缺乏供应商原生的验证机制。 虽然
tool_describe允许模型读取延迟调用工具的架构信息,但供应商仍然只能看到通用的tool_call.arguments对象。因此,Hermes 会在发送请求前在本地对相关参数进行强制转换与验证;而对于那些 Hermes 无法安全验证的架构,比如格式错误的架构或外部引用,仍需由具体的工具或 MCP 服务器负责处理。 - 性能取决于模型质量。 工具搜索功能假定模型能够为其所需工具编写合理的搜索查询。较小的模型在这方面的表现较差;Anthropic 公布的数据(使用工具搜索后 Opus 4 模型的准确率从 49% 提升至 74%)虽然显示出了积极效果,但也表明仍有约 26 个百分点的准确率下降源于检索失败。
- 工具集的修改会使缓存失效。 在会话进行过程中添加或删除工具,都会改变桥接工具的描述信息(其中包含延迟调用工具的数量)以及工具目录,从而导致提示词缓存失效。这与任何工具集的修改所带来的影响是一致的。
实现细节
- 检索机制: 通过分词后的工具名称、来源名称(即该工具所属的MCP服务器或插件工具集,因此搜索“linear”即便工具本身的名称未包含该服务名称,也能找到该服务器下的相关工具)、描述以及参数名称来进行BM25检索;同时会对索引和查询内容应用Snowball词干提取算法(针对英文),以便不同形态的词汇能够被匹配到(例如“issues”可匹配到“create_issue”)。只有当某工具包含查询中最罕见的词元时,它才会作为检索结果出现——这类词元出现在最少的工具文档中,通常就是用于标识意图的词汇,如“gmail”、“github”、“incident”,而非“send”或“create”等。如果查询中最罕见的词元不存在于任何工具中,系统将返回一个空结果集,同时列出相关的来源信息并给出重试提示,而不会像处理仅包含一个共同词汇的工具那样返回有限数量的结果。
- 并行执行会解除桥接限制。 批量规划器会根据
tool_call所对应的底层工具来决定并发度,而非依据实际的桥接名称——因此,那些设置了supports_parallel_tool_calls: true选项的MCP服务器,在其工具通过桥接被调用时仍能保持原有的并发能力;而tool_search/tool_describe这类查询则如同普通的只读工具一样,可以以批量方式并行处理。 - 目录在多轮对话间是无状态的。 系统会基于当前的工具定义列表重新构建整个目录结构——不会使用基于会话键的
Map存储方式。这样一来,就能避免出现已存储的目录与实时工具注册表不同步的故障。 - 目录范围仅限于当前会话所拥有的工具集。
tool_search、tool_describe和tool_call函数仅能访问并调用该会话实际被授权的工具。那些被限制在部分工具集内的子代理、看板工作节点或网关会话,无法通过此桥接机制来发现或调用这些工具集之外的工具——延迟加载的目录仅包含该会话自身已启用/已禁用的工具集中的对应部分,而非整个流程注册表。 - 无需 JS 沙箱环境。 Hermes 采用更为简单的“结构化工具”模式(将搜索、描述和调用操作视为普通函数)。其他一些实现方案所采用的 JS 沙箱“代码模式”存在较高的安全风险,因此 Hermes 未采用该机制。
参考资料
tools/tool_search.py—— 实现代码tests/tools/test_tool_search.py—— 回归测试套件- 原始实现提交记录中的
openclaw-tool-search-reportPDF 文件,其中记载了影响该设计决策的相关研究内容