8月17日、Vinod ChuganiがMachine Learning Masteryに「7 Regression Tests Every AI Agent Should Pass Before Deploy」と題した記事を公開した。AIエージェントを本番環境にデプロイする前に通過すべき7つのリグレッションテストを体系的に解説したもので、障害の根本原因がモデルの性能ではなくオーケストレーション層にあるという実践的な視点が特徴だ。
AIエージェントの障害は、モデルの性能不足ではなく、オーケストレーション層のステート管理の破綻から発生することが多い。多くのチームがこれを本番環境でリアルユーザーのトラフィック下に晒されて初めて気づく。記事ではこの問題を構造的に捉え、CI/CDのゲートとして使える7つのリグレッションテストを提示している。
なお記事では、テストを実行する前提条件として以下を設定するよう求めている:モデルスナップショットを固定し、可能な限りtemperatureをゼロに設定し、統計的に信頼できるパス率を得るために複数回試行することだ。1回の実行でアサートするだけでは、確率的なエージェントの挙動を正しく評価できない。
また「ステート」と「メモリ」の区別も重要だ。ステートはエージェントの実行ステップの決定論的な記録、メモリはプロンプトに注入される確率的な取得コンテキストを指す。エージェントが誤動作するとき、障害の多くはステート層にある。
テスト1:コンテキストロスと取得劣化(最重要)
最も実害の大きい失敗モードがこれだ。会話履歴がプロンプトバジェットの上限に近づくと、オーケストレーション層は何を削除するかを決めなければならない。単純なFIFO(先入れ先出し)方式だと、40分前に収集したユーザーのアカウント情報が削除され、エージェントが同じ情報を再度ユーザーに聞いてしまう。
テスト設計:プロンプトバジェットの約80%を埋める合成会話履歴を与え、最初のターンで確立された事実に正解が依存する質問をする。セマンティック検索でその削除されたターンを復元できた場合、あるいは要約ポリシーがコアなエンティティ関係を保持していた場合のみパスとなる。
落とし穴:「取得で通過」と「要約で通過」を同じテストで評価しないこと。これらは別々のテストとして扱わなければ、どちらが機能しているか判断できない。
テスト2:ツール実行の冪等性(Idempotency)
冪等性とは、同じ操作を複数回実行しても結果が変わらない性質のことだ。現実のネットワーク環境では、エージェントが同じツール呼び出しを複数回発行する状況は起きる。リトライはハーネス、HTTPクライアント、オーケストレーターループから発生する。モデルが曖昧な観測結果でプロンプトの期待を満たせないと判断したときも再発行する。いずれも外部システムへの重複書き込みを引き起こす。
テスト設計:同じツール呼び出しペイロードを実行境界に3回送る。下流システムへの書き込みが正確に1回で、後続の呼び出しにはキャッシュヒットのレスポンスが返された場合のみパス。
冪等性キーはツール名・正規化された引数・ビジネス相関IDのハッシュから導出すること。ステップIDやメッセージ位置を使うと、ループの反復ごとに変化してキーが毎回ユニークになり、仕組みが機能しない。
テスト3:プロンプトインジェクション耐性
悪意あるペイロードを直接のユーザー入力と間接ベクタ(ウェブ検索の取得ドキュメントや外部ナレッジベース)の両方から注入し、エージェントが安全な終了状態に達するかを確認する。
重要なのは、出力テキストではなくツール呼び出しのトレースと副作用でアサートすることだ。エージェントが丁寧な拒否文を返しながら、その下で有害なツール呼び出しを発行していることはあり得る。セキュリティはツール層でのロールベースアクセス制御(RBAC)によって担保される。
テスト4:構造化出力の準拠
スキーマ制約デコーディングにより、構文エラーや不正なキーはほぼ防げる。しかし問題になるのは構文レベルではなく、以下の4つのより微妙なケースだ:
- トランケーション:トークンバジェット超過による不完全な出力。
finish_reasonをパース成功と並行してアサートすること。スキーマ上は「通過」に見えても、出力が途中で切れているケースはここでしか検出できない - リフューザル:nullパースかつrefusalフィールド有りは、リトライではなく403として扱う。リトライループに入ると無限にコストが発生する
- セマンティック不適合:スキーマ上は正しいが値が誤っている。型と値域の両方をバリデーションする必要がある
- モデルバージョンスキュー:エイリアス経由でルーティングされた古いスナップショットがlegacy JSONモードにサイレントフォールバックする。デプロイ後のモデルバージョン固定とスナップショットの追跡が不可欠だ
このテストは「出力が壊れていないか」ではなく「出力が使えるか」を問うものだ。4つのケースをすべてカバーしなければ、本番での意図しない障害を検出できない。
テスト5:非終了と有界オーケストレーション
エージェントコミュニティが「デッドロック」と呼ぶものの多くは、より正確にはライブロック(前進はしているが目標に近づかない状態)だ。真のデッドロックはマルチエージェント間の相互ブロックで、別テストの対象となる。
バジェットはステップ数・累積トークンコスト・ウォールクロックタイムアウトの3つ組で設定する。ステップ数だけでは、1ステップが止まる障害を検出できない。3つを組み合わせることで、「回っているが進んでいない」状態と「完全に止まっている」状態の両方を捕捉できる。暴走エージェントの実コストはレートリミット枯渇ではなく、推論コストとキューの枯渇にある。
テスト設計上の注意点として、終了条件をあえて曖昧にした入力を与え、エージェントがバジェット内で明示的な失敗状態(タイムアウトやステップ超過のエラー)に到達するかを確認する。ループして終わらないことより、適切に失敗を通知することがここで問われる。
テスト6:RAGグラウンディング
RAG(Retrieval-Augmented Generation)とは、外部ナレッジベースから情報を取得しプロンプトに注入することで、モデルの回答精度を高める手法だ。このテストでは、取得パイプラインに一般常識と矛盾する合成事実を注入し、エージェントがパラメトリック知識(モデルに学習済みの知識)より取得した事実を優先するかを確認する。
ただしリスクは双方向であることに注意が必要だ:
- 文脈に常に従うよう調整されたエージェントは、取得ポイズニングの攻撃ベクタになる。悪意ある外部コンテンツを取得させることで、エージェントの回答を意図的に歪められてしまう
- 逆に、明らかに誤った取得事実にはモデルが抵抗できるかも確認が必要だ。「取得優先」と「取得耐性」の両方を別ケースとして設計する
このテストはテスト3のプロンプトインジェクションとも密接に関連する。インジェクションが意図的な攻撃を対象とするのに対し、グラウンディングテストは「悪意のない誤情報」への耐性を問う点が異なる。実装上は、テストデータの合成事実が「もっともらしいが誤っている」精度が結果の質を左右するため、ドメイン知識を持つ人間がレビューすることが望ましい。
テスト7:ステートの再水和(Rehydration)と一貫性
再水和とは、シリアライズして保存したステートを別プロセスで読み込み、処理を引き継がせることを指す。分散デプロイでは、エージェントセッションを開始したプロセスが完了まで担当することはほぼない。プロセスのクラッシュ、スケールイン、ローリングデプロイのいずれもセッション中断の原因になる。
テスト設計:マルチステップワークフローの中間点でフル実行ステートをデータベースにシリアライズし、インメモリオブジェクトを破棄、新しいプロセスで再水和する。次のユーザー入力後にワークフローを正しく完了した場合のみパス。
落とし穴は2つ:バージョンスキュー(旧スキーマのステートが現在バージョンでデシリアライズできない)と、冪等性との結合(ツール呼び出しの途中で再開する場合、副作用が既にコミット済みかを知る必要がある)。このため、テスト2とテスト7は同一テストスイートに置き、インフラを共有すべきだ。
これら7つのテストが検出しないもの
記事は正直にカバレッジの限界も明示している。コスト・レイテンシのリグレッション、上流APIのスキーマ変更によるツールコントラクトのドリフト、ツール引数やトレース内のPII漏洩、新しいエンコーダバージョンのデプロイ時の埋め込み空間スキューは検出対象外だ。
このリグレッションスイートは出発点に過ぎない。固定されたモデルバージョンで、信頼区間付きのパス率閾値で継続的に実行することが、100日後も機能し続けるために不可欠だ。
詳細は7 Regression Tests Every AI Agent Should Pass Before Deployを参照していただきたい。