DEV Community

Cover image for API連携AIエージェントのテスト方法:データ損失を防ぐ
Akira
Akira

Posted on • Originally published at apidog.com

API連携AIエージェントのテスト方法:データ損失を防ぐ

AIコーディングエージェントがスクリプトを実行し、成功したように見えた直後、本番データベースのテーブルが消えました。Hacker Newsで話題になった事後分析のタイトルは「AIがあなたのデータベースを削除したわけではない、あなたがしたのだ」。これは正しい指摘です。エージェントはツール定義に従い、そのツールは実際のAPIに到達し、そのAPIにはガードレールがなく、人間はDELETE FROM usersを止める仕組みを用意していませんでした。r/ClaudeAIでも、課金ループに入ったエージェントが気づかれるまで何百ドル分ものトークンを消費した例が共有されています。表面上は違う事故ですが、原因は同じです。モデルが賢くないことではなく、APIをテストしていなかったことです。

今すぐApidogを試す

💡 APIを呼び出す自律型エージェントを出荷するなら、API側のテスト設計が必須です。この記事では、外部エンドポイントのモック、破壊的操作のサンドボックス化、ツールスキーマの契約テスト、エージェントごとの予算上限、CIでの障害シナリオ再生を実装します。テストの足場には、OpenAPI対応、モックサーバー、シナリオテストを備えたApidogを使います。

要約(TL;DR)

AIエージェントが本番で壊れる主因は、API側のガードレール不足です。

具体的には次のような問題です。

  • レート制限がない
  • 冪等性がない
  • 破壊的エンドポイントを本番に直接向けている
  • OpenAPI仕様とエージェントのツール定義がずれている
  • エージェントごとの予算上限がない

対策は4つです。

  1. エージェントのツール定義をOpenAPI仕様と照合する
  2. 破壊的エンドポイントはモックサーバーでテストする
  3. 書き込み操作には冪等性キーを必須にする
  4. エージェントごとのトークン・API呼び出し・金額上限を設定する

Apidogを使うと、OpenAPIのインポート、モック、シナリオ実行を1つのプロジェクトで扱えます。

はじめに

以前の「AIエージェントのテスト」は、ClaudeやGPTにプロンプトを与え、回答の品質を評価することが中心でした。しかし現在のエージェントは関数を呼び出し、その関数はAPIにアクセスし、APIはデータベース、決済プロバイダー、メール配信、サードパーティサービスに接続します。

つまり、ツール定義の誤りやAPIガードレールの欠如は、単なる品質問題ではありません。本番インシデントです。

Hacker Newsの事例では、AIが勝手にDBを消したのではなく、人間がモデルと本番データの間に制御を置かず、エージェントに書き込み権限を渡していました。Redditの課金ループの例でも、エージェントが失敗した呼び出しをリトライし続け、誰かが気づくまでコストが膨らみました。

どちらも根本原因は同じです。

信頼を置くレイヤーを間違えていた。

モデルレイヤーも重要ですが、被害を止めるのはAPIレイヤーです。この記事では、AIエージェントのAPI連携を実装面からテストする方法を説明します。

エージェントの障害がAPIの障害に見える理由

エージェントの事後分析を読むと、問題の中心はモデルではなくAPIであることが多いです。

プロンプトインジェクション

ユーザーが隠し指示を含むPDFをアップロードし、エージェントがそれを読み込み、次のツール呼び出しで/admin/usersdelete_all=trueを送るケースを考えます。

この場合、修正すべき場所はプロンプトだけではありません。

API側で、ユーザーコンテキスト由来のトークンにdelete_all=trueを許可しない設計が必要です。OWASP LLM Top 10でも、LLM01の軽減策はプロンプトエンジニアリングだけではなく、API側の認可です。

ツールスキーマの不一致

OpenAPI仕様ではamountが「セント単位の整数」と定義されているのに、エージェントのツール定義では「ドル単位の浮動小数点数」と書かれていたとします。

その結果、19セントの返金が19ドルとして処理される可能性があります。

モデルは与えられたスキーマに従っただけです。壊れていたのは契約です。

レート制限の欠如

エージェントのプランナーが「まだ成功していない」と判断し続け、2分間で1000回メール送信APIを呼び出すことがあります。

リトライごとにコストがかかり、実際のメールがキューに積まれます。これもモデルの悪意ではなく、上限のないAPI設計の問題です。

冪等性の欠如

エージェントがPOST /paymentsを呼び出し、ネットワークタイムアウトが発生したとします。エージェントは失敗と判断してリトライし、顧客に2回課金されます。

APIが冪等性キーを受け付けていれば、2回目の呼び出しにはキャッシュ済みレスポンスを返せます。

すべてのエージェント-API連携に必要な4つのガードレール

1. ツールスキーマの契約テスト

OpenAPI仕様をAPIの真実の源にします。エージェントのツール定義は、多くの場合、手書きまたはドキュメントからのコピーです。そのため、時間が経つと必ずずれます。

CIで次のチェックを実行します。

from jsonschema import Draft202012Validator

def validate_tool_against_openapi(tool_def: dict, openapi_spec: dict) -> list[str]:
    """Return mismatch errors. Empty list means pass."""
    errors = []

    op = openapi_spec["paths"][tool_def["path"]][tool_def["method"].lower()]
    api_schema = op["requestBody"]["content"]["application/json"]["schema"]
    tool_schema = tool_def["input_schema"]

    api_props = set(api_schema.get("properties", {}).keys())
    tool_props = set(tool_schema.get("properties", {}).keys())

    for missing in api_props - tool_props:
        if missing in api_schema.get("required", []):
            errors.append(f"Tool missing required field: {missing}")

    for extra in tool_props - api_props:
        errors.append(f"Tool defines field not in API: {extra}")

    for prop, api_def in api_schema.get("properties", {}).items():
        if prop in tool_schema.get("properties", {}):
            tool_prop = tool_schema["properties"][prop]
            if api_def.get("type") != tool_prop.get("type"):
                errors.append(
                    f"Type mismatch on {prop}: "
                    f"API={api_def.get('type')} tool={tool_prop.get('type')}"
                )

    return errors
Enter fullscreen mode Exit fullscreen mode

PRでOpenAPI仕様またはツール定義が変わったら、このチェックを実行します。エラーが1つでもあればビルドを失敗させます。

2. 破壊的エンドポイントのモックとサンドボックス

エージェントは練習環境を必要とします。本番で練習させてはいけません。

基本方針は次の通りです。

  • 開発中:モックサーバー
  • ステージング:サンドボックスDB
  • 本番:人間の承認後のみ

ApidogはOpenAPI仕様からモックレスポンスを生成できます。エージェントのベースURLをモックサーバーに向ければ、DELETE /users/{id}を呼び出しても実DBは変更されません。

契約優先の設計については、契約優先開発も参考になります。

3. 冪等性キーとソフトデリート

エージェントが呼び出せる書き込みAPIには、冪等性キーを必須にします。削除はデフォルトでソフトデリートにし、ハードデリートは人間承認付きの別経路にします。

Expressの例です。

const idempotencyCache = new Map();

function idempotency(req, res, next) {
  const key = req.headers["idempotency-key"];

  if (!key) {
    return res.status(400).json({ error: "Missing Idempotency-Key header" });
  }

  if (idempotencyCache.has(key)) {
    const cached = idempotencyCache.get(key);
    return res.status(cached.status).json(cached.body);
  }

  const originalJson = res.json.bind(res);

  res.json = function (body) {
    idempotencyCache.set(key, {
      status: res.statusCode,
      body,
    });

    setTimeout(() => {
      idempotencyCache.delete(key);
    }, 24 * 60 * 60 * 1000);

    return originalJson(body);
  };

  next();
}

app.post("/payments", idempotency, createPayment);
Enter fullscreen mode Exit fullscreen mode

エージェントは論理操作ごとにUUIDを生成し、リトライ時も同じキーを使います。APIは二重処理せず、同じレスポンスを返します。

4. エージェントごとの予算上限

すべてのエージェントに予算を設定します。

例:

  • 1セッションあたりのトークン数:50,000
  • 1分あたりのAPI呼び出し数:30
  • 累積使用額:500セント
  • ツール呼び出しの深さ:10

上限に達したらHTTP 429を返します。

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-Budget-Exceeded: api_calls_per_minute
Enter fullscreen mode Exit fullscreen mode

エージェントのプランナーは、この応答を受け取ったら人間にエスカレーションするか、処理を停止します。

4つの制御を組み合わせると、事故は次のように変わります。

エージェントが本番データを壊した

ではなく、

エージェントが429に到達し、問題をログに残して停止した

になります。

ApidogでエージェントのAPI呼び出しをテストする

ここからは、Apidogでエージェント-APIテストを構築する手順です。

必要なものは2つです。

  • エージェントが呼び出すAPIのOpenAPI仕様
  • エージェントのツール定義一覧

Apidog UI

ステップ1:OpenAPI仕様をインポートする

Apidogで新しいプロジェクトを作成し、OpenAPI 3.xファイルをインポートします。

Apidogは以下を解析します。

  • paths
  • request schema
  • response schema
  • examples
  • parameters

APIがまだOpenAPIで定義されていない場合は、まず仕様を作成してください。エージェントの安全性は、人間とエージェントが同じ契約を参照することに依存します。

設計優先で始める場合は、設計優先APIワークフローガイドを参照してください。

ステップ2:破壊的エンドポイントのモックレスポンスを定義する

対象は状態変更を行うエンドポイントです。

  • POST
  • PUT
  • PATCH
  • DELETE

各エンドポイントでモックレスポンスを追加します。Apidogはスキーマから現実的な値を生成できますが、テストデータであることが分かる値に上書きするのがおすすめです。

例:

{
  "id": "mock_user_123",
  "email": "mock_user@example.test",
  "deleted_at": "1970-01-01T00:00:00Z"
}
Enter fullscreen mode Exit fullscreen mode

モックサーバーを起動すると、次のような安定したURLを利用できます。

https://mock.apidog.com/m1/your-project-id/
Enter fullscreen mode Exit fullscreen mode

開発中は、エージェントのAPIベースURLをこのモックURLに向けます。

ステップ3:エージェントの呼び出しシーケンスをシナリオ化する

Apidogのシナリオでは、複数のAPI呼び出しを連結し、各ステップでアサーションできます。

サポートチケットをトリアージするエージェントなら、次のようなシナリオになります。

  1. POST /auth/tokenでテスト用トークンを取得する
  2. GET /tickets?status=openで最初のチケットIDを取得する
  3. POST /tickets/{id}/triageを呼び出し、HTTP 200を確認する
  4. POST /notificationsを呼び出し、本文が正規表現に一致することを確認する

これにより、エージェントの実際のツール呼び出しを本番前にリハーサルできます。

シナリオテストの詳細は、QAエンジニアのためのAPIテストを参照してください。

ステップ4:CIから実行する

ApidogのCLIを使うと、GitHub ActionsやGitLab CIからシナリオを実行できます。

例:

apidog run -t scenario-id --env test
Enter fullscreen mode Exit fullscreen mode

PRパイプラインに組み込み、次の変更が入ったときに必ずシナリオを再生します。

  • OpenAPI仕様
  • エージェントのツール定義
  • 認可ロジック
  • 書き込みAPI
  • レート制限
  • レスポンススキーマ

ステップ5:モデルバージョンを比較する

モデルAからモデルBに切り替える前に、同じApidogシナリオを両方で実行します。

比較するポイントは次の通りです。

  • 呼び出したエンドポイント
  • リクエスト順序
  • リクエストボディ
  • 欠落フィールド
  • enum値
  • 日付フォーマット
  • リトライ回数

モデルBが異なるpriorityを渡したり、日付形式を変えたりする場合があります。これは本番投入前に検出すべき動作差分です。

このパターンは、GPT-5.5 API統合でも扱われているように、新しいモデルの評価で繰り返し必要になります。

高度なテクニック

テストでは温度を0に固定する

ツール呼び出しのテストでは、モデルの創造性ではなくAPI呼び出しの正確性を見ます。

{
  "temperature": 0,
  "seed": 1234
}
Enter fullscreen mode Exit fullscreen mode

非決定的な設定は、非決定的なテスト失敗を生みます。

ツール呼び出しトレースをスナップショットする

各テストで、エージェントが行ったツール呼び出しの順序と引数を保存します。

[
  {
    "tool": "get_ticket",
    "args": {
      "status": "open"
    }
  },
  {
    "tool": "triage_ticket",
    "args": {
      "ticket_id": "mock_ticket_1",
      "category": "billing"
    }
  }
]
Enter fullscreen mode Exit fullscreen mode

前回のベースラインと差分を取れば、呼び出し回数の増加や不要なAPIアクセスを早期に検出できます。

本番認証情報をエージェントに渡さない

エージェントにはスコープ付きサービスアカウントを使います。

避けるべき構成:

AGENT_API_KEY=prod_admin_key
Enter fullscreen mode Exit fullscreen mode

推奨:

AGENT_API_KEY=staging_scoped_readonly_key
Enter fullscreen mode Exit fullscreen mode

本番アクセスが必要な場合は、短命トークンを発行するプロキシを経由します。

読み取りキーと書き込みキーを分離する

多くのエージェントタスクは読み取り中心です。

  • 読み取り用キー:通常タスクで使用
  • 書き込み用キー:人間承認後のみ使用

これだけで、侵害時の影響範囲を大きく減らせます。

人間承認が必要な操作にはHTTP 423を使う

エージェントが承認待ち操作を呼んだ場合、403 Forbiddenではなく423 Lockedを返します。

{
  "error": "Human approval required",
  "confirmation_url": "https://example.com/approvals/abc123"
}
Enter fullscreen mode Exit fullscreen mode

403は「実行できない」、423は「まだ実行できない」を表現できます。

スキーマずれは警告ではなくエラーにする

ツール定義とOpenAPI仕様が一致しない場合、CIを失敗させます。

警告にすると見逃されます。本番事故より、数回のビルド失敗の方が安いです。

避けるべきよくある間違い

  • モックURLをプロンプトにハードコードする
  • 「小さいAPIだから」と冪等性を省略する
  • 本番ログにリクエストボディをそのまま出す
  • エージェントにDB直接アクセスを許可する
  • エージェントの確信度スコアをAPI安全性の指標として扱う
  • OpenAPI仕様とツール定義を別々に手動更新する

内部サービスやマイクロサービス群とエージェントを連携させる場合は、マイクロサービステストパターンも参考になります。

代替手段とツール

アプローチ セットアップ時間 強み 弱み 最適な用途
手作りの単体テスト 低い 完全な制御、ベンダーロックインなし メンテナンスが大変、実APIと乖離しやすい 小規模プロジェクト、単独開発
LangSmith / LangGraph評価ハーネス 中程度 トレースリプレイ、モデル評価に強い API側の契約テストは別途必要 評価中心のAIチーム
Postman + Postbot 中程度 慣れたUI、テンプレートが多い モックやシナリオ運用に制約が出る場合がある 既にPostmanを使っているチーム
Apidogシナリオ + モック 中程度 OpenAPI、モック、シナリオ、CIをまとめて扱える Postmanほどのブランド認知度はない 設計、モック、テストを1ツールで行いたいチーム

LangSmithを使っている場合でも、API側の契約テストは別レイヤーとして追加すべきです。Postmanの料金体系やモック運用に課題がある場合は、Apidogは強力な代替手段になります。

プロンプト評価にはLangSmith、API契約とシナリオ再生にはApidog、という組み合わせも有効です。

実世界のユースケース

エージェントが本番データベースの行を更新する

カスタマーサクセスチームが、サポートチケットからアカウントフィールドを更新するエージェントを構築したとします。

出荷前に行うべきことは次の通りです。

  • すべての書き込みAPIで冪等性キーを必須化
  • サンドボックスDBに対してシナリオを実行
  • enum外の値を送った場合に400を返す
  • Apidogで200件以上のリプレイを実行

このリプレイで、subscription_statusにenum外文字列を送るケースを事前に検出できます。

エージェントが決済APIを呼び出す

自動返金エージェントでは、厳格な上限が必要です。

例:

  • 1セッション最大5回の返金
  • 1回あたり最大50ドル
  • すべての呼び出しで冪等性キー必須
  • PRごとにStripeのOpenAPI仕様と契約テストスイートを照合

これにより、重複返金や単位ミスを本番前に防げます。

エージェントがGitHub課題をトリアージする

Clawsweeperに触発された課題トリアージエージェントを作る場合、GitHub APIをApidogでモックし、以下のケースをシナリオ化します。

  • 削除済み課題
  • ラベル不足
  • 不正なユーザー入力
  • レート制限
  • 権限不足
  • APIレスポンスの欠落フィールド

ローンチ前にクラッシュ条件を見つけられます。

結論

問題はエージェントだけではありません。問題はAPIです。そして解決策もAPIです。

実装すべきことは次の5つです。

  1. ツールスキーマを契約として扱い、CIで検証する
  2. 破壊的エンドポイントは必ずモックする
  3. すべての書き込みAPIで冪等性キーを必須にする
  4. エージェントごとの予算上限を設定する
  5. APIまたはツール定義が変わるたびにシナリオを再生する

今年話題になったインシデントは最後ではありません。エージェントを出荷するチームは、いずれ同じ障害モードに遭遇します。復旧が早いチームは、事前にガードレールを実装していたチームです。

まずはApidogをダウンロードし、破壊的エンドポイントのモックから始めてください。QA観点ではQAエンジニア向けのAPIテストツール、エージェント向けツール定義ではAGENTS.mdファイルの書き方も参考になります。

よくある質問

トークンに費用をかけずにAIエージェントのAPI呼び出しをテストするには?

開発中はエージェントをモックサーバーに向けます。ApidogのモックURLは現実的なレスポンスを返すため、実APIクレジットを消費せずにテストできます。

テスト時は次を固定します。

  • temperature:0
  • プロンプトセット:小さく固定
  • APIベースURL:モック環境
  • 認証情報:テスト用

詳細はQAエンジニアのテストチェックリストを参照してください。

エージェントのテストとAPIのテストの違いは?

エージェントのテストは、モデルが正しいツールを選び、正しい引数を入れるかを確認します。

APIのテストは、エンドポイントが呼ばれたときに正しく動くかを確認します。

両方必要です。完璧なエージェントが壊れたAPIを呼んでも失敗します。壊れたエージェントが完璧なAPIを呼んでも失敗します。

すべてのエンドポイントに冪等性キーが必要ですか?

読み取りAPIには不要です。読み取りは通常、定義上冪等です。

ただし、すべての書き込みAPIには必要です。

  • 決済
  • 返金
  • メール送信
  • レコード作成
  • ステータス更新
  • 通知送信

エージェントはリトライします。リトライされても二重処理しないAPIにしてください。

プロンプトインジェクションによる不正なAPI呼び出しを防ぐには?

プロンプトだけに頼らないでください。

APIは、エージェントの要求ではなく、元のユーザーコンテキストに基づいて認可を判断すべきです。

ユーザーが/admin/delete-all-usersにアクセスできないなら、そのユーザーの代理で動くエージェントもアクセスできないようにします。

ClaudeやGPTと直接Apidogを使えますか?

テスト時は、エージェントのツール定義のベースURLをApidogのモックURLに向けます。

環境変数で切り替える構成にします。

API_BASE_URL=https://mock.apidog.com/m1/your-project-id/
Enter fullscreen mode Exit fullscreen mode

ステージングや本番に切り替えるときは、この変数だけを変更します。

エージェントに適切な予算上限は?

最初は厳しく設定し、メトリクスを見ながら緩めます。

初期値の例:

  • 1セッション:50,000トークン
  • 1分:30 API呼び出し
  • 1タスク:5ドル
  • ネストしたツール呼び出し:10回

2週間運用して、正当な処理が頻繁に上限に達するなら引き上げます。一度も達しない上限は下げます。

エージェントのツールとAPI間のスキーマずれを検出するには?

CIで、エージェントのツール定義とOpenAPIリクエストスキーマを比較します。

チェック対象は次の通りです。

  • 必須フィールド
  • 余分なフィールド
  • enum
  • 配列・オブジェクト構造
  • nullable
  • format

差分があればビルドを失敗させます。警告ではなくエラーにしてください。

Top comments (0)