DEV Community

Cover image for 本番環境でAIエージェントが失敗する原因と失敗パターン別テスト手法
Akira
Akira

Posted on • Originally published at apidog.com

本番環境でAIエージェントが失敗する原因と失敗パターン別テスト手法

デモでは動いていたエージェントが、本番では同じ顧客にメールを2回送信し、リトライループで1日分のトークン予算を使い切り、フロントエンドで解析できないペイロードを返す——この差分はモデル性能だけでは説明できません。多くの場合、問題はエージェントが実行するAPI呼び出し、つまり失敗・遅延・スロットリング・仕様変更が起こり得るHTTP境界にあります。

今すぐApidogを試す

信頼性の高いエージェントを作るには、モデルを「信頼する」のではなく、障害パスを意図的にテストします。本記事では、実装時に確認すべき5つの失敗モードと、API契約・モック・アサーションを使ったテスト方法を整理します。Apidogを使うと、エージェントのAPI依存関係を定義し、失敗応答を再現し、期待した回復動作を検証できます。

エージェントはプロンプトではなくAPI境界で失敗する

本番でエージェントが誤動作すると、まずプロンプトを修正したくなります。しかし、原因はAPI統合であることが少なくありません。

エージェントの1ステップを分解すると、次の流れになります。

  1. モデルがツールを選択する
  2. アプリケーションコードがツール呼び出しをHTTPリクエストへ変換する
  3. 外部サービスが応答する
  4. アプリケーションコードが結果をモデルへ返す

このうち、機械学習そのものは最初の1つだけです。残りは通常のAPI統合なので、契約テスト、モック、タイムアウト、スキーマ検証といった既存のテスト手法を適用できます。

重要なのは、次の問いに置き換えることです。

モデルは十分に賢いか?

ではなく、API呼び出しが失敗するすべての経路をテストしたか?

以下の5つを押さえると、エージェントの主要な障害パターンをカバーできます。

失敗モード1:契約から逸脱するツール呼び出し

もっとも一般的な失敗は、モデルが生成したツール呼び出しがAPI契約と一致しないケースです。

たとえば予約APIが次を期待しているとします。

{
  "guests": 2,
  "date": "2025-06-01"
}
Enter fullscreen mode Exit fullscreen mode

しかしエージェントは、次のようなリクエストを生成するかもしれません。

{
  "guests": "two",
  "date": "2025-06-01",
  "priority": "urgent"
}
Enter fullscreen mode Exit fullscreen mode

POST /reservations が400を返せばまだ分かりやすいですが、APIによっては200レスポンス内にエラー情報を埋め込む場合もあります。エージェントがそれを成功として扱うと、後続の処理まで壊れます。

実装すること

各ツールに対して、入力・出力の契約を明示します。

  • 必須フィールド
  • enum
  • 数値の範囲
  • 追加フィールドの可否
  • エラー応答の形式

たとえば、ツール呼び出しの入力をJSON Schemaで表現します。

{
  "type": "object",
  "required": ["guests", "date"],
  "properties": {
    "guests": {
      "type": "integer",
      "minimum": 1
    },
    "date": {
      "type": "string"
    }
  },
  "additionalProperties": false
}
Enter fullscreen mode Exit fullscreen mode

テストでは、エージェントが生成した実際のツール引数をこのスキーマに対して検証します。契約違反を本番で静かに通すのではなく、CIで失敗させる設計にしてください。

AIエージェントのツール呼び出しをテストするでは、この方法を詳しく解説しています。エンドツーエンドの構成は、APIを呼び出すエージェントをテストするも参考になります。

Apidogでの進め方

  1. エージェントが呼び出すAPI仕様をApidogへ登録する
  2. リクエストボディのスキーマを定義する
  3. エージェントの実際のツール呼び出しを送信する
  4. 必須フィールド、型、enum、追加プロパティを検証する
  5. 不一致をテスト失敗として扱う

Apidogで契約を管理すれば、壊れたフィールド名や型の不一致を検証エラーとして確認できます。

失敗モード2:アップストリームエラーとレート制限

エージェントが呼び出す外部APIは、いつでも失敗します。

  • 429 Too Many Requests
  • 500 Internal Server Error
  • 接続タイムアウト
  • 不正なJSON
  • 空レスポンス
  • 応答遅延

脆弱なエージェントは最初のエラーで停止するか、逆に無制限にリトライして障害を悪化させます。エージェントのエラー回復パターンが頻繁に議論されることからも、この問題が一般的であることが分かります。

テストすべきシナリオ

正常系だけをテストしても、回復ロジックは確認できません。依存APIをモックし、次のような応答シーケンスを作ります。

1回目: 429 + Retry-After: 2
2回目: 500
3回目: 200
Enter fullscreen mode Exit fullscreen mode

このとき、次をアサートします。

  • Retry-After を尊重するか
  • バックオフしているか
  • リトライ回数に上限があるか
  • タイムアウト後に適切に失敗するか
  • サーキットブレーカーを開くか
  • ユーザーに安全なエラーを返すか

擬似コードで表すと、最低限のリトライ制御は次のようになります。

for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
  const response = await callApi();

  if (response.ok) {
    return response.data;
  }

  if (response.status === 429) {
    await sleep(getRetryAfter(response) ?? backoff(attempt));
    continue;
  }

  if (response.status >= 500) {
    await sleep(backoff(attempt));
    continue;
  }

  throw new Error("RetryableではないAPIエラー");
}

throw new Error("リトライ上限に到達");
Enter fullscreen mode Exit fullscreen mode

リトライ対象の操作が冪等でない場合は、二重送信や二重課金が起きます。安全にリトライするには、冪等性キーを利用してください。

また、レート制限の再現も必須です。まずはレート制限超過応答の意味を確認し、実際に429を返すモックを用意しましょう。リトライ、タイムアウト、バックオフ、サーキットブレーカーの詳細は、AIエージェントのエラー回復で解説しています。

失敗モード3:非決定的な出力

温度を0にしても、LLMの出力が毎回バイト単位で一致するとは限りません。ハードウェア、バッチ処理、プロバイダー側の更新などが出力差分を生みます。vLLMの議論でも、シードと温度だけでは再現性が不十分であることが扱われています。

そのため、次のようなテストは不安定になりやすいです。

expect(answer).toBe("注文番号1234は発送済みです。");
Enter fullscreen mode Exit fullscreen mode

文字列ではなく構造と意味をテストする

代わりに、以下をアサートしてください。

  • JSON Schemaに準拠している
  • 必須キーが存在する
  • 禁止フィールドが含まれない
  • 数値が妥当な範囲にある
  • 正しいツールを選んでいる
  • ツール引数が契約に準拠している

たとえばカートの合計を返すエージェントなら、次のように検証します。

expect(response).toMatchObject({
  currency: "JPY"
});

expect(response.total).toBeGreaterThanOrEqual(0);
expect(response.total).toBeLessThanOrEqual(cartValue);
Enter fullscreen mode Exit fullscreen mode

「完全に同じ文章」を求めるのではなく、「正しい構造で、安全な値を返すこと」をテスト対象にします。

不安定なテストを放置すると、チームは失敗を無視するようになります。不安定なテストの原因もあわせて確認してください。より具体的な戦略は、非決定的なAIエージェントのテストで解説しています。状態や会話履歴がある場合は、エージェントのメモリがどのように機能するかも重要です。

失敗モード4:暴走するコスト

エージェントはツール呼び出しのループです。そしてループにはコストがかかります。

失敗したAPI呼び出しを何千回もリトライする、同じ検索を繰り返す、必要以上のコンテキストを毎回送る——こうした問題は、トークン消費とAPI利用料を急増させます。

コスト問題は財務だけの問題ではありません。コストを増やすバグは、多くの場合でレイテンシーと予測不能性も増やします。

実装すること

実行ごとに、少なくとも次を計測します。

- LLM呼び出し回数
- ツール呼び出し回数
- リトライ回数
- 入力・出力トークン数
- 実行時間
- タスク単位の総コスト
Enter fullscreen mode Exit fullscreen mode

さらに、上限をコードで強制します。

if (toolCallCount >= MAX_TOOL_CALLS) {
  throw new Error("ツール呼び出し上限に到達");
}

if (tokenUsage >= MAX_TOKENS_PER_TASK) {
  throw new Error("トークン予算上限に到達");
}
Enter fullscreen mode Exit fullscreen mode

モックを使った回復テストでは、「最終的に成功したか」だけでなく、「何回呼び出したか」も確認してください。40回の呼び出しで成功するエージェントは、将来のコストインシデント候補です。

CLI環境での具体策は、エージェントのトークンコストを削減するを参照してください。

失敗モード5:ガードレールがない

もっとも高コストな失敗は、エージェントが指示どおりに動いたのに、その行動自体が危険だったケースです。

たとえば、エージェントが以下を実行できるとします。

  • 顧客へのメール送信
  • レコード削除
  • 注文作成
  • 権限変更
  • 外部システムへのデータ送信

モデルの判断から実行までに障壁がなければ、誤った判断がそのまま副作用になります。

実装すること

副作用の大きいツールには、次のガードレールを設けます。

  1. 許可リスト

    エージェントが実行可能な操作を明示的に限定する。

  2. 人間の承認

    削除、送信、発注などの不可逆操作は確認フローへ送る。

  3. ドライランモード

    実行せず、実行予定のアクションだけを返す。

  4. スコープ制限

    対象ユーザー、レコード件数、金額、アクセス範囲を制限する。

  5. 監査ログ

    実行したツール、入力、承認者、結果を記録する。

たとえば、メール送信を承認付きツールに分けます。

type SendEmailRequest = {
  to: string;
  subject: string;
  body: string;
  approvalId?: string;
};

function sendEmail(request: SendEmailRequest) {
  if (!request.approvalId) {
    return {
      status: "needs_approval",
      message: "メール送信には人間の承認が必要です。"
    };
  }

  // 承認済みの場合のみ送信する
}
Enter fullscreen mode Exit fullscreen mode

テストでは、メール送信や削除のような副作用のあるエンドポイントをモックします。そして、承認なしの実行では実処理に進まず、確認フローに到達することを検証してください。

保護項目のチェックリストとしては、LLMアプリケーションのOWASP Top 10が有用です。承認ゲートや爆発半径の制御については、AIエージェントのガードレールも参照してください。

エージェントテストの構造

5つの失敗モードは、同じテスト構造で実装できます。

  1. エージェントが使用するツールのスキーマを定義する
  2. 各依存APIをモックする
  3. 正常系だけでなく障害シナリオを作る
  4. エージェントを実行する
  5. リクエスト、回復動作、呼び出し回数、ガードレールをアサートする

まずは1つのツールから始めてください。たとえば send_email を対象にする場合、次のケースを追加できます。

シナリオ 確認すること
必須フィールド欠落 契約違反として失敗する
429 + Retry-After バックオフし、即時再試行しない
500が連続する リトライ上限で停止する
不正なJSON 安全にエラー処理する
承認なし送信 実送信せず確認フローへ進む
同じリクエストの再送 冪等性により二重送信しない

このループをツールごとに増やしていくと、ユーザーが発見する前に壊れた呼び出しを検出できます。

エージェントの信頼性チェックリスト

本番リリース前に、次を確認してください。

  • [ ] すべてのツール呼び出しをスキーマに対して検証している
  • [ ] 契約違反がテスト失敗になる
  • [ ] 429、500、タイムアウトをモックで再現している
  • [ ] バックオフ、リトライ上限、サーキットブレーカーを検証している
  • [ ] リトライ対象の副作用操作に冪等性を持たせている
  • [ ] 厳密な文字列比較ではなく、構造と意味をアサートしている
  • [ ] 実行ごとのトークン数・ツール呼び出し数・コストを計測している
  • [ ] タスクごとの予算上限と呼び出し上限を設けている
  • [ ] 破壊的操作を許可リストまたは承認ゲートの背後に置いている
  • [ ] ガードレールをモックでテストしている

Apidogが適合する点(およびしない点)

役割を明確にしておくことが重要です。

Apidogは、エージェントフレームワーク、モデルホスト、または評価ハーネスではありません。エージェント自体を構築・実行するものではありません。

一方で、Apidogはエージェントが依存するAPIレイヤーのテストに適しています。

  • ツールAPIの契約を設計・保存する
  • リクエストをスキーマに対して検証する
  • 429、500、タイムアウト、不正なボディをモックする
  • 正常系・異常系の応答を再現する
  • スキーマ、必須キー、値の範囲をアサートする

つまり、Apidogは、エージェントが呼び出すAPIをテストし、エージェントが処理すべき障害をモックし、返却データを検証するための場所です。より広いQAの視点は、エージェントAIテストの概要も参考になります。

よくある質問

エージェントの信頼性はモデルの問題ですか、それともエンジニアリングの問題ですか?

多くはエンジニアリングの問題です。モデル選定は重要ですが、不正なツール呼び出し、未処理のレート制限、無制限のリトライ、ガードレールの欠如は、統合とテストで改善できます。

実際のAPIにアクセスせずにエージェントをテストできますか?

はい。むしろ、依存APIはモックすべきです。モックならエラー応答、遅延、タイミング、レスポンスボディを制御でき、副作用も回避できます。

実行ごとに出力が変わる場合、どうテストしますか?

厳密な文言ではなく、構造と意味を検証します。JSON Schema、ツール呼び出しの形式、必須キー、数値範囲、禁止フィールドをアサートしてください。詳細は非決定的なAIエージェントのテストを参照してください。

最初に何をテストすべきですか?

まずは破壊的アクションのガードレール、その次にエラー回復です。この2つは、誤送信・誤削除・無限ループ・予算枯渇といった高コストな事故を防ぎます。

まず1つの失敗モードから始める

最初からすべてをテストする必要はありません。

今週中に、もっとも怖い失敗モードを1つ選んでください。多くの場合は次のどちらかです。

  • 副作用のある操作に対するガードレール
  • 429、500、タイムアウト時のエラー回復

依存APIに失敗をプログラムし、エージェントを実行し、期待どおりに停止・回復・承認待ちへ進むかを確認します。シミュレートした429を、予算を使い切るループではなくクリーンなバックオフで処理できれば、それはデモ成功よりも強い信頼性の証拠です。

契約の設計、失敗応答のモック、レスポンスの検証を始めるには、Apidogをダウンロードしてください

Top comments (0)