智能体工具调用失败怎么办:一句话结论
容错设计的核心是先给错误分类,再决定谁修:瞬时错误系统自动重试、模型能自己修复的错误喂回模型、需要人拍板的暂停问人——三类错误用同一套「无脑重试」处理,是生产事故的头号来源。
为什么原型跑得欢,上线就翻车
写 happy path 通常是最简单的部分,让智能体在生产环境活下来的错误处理样板代码(重试、超时、降级)往往比业务逻辑还长。真实世界的失败类型远比演示多:网络抖动、限流 429 、工具超时、参数错误、下游服务 5xx 。 LangChain 官方容错文档按「谁负责修」给错误分了类,这个分类值得直接抄:
| 错误类型 | 谁修 | 策略 |
|---|---|---|
| 瞬时错误(网络超时、限流) | 系统自动 | 指数退避重试 |
| 模型可修复(工具报错、解析失败) | LLM | 转成 error ToolMessage 喂回模型 |
| 用户可修(信息缺失、指令不清) | 人 | 用 interrupt() 暂停提问 |
| 模型服务商整体宕机 | 系统自动 | 降级到备用模型 |
| 失控循环(重复调用) | 系统自动 | 设置每轮调用上限 |
| 未知错误 | 开发者 | 让它抛出来,别吞 |
第一层:重试要带退避和抖动
瞬时错误的标配是指数退避 + 随机抖动(防止所有请求同一时刻重试造成惊群)。 LangChain 的 ToolRetryMiddleware 一行配上:
from langchain.agents.middleware import ToolRetryMiddleware
ToolRetryMiddleware(
max_retries=3,
backoff_factor=2.0,
initial_delay=1.0,
max_delay=60.0, # 封顶退避增长
jitter=True, # ±25% 随机抖动
retry_on=(TimeoutError, ConnectionError), # 只重试瞬时类
)
关键细节:retry_on 默认重试所有异常——这通常是错的。 ValueError 、 TypeError 这类基本是程序 bug,重试一百次也不会好;LangGraph 的 RetryPolicy 默认只重试 ConnectionError 、 5xx 等瞬时类,这个默认值更合理。
第二层:把错误喂回模型
重试耗尽后还有一条路:把错误转成结构化消息交还模型,让它改参数再试。这一层要注意信息披露——返回给模型的错误文案自己写(指明异常类型和修复方向),别把原始异常直接透传,里面可能带内部细节:
def on_error(exc, request):
if isinstance(exc, ValueError):
return f"工具 {request.tool_call['name']} 失败:参数错误,请修正后重试。"
return None # 其他异常照常上抛
第三层:降级与止损
- 模型降级:主服务商全挂时切备用模型(ModelFallbackMiddleware),保证业务不停摆。
- 调用上限:不设上限的智能体陷入循环时,几分钟能烧掉一大笔 API 费用。模型调用和工具调用分别设每轮上限(如 50 / 200)。
- 超时:墙钟超时之外,更实用的是「空闲超时」——只要节点还在产出进度就续命,真正挂死的调用才会被掐断。
降级不是失败兜底的终点,护栏设计要和它配套,参考 智能体护栏的四道防线。
第四层:让未知错误浮出水面
最反直觉的一条:处理不了的错误不要接。 ToolErrorMiddleware 只对你显式返回内容的异常生效,其余照常上抛终止运行——这不是缺陷而是设计,吞掉未知错误的代价是排查时两眼一抹黑。配合上线后的追踪与评估闭环(见 智能体可观测性),错误才能变成改进输入。
常见问题
重试次数设多少合适?
错误喂回模型会不会让它死循环改参数?
什么时候该打断问用户,什么时候该自动兜底?










评论 (0)