# 三行速读

  1. 高完成率系统的关键差异在 “失败后是否能继续”。
  2. 恢复策略必须按失败类型分层,而不是统一重试。
  3. 退出条件同样属于恢复设计的一部分。

# 先修知识

  • 已理解 s06 压缩策略(恢复会复用 compact)。
  • 知道网络失败、超长输入、输出截断是不同问题。

# 读完后你应该能做到(可检验清单)

# 本篇要解决什么

一个能落地的 Agent,不能只会在顺风局工作。s11 解决的是恢复能力:遇到常见失败时,系统如何继续推进而不是直接退出。

# 用一个类比先理解

像导航软件:

  • 路况临时拥堵,不是立刻放弃出行;
  • 会换路、限速、重规划。

错误恢复也是同样思路:先判类型,再走对应恢复路径。

# 为什么这一步必须现在做

后面会引入后台任务、团队协作、外部能力调用。复杂度上升后,失败是常态,不是例外。没有恢复层,完成率会很低。

# 关键代码怎么读

# 1) 恢复阈值与上限

1
2
3
4
MAX_RECOVERY_ATTEMPTS = 3
BACKOFF_BASE_DELAY = 1.0
BACKOFF_MAX_DELAY = 30.0
TOKEN_THRESHOLD = 50000

恢复策略必须有边界,避免无限重试。

# 2) 压缩恢复分支

1
2
3
4
def auto_compact(messages: list) -> list:
...
continuation = "This session continues from a previous conversation..."
return [{"role": "user", "content": continuation}]

上下文过长时,不是失败退出,而是压缩后续跑。

# 3) 退避重试

1
2
3
4
def backoff_delay(attempt: int) -> float:
delay = min(BACKOFF_BASE_DELAY * (2 ** attempt), BACKOFF_MAX_DELAY)
jitter = random.uniform(0, 1)
return delay + jitter

指数退避 + 抖动,是处理瞬时网络问题的标准工程做法。

# 4) max_tokens 续写

1
2
3
if response.stop_reason == "max_tokens":
messages.append({"role": "user", "content": CONTINUATION_MESSAGE})
continue

这是最常见且最实用的一条恢复路径:让模型从中断处继续。

# 你可以怎么复现

  1. 人为降低 max_tokens ,触发续写恢复。
  2. 构造超长上下文,触发 compact 恢复。
  3. 模拟网络失败,观察 backoff 行为。

# 常见误区

  • 误区 1:所有错误都走同一路径处理。
  • 误区 2:重试无上限,导致资源浪费。
  • 误区 3:恢复后不保留连续性信息。

# 一句话总结

s11 的核心是把 “失败” 转化为 “可管理状态”,这直接决定系统真实完成率。

# 补充解读:恢复分支的优先级哲学

# A. 为什么先处理 max_tokens

因为这类问题通常不是任务失败,而是输出被截断。优先续写可以最快恢复主线连续性。

# B. APIError 分层处理

1
2
3
if "overlong_prompt" in error_body or ("prompt" in error_body and "long" in error_body):
messages[:] = auto_compact(messages)
continue

这段把 “提示过长” 与 “网络抖动” 分开处理,避免错误恢复策略错配。

# C. 网络级异常专门分支

1
2
except (ConnectionError, TimeoutError, OSError) as e:
... backoff ...

这说明恢复策略要按错误来源分组,而不是按异常类名粗暴统一。

# D. 恢复失败也要优雅退出

1
2
print(f"[Error] API call failed after {MAX_RECOVERY_ATTEMPTS} retries: {e}")
return

“退出” 本身也是系统行为的一部分。可解释退出比静默失败更重要。

# 进阶练习

  1. 在日志中记录每次恢复策略命中次数。
  2. 增加 circuit breaker:连续多轮恢复失败后自动降级为 plan 模式。
  3. 做一次 “人工故障注入” 演练(限流、断网、超长输入)。

# 再补一层:把恢复链路当作 “失败编排” 来看

很多初学者会把恢复逻辑写成 “哪里报错补哪里”。更稳定的做法是先定义失败类型,再定义恢复动作,再定义退出条件。

# 失败类型建议分三层

  1. 输出层失败: max_tokens (信息不完整)。
  2. 上下文层失败:prompt 过长(输入超载)。
  3. 传输层失败:连接异常 / 限流(通道不稳定)。

每层对应不同恢复策略,千万别混在一起。

# 恢复动作建议与状态字段绑定

你可以额外记录:

  • recovery_attempt_count
  • last_recovery_strategy
  • last_recovery_ts

这样在日志中可以快速回答:系统刚才为什么重试、重试了几次、还要不要继续。

# 退出条件同样重要

恢复逻辑不是 “永不放弃”,而是 “在可控成本内努力恢复”。超过上限后应当给出清晰失败原因,帮助人类接管。

# 统一术语口径(本章)

  • Recovery Strategy :针对不同失败类型的恢复动作。
  • Backoff :失败后延时重试的退避机制。
  • Continuation :中断后保持上下文连续的续跑过程。

# 章节衔接(从易到难)

  • 本章解决 “失败可恢复”。
  • 下一章 s12 进入 “持久任务图如何建模和解锁”。