如何测试 AI 代理在其工具失败时执行的操作

AI 代理调用工具:HTTP API、MCP 服务器和其他服务。 这些工具会超时、触发速率限制、返回错误,并以你没想到的格式发送数据。 模型根据代码传递回的内容决定下一步要执行的操作。 如果代码在长时间等待后传回异常、空字符串或根本不返回任何内容,则代理的行为与收到明确的错误的行为不同。

工具故障如何导致问题

  • 智能体挂起。 没有超时的工具调用使用户等待。
  • 智能体陷入循环。 模型一次又一次调用发生故障的工具,耗尽令牌和工具的请求配额。
  • 智能体掩盖故障。 你的代码吞掉了错误,模型会像工具已成功执行一样回答。
  • 智能体崩溃。 未处理的异常会导致整个对话结束。

如何处理工具故障

  1. 为每个工具调用设置超时,并为整个轮次设置预算。 MCP 规范表示客户端应为工具调用实现超时。
  2. 在代码中重试发生暂时性错误的调用。 在工具代码中处理 429 和 503 响应,遵循Retry-After,并限制尝试次数,这样模型就不必决定何时重试。
  3. 将失败作为清晰明确的结果返回给模型。 MCP 将协议错误(如未知工具或无效参数)与工具执行错误(如 API 失败)分开。 它报告工具结果 isError: true中的执行错误,因此模型可以看到出了什么问题。 说什么失败了,以及是否再次尝试是有意义的。
  4. 限制每个轮次的工具调用数。 失败次数达到设定值后,停止并告知用户。
  5. 在将工具结果传递给模型之前对其进行验证。 MCP 规范表示客户端应执行此操作,并在工具具有输出架构时,对结构化结果根据该架构进行验证。
  6. 告知用户哪些操作不起作用。 基于失败的工具调用构建的答案应明确说明工具调用失败。

如何测试智能体中的工具故障处理

Approach 你找到的内容 您错过的内容
使用 stubbed 客户端对工具包装进行单元测试 你的代码如何映射你设定的故障 模型如何使用它,以及实际工具如何失效
使真实工具发生故障,例如停止服务器或吊销密钥 该类型的真实故障 速率限制、响应缓慢和格式不正确的数据,这些问题并非你能按需触发的
编写模拟 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中定义错误,如用随机错误测试我的应用中所述。 在您的 429 响应中,将 Retry-After 设为 @dynamic。 RetryAfterPlugin 仅检查这些。

对于使用 STDIO 的 MCP 服务器,请通过 devproxy stdio 使用启用 MockStdioResponsePlugin 的配置启动服务器,如 stdio 配置示例 所示。 将其另存为 devproxyrc-stdio.json。 然后将以下内容放入stdio-mocks.json,以便为每个tools/call请求返回工具执行错误:

{
  "$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 启动服务器。 在 mock 上使用 nth 属性仅使特定调用失败,并添加 LatencyPlugin 以减慢服务器的响应速度。

若要测试代理在模型本身失败时执行的操作,LanguageModelFailurePlugin 会使模型产生幻觉、忽略指令或以错误的格式作答。 请参阅 在语言模型故障情况下测试我的应用。

若要安装 Dev Proxy,请参阅 设置 Dev Proxy。

后续步骤

另请参阅