skill-retrieval について
Hermes Agent 向けのスキル検索プラグインです。BM25 でユーザーメッセージごとに関連するスキル説明を選び、システムプロンプト内の説明一覧を名前だけの索引に置き換えて、一部の候補を追加することでプロンプトの負荷を抑えます。
主な機能
- 01BM25 によるスキル説明の順位付け
- 02システムプロンプトのスキル一覧を圧縮
- 03ターンごとに関連説明を追加
- 04索引のキャッシュとスキル変更の検知
- 05中国語、日本語、韓国語での検索
活用シーン
- 01多数のスキルからタスクに関係する項目を検索
- 02リクエストごとのスキル用プロンプトを削減
- 03中国語のクエリで非英語のスキル内容を検索
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ moonlight-lupin/ agent-skills --skill skill-retrievalインストール後、Claude Code で/skill-retrievalを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
__init__.pyLICENSEplugin.yamlREADME.mdrequirements-dev.txtrequirements.txtscripts/__init__.pybm25_retriever.pyjev_rerank.pyrerank_ab_report.pytests/conftest.pytest_bm25_retriever.pytest_cjk_tokenization.pytest_index_cache.pytest_inject_k_split.pytest_issue14_should_show_arity.pytest_issue8_round2.pytest_issue8_round3.pytest_jev_environ.pytest_jev_provider.pytest_jev_rerank.pytest_midscan_fallback.pytest_module_identity.pytest_profile_home_switch.pytest_public_surfaces.pytest_refactor_contract.pytest_regressions.pytest_rerank_hook_wire.pytest_round3_fixes.pytest_round4_fixes.py詳細
スキル検索
これは Hermes Agent のプラグインです。Claude Code のプラグインではないため、
Claude Code では読み込まれません。このランタイムには pre_llm_call イベントも Python の
register() エントリーポイントもなく、plugin.yaml ではなく
.claude-plugin/plugin.json を読み込みます。Hermes Agent >=0.21.1 が必要です(
plugin.yaml の requires_hermes)。
Hermes Agent のスキルを対象とした、BM25 ベースの段階的な情報開示です。すべてのスキルの説明をシステムプロンプトに詰め込む(約 11.5K トークン)のではなく、このプラグインは名前のみのコンパクトなインデックスを保持し、各ターンで関連性の高い上位 K 件の説明だけを挿入します。
機能
2 段階の段階的な情報開示を行います。
-
フェーズ 1 — システムプロンプトの圧縮(
llm_requestミドルウェア): 送信リクエストの<available_skills>ブロックを書き換え、スキル名のみを一覧表示します(説明は除去されます)。すべてのスキルは名前で引き続き検索可能です(約 11.5K トークンから約 2K トークンに削減)。Hermes が構築するシステムプロンプト自体は変更されないため、プロンプトキャッシュへの影響はありません。 -
フェーズ 2 — ターンごとの BM25 検索(
pre_llm_callフック):ユーザーメッセージをトークン化し、アクティブなスキルの説明を BM25 Okapi で順位付けして、関連性の高い上位 K 件(約 300 トークン)をコンテキストとしてユーザーメッセージの後ろに追加します。
アーキテクチャ
Each turn (llm_request middleware)
│
▼
Phase 1: rewrite <available_skills> → names only (~2K tokens, request only)
Each turn (pre_llm_call)
│
▼
Phase 2: BM25Index.retrieve(user_message, top_k)
└── inject "## Retrieved Skills ..." into user message (~300 tokens)
BM25 インデックスは初回のターンで、スタンドアロンのスキル(~/.hermes/skills)とプラグインに同梱されたスキル(~/.hermes/plugins/*/skills)から遅延構築され、その後、Hermes ホーム、ツール機能のスナップショット、セッションのプラットフォーム、解決済みの無効化スキルセットごとにキャッシュされます(Hermes が独自のスキルプロンプトキャッシュのキーに使用するものと同じ入力です)。Hermes がスキルプロンプトキャッシュをクリアしたとき(skill_manage による作成・パッチ適用・削除、ハブからのインストール、スキルの切り替え)、またはディスク上の軽量マニフェストに変更があったとき(スキルのルートおよびカテゴリディレクトリの mtime、トップレベルの SKILL.md ファイル、config.yaml)には、再起動なしでキャッシュが再構築されます。
検索には標準ライブラリのみで実装された転置インデックス(用語 → 事前計算済みの BM25 重みを持つポスティングリスト)を使用し、約 200 個のスキルでもサブミリ秒で処理されます。
トークン削減
| 段階 | トークン数(概算) |
|---|---|
| 変更前(システムプロンプト内のスキル一覧全体) | ~11.5K |
| 変更後 — システムプロンプト内は名前のみ | ~2.0K |
| 変更後 — ターンごとに上位 K 件の説明 | ~0.3K |
| ターンごとの純削減量 | ~2.3K(約 9K 削減) |
約 300 個のスキルがある Hermes 環境で測定しています。削減量はスキル数に応じて増加します。
インストール
このディレクトリを Hermes のプラグインフォルダーにコピーするか、シンボリックリンクを作成します。
# From this repo
ln -s "$(pwd)/plugins/skill-retrieval" ~/.hermes/plugins/skill-retrieval
# Or copy
cp -r plugins/skill-retrieval ~/.hermes/plugins/skill-retrieval
ユーザープラグインはオプトイン方式です。Hermes はディレクトリを検出しますが、有効化されるまで読み込みません(~/.hermes/config.yaml の plugins.enabled に skill-retrieval が追加されます)。
hermes plugins enable skill-retrieval
エージェントのセッションを再起動して register() を実行します。これにより、llm_request ミドルウェアと pre_llm_call フックが登録されます。
依存関係(不足している場合は Hermes の Python 環境にインストールしてください)。
pip install pyyaml
設定
| 設定 | デフォルト | 設定方法 |
|---|---|---|
TOP_K | 6 | 環境変数 SKILL_RETRIEVAL_TOP_K |
INJECT_K | TOP_K | 環境変数 SKILL_RETRIEVAL_INJECT_K。検索候補のうち、いくつを挿入するかを指定します。挿入対象より広い候補プール(例:TOP_K=12、INJECT_K=6)を設定すると、Jev の再順位付けで候補を並べ替えるだけでなく、絞り込めます。TOP_K を超える値は上限に合わせて調整されます。 |
| システムプロンプトの圧縮 | 有効 | SKILL_RETRIEVAL_COMPACT=0 を設定すると、BM25 検索による挿入は維持したまま、圧縮を無効にできます |
BM25 k1 | 1.5 | scripts/bm25_retriever.py 内の定数 |
BM25 b | 0.75 | scripts/bm25_retriever.py 内の定数 |
| Jev による再順位付け | 無効 | 有効にするには SKILL_RETRIEVAL_RERANK=jev を設定します。キーは TYPESAFE_API_KEY(旧 TYPESAFE_KEY も使用可能)で、os.environ から読み取られます |
| 再順位付けプロバイダー | typesafe | SKILL_RETRIEVAL_RERANK_PROVIDER=openrouter を設定すると、OPENROUTER_API_KEY を使用して https://openrouter.ai/api/v1/systemone に接続します |
| 再順位付けログのパス | $HERMES_HOME/data/jev-trial/rerank_ab_log.jsonl(5 MB を超えるとローテーション) | 環境変数 SKILL_RETRIEVAL_RERANK_LOG |
export SKILL_RETRIEVAL_TOP_K=8
export SKILL_RETRIEVAL_INJECT_K=6
export SKILL_RETRIEVAL_COMPACT=0
export SKILL_RETRIEVAL_RERANK=jev
Jev による再ランキング(任意、デフォルトでは無効)
SKILL_RETRIEVAL_RERANK=jev の場合、BM25 の上位 N 件の候補が Jev の system-one 判定モデル(POST https://api.typesafe.ai/v1/systemone、モデル jev-latest)によって再ランキングされます。動作は次のとおりです。
- 失敗時も処理を継続 — タイムアウトは 3.0 秒、再試行はありません。エラーが発生した場合、リクエストは警告をログに記録し、BM25 の元の順序が維持されます。Jev が原因で検索がブロックされることはありません。
- 環境変数のみで設定 — すべての設定は
os.environから取得されます(ファイルは読み込みません)。TYPESAFE_API_KEY(旧TYPESAFE_KEYも使用可能)、またはSKILL_RETRIEVAL_RERANK_PROVIDER=openrouterとともにOPENROUTER_API_KEYを指定します(同じ仕様で、OpenRouter 経由で提供されます)。Hermes は起動時にアクティブなプロファイルの.envを環境変数に読み込むため、別のプロファイルのファイルが読み込まれることはありません。 - A/B ログ — 各クエリについて、タイムスタンプ、セッション ID、クエリの抜粋(200 文字)、両方の順序、確率、トークン数、レイテンシが上記のログパスに記録されます。ログは 5 MB を超えるとローテーションされます。
scripts/rerank_ab_report.pyでローカルに集計できます。 - 送信件数の上限 — 呼び出し側の候補リストがそれより長くても、1 回の呼び出しで送信される説明は最大
RERANK_CANDIDATES件(デフォルトは 12 件)です。残りは末尾に BM25 の順序で残り、マシンの外部に送信されることはありません。
正しく動作しているか確認する
フェーズ 1 は完全な Hermes ランタイム以外では何もせずに終了し、BM25 インデックスも警告なしで空になることがあります。再起動後、Hermes のログを確認してください。
正常な起動 — 次のログ行を確認します。
BM25 index built: N docs …Skill retrieval plugin registered (top_k=…, compact=true)
機能低下 — 次の警告が表示された場合、正常に動作していません。
Cannot locate prompt_builder — compaction skipped(フェーズ 1 に失敗しています。フェーズ 2 は匿名セッションでは引き続き実行されますが、機能スナップショットが記録されないため、名前付きセッションでは挿入がスキップされます)No active skills found for BM25 index(インデックスが空です。検索結果は一切挿入されません)
仕組み
- トークナイザー — テキストを小文字に変換して句読点を除去し、空白で分割します。CJK 文字を含むトークンは、CJK 文字の連続部分についてバイグラムに分割されます(3 文字以上の連続部分はバイグラムに置き換えられます)。対象は、CJK 統合漢字(拡張 A および B を含む)、日本語の仮名(全角および半角)、ハングル音節です。これにより、中国語、日本語、韓国語のクエリで英語以外のコーパステキストに一致させられます。また、
用python写のような混在トークンでもpythonの語句が維持されます。 - コーパス — 各スキルは
"name: description"に、そのフロントマターのtriggers:リストとmetadata.hermes.tags(存在する場合)を加えたものになります。また、SKILL.md の本文に CJK 文字が含まれる場合は、本文の最初の約 3000 文字から抽出した CJK 文字の連続部分を空白で連結したものも加えられます(合計 200 文字が上限です)。本文をコーパスに含めることで、CJK クエリは通常英語で書かれた説明だけでなく、スキルの内容にも一致します。トリガーフレーズ、タグ、CJK 文字の連続部分は検索スコアに反映されますが、プロンプトに挿入されるのは説明のみです。~/.hermes/config.yamlで無効化されたスキルは除外されます。 - インデックス — BM25 Okapi の TF 飽和と Lucene IDF
log(1 + (N-df+0.5)/(df+0.5))を使用します(常に正の値となるため、小規模なコーパスや一般的な語句でもスコアが付きます)。転置インデックスとして保存されます。dict[str, list[tuple[int, float]]]は各語句を、その転置リスト(doc_index、事前計算された BM25 重み)の組に対応付けます。 - 検索 — インデックスに存在する各クエリトークンについて転置リストを走査し、スコアを加算します。スコアの降順で並べ替えます(スコアが 0 より大きいもののみ)。
パフォーマンス
- 初回使用時にインデックスを構築してキャッシュします(CPU のみの VM で 200 個のスキルの場合、約 8 ms)。以降の各ターンでは、キャッシュを検証するため、スキルのルートとその直下のサブディレクトリ、および
config.yamlのみを stat します。スキルが 0 個のインストールでも空の結果をキャッシュします(警告は 1 回のみで、再スキャンは行いません)。 - 検索は 1 ミリ秒未満です(200 個のスキルで平均約 0.03 ms)。転置インデックスは、クエリ用語を共有するドキュメントのみを対象にします。コーパス全体のスキャンは行いません。
- コンパイル済みの依存関係はありません。このプラグインは Python 標準ライブラリのみを使用します(設定/フロントマターの解析には pyyaml も使用します)。これにより、154 MB の numpy/scipy のインストールと約 573 ms のインポートコストをなくせます。これは、サブプロセスの起動経路(例:Claude Code の
UserPromptSubmitバリアント)で重要です。 - フックでエラーが発生した場合は
None(注入なし)を返すため、エージェントは作業を続けられます。
依存関係
pyyaml
制限事項
- BM25 は語彙ベースであり、意味ベースではありません。スキルの説明と共有するトークンが少ない言い換えクエリは、意図が一致していても順位が低くなる場合があります。
- 200 文字を超える説明は、注入されるブロック内で切り詰められます。スキル本文全体を確認するには
skill_view(name)を使用してください。 - コンパクションでは、Hermes の読み取り専用
agent.prompt_builderヘルパーを使用します。インポートできない場合は Phase 1 がスキップされ、検索はスタンドアロンローダーにフォールバックします。 - 名前付きセッションは、このプロセス内でシステムプロンプトが構築された後にのみ注入されます(構築時にそのセッションのツール機能が記録されます)。再起動後、プロンプトを再構築せずに復元されたセッションには、Hermes が非表示にしているスキルを誤って渡すリスクを避けるため、注入されません。
- Hermes の外部(例:エディター)でネストされた
SKILL.md(category/skill/SKILL.md)の内容だけを編集しても、ディレクトリの mtime は変わらないため、Hermes がスキルのプロンプトキャッシュをクリアするか、エージェントを再起動した後にのみ反映されます。skill_manage経由の編集、およびスキルの追加/削除や設定の変更は、次のターンに反映されます。 - Phase 1 は Hermes の内部機能(
agent.prompt_builder)に依存しているため、Hermes のアップグレードで動作しなくなる可能性があります。 - BM25 の top-1 精度は安定していません。最も一致するスキルが 1 位にならないことはよくありますが、通常は上位数件に入ります。順位はユーザー自身のコーパスと説明の文言に全面的に依存するため、
TOP_Kを約 5 未満にすることは推奨されません。 - コンパクションでは、
agent.prompt_builderとagent.skill_utilsから読み取り専用インポートを使用して、リクエストの<available_skills>ブロックを書き換えます(README では 10 個すべてを開示しています)。それらのシグネチャが変更された場合、スキャン中に例外が発生するとスタンドアロンローダーにフォールバックするため、コーパスが気付かないうちに空になることはありません。 - 標準ライブラリのインデックスは float64 で計算します(以前の scipy 版では float32 を使用していました)。スコアが等しいスキルの順序は以前と異なる場合があります。これは問題ありません。スコアは実際に同点(差は約 1e-6)だからです。ただし、scipy 版からの動作上の変更ではあります。