AIコーディングエージェントがスクリプトを実行し、成功したように見えた直後、本番データベースのテーブルが消えました。Hacker Newsで話題になった事後分析のタイトルは「AIがあなたのデータベースを削除したわけではない、あなたがしたのだ」。これは正しい指摘です。エージェントはツール定義に従い、そのツールは実際のAPIに到達し、そのAPIにはガードレールがなく、人間はDELETE FROM usersを止める仕組みを用意していませんでした。r/ClaudeAIでも、課金ループに入ったエージェントが気づかれるまで何百ドル分ものトークンを消費した例が共有されています。表面上は違う事故ですが、原因は同じです。モデルが賢くないことではなく、APIをテストしていなかったことです。
💡 APIを呼び出す自律型エージェントを出荷するなら、API側のテスト設計が必須です。この記事では、外部エンドポイントのモック、破壊的操作のサンドボックス化、ツールスキーマの契約テスト、エージェントごとの予算上限、CIでの障害シナリオ再生を実装します。テストの足場には、OpenAPI対応、モックサーバー、シナリオテストを備えたApidogを使います。
要約(TL;DR)
AIエージェントが本番で壊れる主因は、API側のガードレール不足です。
具体的には次のような問題です。
- レート制限がない
- 冪等性がない
- 破壊的エンドポイントを本番に直接向けている
- OpenAPI仕様とエージェントのツール定義がずれている
- エージェントごとの予算上限がない
対策は4つです。
- エージェントのツール定義をOpenAPI仕様と照合する
- 破壊的エンドポイントはモックサーバーでテストする
- 書き込み操作には冪等性キーを必須にする
- エージェントごとのトークン・API呼び出し・金額上限を設定する
Apidogを使うと、OpenAPIのインポート、モック、シナリオ実行を1つのプロジェクトで扱えます。
はじめに
以前の「AIエージェントのテスト」は、ClaudeやGPTにプロンプトを与え、回答の品質を評価することが中心でした。しかし現在のエージェントは関数を呼び出し、その関数はAPIにアクセスし、APIはデータベース、決済プロバイダー、メール配信、サードパーティサービスに接続します。
つまり、ツール定義の誤りやAPIガードレールの欠如は、単なる品質問題ではありません。本番インシデントです。
Hacker Newsの事例では、AIが勝手にDBを消したのではなく、人間がモデルと本番データの間に制御を置かず、エージェントに書き込み権限を渡していました。Redditの課金ループの例でも、エージェントが失敗した呼び出しをリトライし続け、誰かが気づくまでコストが膨らみました。
どちらも根本原因は同じです。
信頼を置くレイヤーを間違えていた。
モデルレイヤーも重要ですが、被害を止めるのはAPIレイヤーです。この記事では、AIエージェントのAPI連携を実装面からテストする方法を説明します。
エージェントの障害がAPIの障害に見える理由
エージェントの事後分析を読むと、問題の中心はモデルではなくAPIであることが多いです。
プロンプトインジェクション
ユーザーが隠し指示を含むPDFをアップロードし、エージェントがそれを読み込み、次のツール呼び出しで/admin/usersにdelete_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
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);
エージェントは論理操作ごとに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
エージェントのプランナーは、この応答を受け取ったら人間にエスカレーションするか、処理を停止します。
4つの制御を組み合わせると、事故は次のように変わります。
エージェントが本番データを壊した
ではなく、
エージェントが429に到達し、問題をログに残して停止した
になります。
ApidogでエージェントのAPI呼び出しをテストする
ここからは、Apidogでエージェント-APIテストを構築する手順です。
必要なものは2つです。
- エージェントが呼び出すAPIのOpenAPI仕様
- エージェントのツール定義一覧
ステップ1:OpenAPI仕様をインポートする
Apidogで新しいプロジェクトを作成し、OpenAPI 3.xファイルをインポートします。
Apidogは以下を解析します。
- paths
- request schema
- response schema
- examples
- parameters
APIがまだOpenAPIで定義されていない場合は、まず仕様を作成してください。エージェントの安全性は、人間とエージェントが同じ契約を参照することに依存します。
設計優先で始める場合は、設計優先APIワークフローガイドを参照してください。
ステップ2:破壊的エンドポイントのモックレスポンスを定義する
対象は状態変更を行うエンドポイントです。
POSTPUTPATCHDELETE
各エンドポイントでモックレスポンスを追加します。Apidogはスキーマから現実的な値を生成できますが、テストデータであることが分かる値に上書きするのがおすすめです。
例:
{
"id": "mock_user_123",
"email": "mock_user@example.test",
"deleted_at": "1970-01-01T00:00:00Z"
}
モックサーバーを起動すると、次のような安定したURLを利用できます。
https://mock.apidog.com/m1/your-project-id/
開発中は、エージェントのAPIベースURLをこのモックURLに向けます。
ステップ3:エージェントの呼び出しシーケンスをシナリオ化する
Apidogのシナリオでは、複数のAPI呼び出しを連結し、各ステップでアサーションできます。
サポートチケットをトリアージするエージェントなら、次のようなシナリオになります。
-
POST /auth/tokenでテスト用トークンを取得する -
GET /tickets?status=openで最初のチケットIDを取得する -
POST /tickets/{id}/triageを呼び出し、HTTP 200を確認する -
POST /notificationsを呼び出し、本文が正規表現に一致することを確認する
これにより、エージェントの実際のツール呼び出しを本番前にリハーサルできます。
シナリオテストの詳細は、QAエンジニアのためのAPIテストを参照してください。
ステップ4:CIから実行する
ApidogのCLIを使うと、GitHub ActionsやGitLab CIからシナリオを実行できます。
例:
apidog run -t scenario-id --env test
PRパイプラインに組み込み、次の変更が入ったときに必ずシナリオを再生します。
- OpenAPI仕様
- エージェントのツール定義
- 認可ロジック
- 書き込みAPI
- レート制限
- レスポンススキーマ
ステップ5:モデルバージョンを比較する
モデルAからモデルBに切り替える前に、同じApidogシナリオを両方で実行します。
比較するポイントは次の通りです。
- 呼び出したエンドポイント
- リクエスト順序
- リクエストボディ
- 欠落フィールド
- enum値
- 日付フォーマット
- リトライ回数
モデルBが異なるpriorityを渡したり、日付形式を変えたりする場合があります。これは本番投入前に検出すべき動作差分です。
このパターンは、GPT-5.5 API統合でも扱われているように、新しいモデルの評価で繰り返し必要になります。
高度なテクニック
テストでは温度を0に固定する
ツール呼び出しのテストでは、モデルの創造性ではなくAPI呼び出しの正確性を見ます。
{
"temperature": 0,
"seed": 1234
}
非決定的な設定は、非決定的なテスト失敗を生みます。
ツール呼び出しトレースをスナップショットする
各テストで、エージェントが行ったツール呼び出しの順序と引数を保存します。
[
{
"tool": "get_ticket",
"args": {
"status": "open"
}
},
{
"tool": "triage_ticket",
"args": {
"ticket_id": "mock_ticket_1",
"category": "billing"
}
}
]
前回のベースラインと差分を取れば、呼び出し回数の増加や不要なAPIアクセスを早期に検出できます。
本番認証情報をエージェントに渡さない
エージェントにはスコープ付きサービスアカウントを使います。
避けるべき構成:
AGENT_API_KEY=prod_admin_key
推奨:
AGENT_API_KEY=staging_scoped_readonly_key
本番アクセスが必要な場合は、短命トークンを発行するプロキシを経由します。
読み取りキーと書き込みキーを分離する
多くのエージェントタスクは読み取り中心です。
- 読み取り用キー:通常タスクで使用
- 書き込み用キー:人間承認後のみ使用
これだけで、侵害時の影響範囲を大きく減らせます。
人間承認が必要な操作にはHTTP 423を使う
エージェントが承認待ち操作を呼んだ場合、403 Forbiddenではなく423 Lockedを返します。
{
"error": "Human approval required",
"confirmation_url": "https://example.com/approvals/abc123"
}
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つです。
- ツールスキーマを契約として扱い、CIで検証する
- 破壊的エンドポイントは必ずモックする
- すべての書き込みAPIで冪等性キーを必須にする
- エージェントごとの予算上限を設定する
- 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/
ステージングや本番に切り替えるときは、この変数だけを変更します。
エージェントに適切な予算上限は?
最初は厳しく設定し、メトリクスを見ながら緩めます。
初期値の例:
- 1セッション:50,000トークン
- 1分:30 API呼び出し
- 1タスク:5ドル
- ネストしたツール呼び出し:10回
2週間運用して、正当な処理が頻繁に上限に達するなら引き上げます。一度も達しない上限は下げます。
エージェントのツールとAPI間のスキーマずれを検出するには?
CIで、エージェントのツール定義とOpenAPIリクエストスキーマを比較します。
チェック対象は次の通りです。
- 必須フィールド
- 余分なフィールド
- 型
- enum
- 配列・オブジェクト構造
- nullable
- format
差分があればビルドを失敗させます。警告ではなくエラーにしてください。

Top comments (0)