AIエージェントの肥大化した指示ファイルのためのロスレスダイエット
常時ロードされるエージェントの指示ファイルが33,000トークンにまで膨れ上がりました。ここでは、一文も削除することなくそれを10KBのルーティングハブにまで削減し、何も失われていないことを証明した方法をご紹介します。
ほとんどのAIコーディングエージェントは、セッションごとにプロジェクト指示ファイルを読み込みます。それは規約をまとめたページとして始まり、やがてチームのガラクタ入れになります。デプロイのランブック、インシデントの事後分析、スキーマ移行のメモ、フロントエンドの奇妙な仕様などです。私たちのファイルは253行、67KB、約33,000トークンに達し、エージェントがコードを一行も読む前に注入されていました。この記事では、それを10KBのルーティングハブに削減した再構築、残さなければならなかった安全規則、そしてその移行で何も失われなかったことを証明した検証について説明します。すべてを決定づけた制約は、コンテンツは削除できず、移動またはマージのみが可能であるということでした。
なぜ指示ファイルは肥大化するのか:宛先のないルール
ファイルが肥大化したのは、誰かが不注意だったからではありません。それは、一見すると責任感のあるルール、「ポリシーや設定が変更された場合は、直ちに指示ファイルを更新すること」が原因でした。このルールにはルーティングがありませんでした。すべてのプロジェクトが、将来のエージェントが必ず参照すると保証されている唯一の場所であるという理由から、確実に読まれるその1つのファイルに運用上の詳細を追加していったのです。
それこそが、あなた自身のセットアップにおいても認識すべき構造的な原因です。追記のみの文化を持つ、常に読み込まれる単一のファイルは、単調に増加し続けます。過去の何らかのインシデントによってすべての警告がその場所を得たため、誰も警告を削除できる立場にありません。1年後には、テーブルのセルに10パラグラフもの経緯が書き込まれるようになり、エージェントは、それらのパラグラフが記述しているサブシステムに全く触れないセッションを含め、すべてのセッションでトークンコストを支払っていました。
無駄になるのはお金だけではありません。33,000トークンのプリアンブルは注意を散漫にします。モデルは、長いコンテキストの途中に埋もれた指示を見失うことが実証されています。エージェントに最も遵守してほしかった警告が、数ヶ月前のクローラーの奇妙な動作に関するパラグラフと競合していたのです。
トピックごとに分割し、ストップゲートは維持する
解決策は明白です。詳細をオンデマンドで読まれるトピックドキュメントに移動し、ハブは小さく保つことです。私たちは最終的に8つの運用ドキュメント (deploy、cloud resources、admin web、licensing、speech pipeline、data collection、crawling、nightly batch) と、マイグレーションファイル自体の隣に置かれるマイグレーションのREADMEを作成しました。各ドキュメントは、そのドメインを完全に所有します。ハブはトピックごとに1行とポインターを保持します。
ファイルのレイアウトよりも重要だった設計上の決定が2つありました。
第一に、インデックスは丁寧なリンクのリストであってはなりません。時間的プレッシャーにさらされている担当者は、リストをざっと見てコーディングを始めてしまいます。私たちはそれを条件付きルーティングテーブルとして記述しました。「ライセンスまたはシートロジックを変更する場合: まずdocs/ops/license.mdを必ず読むこと。」この違いは表面的なものに聞こえます。実際には、前提条件として表現された指示は守られますが、参照として表現されたリンクは読み飛ばされます。また、何がどこにあるか不確かな場合にファイル全体を読むのではなく、docsディレクトリをキーワードでgrepするように担当者に伝える一文も追加しました。
第二に、そしてこれが事故を防ぐ部分なのですが、一部の警告は移動させてはなりません。もし「デプロイ前に保留中のスキーママイグレーションを実行する」という記述が、担当者が開かないかもしれないドキュメントにしか存在しない場合、いつか担当者はそれを読まずにデプロイし、その警告が記録しているインシデントが再び発生するでしょう。私たちはこれらのうち7つを「ストップゲート (Stop Gates)」という見出しの下にハブに残しました。それぞれが要約された不変条件であり、その詳細は委譲されています。
- あらゆるデプロイの前にマイグレーションの状態を確認すること。不明な場合はデプロイしないこと。ロールバックはまずイメージを元に戻すことで行い、スキーマを元に戻すのはその後にのみ検討すること。
- 管理されたランタイム環境変数を変更する際は、追加型のフラグのみを使用すること。すべてを置き換えるタイプのものは、無関係な設定をサイレントに消去してしまう。
- ユーザーの音声の書き起こし、翻訳、またはシークレット値をログ、ドキュメント、またはコミットに決して書き込まないこと。
- データベースのIP許可リストのフラグはリスト全体を上書きする。まず現在のリストを読み込み、その和集合でパッチを当てること。
- 収集された音声の保持および同意に関するルールは不変条件である。それらに触れる前にデータドキュメントを読むこと。
- 本番ストアに対する破壊的または上書き形式のコマンドには、まず対象、影響範囲、およびロールバックの確認が必要である。
- 有料エンドポイントの認証、レート制限、またはフェイルクローズのトライアルロジックを決してバイパス、削除、または弱体化させないこと。
私たちが使用した選択ルールは次のとおりです。それを無視することが停止、データ損失、コンプライアンス違反、または顧客に見える障害を引き起こす場合、警告はハブに残り、それを無視することが単に午後を無駄にするだけの場合は外に出されます。使用頻度ではなく、無視した場合のコストです。
もう一文、ハブの最上部、すべてのものの上に加えられました。ドキュメントとコードが一致しない場合、コードが真実である。ドキュメントを更新し、古いドキュメントに合わせて動作中のコードを「修正」してはならない。分割されたドキュメントシステムは、担当者が矛盾を発見し、それを間違った方向に解決したときに最悪の失敗をします。
移行によって何も失われなかったことの証明
「すべて移行しました、信じてください」は検証ではありません。私たちの最初の計画は、grep を使って特徴的なトークンをいくつか抜き打ちでチェックすることでした。あるレビュー担当者が、サンプリングでは検出できない3つの失敗モードを指摘しました。トークンは残っていてもその周りの文が失われる、テキストが古いファイルから移動していなくても新旧ファイルの和集合ではチェックを通過してしまう、そしてテキストが間違ったドキュメントに配置される、というものです。サンプリングはサンプルを証明するだけで、それ以外の何も証明しません。
代わりに実行したのは、すべて機械的なチェックです。
まず、編集前のオリジナルファイルのナップショットから、すべてのインラインコードスパンを抽出します。これは、バッククォートで囲まれたすべてのコマンド、フラグ、パス、識別子を意味します。583個のユニークなスパンが得られました。それぞれのスパンは、新しいドキュメント群の和集合のどこかに現れなければなりません。比較は、正規表現としてではなく、空白を正規化した後の固定文字列として行います。なぜなら、$1::text[] のようなスパンは正規表現のメタ文字で満ちているからです。
次に、警告マーカー(禁止、必須、注意を表す単語、および私たちのドキュメントで使われている警告の絵文字)を含むすべての文を抽出します。61個の文が得られました。この移行は書き換えではなく逐語的な移動だったため、各文は正規化後に一字一句違わずにどこかに存在しなければなりませんでした。これがチェックを強力にする秘訣です。移動された文は、その条件、重要度、そして理由を伴って移動します。このチェックに失敗したのは3つの文だけで、それぞれに文書化された理由がありました。1つは重複と統合され、1つはテーブルの行をまたいで分割され、1つは内部相互参照が意図的に書き換えられていました。理由のないものは、すべて出荷停止(stop-ship)案件でした。
第三に、ゲートとしてではなく、異常検知器としてのバイト数集計です。新しいファイルの合計は、オリジナルファイルの129パーセントになりました(ヘッダー、ポインター、そして新しい「ストップゲート」セクションによってテキストが追加されたため)。もしこの数値が60パーセントであったなら、原因究明に乗り出していたでしょう。これを合否のしきい値にしなかったのは意図的です。なぜなら、正当な重複排除処理によって合計サイズが縮小することもあり得ますし、一方で、不適切な移行でも間違ったテキストがそのまま残った状態でしきい値を通過してしまう可能性もあるからです。
スパンのチェックはすぐにその価値を発揮しました。ウェブサーバーのサブコマンドが、似た名前のリアルタイムサブコマンドとは別物であるという注意書きの文が、移行マップの隙間から抜け落ちていました。67KBのテキストを2回読んでも、人間が1つの欠落した節を見つけることはできなかったでしょう。583個のスパンに対する固定文字列比較が数秒でそれを検出し、私たちは出荷前にそれを復元しました。
新しいエージェントでルーティングのスモークテストを実施
テキスト検証は、内容の維持を証明しますが、新しいレイアウトが機能することを証明するものではありません。そのために、私たちは新しいエージェントセッションに新しい10KBのハブのみを与え、「シートライセンスロジックを変更する前に何を読みますか」、「管理Webサービスをどのようにデプロイしますか」、そして「本番環境のキャッシュをワイプしてください」という3つの質問をしました。
最初の2つの回答は、正しいドキュメントにルーティングされ、移行ゲートを引用しました。3つ目の回答こそが、設計する価値のあるものでした。エージェントはあらゆる実行を拒否し、破壊的作業のストップゲートを引用し、どの環境でどのキープレフィックスかを尋ね、完全なフラッシュの代わりに、スコープを限定した削除を提案しました。その拒否こそが、常時ロードされるファイルにストップゲートを保持する目的のすべてでした。もしスモークテストでエージェントに何かを拒否させることができないのであれば、そのテストは甘すぎます。
小さく保つ: ファイルだけでなく、ルールを修正する
最後の変更は、6ヶ月後にこの作業全体を再実行するのを防ぐものでした。元の増大ルール(「常に指示ファイルを更新する」)は、ルーティングルールに置き換えられました。恒久的なルール、座標、およびストップゲートは、120行および10KBというハードキャップ付きでハブに配置されます。運用上の詳細、フラグ、およびインシデント履歴は、そのドメインを所有するトピックドキュメントに配置されます。決定の論理的根拠は設計ログに配置されます。そしてコードローカルなトラップは、それらが保護するコードの隣のコードコメントに配置されます。ハブがキャップを超えた場合、求められる対応はコンテンツを分割して外に出すことであり、キャップを交渉することではありません。
規模感を示すために、最後にいくつかの数字を挙げます。ハブは67KBから10KB未満になり、エージェントが実行されるたびに約28,000トークンのセッションコンテキストが節約されました。検証スイートは、約40行のPythonとシェルです。3回のレビューと検証ツールを含め、移行全体は1日の作業でした。もしあなたのエージェントの指示ファイルが数千トークンを超えて増え続けているなら、このダイエットは安価であり、トークンの節約は日々複利で増えていき、スパンレベルの検証があれば、小さなファイルと安全なファイルのどちらかを選ぶ必要はありません。