LlamaIndexに最近投稿された統合提案 — シールドされたメモリバンドルのエクスポート/インポート — は、エージェント間で検証済みの状態を持ち回るという問題領域そのものです。私たちはエージェント間検証のためのTrust Cardプロトコルを運用しており、シール済みでポータブルなバンドルはその中核アーティファクトです。運用の中で痛い目を見て学んだ4つの教訓を、この提案(そして自分のバンドル設計)に直接適用できる形で共有します。
1. クロックをピンしないと、有効性期間はリプレイ可能のまま
「3月に検証済み」が「4月についての証拠」であるためには、検証タイムスタンプ自体が信頼対象になっていなければなりません。インポータが自ホストのローカル時計に対して有効性を評価する設計だと、バンドルはクロックスキューした環境や時計を操作されたホスト間でいくらでもリプレイできます。
私たちに効いた変更は1つでした。検証結果の隣に明示的な evaluation_clock を記録し、インポータのローカル時間ではなく、そのピンされた参照時刻に対して有効性を評価する。これだけで有効期間はリプレイ耐性を持ちます。
Qiitaでよく見かけるTool Poisoning・Rug Pull・Shadow MCPという3リスクの分類に当てはめると、これは「検証した時点」そのものを攻撃面として扱う話です。ツール定義が後から差し替えられる(Rug Pull)のと同じ構造が、時刻の側にも存在します。
2. 証拠はキャップし、残りはハッシュで
メモリごとのレシート(コマンド、結果、終了コード、出力ハッシュ)は際限なく成長します。稼働中のエージェント1ヶ月分の証拠だけで、バンドルは配送不可能なサイズになります。
実用的なパターンはこうです。サイズ上限付きの出力抜粋に加えて、完全なレシートのハッシュを保存する。完全なレシート自体は帯域外(out-of-band)に置き、紛争解決時にだけ取り出す。「証拠があること」と「証拠を常に運搬すること」を分離すれば、バンドルは小さく、検証は完全なままです。
3. インポータを敵対的にテストする
シールドされたバンドルのパーサは、信頼できない入力に対して信頼の決定を実行するコードです。つまりtool poisoningと同じ攻撃形状を持ちます。
私たちのやり方は、署名境界をまたぐ2点変異バンドルを機械的に生成し、インポータがそれら全てを拒否することをリリース条件にすることです。「実装した」と言えるのは、この敵対的入力テストを通過した後だけにすべきです。observed-until-reverified(再検証まで観測済み扱い)というモデルは、再検証パス自体が敵対的入力で生き残って初めて名前に値します。
4. バンドルの妥当性を、エクスポータへの信頼に依存させない
バンドルのダイジェストを公開の透過性ログ(Rekor風)にアンカリングし、署名済みリリースチェーンと組み合わせると、中央機関なしで第三者検証と失効のストーリーが得られます。これはマルチホップのコスト・オブ・カストディ(多段の保管責任)にもきれいにマップします。ホップごとの署名だけでは、段数が増えると担保が劣化しますが、公開ログへのアンカリングは劣化しません。
残る問い:識別の鮮度
鍵ローテーションを考えると、古い鍵で署名されたバンドルは回転後も暗号学的には有効です。observed-until-reverifiedはコンテンツの鮮度をカバーしますが、識別(identity)の鮮度は誰が、どう決めるのでしょうか。ローテーション済みの鍵で署名されたバンドルはインポート可能のままか。この質問への答えをまだ見たことがなく、それ自体が設計上の空き地だと思っています。
アーティファクトは全て公開
上記1〜4の背後にある具体的な成果物は公開されています。ピンされたクロック規則と敵対的ウィンドウ分布を含むコンフォーマンスベクトルと、リファレンスランナー(MIT):
- リポジトリ: https://github.com/alicelabs-llc/universal-trust-adapter
- ベクトルインデックス: https://www.marketnow.site/uta/conformance/vectors/_index.json
- オフライン検証SDK(node:cryptoのみ、CA鍵レジストリ埋め込み): https://www.npmjs.com/package/agent-trust-card
日本語での質問・ツッコミ歓迎です。特に「識別の鮮度」の問いに対する設計回答を持っている方はぜひ。
Top comments (0)