设 isError: true 只是起点。通用的”Operation failed”只有 15% 的 agent 恢复率。加上结构化元数据——错误类别、是否可重试、客户消息、建议动作——能实现不同错误类型 78-95% 的恢复率。
恢复率数据
| 错误类型 | 提供的元数据 | 恢复率 |
|---|---|---|
| 瞬时性(isRetryable: true) | 类别 + 可重试性 | 92% 自动恢复 |
| 校验性(字段级详情) | 类别 + 具体错误 | 78% 自动纠正 |
| 业务性(isRetryable: false) | 类别 + 客户消息 | 95% 正确升级 |
| 通用(“Operation failed”) | 无 | 15% 恢复 |
15% 和 78-95% 之间的差距是结构化元数据,不是模型能力。
基本模式
{
"content": [{"type": "text", "text": "Database query timed out after 30s"}],
"isError": true
}
Agent 知道工具失败了、看到了原因。但它不知道:该不该重试?这是暂时的吗?该告诉用户什么?
完整结构化模式
{
"content": [{"type": "text", "text": "Refund of $750 exceeds $500 policy limit"}],
"isError": true,
"structuredContent": {
"errorCategory": "business",
"isRetryable": false,
"customerMessage": "Refunds over $500 require manager approval",
"suggestedAction": "escalate_to_human"
}
}
现在 agent 知道:这是业务规则(不是瞬时性的)、重试没用、可以告诉客户为什么、应该升级。
四种反模式
静默吞没:数据库不可达时 isError: false 加空内容。Agent 告诉研究者”没找到论文”——但实际上有几千篇。数据库是挂了,不是空的。
成功带错误文本:isError: false 加内容”error occurred”。Agent 把这当作返回的数据处理。
未处理的异常:工具错误作为异常传播 → JSON-RPC 协议错误导致连接崩溃。把可恢复的工具错误转成了不可恢复的协议错误。
通用消息:所有错误类型都返回”Operation failed”。Agent 对一切做相同的重试——在不可重试的错误上浪费尝试。
协议错误 vs 工具执行错误
| 类型 | 示例 | LLM 可恢复性 |
|---|---|---|
| 协议错误 | 工具名拼写错误 | 低(需要代码修复) |
| 工具执行错误 | API 超时 | 高(LLM 可重试/适应) |
MCP 规范说工具执行错误应该提供给 LLM 做自我纠正。让异常传播会把可恢复的错误转成不可恢复的。
安全:净化后的错误内容
错误做法:“Connection to db-prod-3.internal:5432 refused。“正确做法:“Database temporarily unavailable。“包含错误类别和可恢复性。完整技术细节记到服务端日志。Agent 得到恢复元数据但不暴露敏感内部信息。
一句话总结: isError: true 加结构化元数据(errorCategory、isRetryable、customerMessage、suggestedAction)实现 78-95% 的 agent 恢复率 vs 通用消息的 15%——所有工具错误捕获为 CallToolResult,绝不让它们变成协议异常,并为安全做净化。