どのAIエージェントコマンドを実際に使用していますか?監査しましょう
カスタムエージェントコマンドは、廃止するよりも速く蓄積されます。セッションログから実際の使用状況をカウントし、使われていないコマンドは削除せずに無効化します。
コーディングエージェント用に作成するカスタムコマンドは、どれも最初は良いアイデアから始まります。それが2ダースにもなると、そのフォルダーは博物館のようです。数千のセッショントランスクリプトを対象に実際の呼び出し回数を数えたところ、私のコマンドの半分が2ヶ月間一度も呼び出されていなかったことがわかりました。クリーンアップは簡単でした。しかし、数字を正しく読み解くことは、そうではありませんでした。
コマンドは蓄積するが、利用は伴わない
エージェントコマンドとは、パッケージ化された命令セットであり、スラッシュ名を入力したときにエージェントが読み込む定義ファイルが入ったフォルダーです。1つ作成するのに半日かかります。廃止するのにコストはかからないため、誰もそれをしません。
この山積みは、単なる散らかりではありません。各定義は、セッションごとにその名前と説明をエージェントのコンテキストに追加し、混雑した名前空間は、モデルに間違ったツールを選択させてしまいます。しかし、本当のコストは別の形で現れました。私の26個のコマンドは、26個の同じものではありませんでした。それらは、1つのフラットなフォルダーに存在する3つの異なる種類のオブジェクトでした。
メモリからではなく、セッションログからカウントする
エージェントは会話のトランスクリプトをディスクに書き込みます。通常、セッションごとに1行1JSON形式のファイルとして保存されます。モデルが行ったすべてのツール呼び出しは、構造化されたブロックとしてそこに含まれています。そのファイルツリーは、誰もクエリを実行しようと考えない使用状況データベースです。
コマンドの呼び出しは、その入力にコマンド名が含まれたツール使用ブロックとして表示されます。ツリー全体にgrepを1回実行するだけで、ランキングリストが得られます:
# adjust the JSON shape to match your agent's transcript format
rg -o -I '"name":"<ToolName>","input":\{"<field>":"([^"]+)"' -r '$1' \
--glob '*.jsonl' "$TRANSCRIPT_DIR" | sort | uniq -c | sort -rn
単なる名前ではなく、ブロックに一致させてください。コマンド文字列に対して安易に grep を実行すると、文章中で名前が言及されただけの場合もすべてヒットしてしまい、その結果、めったに使われないコマンドほど使用回数が最も水増しされます。それは意図したものと正反対の結果になります。
最も重要なステップは、カウントを時間で分割することです。それを2回実行します。1回はすべてを対象に、もう1回は過去60日間に変更されたファイルを対象にします:
find "$TRANSCRIPT_DIR" -name '*.jsonl' -mtime -60 > recent.txt
rg -o -I '"name":"<ToolName>","input":\{"<field>":"([^"]+)"' -r '$1' $(cat recent.txt) \
| sort | uniq -c | sort -rn
生涯合計は嘘をつきます。去年の春に多用し6月には使わなくなったコマンドを、今朝実行したコマンドと同じバケツに押し込めてしまうのです。私のデータでは、その2つの列はリストのおよそ3分の1で食い違っていました。あるコマンドは生涯呼び出し回数が数百回で、最近の呼び出しはゼロでした。別のコマンドは、生涯呼び出し回数がわずか十数回でしたが、まだ週に一度は使われていました。
呼び出しがゼロでも、必ずしもデッドであるとは限らない
私のコマンドのうち3つは、呼び出し回数がほぼゼロでしたが、セット全体の中で最も負荷を担うファイルでした。
それらは、より大きなパイプラインコマンドの本体でした。そのパイプラインは、それらをツールとして呼び出しません。ディスクからそれらの定義ファイルを読み込み、内部のステップに従います。ログから見ると、その3つは使われていないように見えます。それらを削除すると、パイプラインは実行の途中で壊れます。
これは一般的な落とし穴です。使用状況のテレメトリは、インストルメンテーションが認識しているエントリーポイントしか見ることができません。ファイルの読み込み、シェルアウト、またはドキュメントの参照による構成は、それには見えません。ゼロという結果に基づいて行動する前に、コマンド名だけでなくファイルパスで、残りのツールをgrepしてください:
# does anything else read the definition file directly?
rg -n '<commands-dir>/[a-z-]+/<definition-file>' .
その1つのコマンドが、削除リストを維持リストに変えてしまいました。「昨年は数百回の直接呼び出しがあったが、今四半期はゼロ」という状況の正しい解釈は、そのコマンドが使われなくなったということではありませんでした。より新しいパイプラインがそれをエントリーポイントとして吸収し、その親が呼び出されるたびに、その下にあるステップは依然として実行されていたのです。
つまり、そのフラットなフォルダーにあった3つの種類とは、私が直接呼び出すもの、パイプラインが読み込むもの、そして誰も触らないものでした。クリーンアップの候補となるのは、3番目のグループだけです。2番目のグループについては、パイプラインが意図されたエントリーポイントであることを示すように説明を変更し、ファイルは元の場所にそのまま残しました。なぜなら、パイプラインがそれらのパスをハードコーディングしているからです。
削除ではなく、階層の深さで無効化する
休止中の13個について、削除するのは違うと感じました。それらは機能し、ドキュメント化されています。6ヶ月後には、また使いたくなるかもしれません。
ほとんどのエージェントは、ディレクトリをちょうど1階層の深さまでスキャンすることでコマンドを検出します。各子ディレクトリで定義ファイルを確認するのです。再帰はありません。ドキュメントを信用するのではなく、自身のエージェントのローダーを読んでください。私が確認したものでは、その動作は明確でした。ディレクトリ自体に定義ファイルがない場合、その子ディレクトリが調査されることはありません。
これにより、簡単な無効化メカニズムが手に入ります。フォルダを1階層深く移動させます:
mkdir -p commands/_attic
git mv commands/old-command commands/_attic/old-command
定義は今や深さ2の階層にあります。スキャナーは決してそこには到達しません。コマンドはメニューから消え、コンテキストの消費を停止します。そして、そのすべてのバイトは依然としてディスク上にあり、バージョン管理下にあります。復元は、git mvを1回実行するだけです。
ここで実際に機能しているものに注目してください。それはフォルダー名のアンダースコアではなく、ツールが遵守を約束する命名規則でもありません。それは深さの変更です。これに依存する前に、ご自身のエージェントのスキャン動作を検証してください。なぜなら、定義ファイルを再帰的にglobするローダーは、アーカイブされたすべてのコマンドをサイレントに存続させてしまうからです。2人のレビューアーが私にまさにそのリスクを指摘しましたが、ローダーを読むことでようやく決着がつきました。
そのコードを読んで、もう一つ分かったことがあります。スキャナーはコマンドとしてシンボリックリンクも受け入れます。私は言語エイリアスとして別のコマンドフォルダーを指すシンボリックリンクを設定していたのですが、これはそのコマンドがセッションごとに2回登録されていたことを意味していました。エイリアスは説明テキストによって既に処理されていました。そのシンボリックリンクは完全な重複であり、ディスカバリーがどのように機能するかを読むまで気づきませんでした。
クリーンアップ中に壊れたもの
移動は些細なものでした。問題が起きたのはドキュメントで、同じ欠陥が3つの異なるファイルで3回も表面化しました。
編集すると行番号がずれる。 私の計画では、編集箇所を上から下への絶対的な行範囲としてリストアップしていました。496行目を削除すると、それ以降のすべての範囲が1行ずれます。あるファイルでは、対象のセクションヘッダーが計画上の開始位置より1行上になってしまったため、削除処理によって本文が削除され、ヘッダーが水平線の下に孤立してしまいました。その後、まったく同じバグが2つ目のファイルで、さらには9つの範囲を含む3つ目のファイルでも発生しました。修正方法はありきたりで絶対的なものです。ファイルの下から上に向かって編集するか、行番号の代わりに文字列をアンカーにすることです。
検証スコープは編集スコープと一致させる必要がある。 私はリポジトリ全体で古い参照がゼロであることを確認するチェックを書きましたが、編集対象としてリストアップしたのは4つのファイルだけでした。5つ目のファイルに参照が残っていました。そのチェックは実行する前から失敗することが確実でした。受け入れ基準がディレクトリ全体を走査する場合、そのディレクトリ内のすべてのファイルがスコープ内にあるか、あるいは基準に明示的な除外リストが必要になります。
アーカイブされたアイテム内のパスは更新しないこと。 アーカイブされた定義には、それ自身の古いパスが含まれています。それらを新しいアーカイブの場所に書き換えるのは、見た目はすっきりしますが間違いです。フォルダを復元すると元のパスに戻り、書き換えられた参照は何も指さなくなってしまいます。アーカイブ内にある古く見えるパスは、そのアーカイブが復元されると想定している状態にとっては正しいパスなのです。
記録しておくべき決断がもう一つありました。私は設定ファイルから2つのセクションを丸ごと削除する計画でしたが、そのうちの1つがまだ稼働中のインフラを文書化したものであることが判明しました。そこに記載されていたコマンドは廃止されていましたが、その下にあるフックやスクリプトはそうではありませんでした。そのセクションを削除していたら、稼働中の仕組みに関する唯一の記述が失われていたでしょう。私はコマンドに関する2行を削除し、残りを残しました。
FAQ
数値が意味を持つようになるまでには、何回セッションが必要ですか? 直近のウィンドウに通常の作業が混在して含まれる程度に十分な回数です。私の場合、2ヶ月間の毎日の使用で十分でした。直近の列の総呼び出し回数が数百回未満の場合は、低いカウントは証拠ではなくノイズとして扱ってください。
アーカイブする代わりに削除すべきですか? リポジトリに履歴がある場合、削除は回復可能であり、最もクリーンなツリーを残します。アーカイブは、コンテンツを再アクティブ化せずに参照することを期待する場合に優れています。これは、手動で手順を追う可能性のあるコマンドの一般的なケースです。どちらも元に戻すことができます。どちらか一方を選び、一貫性を保ってください。
コマンドが休止状態にもかかわらず、ドキュメントではまだ使用するように指示されている場合はどうすればよいですか? 同じ変更でドキュメントを修正し、明らかなファイルだけでなく、リポジトリ全体を確認してください。スクリプト、サブプロジェクトのREADME、ワークフローガイドには、すべて参照が蓄積されます。私の場合、それらは5つのファイルに分散しており、そのうちの1つは、私がアーカイブしようとしていたコマンドを呼び出すように読者に指示していました。
リストを整理することで、実際に測定可能な改善はありますか? コンテキストの節約は実際にありますが、わずかです。より大きな効果は選択の正確さです。重複に近い説明が減ることで、誤った選択が減ります。私なら、トークン数だけを理由にこれを行うことはありません。3種類の異なるオブジェクトが同じように見えるフォルダは、最終的にあなたを誤解させるフォルダになるため、私はこれを行います。