Agent 可靠性:把失败路径设计成系统能力
可靠的 Agent 不是“尽量别出错”,而是出错之后仍然知道该重试、该回退、该暂停还是该交给人。
01.Agent 可靠性不是“少报错”,而是“报错后还能继续控制”
普通软件里的可靠性,很多时候可以靠明确输入输出和严谨逻辑来托底。但 Agent 系统的问题更复杂:
- •模型会输出非确定性结果
- •工具调用依赖外部系统
- •一次任务可能跨很多步骤
- •有些动作还会产生真实副作用
所以可靠性不能只理解成“尽量不失败”。更准确的目标应该是:
当失败发生时,系统仍然知道下一步该自动重试、回退、暂停、转人工,还是直接中止。
这个视角非常重要,因为 Agent 的很多事故不是“第一次出错”,而是“出错后继续乱跑”。
02.先把错误分清楚,再谈策略
LangGraph 当前官方 “Thinking in LangGraph” 文档对错误的分类很实用,基本可以直接变成我们的设计准则。
1. 瞬时错误
例如:
- •网络抖动
- •临时超时
- •429 / 5xx
这类错误通常适合系统自动重试。
2. LLM 可恢复错误
例如:
- •工具执行失败
- •工具参数不对
- •解析结果不符合预期
这类错误不一定要立刻中断。更常见的做法是把错误信息写回状态,再让模型基于错误重新规划。
3. 需要用户或人工修复的错误
例如:
- •缺少关键参数
- •意图不明确
- •高风险动作需要批准
这类错误不该自动重试,而应该暂停执行,等待用户或审核人补信息。
4. 真正的程序错误
例如:
- •代码 bug
- •状态结构损坏
- •未预期异常
这类错误应该直接暴露,让开发者修,而不是试图用模型把问题“糊过去”。
只要这个分类做清楚,后面的可靠性策略通常就不会乱。
03.可靠性最常见的误区,是把所有错误都交给重试
这是很多旧稿最容易跑偏的地方:一遇到失败就写 retry(3)。
但现实里,不同错误的处理方式完全不同:
- •瞬时失败:重试
- •参数错误:修正输入
- •缺少信息:暂停询问
- •程序 bug:中止并报警
如果把这些情况混在一起,结果往往是:
- •该停的不停
- •该转人的不转
- •该上报的问题被悄悄吞掉
所以对 Agent 来说,重试只是可靠性工具箱的一部分,不是全部。
04.在 LangGraph 里,重试应该绑在节点上
对会访问外部服务的节点,LangGraph 的 RetryPolicy 很适合作为第一层保护。
from langgraph.graph import StateGraph
from langgraph.types import RetryPolicy
def search_docs(state):
...
builder = StateGraph(...)
builder.add_node(
"search_docs",
search_docs,
retry_policy=RetryPolicy(max_attempts=3, initial_interval=1.0),
)这样做的好处是:
- •重试边界清晰
- •只对可能瞬时失败的节点生效
- •不会把整个图包装成黑箱重试
对搜索、检索、外部 API、消息发送这类节点,这种策略通常都很合适。
05.LLM 可恢复错误,应该让模型“看见错误”
LangGraph 文档还给了一个很实用的思路:当工具失败时,不一定立刻中止,而是把错误写回 state,再回到 agent 节点。
from langgraph.types import Command
def execute_tool(state):
try:
result = run_tool(state["tool_call"])
return Command(update={"tool_result": result}, goto="agent")
except Exception as error:
return Command(
update={"tool_result": f"Tool error: {error}"},
goto="agent",
)这种模式的关键不是“优雅”,而是它把失败变成下一轮规划的输入。
当然,前提是这类错误真的适合让模型修复。例如:
- •查询参数填错
- •返回格式没对上
- •可以换一种工具路径
但如果是高风险副作用失败,就不该继续让模型反复尝试。
06.缺信息或高风险动作时,暂停比重试更可靠
很多 Agent 系统的问题,不是执行能力不足,而是信息不足却还在继续推断。
LangGraph 的 interrupt() 非常适合这种情况:
from langgraph.types import interrupt
def human_review(state):
decision = interrupt(
{
"action": "审批是否发送正式通知",
"draft": state["draft_message"],
}
)
return {"approved": decision["approved"]}这个机制的重要性在于:
- •它让系统可以暂停
- •它让人工审批变成流程的一部分
- •它必须与 checkpointer 配合,才能恢复执行
从可靠性视角看,这类“知道什么时候停”往往比“再试一次”更有价值。
07.LangChain middleware 很适合做通用防护层
如果你的 Agent 是用 create_agent 构建的,LangChain 当前的 prebuilt middleware 已经能承担不少可靠性工作。
比较直接有用的几类包括:
- •
ModelRetryMiddleware - •
ModelFallbackMiddleware - •
ToolRetry - •
HumanInTheLoopMiddleware - •
ModelCallLimit - •
ToolCallLimit
例如:
from langchain.agents import create_agent
from langchain.agents.middleware import (
ModelRetryMiddleware,
ModelFallbackMiddleware,
)
agent = create_agent(
model="gpt-4.1",
tools=[search_tool, ticket_tool],
middleware=[
ModelRetryMiddleware(max_retries=3, backoff_factor=2.0),
ModelFallbackMiddleware("gpt-4.1-mini"),
],
)这里最值得注意的是:middleware 适合做跨整个 agent loop 的共性保护,而不是替代业务层的错误分类。
08.Durable execution 是可靠性的底座,不是可选项
如果一个任务需要多步执行、暂停、恢复或者人工介入,那么 durable execution 就不是“以后再说”的优化点,而是可靠性的基础。
这也是 LangGraph 为什么反复强调 checkpointer 的原因。没有状态持久化,你就会遇到这些问题:
- •服务重启后任务丢失
- •人工审核后无法恢复
- •中途失败只能从头再跑
在真实系统里,这些问题往往不是体验问题,而是业务事故。
09.副作用边界决定可靠性上限
Agent 里最危险的不是“答错一句话”,而是对外部系统做了不该做的动作。
像下面这类工具,一定要单独看待:
- •发正式通知
- •改配置
- •下单
- •删除或覆盖数据
对这些动作,更可靠的做法通常是:
- •先生成计划,再审批
- •记录幂等键
- •明确是否允许重试
- •把执行结果完整写回状态
可靠性在这里和安全、审计其实是连在一起的。
10.一个更实用的可靠性清单
如果你正在给 Agent 系统补可靠性,我建议优先把这些能力补齐:
- •节点级错误分类
- •外部调用的重试边界
- •模型与工具 fallback
- •checkpointer 持久化
- •高风险动作人工审核
- •模型调用和工具调用上限
- •trace 与异常日志
先把这几项做好,再去讨论更复杂的自愈和自治能力,收益会更大。
11.总结
Agent 可靠性的重点,不是继续堆“容错技巧”,而是把失败处理变成系统结构的一部分:
- •瞬时失败用重试
- •可恢复失败写回状态再规划
- •需要信息或审批时暂停
- •真正的程序错误直接暴露
只要这条主线清楚,Agent 才更像一个可治理系统,而不是一个碰运气的自动化脚本。