Failover-Aware Model Fallback
Clawdbot Contributors· validated-in-production
问题
AI模型请求失败的原因多种多样,且通常难以明确。简单的重试逻辑无法区分以下三类情况:
- 临时故障(超时、速率限制):此类故障通过带退避机制的重试可得到解决
- 语义故障(认证错误、计费问题):此类故障重试毫无意义
- 用户终止请求:此类场景下重试会浪费资源,还会引发用户不满
使用多模型或多服务商的Agent需要智能回退策略,既要契合故障的语义属性,避免陷入重试循环,又要提供清晰的诊断信息。
方案
基于智能回退链的语义错误分类 每一次失败都会被归类为特定的原因类型,回退行为会根据该原因量身定制。多模型回退链按请求配置,同时支持供应商专属白名单和冷却跟踪。
核心概念:
- 错误分类:将失败映射为语义原因类型(
timeout[超时]、rate_limit[速率限制]、auth[认证]、billing[计费]、format[格式]、context_overflow[上下文溢出])。 - 基于原因的回退:不同原因触发不同的回退行为:
timeout、rate_limit:使用链中的下一个模型重试auth、billing:立即终止;重试无济于事format、context_overflow:可调整请求后重试
- 用户终止检测:区分用户主动终止与超时导致的终止。用户主动终止会立即重新抛出异常;超时则触发回退。
- 多模型链:
{供应商, 模型}候选者的有序列表。每一次尝试都会使用下一个候选者,直到成功或候选者耗尽。 - 供应商白名单:可选的供应商专属模型限制可避免回退到不兼容的模型。
- 诊断跟踪:每一次失败尝试都会记录错误详情、原因、状态码以及供应商/模型信息,以便调试。
实现概要:
// 回退原因类型
type FailoverReason =
| "timeout"
| "rate_limit"
| "auth"
| "billing"
| "format"
| "context_overflow";
// 模型候选者
type ModelCandidate = {
provider: string;
model: string;
};
/**
* 执行带模型回退的任务
* @param params 配置参数
* @returns 包含执行结果、使用的供应商/模型以及尝试记录的对象
*/
async function runWithModelFallback<T>(params: {
candidates: ModelCandidate[];
run: (provider: string, model: string) => Promise<T>;
}): Promise<{ result: T; provider: string; model: string; attempts: Attempt[] }> {
const attempts: Attempt[] = [];
for (const candidate of params.candidates) {
try {
const result = await params.run(candidate.provider, candidate.model);
return { result, provider: candidate.provider, model: candidate.model, attempts };
} catch (err) {
// 分类回退原因
const reason = classifyFailoverReason(err);
// 认证或计费错误直接抛出,重试无效
if (reason === "auth" || reason === "billing") {
throw err;
}
// 用户主动终止则直接抛出,不触发回退
if (isUserAbort(err)) {
throw err;
}
// 记录本次失败尝试
attempts.push({ provider: candidate.provider, model: candidate.model, error: err, reason });
// 继续尝试下一个候选模型
}
}
throw new Error(`所有模型执行失败: ${attempts.map(a => a.error).join(" | ")}`);
}
/**
* 分类回退原因
* @param err 捕获到的异常
* @returns 回退原因或null
*/
function classifyFailoverReason(err: unknown): FailoverReason | null {
const status = getStatusCode(err);
if (status === 402) return "billing";
if (status === 429) return "rate_limit";
if (status === 401 || status === 403) return "auth";
if (status === 408) return "timeout";
const message = getErrorMessage(err).toLowerCase();
if (message.includes("timeout") || message.includes("timed out")) return "timeout";
if (message.includes("rate limit") || message.includes("too many requests")) return "rate_limit";
if (message.includes("context window") || message.includes("context length")) return "context_overflow";
return null;
}
用户终止与超时的区分:
/**
* 判断是否为用户主动终止
* @param err 捕获到的异常
* @returns 是否为用户主动终止
*/
function isUserAbort(err: unknown): boolean {
// 仅将明确的AbortError判定为用户主动终止
// 基于消息的检查(如"aborted")可能会误判超时
if (!err || typeof err !== "object") return false;
const name = "name" in err ? String(err.name) : "";
return name === "AbortError" && !isTimeoutError(err);
}
配置示例:
agents:
defaults:
model:
primary: "anthropic/claude-sonnet-4-20250514" # 主模型
fallbacks:
- "openai/gpt-4o" # 第一回退模型
- "google/gemini-2.0-flash" # 第二回退模型
如何使用
- 定义回退链:针对不同用例(代码生成vs通用对话)指定备选模型的有序列表。
- 配置白名单:将回退范围限制为支持请求格式的模型(例如仅图像模型)。
- 错误分类:将供应商特定的错误代码映射为语义化原因,实现一致性处理。
- 追踪尝试过程:记录每次回退尝试的供应商、模型、错误信息及原因,提升可观测性。
- 处理耗尽场景:当所有候选模型均失败时,汇总错误信息以提供可执行的反馈。
需规避的陷阱:
- 过度回退:过多回退链可能导致故障在供应商间连锁扩散,应采用指数退避策略。
- 语义不匹配:回退模型可能具备不同的能力(视觉、工具调用),需按所需功能进行筛选。
- 静默故障:部分错误(如格式类错误)表明请求不兼容,回退操作可能会以相同方式失败。
权衡
优点:
- 韧性强:瞬时故障(如超时、速率限制)不会导致Agent阻塞。
- 成本优化:当高端模型不可用时,可降级至更便宜的模型。
- 诊断清晰:尝试历史可展示哪些模型失败及具体失败原因。
- 尊重用户终止操作:可区分用户取消与超时场景,避免不必要的降级操作。
缺点/注意事项:
- 延迟损耗:每次失败尝试都会在成功前增加往返耗时。
- 输出不一致:不同模型的响应可能存在差异,会影响下游解析环节。
- 成本累积:针对单个逻辑请求,降级链可能会产生多次API费用。
- 配置复杂:管理白名单、降级链以及供应商特定行为会增加运维开销。
参考文献
关键词:
:涵盖Clawdbot项目中模型降级编排、错误分类、错误原因判定三类核心故障处理代码文件,以及关联的扩展一致性工作会话可靠性模式参考文档。
直译:
- Clawdbot model-fallback.ts - 模型降级编排
- Clawdbot failover-error.ts - 错误分类
- Clawdbot 错误辅助工具 - 原因分类逻辑
- 相关内容:扩展一致性工作会话——用于可靠性模式
来源摘要
正在获取来源并生成中文摘要…
来源: https://github.com/clawdbot/clawdbot/blob/main/src/agents/model-fallback.ts