欢迎光临
我们一直在努力

AG-26_错误处理与自动恢复机制

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 错误的分类

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. 重试策略:指数退避与抖动

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 硬降级

4. 降级策略:优雅降级 vs 硬降级

当重试无法解决问题时,降级(Fallback)成为维持系统可用性的关键手段。

4.1 优雅降级(Graceful Degradation)

优雅降级的核心思想是:在核心功能不可用时,提供一个"足够好"的替代方案。

在 Claude Code 中,典型的优雅降级场景包括:

  • 模型降级:主模型不可用时,切换到备用模型(如 Claude Opus → Claude Sonnet)
  • 工具降级:某个工具调用失败时,尝试替代工具或回退到纯文本推理
  • 功能降级:复杂分析不可用时,提供简化版本的输出
  • /**
    * 优雅降级管理器
    *
    * 设计模式: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 通过以下机制保障幂等性:

  • 操作 ID 去重:每个操作分配唯一 ID,重复执行时检测并跳过
  • 状态快照比对:执行前检查当前状态,避免不必要的重复操作
  • 乐观锁:使用版本号机制,防止并发冲突

  • 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 的错误处理是一个系统工程,涉及多个层次的协同:

  • 分类是前提:不同类型的错误需要不同的处理策略,盲目重试是最大的反模式
  • 重试要聪明:指数退避 + 抖动是基础,但更重要的是知道何时不重试
  • 降级要分层:优雅降级保持功能可用,硬降级保障系统存活
  • 恢复要自动化:检查点恢复和补偿操作让 Agent 能够"自愈"
  • 可观测性是眼睛:没有可观测性的错误处理是"盲人摸象"
  • 正如 Nygard 所言,系统的可用性取决于其最脆弱的环节。对于 AI Agent 来说,错误处理不是附加功能,而是核心竞争力。


    参考资料

  • Nygard, M. (2018). Release It! Design and Deploy Production-Ready Software (2nd ed.). Pragmatic Bookshelf.
  • Brooker, M. (2015). “Exponential Backoff and Jitter.” AWS Architecture Blog. https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
  • Anthropic. (2025). Claude Code Source Code Analysis — Error handling modules. https://github.com/anthropics/claude-code
  • Richardson, C. (2018). Microservices Patterns. Manning Publications. (Chapter 4: Managing transactions with the Saga pattern)
  • Fowler, M. (2017). “Circuit Breaker.” https://martinfowler.com/bliki/CircuitBreaker.html

  • 本系列覆盖 AI 大模型基础、Agent 开发、MCP 协议、Skill 开发、RAG、模型微调、部署推理 七大方向,从入门到实战的全栈内容持续更新中。

    所有文章的 Markdown 源文件、可运行代码、高清配图已整理成完整资料包。

    👍 点赞 + ⭐ 关注,评论区扣「1」,挨个发你领取方式 👇

    赞(0)
    未经允许不得转载:171主机测评 » AG-26_错误处理与自动恢复机制
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址