在前十章,我们的 Agent 已经拥有循环、工具、计划、子代理、技能、压缩、权限、Hook、记忆、提示词流水线十大核心能力,能稳定执行任务、长期沉淀知识、结构化输入输出。
但只要遇到一点异常 —— 模型截断、上下文超限、网络抖动 —— 系统就会直接崩溃停止。一个真正可用的 Agent,必须能自己处理错误、自动恢复,而不是一遇到问题就卡死。
这一章 S11,我们给系统装上错误恢复机制:把错误变成主循环的正常分支,先分类、再重试、最后才失败,让系统稳如生产线。
本章核心信息
- 核心原则:先恢复,再继续,错误不是崩溃,而是正常流程分支
- 工具数量:4 个
- 核心思想:系统必须清楚自己是在继续、重试,还是进入恢复流程
先看懂本章所有名词
-
错误恢复不是隐藏错误,而是识别临时问题 → 执行有限补救 → 恢复执行,无法恢复才明确上报失败。
-
重试预算每种错误最多重试几次,防止无限循环,例如续写最多 3 次、网络重试最多 3 次。
-
状态机主循环在固定状态间切换:正常执行 → 续写恢复 → 压缩恢复 → 退避重试 → 最终失败。
-
恢复决策根据错误类型,决定走哪条恢复路径:继续、压缩、退避、失败。
-
续写提示模型输出截断时,专用提示词让模型直接断点续接,不重复、不重启、不总结。
-
退避重试网络 / 超时 / 限流错误,等待一小段时间再重试,避免瞬间反复请求加重故障。
这一章到底要解决什么问题?
到 S10,Agent 已经是一个真正能干活的系统,但错误处理几乎为零:
- 模型输出写到一半被截断 → 直接卡住
- 上下文太长装不进窗口 → 直接报错
- 网络抖动 / API 超时 / 限流 → 直接中断
没有错误恢复的系统,只能在 “完美环境” 里运行。而现实是:错误一定会发生。
本章目标只有一个:把 **“报错就崩”升级成“先判断错误类型 → 走对应恢复路径 → 继续执行”**。
最小心智模型:3 类错误 → 3 条恢复路径
LLM 调用发起
│
┌───┴───────────────────────────────────
│ 1. 输出截断 (max_tokens)
│ → 注入续写提示 → 重试
│
│ 2. 上下文过长 (prompt too long)
│ → 压缩旧上下文 → 重试
│
│ 3. 临时故障 (超时/限流/网络抖动)
│ → 退避等待 → 重试
└───────────────────────────────────────
│
无法恢复 → 明确失败
一句话:错误先分类,恢复再执行,失败最后才暴露给用户。
最推荐的 3 条基础恢复路径
1. 输出截断恢复(continue)
- 触发:模型输出被
max_tokens截断 - 动作:追加续写提示,让模型从中断点直接继续
- 约束:最多重试 3 次
2. 上下文过长恢复(compact)
- 触发:提示词超出模型窗口长度
- 动作:用 S06 压缩机制,把历史对话变成摘要
- 目标:保留任务核心,扔掉冗余,让请求能重新发出去
3. 临时网络故障恢复(backoff)
- 触发:超时、限流、服务不可用、连接错误
- 动作:等待一小段时间(指数退避),再重试
- 目标:避开瞬时拥堵,提高成功率
核心数据结构(本章灵魂)
1. 恢复状态(防无限循环)
recovery_state = {
"continuation_attempts": 0, # 续写次数
"compact_attempts": 0, # 压缩重试次数
"transport_attempts": 0 # 网络故障次数
}
2. 恢复决策(统一判断)
{
"kind": "continue" | "compact" | "backoff" | "fail",
"reason": "错误原因说明"
}
3. 标准续写提示(避免重复)
CONTINUE_MESSAGE = (
"Output limit hit. Continue directly from where you stopped. "
"Do not restart or repeat."
)
最小实现代码(极简可运行)
第一步:错误分类选择器
def choose_recovery(stop_reason: str | None, error_text: str | None) -> dict:
# 1. 输出截断
if stop_reason == "max_tokens":
return {"kind": "continue", "reason": "output truncated"}
# 2. 上下文过长
if error_text and "prompt" in error_text and "long" in error_text:
return {"kind": "compact", "reason": "context too large"}
# 3. 临时网络/服务故障
if error_text and any(word in error_text for word in [
"timeout", "rate", "unavailable", "connection"
]):
return {"kind": "backoff", "reason": "transient failure"}
# 无法恢复
return {"kind": "fail", "reason": "non-recoverable error"}
第二步:接入主循环
while True:
try:
response = client.messages.create(...)
decision = choose_recovery(response.stop_reason, None)
except Exception as e:
response = None
decision = choose_recovery(None, str(e).lower())
# 续写恢复
if decision["kind"] == "continue":
messages.append({"role": "user", "content": CONTINUE_MESSAGE})
continue
# 压缩恢复
if decision["kind"] == "compact":
messages = auto_compact(messages)
continue
# 退避重试
if decision["kind"] == "backoff":
time.sleep(backoff_delay(attempt))
continue
# 彻底失败
if decision["kind"] == "fail":
break
三条恢复路径分别在解决什么
1. 输出截断 → 续写
模型没说完,只是空间不够。追加续写提示,直接断点继续,不重复、不重启。
2. 上下文太长 → 压缩
不是任务失败,只是历史太占空间。压缩成摘要,保留任务核心,轻量重启。
3. 网络 / 限流 → 退避
不是系统坏了,只是拥堵。等一下再试,避开高峰期,大幅提高成功率。
初学者最容易踩的 5 个坑
-
把所有错误混为一谈该续写的去压缩,该等待的去重试,逻辑完全混乱。
-
没有重试预算不限制次数,系统会进入无限死循环。
-
续写提示太模糊只写 “continue”,模型会重复、总结、重启。
-
压缩后不告诉模型是续场模型以为是新对话,重新提问,任务断裂。
-
恢复过程无日志开发者看不见系统在做什么,出问题无法排查。
S10 → S11 升级了什么?
| 模块 | S10 | S11 |
|---|---|---|
| 错误处理 | 遇到错误直接崩溃 | 错误变成正常流程分支 |
| 稳定性 | 只能在完美环境运行 | 可抗截断、超限、网络抖动 |
| 主循环 | 执行 → 工具 | 执行 → 错误判断 → 恢复 → 工具 |
| 可靠性 | 低,随时可能中断 | 高,具备生产级韧性 |
| 架构地位 | 输入结构化 | 系统韧性加固层 |
本章教学边界
本章不讲:
- 复杂的分布式错误恢复
- 全量异常类型捕获
- 持久化断点恢复
- 多级降级策略
只牢牢守住一条主线:错误不是例外,是主循环必须预留的正常分支;先恢复,再继续。
一句话总结本章
错误恢复不是给系统打补丁,而是给主循环装上安全网:先分类错误,再走对应恢复路径,有限重试,无法恢复才明确失败。让 Agent 从 “能跑”,真正变成 “稳定可靠、能干活的生产级系统”。
372

被折叠的 条评论
为什么被折叠?



