- 当前提供商内的凭证配置轮换。
- 模型回退到
agents.defaults.model.fallbacks中的下一个模型。
运行时流程
这有意比“保存并恢复整个会话”更窄。回复运行器只持久化它为回退拥有的模型选择字段:
providerOverride、modelOverride、modelOverrideSource、authProfileOverride、authProfileOverrideSource、authProfileOverrideCompactionCount。这样可以防止失败的回退重试覆盖较新的无关会话变更,例如在尝试运行期间发生的手动 /model 变更或会话轮换更新。
选择来源策略
选择来源控制是否允许回退链:- 已配置默认值:
agents.defaults.model.primary使用agents.defaults.model.fallbacks。 - 智能体主模型:
agents.list[].model是严格的,除非该智能体的模型对象包含自己的fallbacks。使用fallbacks: []可显式声明严格行为,或使用非空列表让该智能体选择加入模型回退。 - 自动回退覆盖:运行时回退会在重试前写入
providerOverride、modelOverride、modelOverrideSource: "auto"和所选源模型。此覆盖会继续沿着已配置的回退链前进,而不会在每条消息上探测主模型,但 OpenClaw 每 5 分钟(不可配置)探测一次已配置源模型,并在其恢复后清除覆盖。/new、/reset和sessions.reset也会清除自动来源的覆盖。未显式设置heartbeat.model的 Heartbeat 运行会在其源模型不再匹配当前已配置默认值时清除直接自动覆盖。 - 用户会话覆盖:
/model、模型选择器、session_status(model=...)和sessions.patch会写入modelOverrideSource: "user"。这是精确的会话选择。如果所选提供商/模型在生成回复前失败,OpenClaw 会报告失败,而不是从无关的已配置回退中回答。 - 旧版会话覆盖:较旧的会话条目可能有
modelOverride但没有modelOverrideSource。OpenClaw 将其视为用户覆盖,因此显式旧选择不会被静默转换为回退行为。 - Cron 载荷模型:cron 作业
payload.model/--model是作业主模型,不是用户会话覆盖。除非作业提供payload.fallbacks,否则它使用已配置回退;payload.fallbacks: []会让 cron 运行变为严格模式。
凭证失败跳过缓存
默认情况下,每个新轮次会保留现有回退重试行为:OpenClaw 会再次重试每个已配置回退候选,包括最近因auth 或 auth_permanent 失败的非主候选。
选择启用重复凭证失败抑制:
0 或未设置会禁用缓存。正值会被限制在 1 秒到 10 分钟之间。
用户可见的回退通知
当会话移动到自动选择的回退时,OpenClaw 会在同一回复界面发送状态通知:凭证存储(密钥 + OAuth)
OpenClaw 对 API 密钥和 OAuth 令牌都使用凭证配置。- 密钥和运行时凭证路由状态位于
~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite。 - 配置
auth.profiles/auth.order仅是元数据 + 路由(不含密钥)。 - 旧版仅导入 OAuth 文件:
~/.openclaw/credentials/oauth.json(首次使用时导入到每智能体凭证存储)。 - 旧版
auth-profiles.json、auth-state.json和每智能体auth.json文件会由openclaw doctor --fix导入。
type: "api_key"→{ provider, key }type: "oauth"→{ provider, access, refresh, expires, email? }(某些提供商还包括projectId/enterpriseUrl)type: "token"→ 静态 bearer 风格令牌,可选过期时间;OpenClaw 不会刷新它(用于aws-sdk和其他凭据链凭证模式)
配置 ID
OAuth 登录会创建不同配置,以便多个账号可以共存。- 默认:没有可用 email 时为
provider:default。 - 带 email 的 OAuth:
provider:<email>(例如google-antigravity:user@gmail.com)。
openclaw-agent.sqlite 凭证配置存储中。
轮换顺序
当提供商有多个配置时,OpenClaw 会按如下顺序选择: 如果未配置显式顺序,OpenClaw 会使用轮询顺序:- **主键:**配置类型(OAuth,然后静态令牌,然后 API 密钥)。
- 次键:
usageStats.lastUsed(每种类型内最旧的优先)。 - 冷却/禁用配置会移到末尾,并按最早到期时间排序。
会话粘性(缓存友好)
OpenClaw 会按会话固定所选凭证配置,以保持提供商缓存预热。它不会在每个请求上轮换。固定配置会一直复用,直到:- 会话被重置(
/new//reset) - 压缩完成(压缩计数递增)
- 配置处于冷却/禁用状态
/model …@<profileId> 进行手动选择会为该会话设置用户覆盖,并且在新会话开始前不会自动轮换。
自动固定配置(由会话路由器选择)会被视为一种偏好:它们会先尝试,但 OpenClaw 可能会在速率限制/超时时轮换到另一个配置。当原始配置再次可用时,新的运行可以再次偏好它,而无需更改所选模型或运行时。用户固定配置会锁定到该配置;如果它失败且已配置模型回退,OpenClaw 会移动到下一个模型,而不是切换配置。
OpenAI Codex 订阅加 API 密钥备份
对于 OpenAI 智能体模型,凭证和运行时是分离的。openai/gpt-* 保持在 Codex harness 上,而凭证可以在 Codex 订阅配置和 OpenAI API 密钥备份之间轮换。
使用 auth.order.openai 设置面向用户的顺序:
openai:*。当订阅触达 Codex 使用限制时,如果 Codex 提供确切重置时间,OpenClaw 会记录该时间,尝试下一个有序凭证配置,并让运行保持在 Codex harness 内。重置时间过去后,订阅配置会再次符合条件,下一次自动选择可以返回它。
只有当你想强制该会话使用某个账号/密钥时,才使用用户固定配置。用户固定配置有意保持严格,不会静默跳转到另一个配置。
冷却
当配置因凭证/速率限制错误(或看起来像速率限制的超时)而失败时,OpenClaw 会将其标记为冷却并移动到下一个配置。进入速率限制 / 超时桶的内容
进入速率限制 / 超时桶的内容
该速率限制桶比单纯的
429 更宽:它还包括提供商消息,例如 Too many concurrent requests、ThrottlingException、concurrency limit reached、workers_ai ... quota limit exceeded、throttled、resource exhausted,以及周期性使用窗口限制,例如 weekly limit reached 或 monthly limit exhausted。格式/无效请求错误通常是终止性的,因为重试相同载荷会以相同方式失败,所以 OpenClaw 会显露这些错误,而不是轮换凭证配置。已知的重试修复路径可以显式选择加入:例如 Cloud Code Assist 工具调用 ID 校验失败会被清理并通过 allowFormatRetry 策略重试一次。OpenAI 兼容的停止原因错误,例如 Unhandled stop reason: error、stop reason: error 和 reason: error,会被归类为超时/故障转移信号。当来源匹配已知瞬时模式时,通用服务器文本也可以进入该超时桶。例如,裸模型运行时流包装器消息 An unknown error occurred 会被视为对每个提供商都值得故障转移,因为共享模型运行时会在提供商流以 stopReason: "aborted" 或 stopReason: "error" 结束且没有具体详情时发出它。包含瞬时服务器文本的 JSON api_error 载荷,例如 internal server error、unknown error, 520、upstream error 或 backend error,也会被视为值得故障转移的超时。OpenRouter 专用的通用上游文本,例如裸 Provider returned error,只有在提供商上下文确实是 OpenRouter 时才会被视为超时。通用内部回退文本,例如 LLM request failed with an unknown error.,会保持保守,单独不会触发故障转移。SDK retry-after 上限
SDK retry-after 上限
否则,一些提供商 SDK 可能会先休眠一个很长的
Retry-After 窗口,才把控制权交还给 OpenClaw。对于基于 Stainless 的 SDK,例如 Anthropic 和 OpenAI,OpenClaw 默认会将 SDK 内部的 retry-after-ms / retry-after 等待限制为 60 秒,并立即暴露更长的可重试响应,以便这条故障转移路径可以运行。使用 OPENCLAW_SDK_RETRY_MAX_WAIT_SECONDS 调整或禁用该上限;参见重试行为。按模型限定的冷却
按模型限定的冷却
速率限制冷却也可以按模型限定:
- 当失败的模型 id 已知时,OpenClaw 会为速率限制失败记录
cooldownModel。 - 当冷却限定到不同模型时,仍可以尝试同一提供商上的同级模型。
- 账单/禁用窗口仍会跨模型阻止整个配置档。
- 第 1 次失败:30 秒
- 第 2 次失败:1 分钟
- 第 3 次及以后失败:5 分钟(上限)
auth.cooldowns.failureWindowHours,默认 24)。
状态存储在按智能体的 SQLite 凭证状态中的 usageStats 下:
账单禁用
账单/额度失败(例如 “insufficient credits” / “credit balance too low”)会被视为值得故障转移,但它们通常不是暂时性的。OpenClaw 不会使用短冷却,而是将该配置档标记为禁用(并使用更长的退避),然后轮换到下一个配置档/提供商。并非每个形似账单的响应都是
402,也并非每个 HTTP 402 都会进入这里。即使提供商返回的是 401 或 403,OpenClaw 也会把明确的账单文本保留在账单通道中,但特定提供商的匹配器仍限定在拥有它们的提供商范围内(例如 OpenRouter 403 Key limit exceeded)。同时,临时的 402 使用窗口以及组织/工作区支出限制错误,会在消息看起来可重试时被归类为 rate_limit(例如 weekly usage limit exhausted、daily limit reached, resets tomorrow,或 organization spending limit exceeded)。这些错误会留在短冷却/故障转移路径上,而不是进入较长的账单禁用路径。auth.cooldowns.*):
| 键 | 默认值 | 用途 |
|---|---|---|
billingBackoffHours | 5 | 基础账单退避,每次账单失败后翻倍 |
billingMaxHours | 24 | 账单退避上限 |
authPermanentBackoffMinutes | 10 | 高置信度永久凭证失败的基础退避 |
authPermanentMaxMinutes | 60 | 该退避的上限 |
failureWindowHours | 24 | 如果此窗口内没有发生失败,则重置失败计数器 |
overloadedProfileRotations | 1 | 过载时,在模型回退之前允许的同提供商配置档轮换次数 |
overloadedBackoffMs | 0 | 过载轮换重试前的固定延迟 |
rateLimitedProfileRotations | 1 | 速率限制时,在模型回退之前允许的同提供商配置档轮换次数 |
模型回退
如果某个提供商的所有配置档都失败,OpenClaw 会移动到agents.defaults.model.fallbacks 中的下一个模型。这适用于已经耗尽配置档轮换的凭证失败、速率限制和超时(其他错误不会推进回退)。没有暴露足够细节的提供商错误仍会在回退状态中被精确标记:empty_response 表示提供商没有返回可用消息或状态,no_error_details 表示提供商明确返回了 Unknown error (no error details in response),unclassified 表示 OpenClaw 保留了原始预览,但还没有分类器匹配到它。
ModelNotReadyException 等提供商繁忙信号会进入过载类别,并遵循与速率限制相同的一次轮换后回退策略(参见上面的默认值表)。
当一次运行从已配置的默认主模型、cron 任务主模型、带显式回退的智能体主模型,或自动选择的回退覆盖开始时,OpenClaw 可以遍历匹配的已配置回退链。没有显式回退的智能体主模型和显式用户选择(例如 /model ollama/qwen3.5:27b、模型选择器、sessions.patch,或一次性 CLI 提供商/模型覆盖)是严格的:如果该提供商/模型不可达,或在生成回复前失败,OpenClaw 会报告失败,而不是从不相关的回退中回答。
候选链规则
OpenClaw 会从当前请求的provider/model 加上已配置的回退构建候选列表。
规则
规则
- 请求的模型始终排在第一位。
- 显式配置的回退会去重,但不会被模型允许列表过滤。它们会被视为显式的操作员意图。
- 如果当前运行已经位于同一提供商系列中的某个已配置回退上,OpenClaw 会继续使用完整的已配置链。
- 当未提供显式回退覆盖时,即使请求的模型使用不同提供商,也会先尝试已配置的回退,再尝试已配置的主模型。
- 当未向回退运行器提供显式回退覆盖时,已配置的主模型会追加到末尾,这样链条在前面的候选都耗尽后可以回到正常默认值。
- 当调用方提供
fallbacksOverride时,运行器会严格使用请求的模型加上该覆盖列表。空列表会禁用模型回退,并防止已配置的主模型作为隐藏重试目标被追加。
哪些错误会推进回退
- 继续于
- 不继续于
- 凭证失败
- 速率限制和冷却耗尽
- 过载/提供商繁忙错误
- 形似超时的故障转移错误
- 账单禁用
LiveSessionModelSwitchError,它会被规范化为故障转移路径,避免陈旧的持久化模型创建外层重试循环- 当仍有剩余候选时的其他未识别错误
冷却跳过与探测行为
当某个提供商的每个凭证配置档都已处于冷却中时,OpenClaw 不会自动永远跳过该提供商。它会按候选做出决策:按候选决策
按候选决策
- 持久凭证失败会立即跳过整个提供商。
- 账单禁用通常会跳过,但主候选仍可以按节流探测,这样无需重启也可能恢复。
- 主候选可以在接近冷却到期时按每提供商节流进行探测。
- 当失败看起来是暂时性的(
rate_limit、overloaded或未知)时,即使处于冷却中,也可以尝试同提供商的回退同级项。当速率限制按模型限定且同级模型可能仍可立即恢复时,这一点尤其相关。 - 暂时性冷却探测限制为每次回退运行中每个提供商一次,这样单个提供商不会阻塞跨提供商回退。
会话覆盖和实时模型切换
会话模型变更是共享状态。活动运行器、/model 命令、压缩/会话更新以及实时会话协调都会读取或写入同一会话条目的部分内容。
这意味着回退重试必须与实时模型切换协调:
- 只有显式的用户驱动模型变更才会标记待处理实时切换。这包括
/model、session_status(model=...)和sessions.patch。 - 系统驱动的模型变更,例如回退轮换、Heartbeat 覆盖或压缩,本身永远不会标记待处理实时切换。
- 用户驱动的模型覆盖会被视为回退策略的精确选择,因此不可达的已选提供商会暴露为失败,而不是被
agents.defaults.model.fallbacks掩盖。 - 在回退重试开始之前,回复运行器会将选中的回退覆盖字段持久化到会话条目中。
- 自动回退覆盖会在后续轮次保持选中,这样 OpenClaw 不会在每条消息上探测已知有问题的主模型。OpenClaw 会定期再次探测已配置的来源,并在其恢复时清除自动覆盖;
/new、/reset和sessions.reset会立即清除自动来源的覆盖。 - 用户回复会在每次状态变更时宣布回退转换和已清除回退的恢复。粘性回退轮次不会重复通知。
/status会显示选中的模型,并且当回退状态不同时,显示活动回退模型和原因。- 实时会话协调会优先使用已持久化的会话覆盖,而不是陈旧的运行时模型字段。
- 如果实时切换错误指向活动回退链中的后续候选,OpenClaw 会直接跳转到该选中模型,而不是先遍历不相关的候选。
- 如果回退尝试失败,运行器只会回滚它写入的覆盖字段,并且仅当这些字段仍匹配该失败候选时才回滚。
持久化的回退覆盖关闭了这个窗口,而窄范围回滚会保持更新的手动或运行时会话变更不受影响。
可观测性和失败摘要
runWithModelFallback(...) 会记录每次尝试的细节,这些细节会进入日志和面向用户的冷却消息:
- 已尝试的提供商/模型
- 原因(
rate_limit、overloaded、billing、auth、model_not_found以及类似的故障转移原因) - 可选状态/代码
- 人类可读的错误摘要
model_fallback_decision 日志也会包含扁平的 fallbackStep* 字段。这些字段会明确已尝试的转换(fallbackStepFromModel、fallbackStepToModel、fallbackStepFromFailureReason、fallbackStepFromFailureDetail、fallbackStepFinalOutcome),让日志和诊断导出器即使在最终故障转移也失败时,也能重建主要失败原因。
当所有候选项都失败时,OpenClaw 会抛出 FallbackSummaryError。外层回复运行器可以用它构建更具体的消息,例如“所有模型都暂时受到速率限制”,并在已知时包含最早的冷却到期时间。
该冷却摘要会感知模型:
- 对于已尝试的提供商/模型链,会忽略无关的模型级速率限制
- 如果剩余阻塞是匹配的模型级速率限制,OpenClaw 会报告仍阻塞该模型的最后一个匹配到期时间
相关配置
参见 Gateway 配置 了解:auth.profiles/auth.orderauth.cooldowns.billingBackoffHours/auth.cooldowns.billingBackoffHoursByProviderauth.cooldowns.billingMaxHours/auth.cooldowns.failureWindowHoursauth.cooldowns.authPermanentBackoffMinutes/auth.cooldowns.authPermanentMaxMinutesauth.cooldowns.overloadedProfileRotations/auth.cooldowns.overloadedBackoffMsauth.cooldowns.rateLimitedProfileRotationsagents.defaults.model.primary/agents.defaults.model.fallbacksagents.defaults.imageModel路由