AI agent 失败并不可怕,可怕的是所有失败都叫 failed。模型不知道能不能重试,用户不知道要补什么,系统不知道是否要暂停。结果就是 agent 要么反复尝试,要么直接放弃。
错误分类的目标,是让每种失败都有明确下一步。一个成熟的 agent 不只是“能成功”,还要在失败时知道应该重试、追问、暂停、转人工,还是彻底停止。
先给结论:至少分五类错误
| 类型 | 示例 | 恢复方式 |
|---|---|---|
| input_missing | 缺少目标、时间、权限信息 | 请求补充 |
| validation_error | 输出格式不符合 schema | 重新生成或修正 |
| tool_timeout | 外部工具超时 | 限次重试 |
| permission_denied | 用户无权限 | 停止并说明 |
| state_conflict | 状态已被别人修改 | 重新读取后判断 |
错误类型越清楚,恢复路径越稳定。
一套更完整的错误分类表
实际项目里,五类错误还不够。建议至少按“谁能解决”来分层:用户能解决、系统能重试、开发要修、人工要判断。
| errorType | 谁处理 | 是否重试 | nextAction | 典型提示 |
|---|---|---|---|---|
| input_missing | 用户 | 否 | ask_user | 还缺少目标用户或时间范围 |
| invalid_request | 用户 | 否 | ask_user | 当前请求超出任务范围 |
| validation_error | agent / 系统 | 可局部重试 | retry | 输出格式不符合 schema |
| tool_timeout | 系统 | 是 | retry | 外部工具超时,稍后重试 |
| rate_limited | 系统 | 延迟重试 | retry | 当前调用过于频繁 |
| permission_denied | 权限管理员 | 否 | stop | 当前用户无权执行此动作 |
| state_conflict | 系统 + 人 | 否 | human_review | 目标状态已变化,需要确认 |
| low_confidence | 用户或人 | 否 | ask_user / human_review | 依据不足,不能继续 |
| policy_blocked | 系统 | 否 | stop | 触发系统限制 |
| unknown | 开发 | 否 | human_review | 未分类错误,需要排查 |
这张表要进入代码和文档,而不是只存在于团队共识里。
一、输入缺失不要假装继续
当用户没有提供必要信息时,agent 应该明确追问,而不是编造默认值。
例如生成项目 brief 缺少目标用户时,可以返回:
{
"errorType": "input_missing",
"missingFields": ["targetAudience"],
"nextAction": "ask_user"
}
这比输出一段看似完整但基础不稳的内容更可靠。
用户提示也要具体。不要说“信息不足”,要说缺什么、为什么缺、补充后能做什么:
{
"errorType": "input_missing",
"missingFields": [
{
"field": "targetAudience",
"whyNeeded": "目标用户会影响结构、语气和案例选择",
"question": "这次内容主要写给哪类读者?"
}
],
"nextAction": "ask_user"
}
好的追问能减少来回沟通;空泛追问只会把用户推走。
二、工具超时可以重试,但必须限次
超时、限流、临时网络错误可以重试。但重试要有上限、退避时间和幂等保护。
不要让 agent 自由决定“再试一次”。工具层应该控制:最多重试几次,每次间隔多久,超过后如何降级。
一个可用的重试策略可以这样定义:
| 错误 | 重试次数 | 间隔 | 超过后 |
|---|---|---|---|
| tool_timeout | 2 | 1s, 3s | human_review 或降级 |
| rate_limited | 1 | 10s | 排队等待 |
| validation_error | 1 | 立即 | stop 并记录样本 |
| permission_denied | 0 | 无 | stop |
| state_conflict | 0 | 无 | 重新读取状态 |
重试只应该用于“有可能临时恢复”的错误。参数错、权限错、状态冲突都不应该盲目重试。
三、权限不足必须停止
权限不足不是靠换一种问法解决的。agent 应该停止当前动作,并说明需要哪个权限或谁来处理。
如果继续尝试,可能导致越权请求、错误日志泛滥,甚至触发安全规则。
权限错误的输出要避免泄露过多内部信息。用户需要知道“不能做什么”和“找谁处理”,不需要看到内部权限树。
{
"errorType": "permission_denied",
"nextAction": "stop",
"userMessage": "当前账号不能发布该内容,可以请拥有发布权限的成员确认。",
"internalCode": "publish:write_denied"
}
四、状态冲突要重新读取
当工具返回状态冲突,说明任务状态已经变了。例如原计划要更新草稿,但草稿已被人工发布。此时不能继续按旧上下文执行,必须重新读取最新状态。
状态冲突的恢复方式通常是:重新读取 -> 比较差异 -> 请求确认。
状态冲突最适合用乐观锁或版本号处理:
{
"entityId": "draft_123",
"expectedVersion": 7,
"patch": { "status": "ready_for_review" }
}
如果工具返回当前版本已经是 9,agent 必须停止旧写入,重新读取差异。否则它可能覆盖人工刚做的更新。
五、把恢复路径做成状态机
错误处理不要散落在各个 prompt 里。可以用一个最小状态机统一控制:
running
-> tool_error_retrying
-> waiting_user_input
-> waiting_human_review
-> completed
-> failed_final
状态机的好处是:前端能展示进度,日志能统计卡点,队列能决定是否释放 worker。agent 只负责解释和生成下一步建议,真正的流程切换由系统完成。
六、失败案例:所有错误都重试,造成调用风暴
一个 agent 工具层把所有失败都当成可重试。权限不足也重试,参数错误也重试,最终短时间内产生大量无效请求。
修复后,团队把错误分成可重试、需补充、需人工、必须停止四类。只有超时和限流进入重试,其他错误直接进入对应路径。同时他们把 retry queue 从主队列拆出来,限制并发,避免失败任务挤占正常任务。
七、错误分类 Checklist
- 错误是否有 type,而不是只有 message
- 输入缺失是否能列出 missingFields
- 工具超时是否限次重试
- 权限不足是否停止
- 状态冲突是否重新读取
- 每类错误是否有 nextAction
- 错误样本是否进入评测集
- 重试队列是否与正常队列隔离
- 用户提示是否说明缺什么和下一步
- 未分类错误是否进入工程排查
结语
AI agent 的错误处理不应该靠模型临场发挥。把错误分类写清楚,把恢复路径结构化,把重试和状态流转交给系统控制,agent 才能在失败时有秩序地继续、暂停或交给人处理。
延伸阅读:


