run.ts 下篇 —— OpenClaw故障转移、重试策略与结果封装
关键词:Failover|多级重试|错误分类|幂等性|结果标准化
在上一篇中,我们探讨了 run.ts 如何通过模型调度、账号轮询与上下文压缩构建第一道防线。然而,在真实生产环境中,失败是常态——网络抖动、模型返回乱码、工具执行超时……系统必须具备智能的故障转移(Failover)能力。
本文将深入 run.ts 的下半部分,解析其如何:
- 精准分类错误类型
- 实施多级重试与降级
- 保证结果一致性与可观测性
这一切共同构成了 OpenClaw “永不放弃,但知道何时止损” 的韧性哲学。
一、错误分类:不是所有失败都值得重试
run.ts 的核心思想是:不同错误需要不同应对策略。它通过 categorizeError() 函数对异常进行语义化分类:
enum ErrorCategory {
TRANSIENT, // 可重试(如网络超时)
RATE_LIMITED, // 需冷却(如 API 限流)
CONTEXT_OVERFLOW, // 需压缩上下文
MODEL_MISBEHAVIOR, // 模型返回无效格式(如缺失 tool_call)
PERMANENT // 不可恢复(如无效 API Key)
}
分类逻辑示例

精准分类是智能 Failover 的前提。
二、多级故障转移策略:从“微调”到“彻底切换”
OpenClaw 的 Failover 不是一次性跳转,而是分阶段尝试,优先保留原始意图:
阶段 1:同模型,换账号
- 适用于
RATE_LIMITED或TRANSIENT - 从同一提供商(如 OpenAI)的其他健康账号中选择
- 优势:模型行为一致,用户体验无缝
阶段 2:换模型,同任务
- 适用于
CONTEXT_OVERFLOW(目标模型不支持长上下文)或MODEL_MISBEHAVIOR - 切换到配置中的下一优先级模型(如 Claude → GPT-4o)
- 注意:若新模型不支持
/think,自动移除相关指令
阶段 3:简化请求,保底回复
- 若所有模型均失败,进入“安全模式”:
- 移除所有工具调用权限
- 仅使用最简提示词
- 返回友好错误:“我暂时无法执行操作,但可以回答问题”
转移不是逃避,而是策略性迂回。
三、重试控制:避免雪崩与无限循环
为防止重试引发资源耗尽,run.ts 实施严格限制:
1. 全局重试上限
const MAX_TOTAL_RETRIES = 3; // 整个 run 最多重试 3 次
2. 每阶段重试限制
- 同模型换账号:最多 2 次
- 换模型:最多 2 个备选
- 上下文压缩:仅尝试 1 次(避免反复压缩失真)
3. 指数退避(Exponential Backoff)
- 首次重试:立即
- 第二次:等待 1 秒
- 第三次:等待 3 秒
- (防止对限流接口持续冲击)
重试是手段,不是目的——超过阈值即终止。
四、结果封装:标准化输出,统一客户端体验
无论经历多少次 Failover,run.ts 最终返回一个结构化结果对象:
interface AgentRunResult {
status: 'success' | 'partial' | 'failed';
finalResponse: string; // 给用户的最终回复
usedModel: string; // 实际使用的模型 ID
tokenUsage: { prompt: number, completion: number };
errorSummary?: string; // 若失败,提供人类可读摘要
debugTrace: RunTrace[]; // 用于日志分析(脱敏后)
}
关键设计点:
status字段:告知客户端是否完整完成任务partial:例如“工具执行失败,但已给出建议”
errorSummary:不暴露技术细节(如 429),而是说“当前服务繁忙,请稍后再试”debugTrace:记录每一步尝试(模型、错误、耗时),供 SRE 排查
结果封装是“内部复杂,外部简单”的体现。
五、幂等性保障:防止重复执行
用户网络不稳定时可能重复发送请求。run.ts 通过 idempotencyKey 避免副作用:
if (request.idempotencyKey) {
const cached = await cache.get(request.idempotencyKey);
if (cached) return cached; // 直接返回历史结果
}
- 缓存有效期:5 分钟
- 仅缓存无副作用的操作(纯问答)
- 若涉及
exec等工具调用,则拒绝重复提交
安全第一:宁可让用户重发,也不让 AI 重复删库。
六、实战案例:一次完整的 Failover 旅程
用户请求:
“分析昨天的日志,找出 5xx 错误最多的接口”
系统流程:
- 首次尝试:Claude + Team-A Key
→ 返回429 Rate Limit
→ 分类:RATE_LIMITED - 阶段 1:切换至 Team-B Key(同 Claude)
→ 网络超时(TimeoutError)
→ 分类:TRANSIENT - 阶段 2:降级至 GPT-4o + Primary Key
→ 成功执行bash_exec("grep '500' /logs/app.log")
→ 返回结构化分析 - 结果封装:
{ "status": "success", "finalResponse": "共发现 127 次 5xx 错误,/api/v1/payment 最频繁...", "usedModel": "gpt-4o", "tokenUsage": { "prompt": 4200, "completion": 320 } }
用户全程无感知,仅看到一条正确回复。
结语:Failover 是艺术,不是蛮力
run.ts 的故障处理逻辑,体现了 OpenClaw 的工程信条:
- 不盲目重试,而是基于语义分类决策;
- 不隐藏失败,而是转化为用户可理解的反馈;
- 不牺牲安全,哪怕以降低功能为代价。
这种“有策略的韧性”,正是工业级 AI 系统的核心竞争力。
在下一篇中,我们将转向记忆系统,解析
memory-search.ts如何实现混合检索与配置合并。
下一篇预告:
第 7 篇:记忆系统基石 —— memory-search.ts 中的 RAG 配置解析与合并逻辑
您的 AI 助手,从此由您定义。若感兴趣可以浏览本书其他章节内容:
浙公网安备 33010602011771号