AG-26:错误处理与自动恢复机制
系列:AI Agent 工程化 | 关键词:重试策略、降级、自动恢复、幂等性、可观测性
在分布式系统中,故障不是异常,而是常态。对于 AI Agent 而言,错误处理不仅是防御性编程,更是系统韧性的核心支柱。本文从源码视角剖析 Agent 系统中的错误分类、重试策略、降级机制与自动恢复模式,揭示如何构建一个"打不倒"的 Agent。
1. 前言
2018 年,Michael Nygard 在经典著作 Release It! 中写道:
“The biggest threat to your availability is not hardware failure. It’s your own software.”
这句话在 AI Agent 时代变得更加深刻。Agent 系统面对的错误来源远比传统微服务复杂:LLM API 的不确定性响应、工具调用的超时与失败、上下文窗口的溢出、网络的瞬时抖动……每一个环节都可能成为故障的引爆点。
Claude Code 作为 Anthropic 官方的终端 Agent,其错误处理机制值得深入研究。本文将从源码出发,系统性地拆解 Agent 系统中的错误处理与自动恢复模式。
2. Agent 错误的分类

要处理错误,首先要理解错误。Agent 系统中的错误可以按多个维度进行分类:
2.1 按可恢复性分类
| 瞬态错误(Transient) | 短暂存在,自动恢复 | 网络抖动、API 429 限流 | 重试 + 退避 |
| 持续错误(Persistent) | 需要人工干预 | API Key 失效、磁盘满 | 告警 + 降级 |
| 逻辑错误(Logical) | 语义层面的错误 | 模型幻觉、参数误判 | 补偿 + 重规划 |
| 不可恢复错误(Fatal) | 系统性崩溃 | 核心依赖不可用 | 优雅终止 |
2.2 按错误来源分类
Agent 错误
├── LLM 层错误
│ ├── API 超时(timeout)
│ ├── 限流(429 Too Many Requests)
│ ├── 上下文溢出(context window exceeded)
│ ├── 内容过滤(content policy violation)
│ └── 模型幻觉(hallucination)
├── 工具层错误
│ ├── 工具执行超时
│ ├── 权限不足
│ ├── 资源不存在
│ └── 输出格式异常
├── 系统层错误
│ ├── 内存溢出
│ ├── 磁盘空间不足
│ └── 进程崩溃
└── 网络层错误
├── DNS 解析失败
├── 连接超时
└── TLS 握手失败
在 Claude Code 的源码中,错误处理的设计遵循一个核心原则:every error must be caught, classified, and handled at the appropriate layer。这意味着错误不是简单地向上抛出,而是在最接近错误源的层级进行分类和初步处理。
3. 重试策略:指数退避与抖动

重试是最基础也是最重要的错误恢复手段。但"盲目重试"往往是灾难的开始。Claude Code 的重试机制体现了分布式系统领域多年积累的最佳实践。
3.1 指数退避(Exponential Backoff)
指数退避的核心思想是:每次重试等待的时间指数增长,避免在系统恢复期间施加更大压力。
/**
* 指数退避重试器
*
* 核心设计:
* 1. 基础延迟 × 指数增长因子
* 2. 加入随机抖动防止惊群效应
* 3. 设置最大重试次数和最大延迟上限
*/
class ExponentialBackoffRetrier {
private readonly baseDelay: number = 1000; // 基础延迟 1 秒
private readonly maxDelay: number = 30000; // 最大延迟 30 秒
private readonly maxRetries: number = 5; // 最大重试次数
private readonly jitterFactor: number = 0.5; // 抖动因子
async retry<T>(
fn: () => Promise<T>,
shouldRetry: (error: Error) => boolean // 判断是否值得重试
): Promise<T> {
let lastError: Error | undefined;
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error as Error;
// 关键:不是所有错误都应该重试
if (!shouldRetry(lastError)) {
throw lastError;
}
if (attempt < this.maxRetries) {
const delay = this.calculateDelay(attempt);
console.log(`Retry ${attempt + 1}/${this.maxRetries} after ${delay}ms`);
await this.sleep(delay);
}
}
}
throw lastError;
}
private calculateDelay(attempt: number): number {
// 指数增长:1s, 2s, 4s, 8s, 16s…
const exponentialDelay = this.baseDelay * Math.pow(2, attempt);
// 加入随机抖动:delay ∈ [baseDelay * 2^attempt * 0.5, baseDelay * 2^attempt * 1.5]
const jitter = exponentialDelay * this.jitterFactor * Math.random();
return Math.min(exponentialDelay + jitter, this.maxDelay);
}
}
3.2 抖动策略的三种变体
AWS 的 Architecture Blog 在 2015 年提出了三种抖动策略,Claude Code 的实现综合了其中的"全抖动"(Full Jitter)策略:
Full Jitter: sleep = random(0, base * 2^attempt)
Equal Jitter: sleep = base * 2^attempt / 2 + random(0, base * 2^attempt / 2)
Decorrelated Jitter: sleep = min(cap, random(base, prev_sleep * 3))
Full Jitter 在高竞争场景下表现最优——它最大化了重试时间的分散度,有效降低了"惊群效应"(Thundering Herd)的概率。
3.3 Claude Code 中的 LLM API 重试
Claude Code 对 LLM API 调用的重试策略尤为精细。它不仅考虑 HTTP 状态码,还解析响应体中的错误类型:
- 429 Too Many Requests:指数退避重试,最大等待 60 秒
- 500/502/503:服务端瞬态错误,指数退避重试
- 408 Request Timeout:立即重试(可能是连接层面的超时)
- 400 Bad Request:不重试(请求本身有问题)
- 401/403:不重试(认证/授权问题)
这种分类决策是重试策略的核心——知道什么时候不重试,比知道什么时候重试更重要。
4. 降级策略:优雅降级 vs 硬降级

当重试无法解决问题时,降级(Fallback)成为维持系统可用性的关键手段。
4.1 优雅降级(Graceful Degradation)
优雅降级的核心思想是:在核心功能不可用时,提供一个"足够好"的替代方案。
在 Claude Code 中,典型的优雅降级场景包括:
/**
* 优雅降级管理器
*
* 设计模式:Chain of Responsibility
* 每个降级层级定义了替代方案和降级条件
*/
class GracefulDegradationManager {
// 降级链:按优先级排列的替代方案
private readonly fallbackChain: FallbackLevel[];
constructor(fallbackChain: FallbackLevel[]) {
this.fallbackChain = fallbackChain;
}
async executeWithFallback<T>(
primaryFn: () => Promise<T>,
context: RequestContext
): Promise<DegradedResult<T>> {
// 首先尝试主方案
try {
const result = await primaryFn();
return { result, degraded: false, level: 'primary' };
} catch (primaryError) {
console.warn('Primary execution failed, entering fallback chain');
}
// 逐级尝试降级方案
for (const level of this.fallbackChain) {
try {
console.log(`Attempting fallback level: ${level.name}`);
const result = await level.execute(context);
// 记录降级事件(用于可观测性)
this.metrics.recordDegradation(level.name, context);
return { result, degraded: true, level: level.name };
} catch (fallbackError) {
console.warn(`Fallback ${level.name} also failed, trying next`);
}
}
// 所有降级方案都失败
throw new AllFallbacksExhaustedError('No fallback succeeded');
}
}
// 使用示例:Claude Code 的多模型降级链
const modelFallbackChain: FallbackLevel[] = [
{ name: 'claude-opus', execute: (ctx) => callModel('claude-opus-4', ctx) },
{ name: 'claude-sonnet', execute: (ctx) => callModel('claude-sonnet-4', ctx) },
{ name: 'claude-haiku', execute: (ctx) => callModel('claude-3-5-haiku', ctx) },
];
4.2 硬降级(Hard Degradation)
硬降级是一种更激进的策略:主动关闭非核心功能,集中资源保障核心路径。
在 Agent 系统中,硬降级的典型场景:
- 禁用并行工具调用:在系统高负载时,改为串行执行
- 缩短上下文:在接近上下文窗口限制时,截断历史消息
- 降低采样温度:在需要稳定输出时,降低模型的创造性
4.3 降级策略对比
| 触发条件 | 主方案失败 | 系统资源紧张 |
| 用户体验 | 功能略有降质 | 部分功能不可用 |
| 实现复杂度 | 中(需要替代方案) | 低(关闭功能开关) |
| 适用场景 | 单一功能故障 | 系统级压力 |
| 恢复方式 | 主方案恢复后自动切回 | 资源释放后手动/自动恢复 |
5. 自动恢复:回滚与补偿
当错误已经产生副作用时(如部分写入的文件、已执行的命令),简单的重试不够——需要回滚(Rollback)或补偿(Compensation)。
5.1 Saga 模式
Agent 的多步骤任务执行天然适配 Saga 模式。每个步骤都有对应的补偿操作:
正常流程:Step1 → Step2 → Step3 → Done
失败恢复:Step1 → Step2 → Step3(失败)
← Compensate2
← Compensate1
→ 报告错误
Claude Code 在执行多步骤任务时,维护一个"操作日志"(Operation Log),记录每个可逆操作的执行状态和补偿方法。当后续步骤失败时,按逆序执行补偿操作。
5.2 检查点恢复(Checkpoint Recovery)
对于长时间运行的任务,Claude Code 会定期保存检查点(Checkpoint)。当任务中断时,可以从最近的检查点恢复,而不是从头开始:
/**
* 检查点恢复管理器
*
* 每个任务步骤完成后保存状态快照
* 任务中断后,从最近的有效检查点恢复
*/
class CheckpointManager {
private checkpoints: Map<string, Checkpoint> = new Map();
// 保存检查点
async saveCheckpoint(taskId: string, stepIndex: number, state: TaskState): Promise<void> {
const checkpoint: Checkpoint = {
taskId,
stepIndex,
state: JSON.parse(JSON.stringify(state)), // 深拷贝,防止引用污染
timestamp: Date.now(),
// 保存校验和,用于验证检查点完整性
checksum: this.computeChecksum(state),
};
this.checkpoints.set(taskId, checkpoint);
// 持久化到磁盘
await this.persistToDisk(checkpoint);
}
// 从检查点恢复
async recover(taskId: string): Promise<RecoveryResult> {
const checkpoint = this.checkpoints.get(taskId);
if (!checkpoint) {
return { recovered: false, reason: 'no_checkpoint_found' };
}
// 验证检查点完整性
const currentChecksum = this.computeChecksum(checkpoint.state);
if (currentChecksum !== checkpoint.checksum) {
return { recovered: false, reason: 'checkpoint_corrupted' };
}
return {
recovered: true,
stepIndex: checkpoint.stepIndex,
state: checkpoint.state,
// 告知调用方从哪一步继续
resumeFromStep: checkpoint.stepIndex + 1,
};
}
}
5.3 幂等性保障
自动恢复的前提是幂等性(Idempotency)——同一个操作执行多次,结果与执行一次相同。Claude Code 通过以下机制保障幂等性:
6. 错误处理中间件:完整代码示例
以下是一个完整的错误处理中间件实现,综合了本文讨论的所有模式:
/**
* Agent 错误处理中间件
*
* 职责链模式:
* 1. 错误捕获与分类
* 2. 瞬态错误自动重试
* 3. 持续错误触发降级
* 4. 不可恢复错误优雅终止
* 5. 所有错误记录到可观测性系统
*
* 参考:Claude Code 的错误处理管道设计
*/
interface ErrorContext {
operation: string; // 操作名称
attempt: number; // 当前尝试次数
metadata: Record<string, unknown>; // 上下文元数据
}
type ErrorHandler<T> = (
fn: () => Promise<T>,
ctx: ErrorContext
) => Promise<T>;
function createErrorMiddleware<T>(config: MiddlewareConfig): ErrorHandler<T> {
const retrier = new ExponentialBackoffRetrier(config.retry);
const degradationManager = new GracefulDegradationManager(config.fallbacks);
const metrics = new ErrorMetricsCollector();
return async (fn: () => Promise<T>, ctx: ErrorContext): Promise<T> => {
const startTime = Date.now();
try {
// 第一层:重试(仅对瞬态错误)
const result = await retrier.retry(fn, (error) => {
const category = classifyError(error);
// 只有瞬态错误才值得重试
return category === 'transient';
});
// 记录成功指标
metrics.recordSuccess(ctx.operation, Date.now() – startTime);
return result;
} catch (error) {
const classified = classifyError(error as Error);
const duration = Date.now() – startTime;
// 记录错误指标
metrics.recordError(ctx.operation, classified, duration);
switch (classified) {
case 'transient':
// 重试耗尽 → 触发降级
console.error(`[${ctx.operation}] Retries exhausted, entering degradation`);
return degradationManager.executeWithFallback(fn, ctx);
case 'persistent':
// 持续错误 → 告警 + 降级
await alerting.send({
severity: 'high',
operation: ctx.operation,
error: error,
context: ctx.metadata,
});
return degradationManager.executeWithFallback(fn, ctx);
case 'logical':
// 逻辑错误 → 通知 Agent 进行重规划
throw new Rep lanRequiredError(
`Logical error in ${ctx.operation}: ${(error as Error).message}`,
{ originalError: error, context: ctx }
);
case 'fatal':
// 不可恢复 → 优雅终止
console.error(`[${ctx.operation}] Fatal error, graceful shutdown`);
await gracefulShutdown(ctx.operation, error as Error);
throw error;
}
}
};
}
7. 错误处理的可观测性
错误处理不仅要"处理"错误,还要让人能"看见"错误。Claude Code 的可观测性体系包含三个支柱:
7.1 结构化日志
每条错误日志包含完整的上下文信息:
{
"timestamp": "2025-07-14T10:30:00Z",
"level": "error",
"operation": "tool.execution",
"error": {
"type": "timeout",
"message": "Tool execution exceeded 30s timeout",
"retriable": true,
"attempt": 2,
"maxAttempts": 5
},
"context": {
"toolName": "bash",
"command": "npm install",
"sessionId": "abc-123"
},
"duration_ms": 30042
}
7.2 错误率监控
Claude Code 维护一个滑动窗口计数器,实时计算各类错误的发生率。当错误率超过阈值时,自动触发更激进的降级策略。
7.3 错误链追踪
在 Agent 的多步骤执行中,一个错误可能引发连锁反应。Claude Code 使用因果链(Causal Chain)追踪错误的传播路径,帮助开发者定位根因。
8. 总结
AI Agent 的错误处理是一个系统工程,涉及多个层次的协同:
正如 Nygard 所言,系统的可用性取决于其最脆弱的环节。对于 AI Agent 来说,错误处理不是附加功能,而是核心竞争力。
参考资料
本系列覆盖 AI 大模型基础、Agent 开发、MCP 协议、Skill 开发、RAG、模型微调、部署推理 七大方向,从入门到实战的全栈内容持续更新中。
所有文章的 Markdown 源文件、可运行代码、高清配图已整理成完整资料包。
👍 点赞 + ⭐ 关注,评论区扣「1」,挨个发你领取方式 👇






