Home Projects Blog Resume Contact 中文
Back to list
2026年7月2日 3,607 words 8 min read

09:Fallback & Recovery:Claude Code 如何在失败后继续推进

上一篇把 Observability 理解成 Agent 的证据层。

#Claude Code#Coding Agent#工程化#Agent Runtime

09:Fallback & Recovery:Claude Code 如何在失败后继续推进

上一篇把 Observability 理解成 Agent 的证据层。

工具调用、权限裁决、错误、成本和上下文变化留下轨迹以后,失败就会从屏幕上的一段报错,变成可以继续判断的材料。它会带着证据回到 Agent Loop:刚才尝试了什么,哪里失败了,失败类型是什么,还有没有更合适的下一步。

所以 09 要接着追:

Claude Code 遇到失败以后,怎样决定是换路继续,还是回到一个可继续的位置重新走?

这里我会把 Fallback & Recovery 分成两条线:

  • fallback 关心“当前这一步失败以后怎么换路继续”。
  • recovery 关心“整段任务走坏以后怎么回到可继续的位置”。

前者靠失败分类和下一步策略,后者靠 checkpoint、transcript、diff 和任务状态。

ChatGPT Image Jul 1, 2026, 06_58_51 PM

这篇文章的主线

08 讲的是“留下证据”。09 讲的是“使用证据”。

一次失败回来以后,Agent 需要看到一组能继续判断的信息:

  • 失败发生在哪一步。
  • 失败属于哪一类。
  • 有没有新的证据。
  • 当前路线还能不能继续。
  • 要不要换工具、换参数、问用户、回到 checkpoint,或者停下来。

所以这篇先把重点放在两条失败处理路径上。retry、fallback、checkpoint、rewind、rollback、resume 都有用,只是它们处在不同层级。几篇源码解析资料也把失败处理放回工具执行层和 runtime 主循环里看:工具层负责把错误包装成可读结果,主循环再决定下一步怎么走[1],[2]。

这篇只抓两条主线:

  1. fallback。
    这一步失败了,怎样用失败证据换一个动作继续。

  2. recovery。
    这一段任务走坏了,怎样回到一个还能继续判断的位置。

fallback 和 recovery 的区别

fallback 和 recovery 都发生在失败之后,但它们处理的失败尺度不一样。

机制关心的问题常见动作
fallback当前这一步失败以后怎么继续重试、换参数、读更多证据、缩小范围、问用户、停止
recovery当前路线走坏以后回到哪里checkpoint、rewind、恢复 diff、保留失败摘要、重新规划

fallback 更像局部换路。

比如 pnpm test 失败了,下一步可以读取失败堆栈;路径不存在,下一步可以搜索文件;命令参数错了,下一步可以修正参数再调用。

recovery 更像任务级恢复。

比如 agent 改了一大串文件以后发现方向错了,或者连续几轮修改让测试失败更多,这时继续在坏状态上修补会越来越乱。更稳的做法是回到某个 checkpoint,把文件和会话状态拉回去,再带着失败摘要换一条路线。

这两个层级分清以后,09 就不会变成术语堆叠。

fallback 解决“下一步怎么走”,recovery 解决“回到哪里再走”。

fallback:先判断失败类型

fallback 的第一步是分类。

同样是失败,下一步可能完全不同。网络抖动可以短暂重试,参数错误要改参数,权限拒绝要换低风险路径,测试失败要读错误输出,上下文不足要补证据。

可以先把失败分成几类:

失败类型典型信号更合适的方向
临时错误网络抖动、外部服务短暂不可用短暂重试,限制次数
参数错误路径不存在、命令参数不对、schema 校验失败修正参数,重新调用
测试失败断言失败、快照不匹配、编译报错读取失败输出,定位原因
权限拒绝动作被 permission / settings / hook 拦住换低风险路径或询问用户
上下文不足缺少关键文件、约束丢失、判断开始漂移搜索、读文件、整理状态
方向错误连续失败、改动变大、目标偏航更新 plan 或进入 recovery

这一步看起来朴素,但很关键。

如果 Agent 把所有失败都当成“再试一次”,就容易原地打转。测试失败需要定位原因;权限拒绝需要换低风险路径或询问用户;路径错了,需要先修正路径。

所以 fallback 的重点是让每次尝试都带着新的判断。

再选择下一步策略

失败分类以后,Agent 才能选择策略。

常见的下一步大概有几种:

  1. retry。
    适合临时错误,比如网络抖动、短暂超时、外部服务偶发失败。retry 要有限制,也要有退路。

  2. 换参数。
    适合路径、命令、schema 这类问题。先修正输入,再调用同一个工具。

  3. 读更多证据。
    适合测试失败、构建失败、上下文不足。先读错误输出、相关文件、日志或配置,再决定改哪里。

  4. 缩小范围。
    适合大任务开始失控。先修一个测试、一个模块或一个更小的目标。

  5. 询问用户。
    适合权限、需求、外部系统、业务判断不清楚的情况。这里问的是决策,要把已经观察到的证据和风险一起交代清楚。

  6. 停止并说明。
    适合连续失败、证据不足或风险过高。成熟的 agent 要能讲清已经试过什么、卡在哪里、下一步需要什么。

这里最容易被滥用的是 retry。

retry 只是 fallback 的一种策略。它只有在“下一次尝试有机会因为外部状态变化而成功”时才合理。对于测试失败、参数错误、权限拒绝和方向错误,重复执行通常只是消耗时间和上下文。Blake Crosley 提到的 frustration detection 和 compact failure circuit breaker,也可以放在这个方向理解:系统需要识别原地打转,并在必要时换策略或停下来[3]。

一个简单原则是:

重试应该带着新信息发生。

如果没有新信息,就换观察路径、换策略,或者停下来说明。

permission denied 是特殊失败

权限拒绝要单独讲。

它和网络超时、命令报错不一样。permission denied 更像边界信号。07 里已经说过,权限裁决会给下一轮判断增加一个事实:这条路现在走不通,或者需要用户明确裁决。Haseeb Qureshi 的源码摘记里也把 permission denied 放在回流到下一轮判断的线索里[4]。

比如 agent 想读 .env,被拒绝了。更合适的 fallback 是换一条低风险路径:

  1. .env.example、README 或配置 schema。
  2. 查看配置加载代码。
  3. 根据报错推断缺失字段。
  4. 让用户提供脱敏后的关键信息。
  5. 如果真实值确实必要,讲清原因和风险,请用户决定。

命令也一样。

curl | sh 被拒绝以后,可以改成阅读安装说明、固定下载来源,或者让用户手动执行。rm -rf dist 被拒绝以后,可以先列出目标目录、说明清理范围,再请求确认。

这里的重点是尊重边界。

安全系统是任务执行的一部分。权限拒绝处理得好,Agent 还能继续推进;处理不好,它就会在同一个被拒绝动作上反复撞墙。

recovery:回到可继续的位置

fallback 处理的是局部失败。recovery 处理的是任务级走坏。

长任务里经常会出现这种情况:单步看都说得过去,但整条路线已经偏了。

比如:

  • 修一个 bug 时顺手重构了太多文件。
  • 测试越修失败越多。
  • 前面已经约定不动数据库结构,后面却开始改模型字段。
  • 用户看完结果后说“回到刚才那个版本,我们换一种做法”。

这时更需要回到一个可继续的位置,继续换小动作修补只会让状态更乱。

Claude Code 的 recovery 可以理解成几类材料配合:

材料回答的问题
checkpoint文件和会话可以回到哪个点
transcript这次任务走到哪里,为什么走到这里
diff哪些文件被改过,改动范围有多大
失败证据刚才为什么失败,哪条路线已经尝试过
任务状态回去以后应该保留哪些约束和判断

checkpoint 解决“能回到哪里”。transcript 和 diff 解决“回去时要恢复什么”。失败证据和任务状态解决“回去以后别再踩同一个坑”。几篇 rewind / checkpoint 实践资料主要支撑的就是这一点:恢复既要看文件,也要看会话时间线和恢复后的新路线[5],[6]。

所以 recovery 更像把任务时间线拉回一个安全点,同时保留刚才学到的东西。

checkpoint 还需要任务状态

checkpoint 很重要,但它只是 recovery 的底座。

只有文件回滚时,Agent 回去以后可能忘记为什么要回滚。
只有 transcript 时,回放以后也很难还原真实仓库。
两者都有但缺少失败摘要时,Agent 可能会从同一个地方再摔一次。

所以 recovery 还需要任务状态。

这个任务状态至少要包括:

  • 当前目标是什么。
  • 用户有哪些硬约束。
  • 已经读过哪些证据。
  • 哪些路线尝试过。
  • 哪些失败说明方向不对。
  • 哪些文件改动应该撤掉。
  • 回到 checkpoint 后下一步换什么策略。

一个更稳的 recovery 流程可以是:

  1. 选择 checkpoint,恢复文件和会话位置。
  2. 从 transcript 里找到刚才为什么失败。
  3. 保留一段失败摘要。
  4. 标记已尝试路线,避免重复。
  5. 从恢复点继续,但换观察路径或实现方案。

这比普通 undo 更适合 Agent。文件状态回去了,有价值的学习结果留下来。下一轮可以带着失败证据重新判断。

例子:测试失败别原地打转

用测试失败把两条线串起来。

用户说:

“帮我把这几个测试修好。”

第一次运行 pnpm test 失败,这通常还在 fallback 范围内。Agent 应该先看失败证据:

  1. 失败的是哪个测试。
  2. 错误类型是断言失败、类型错误、环境错误,还是快照变化。
  3. 失败堆栈指向哪些文件。
  4. 当前修改是否和失败区域有关。

接下来可以选择策略:

  • 如果是命令参数问题,修正参数。
  • 如果是环境问题,先读配置或问用户。
  • 如果是断言失败,读最小相关代码。
  • 如果是快照变化,确认是预期变化还是回归。

修改以后再次运行同一组测试。第二次结果很关键。

  • 失败减少了,说明方向可能有效。
  • 失败类型变了,说明系统状态被推动了,需要重新分类。
  • 失败完全一样,说明修改没有触达原因,要换观察路径。
  • 失败更多了,说明改动可能扩大了问题,需要考虑回滚。

如果连续几轮都没有进展,或者修改已经偏离目标,就进入 recovery。此时更合理的做法是回到最近的 checkpoint,撤掉坏改动,同时保留失败摘要:


已尝试路线:修改 A 模块以修复 test-x。
结果:失败类型没有变化,并额外引入 test-y 失败。
下一步:回到 checkpoint,改为先阅读 B 模块和测试 fixture。

这个摘要很重要。它让 recovery 之后的 Agent 不会回到过去再走同一条错路。

所以测试失败里的关键是:每一次失败都要改变下一步判断。

对我写 Agent 的启发

写到这里,我会把失败处理拆成两个对象,避免塞进一个大而全的异常里。

第一个是 FailureEvent

它描述这次失败本身:

字段作用
failure_typetool_error、command_failed、permission_denied、test_failed、context_missing、direction_wrong
attempted_action刚才尝试了什么
evidence错误输出、权限结果、diff、测试摘要或上下文线索
recoverabilitytransient、recoverable、needs_user、terminal
retry_count同类失败已经发生几次
next_optionsretry、read_more、change_params、ask_user、rewind、stop

第二个是 RecoveryPoint

它描述任务可以回到哪里:

字段作用
checkpoint_id可以恢复到哪个点
transcript_ref对应哪段会话时间线
diff_summary这个点之后改过什么
failure_summary为什么要回到这里
preserved_lessons回滚后仍要保留的判断
next_plan回到这里以后换什么策略

这样设计以后,fallback 和 recovery 的职责会更清楚。

FailureEvent 帮 Agent 判断这一步怎么换路。
RecoveryPoint 帮 Agent 判断任务走坏后回到哪里。

我现在对 Fallback & Recovery 的理解可以收成一句话:

fallback 让失败变成下一步策略,recovery 让走坏的任务回到可继续的位置。

参考资料

Claude Code 源码解析与恢复机制

Checkpoint 与恢复实践

下一篇继续追的问题

这一篇把 Fallback & Recovery 拆成了两条线。

核心结论是:

fallback 处理局部失败后的换路,recovery 处理任务走坏后的恢复。

到这里,Claude Code 已经能计划动作、裁决动作、执行动作、记录动作,也能在失败后继续或恢复。

下一篇要看的是另一种能力:把外部工作流和可复用能力接进来。

真实工程里,Agent 需要自己会循环、会恢复,也要接团队已有的规则、工具、自动化和外部系统。格式化、审计、通知、MCP、slash command、skill、hook,都会把 runtime 打开一个入口。

所以 10 要继续追的问题是:

Claude Code 如何通过 hooks、skills、slash command 和 MCP,把外部工作流接进 Agent Runtime?