ツールが失敗したときに AI エージェントの動作をテストする方法

AI エージェントは、ツール (HTTP API、MCP サーバー、およびその他のサービス) を呼び出します。 これらのツールはタイムアウトし、レート制限に達し、エラーを返し、予期していなかった形でデータを送信します。 モデルは、コードがモデルに返す内容に基づいて、次に何を行うかを決定します。 長い待機の後にコードから例外、空の文字列、または何も返されなかった場合、エージェントの動作は明確なエラーが発生した場合とは異なります。

ツール障害で何が問題になるか

  • エージェントがハングします。 タイムアウトのないツール呼び出しは、ユーザーを待たせ続けます。
  • エージェントはループします。 このモデルは、トークンとツールのレート制限を消費しながら、失敗するツールを何度も繰り返し呼び出します。
  • エージェントがそれを隠します。 コードはエラーを飲み込み、モデルはツールが成功したかのように答えます。
  • エージェントが異常終了します。 未処理の例外により、会話全体が終了します。

ツールの障害を処理する方法

  1. すべてのツール呼び出しでタイムアウトを設定し、ターン全体の予算を設定します。 MCP 仕様では、クライアントはツール呼び出しに対するタイムアウトを実装すべきです。
  2. コード内の一時的なエラーを再試行します。 ツール コードで 429 と 503 の応答を処理し、Retry-Afterを尊重し、試行回数を制限することで、モデルが再試行するタイミングを判断する必要がないようにします。
  3. 失敗を明確な結果としてモデルに返す。 MCP は、不明なツールや無効な引数などのプロトコルエラーを、API の障害などのツール実行エラーと区別します。 isError: trueを使用してツールの結果で実行エラーを報告するため、モデルが何が問題だったかを確認できます。 何が失敗したのか、また再試行する意味があるかどうかを伝えます。
  4. ターンあたりのツール呼び出しの数を上限にします。 一定の数の失敗が発生した後、停止してユーザーに通知します。
  5. ツールの結果をモデルに渡す前に検証してください。 MCP 仕様では、クライアントはこれを行うべきであり、ツールに出力スキーマがある場合は、構造化された結果をそのスキーマに照らして検証すべきであるとしています。
  6. 動作しなかったことをユーザーに伝えます。 失敗したツール呼び出しに基づいて構築された回答は、その旨を明記する必要があります。

エージェントにおけるツールの失敗時の処理をテストする方法

Approach 見つけたもの 見逃したもの
スタブ クライアントを使用してツール ラッパーを単体テストする 記述した障害をコードがどのようにマッピングするか モデルがそれをどう扱うかと、実際のツールがどのように失敗するか
たとえばサーバーを停止したりキーを取り消したりして、実際のツールを壊す その種の本当の失敗 レート制限、遅い応答、形式が不正なデータなど、自分では任意のタイミングで発生させられないもの
偽の API または MCP サーバーを作成する スクリプト化する応答 エージェントの向き先を偽物に設定する必要があり、すると本物のツールから乖離していきます
エージェントの実際のツール トラフィックをインターセプトし、障害を挿入する 実行中のエージェントとモデルが、エラー、レイテンシ、実際のツールから返される不正なデータにどう対処するか コード単体。 そのために単体テストを取っておいてください。

モデルの出力は実行によって異なる可能性があるため、各失敗シナリオを複数回実行してください。

アプリで試す

Dev Proxy は、エージェントとそのツールの間に配置され、エージェントのコードに変更を加えずにエラーを挿入します。

HTTP API を呼び出すツールの場合は、ランダム エラー、待機時間、および API が要求する限りエージェントが待機するチェックを組み合わせます。

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
  "plugins": [
    {
      "name": "RetryAfterPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
    },
    {
      "name": "LatencyPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "latencyPlugin"
    },
    {
      "name": "GenericRandomErrorPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "errorsContosoApi"
    }
  ],
  "urlsToWatch": [
    "https://api.contoso.com/*"
  ],
  "latencyPlugin": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/latencyplugin.schema.json",
    "minMs": 2000,
    "maxMs": 10000
  },
  "errorsContosoApi": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
    "errorsFile": "errors-contoso-api.json",
    "rate": 50
  }
}

errors-contoso-api.json でエラーを定義します。詳しくは、「ランダム エラーでアプリをテストする」をご覧ください。 Retry-Afterの応答で429を@dynamicに設定します。 RetryAfterPlugin では、これらのみをチェックします。

STDIO を使用する MCP サーバーの場合は、devproxy stdioに示すように、MockStdioResponsePlugin を有効にする構成で stdio を介してサーバーを起動します。 devproxyrc-stdio.jsonとして保存します。 次に、すべての tools/call 要求に対してツールの実行エラーを返すには、これを stdio-mocks.json に入力します。

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/mockstdioresponseplugin.mocksfile.schema.json",
  "mocks": [
    {
      "request": {
        "bodyFragment": "tools/call"
      },
      "response": {
        "stdout": "{\"jsonrpc\":\"2.0\",\"id\":@stdin.body.id,\"result\":{\"content\":[{\"type\":\"text\",\"text\":\"Failed to fetch weather data: API rate limit exceeded\"}],\"isError\":true}}\n"
      }
    }
  ]
}
devproxy stdio --config-file devproxyrc-stdio.json npx -y @modelcontextprotocol/server-filesystem

エージェントがそれを使用するには、エージェントの MCP サーバー構成でコマンドを変更し、devproxy stdioを介してサーバーを起動するようにします。 モックで nth プロパティを使用して特定の呼び出しのみを失敗させ、LatencyPlugin を追加してサーバーの応答を遅くします。

モデル自体が失敗したときにエージェントが何を行うかをテストするために、LanguageModelFailurePlugin はモデルにもっともらしい誤情報を生成させたり、命令を無視したり、間違った形式で回答したりします。 「言語モデルの障害時にアプリをテストする」を参照してください。

開発プロキシをインストールするには、「 開発プロキシの設定」を参照してください。

次のステップ

参照