55 异常兜底:工具失败、非法输出、空检索与超时
异常兜底:工具失败、非法输出、空检索与超时
Agent 的可靠性主要体现在失败时。工具、模型、检索和网络都会出错,系统需要分级兜底。
[TOC]
1. 先把 Agent 想清楚
很多人一听到 Agent,就会想到“让大模型自己决定下一步”。这个理解只对了一半。
在真实项目里,Agent 更像是一个被工程规则约束住的任务执行器:
模型负责理解自然语言、提出候选动作
代码负责权限校验、状态流转、工具执行
业务接口负责最终事实和写操作
日志审计负责事后追踪
如果所有异常都丢给模型“再想想”,Agent 会变得不可控。失败必须被分类、限次重试、降级或转人工。
所以这一阶段的博客不要写成“模型越来越聪明”。更准确的写法是:我们正在学习如何把模型的灵活性放进一个可控的工作流里。
2. 一个业务场景
订单接口超时,模型输出非法 JSON,知识库检索为空。三个问题处理方式不同:超时可能重试,非法输出可修复一次,空检索应该拒答或澄清。
这个场景里面最重要的不是模型能不能说出一段好听的话,而是:
它选了哪个工具
为什么能调用这个工具
参数从哪里来
权限在哪里校验
失败后状态怎么变
用户确认前会不会产生真实写入
把这些问题问清楚,Agent 项目才不会变成一个不可解释的聊天黑盒。
这一节要先列出会失败的地方。
判断顺序可以这样写:
1. 模型可能失败,工具可能失败,检索可能失败,业务 API 可能失败
2. 每类失败要定义可恢复、可重试、可降级还是必须终止
3. 降级后要明确告诉用户当前能力受限,不能装作正常回答
4. 对写操作失败不能自动重试到产生重复业务动作
5. 最后用模型超时、工具异常、检索为空和业务接口失败样例验证
它解决的是失败可控问题:Agent 不可能永远成功,但失败时必须有边界、有日志、有用户可理解的返回。
3. 核心概念
1. 工具失败
参数错误、权限失败、业务冲突、限流、超时要分开处理。
这一点在项目里要落到具体规则:失败要分类。
2. 非法输出
结构化输出要做 schema 校验,最多修复一次。
这一点在项目里要落到具体规则:模型输出要校验。
3. 空检索
空结果不是系统异常,可能代表资料不足或问题不清。
这一点在项目里要落到具体规则:没资料不要猜。
4. 超时重试
重试要有 deadline、次数上限和幂等保护。
这一点在项目里要落到具体规则:重试要有边界。
4. 整体流程
graph TD;
A["检测失败类型"] --> B["判断可重试"];
B["判断可重试"] --> C["有限重试或修复"];
C["有限重试或修复"] --> D["降级或澄清"];
D["降级或澄清"] --> E["转人工或结束"];
E["转人工或结束"] --> F["记录样本和告警"];
这张图里,模型只是其中一个节点。工具、状态、权限、确认和审计同样重要。
如果一个 Agent 图里只有“用户 -> 模型 -> 答案”,那它还只是 Chat,不是能进入业务系统的 Agent。
5. 落地步骤
1. 建立错误矩阵
每种错误定义用户提示、重试次数、降级动作和告警级别。
完成后应该能看到:错误策略表。
2. 设置全局预算
最大步数、模型调用、工具调用和总时长都要限制。
完成后应该能看到:预算配置。
3. 准备降级话术
模型不可用或资料不足时返回清晰状态,不编造答案。
完成后应该能看到:降级响应。
4. 模拟故障
主动测试 429、超时、非法 JSON、空结果和服务重启。
完成后应该能看到:故障测试集。
6. 状态、接口或工具字段
| 项目 | 说明 |
|---|---|
| errorCode | 错误码 |
| retryable | 是否可重试 |
| maxRetry | 最大重试次数 |
| fallback | 降级动作 |
| alertLevel | 告警级别 |
字段设计要尽量结构化。不要让下游通过一段自然语言去猜“成功了没有”“下一步要干什么”。
7. 示例
INVALID_ARGUMENT -> 请求用户补充,不重试
FORBIDDEN -> 终止并审计
RATE_LIMITED -> 退避重试 2 次
TOOL_TIMEOUT -> 查询幂等结果后再决定
EMPTY_RETRIEVAL -> 澄清或拒答
INVALID_MODEL_OUTPUT -> 修复 1 次后降级
示例里的字段不一定照搬,但思想要保留:输入、状态、输出、错误和审计信息要分开。
8. 哪些事不能交给模型自由发挥
模型不能决定无限重试,也不能在工具失败后凭空编造工具结果。工具结果必须来自真实调用或明确降级。
这一节特别重要。Agent 项目最容易踩坑的地方,就是把“理解意图”和“执行业务动作”混成一件事。
模型可以建议动作,但最终执行必须经过代码和业务系统的规则。
9. 测试路径
建议至少准备下面几类测试:
正常路径:输入完整,工具正常返回
缺字段路径:订单号、联系方式、问题描述不完整
权限路径:查询别人的订单、访问无权知识库
失败路径:工具超时、模型输出非法、检索为空
恢复路径:等待确认时服务重启、重复确认、超时确认
Agent 的测试不能只看最终回答。你要检查状态、工具调用、审计日志和是否产生真实副作用。
10. 工程取舍
- 重试能提高成功率,但会放大成本和延迟。
- 降级会牺牲体验,但能保护系统边界。
- 转人工不是失败,而是可靠性策略。
取舍讲清楚以后,读者才能知道你为什么这样设计,而不是以为所有问题都要上 Agent。
11. 常见坑
- 每一层都重试三次,次数爆炸。
- 写操作超时后直接重放。
- 非法 JSON 无限修复。
- 空检索后让模型自由回答。
这些坑本质上都和一个问题有关:没有把模型输出当成“不可信的候选结果”。
12. 验收清单
- ☐ 错误矩阵完整
- ☐ 全局预算生效
- ☐ 写操作重试有幂等
- ☐ 降级不编造事实
- ☐ 失败样本进入评测
如果这些检查项没过,先不要急着加更多工具。工具越多,边界越乱,排查越难。
13. 落地清单
- ☐ 写错误策略表
- ☐ 模拟工具超时
- ☐ 测试非法 JSON 修复
- ☐ 设计空检索拒答
练习时可以先用 Mock,不要一开始就连真实订单或工单系统。等状态、权限和幂等设计清楚后,再接真实接口。
14. 小结
异常兜底让 Agent 在坏天气里也不乱跑。真正可靠的系统,不是永远成功,而是失败时有边界。
Agent 的价值来自灵活性,但安全来自边界。能把这两件事同时讲清楚,才算真正理解 Agent 工程化。