---
title: "ツール検索"
description: ""
upstream_path: user-guide/features/tool-search.md
upstream_blob: 8264594632c1331fe0f5d9bd30cb6e6b2f5e4283
sources:
  - https://hermes-agent.nousresearch.com/docs/user-guide/features/tool-search
---

# ツール検索 {#tool-search}

セッションにたくさんの MCP サーバーやコア以外のプラグインツールをつないで
いると、それらの JSON スキーマだけで毎ターンのコンテキストウィンドウを
かなり食ってしまいます。ユーザーが実際に頼んだ内容に関係するツールが
ほんの数個しかない場合でも同じです。

**ツール検索**は、その問題に対して Hermes が用意した、任意で有効にできる
段階開示のしくみです。有効になると、モデルから見えるツール一覧では MCP
ツールとプラグインツールが 3 つの橋渡しツールに置き換わり、モデルは必要に
なったときだけ個別ツールのスキーマを読み込みます。

:::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 ツールと、コア以外のプラグインツールだけです。
:::

## しくみ {#how-it-works}

そのターンでツール検索が有効になると、後回しにされたツールの代わりに、
モデルには次の 3 つのツールが見えます。

```
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(name, arguments)     — invoke a deferred tool
```

やり取りは、たとえば次のように進みます。

```
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("mcp_github_create_issue", { title: "...", body: "..." })
  → { ok: true, issue_number: 42 }
```

`tool_search` に渡したクエリは、それぞれ独立に同じカタログへ照会されます
（`limit` はクエリごとに効きます）。クエリごとのまとまりに載るのはツール名
だけで、一致した各ツールの説明と必須パラメーター名は、共有の `tools` マップに
一度だけ入ります。クエリは語幹に落として扱われるので、「issues」でも
`create_issue` が見つかります。一致が 0 件だったクエリのまとまりには、
つないでいるサーバーをまとめた `available_sources` が付きます。言葉が
引っかからなかっただけなのに、その機能自体がないと取り違えないためです。
`tool_describe` は、要求された名前をすべて 1 回の呼び出しで解決します。
知らない名前は `not_found` に入れて報告され、残りの一括処理は失敗しません。

モデルが `tool_call` を呼ぶと、Hermes は **橋渡しの層を外して**、
モデルが直接呼んだ場合とまったく同じように本来のツールを実行します。
ツール呼び出し前のフック、ガードレール、承認の確認、
そして呼び出し後のフックは、すべて本来のツール名に対して動きます。
`tool_call` に対してではありません。CLI とゲートウェイの実行ログでも
橋渡しの層は外れるので、見えるのは本来のツールであって橋渡しではありません。

## いつ有効になるか {#when-does-it-activate}

ツール検索は **段階的な開示** を使います。後回しにできる（MCP・プラグインの）
ツールが *ひとつでも* あれば橋渡しは有効になります。カタログの大きさで変わるのは、
カタログのどこまでを見せ続けるかであって、スキーマを後回しにするかどうかではありません。

| 段階 | 条件 | モデルに見えるもの |
| --- | --- | --- |
| **0** | MCP・プラグインのツールがない | すべてのツールをそのまま読み込む。橋渡しなし。素通し。 |
| **1** | 後回しカタログの一覧が予算に収まる | 橋渡し＋後回しツール全件のスキル風の目録（名前と短い説明。予算を超えると名前だけに落とす）。落とし方は **サーバー単位** です。大きすぎるサーバー（Cloudflare）を小さなサーバー（Linear）と一緒につないでいる場合、小さいほうはツールごとの一覧を保ったまま、大きすぎるサーバーだけが要約 1 行にまとまります。 |
| **2** | どのサーバーも名前だけにしてなお、ツールごとの一覧が予算を超える（例: Cloudflare の平坦な API 面だけで約 3,300 ツール、その名前だけで約 32K トークン） | 橋渡しのみ＋サーバー 1 行ずつの要約（サーバー名とツール数）。どの領域に手が届くかはモデルに伝わり、個々のツールは `tool_search` からしかたどれません。 |

一覧の予算は `min(threshold_pct% of context, listing_max_tokens)` です。
判定はツール一覧を組み立てるたびにやり直されるので、セッションの途中で
MCP サーバーを足したり外したりすると、次の組み立てのときに段階が変わります。

## 設定 {#configuration}

```yaml
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` は、後回しにできるツールが 1 つでもあれば有効にします。`off` は完全に無効にします（すべてそのまま読み込まれます）。`auto` は今のところ `on` の別名です。スキーマがコンテキストに収まるときは埋め込み、収まらないときだけ後回しにする将来のモードのために予約されています。今の挙動をアップグレード後も確実に保ちたいなら `on` か `off` を明示してください。 |
| `threshold_pct` | `5` | 使用中モデルのコンテキスト長に対する、一覧の予算の割合。範囲は 0〜100 です。 |
| `search_default_limit` | `5` | モデルが `limit` なしで `tool_search` を呼んだときに、クエリごとに返る件数。 |
| `max_search_limit` | `25` | モデルが `limit` で要求できる上限（クエリごと）。範囲は 1〜50 です。 |
| `listing` | `auto` | 後回しツール全件のスキル風の目録（名前と説明の最初の 1 文、60 文字以内、MCP サーバーごとにまとめる）を `tool_search` の説明文に埋め込みます。`auto` は予算に収まるときだけ埋め込み（収まらなければ名前だけ、さらに段階 2 のサーバー要約へ落とします）、`on` と `off` はどちらかに固定します。 |
| `listing_max_tokens` | `4000` | コンテキストの大きさに関係なく、埋め込む一覧にかける絶対の上限。範囲は 200〜60000 です。大きなカタログは名前だけ、あるいはサーバーごとの要約に落ちますが、完全なスキーマは検索から取り出せます。 |

1 回の呼び出しで渡せる配列の上限は、設定ではなく内部の安全弁です。上限を
超えた呼び出しはエラーを返すので、モデルは数を減らして呼び直せます。

### 一覧がある理由 {#why-the-listing-exists}

一覧がないと、後回しにされた能力は *見えなく* なります。実際のベンチマークでは、
モデルが目に見えるコアツールで代用したり（後回しの GitHub ツールを探さずに
ターミナルで `gh` を実行する）、`tool_search` を呼ばずに「そんな機能はない」と
言い切ったりする様子が観測されました。この一覧は、スキルのやり方をツールにも
持ち込んだものです。どの能力もつねに名前で見つけられるようにしつつ、
パラメーターの完全なスキーマは後回しのままにします。目当てのツール名が
一覧に載っていれば、モデルは `tool_search` を飛ばして `tool_describe` に
直行でき、往復を 1 回減らせます。

以前からある真偽値の書き方も使えます。

```yaml
tools:
  tool_search: true   # equivalent to {enabled: auto}
```

## 使わないほうがよい場合 {#when-not-to-use-it}

ツール検索は、後回しにしたスキーマぶんの節約と引き換えに、毎ターン一定の
トークン費用（橋渡しツール 3 つのスキーマとカタログの一覧）と、まだ読み込んで
いないツールでの少なくとも 1 往復（describe → call）を支払います。
段階 1 では一覧がすべての能力を見せているので、探すための往復はたいてい
なくなり、モデルは `tool_describe` へ直行します。実際のベンチマークでは、
一覧ありのモードは、そのまま全部読み込む方式と同じくらいタスクを成功させながら、
橋渡しだけの構成より費用が少なくて済みました。

小さなツール群で以前のような常時読み込みの挙動にしたいときは、
`enabled: off` にしてください。

## 消えないトレードオフ {#trade-offs-that-dont-go-away}

次の点は、プロンプトキャッシュの整合性を保つという前提から来るもので、
この実装に固有ではなく、段階開示という設計そのものに付いて回ります。

- **まだ読み込んでいないツールでは往復が 1 回増える。** 後回しのツールが
  最初に必要になったとき、スキーマを探して読み込むためにモデル呼び出しが
  1〜2 回余分にかかります。静的な側でのトークン節約は本物ですが、
  その一部は実行時に払い戻す形になります。
- **後回しのスキーマにはキャッシュの恩恵がない。** 読み込んだ
  `tool_describe` の結果は会話履歴に入るので、以降のターンではキャッシュに
  乗ります。ただしシステムプロンプトのキャッシュ接頭辞の恩恵は受けません。
- **モデルの実力に左右される。** ツール検索は、欲しいツールに対して
  モデルがまともな検索語を書けることを前提にしています。小さいモデルほど
  これが苦手です。公表されている Anthropic の数値（Opus 4 でツール検索
  あり 74%、なし 49%）は伸びしろを示す一方で、なお約 26 ポイントぶんは
  探し出せずに終わっていることも示しています。
- **ツール構成をいじるとキャッシュが無効になる。** セッションの途中で
  ツールを足したり外したりすると、橋渡しツールの説明文（後回しツールの
  件数を含みます）とカタログが変わるので、プロンプトキャッシュが無効に
  なります。これはツール構成をいじるとき全般に共通するトレードオフです。

## 実装の詳細 {#implementation-details}

- **検索:** ツール名、提供元の名前（そのツールが属する MCP サーバーや
  プラグインのツール群。おかげで `"linear"` で検索すれば、ツール名自体に
  サービス名が入っていなくてもそのサーバーのツールが見つかります）、説明、
  パラメーター名をトークン化した上での BM25 です。索引にも検索語にも
  Snowball の語幹処理（英語）をかけるので、語形が変わっても一致します
  （「issues」で `create_issue` が見つかります）。どの検索語もどの文書にも
  当たらないときは、ツール名に対する部分一致にフォールバックします
  （たとえばトークンが `github` のところを `"hub"` で検索した場合）。
- **並列実行でも橋渡しの層は外れる。** 一括実行の計画は、`tool_call` の
  リテラルな橋渡し名ではなく *本来の* ツールを見て同時実行の可否を決めます。
  そのため `supports_parallel_tool_calls: true` で並列を有効にした MCP
  サーバーは、橋渡し経由で呼ばれても同時実行を保ちますし、`tool_search` や
  `tool_describe` の照会も、読み取り専用のツールと同じようにまとめて
  同時に走ります。
- **カタログはターンをまたいで状態を持たない。** 組み立てのたびに現在の
  ツール定義一覧から作り直します。セッションをキーにした `Map` は使いません。
  保存したカタログが実際のツール登録内容とずれていく、という類のバグを
  これで避けています。
- **カタログはセッションのツール群の範囲に閉じている。** `tool_search`、
  `tool_describe`、`tool_call` が見たり呼んだりできるのは、そのセッションに
  実際に与えられたツールだけです。サブエージェント、かんばんのワーカー、
  ゲートウェイのセッションが一部のツール群に絞られている場合、橋渡しを使って
  その外のツールを見つけたり呼んだりはできません。後回しカタログは、
  プロセス全体の登録内容ではなく、そのセッション自身で有効・無効にした
  ツール群のうち後回しにできる部分だけです。
- **JS サンドボックスは使わない。** Hermes は、より単純な「構造化ツール」
  方式（search / describe / call をただの関数として扱う）を採っています。
  ほかの実装にある JS サンドボックスの「コードモード」は面が広すぎるので、
  見送っています。

## 関連 {#see-also}

- `tools/tool_search.py` — 実装
- `tests/tools/test_tool_search.py` — 回帰テスト一式
- 設計のもとになった調査については、最初の実装 PR にある
  `openclaw-tool-search-report` の PDF
