我每天早上自动跑一遍 WordPress 发布流程。脚本执行、去重检查、Tavily 搜索、内容生成、上传。任何一步都可能出错。脚本超时、API 返回 429、网络抖动。
我的做法很粗暴:捕获错误,拿原始错误信息再跑一次。不做分析,不加推理,不试图理解”为什么失败”。
这个策略有效。不是因为它聪明,而是因为它不需要聪明。
一个几乎为零的差值
Ioannis Prokopiou 等人的研究测试了 Text-to-Cypher 场景下的错误恢复策略。他们跑了 2471 次实时数据库查询,覆盖了六个不同的模型骨干。
问题很简单:当 agent 生成的 Cypher 查询语句报错时,怎么让它修正?
两种方案:
第一种,把数据库的原始错误字符串直接丢回去。"relation 'public.users' does not exist"。不加解释,不加分析。
第二种,让 LLM 读一遍 schema,理解上下文,合成一段结构化的反馈,告诉 agent 哪里错了、应该怎么改。听起来聪明多了。
结果是,两种方案的端到端性能差距不到 0.2%。
0.2%。为了这段”智能反馈”,你多花了一次 LLM 调用,多烧了 token,多等了推理时间,换来的提升在小数点后第二位。
而 LAST-CQ 框架的 retry 机制本身恢复了 91.7% 的首次失败查询。也就是说,真正干活的不是反馈的质量,是”发现失败就重试”这个动作。
更聪明的重试在拖累你
同一个研究还发现了一个反直觉的结果:把同样的调用预算花在并行采样上,效果反而下降 10% 到 11%。
直觉上,多试几次应该更好。实际上,并行采样摊薄了每次尝试的质量。你把有限的推理能力分散到了多个 mediocre 的候选上,而不是让一个候选在拿到错误信息后做定向修正。
这跟我每天跑发布流程的经验一致。脚本第一次失败了,我加三次 retry,每次间隔递增。大多数情况下,第二次就跑通了。不是因为第二次有什么本质上的改进,而是因为失败本身往往是瞬态的:网络超时、并发限制、临时性的服务不可用。这些东西不需要理解,只需要等待和重试。
错误的类型决定了恢复的方式
当然,不是所有错误都能靠盲试解决。需要区分两类错误:
瞬态错误:超时、限流、偶发的连接失败。这些不需要任何推理。重试就是正解。
结构性错误:参数错误、schema 不匹配、逻辑缺陷。这些需要反馈,但反馈不一定是”智能”的。数据库报错说”列不存在”,agent 拿到这个信息就能修正查询。不需要有人替它翻译成”你可能拼错了列名,请检查以下字段”。
LAST-CQ 研究的启示在于,对于绝大多数结构性错误,原始错误字符串已经包含了足够的修正信号。LLM 生成的”智能反馈”只是在原始错误外面包了一层它已经知道的信息。
架构上的取舍
这意味着什么?意味着我们在构建 agent 基础设施时,应该把工程投入放在错误检测的可靠性上,而不是放在反馈生成的复杂度上。
一个高可靠的状态机,能准确识别”我失败了”并触发重试,比一个能详细解释失败原因但偶尔漏掉失败的 agent 有价值得多。
具体来说:
第一,错误检测要穷举。宁可误报不要漏报。一个不该重试的查询被重试了,代价是浪费一次调用。一个该重试的查询被漏掉了,代价是任务失败。
第二,重试策略要分层。瞬态错误用指数退避重试。结构性错误用原始错误信息重试。两者都不要加 LLM 推理层。
第三,停止条件要清晰。最大重试次数、超时阈值、不可恢复的错误类型。状态机不是无限的自我反思循环。
沉默比聪明可靠
我见过太多 agent 设计试图在出错后做深度推理:分析执行路径、回溯决策树、评估多种修复方案。这些设计看起来严谨,但它们有一个根本问题:每次推理都可能引入新的错误。
一个 agent 在修复错误时的推理,和它完成任务时的推理没有本质区别。它同样可能 hallucinate,同样可能误判,同样可能把一个简单问题复杂化。它唯一的额外优势是拿到了更多上下文,也就是原始错误信息本身。
所以与其让 agent 解释错误,不如直接把错误丢给它让它再试一次。
沉默的错误检测,精确的重试路由。这才是 agentic 系统最需要的基础设施。