OpenClaw 模型供应商怎么接、挂了怎么自动转移:从 provider/model 到 fallback 链

2026-08-17

跑 Agent 的人迟早会撞上同一件事:某个模型供应商限流了、余额没了、或者干脆返回一句语焉不详的 An unknown error occurred,整条对话就卡在那里。手工换模型当然可以,但半夜定时任务跑到一半没人换。

OpenClaw 的官方文档把这件事拆成了两级来处理:先在当前供应商内部轮换鉴权档案(auth profile),轮不动了再切到 agents.defaults.model.fallbacks 里的下一个模型。这两级的边界、各自认哪些错、切换后会不会一直留在备用模型上,恰恰是最容易配错的地方。这篇按官方文档的口径把这条链路串一遍。

需要先说清楚:这里讲的 provider 是 LLM 供应商,不是 WhatsApp、Telegram 那种聊天渠道,文档里这两个概念是分开的。

模型引用长什么样,配置键各管什么

模型引用统一是 provider/model 形式,例如 opencode/claude-opus-4-6。相关的配置键分工如下:

配置键作用
agents.defaults.model.primary主模型(也可直接写成字符串形式的 agents.defaults.model
agents.defaults.model.fallbacks备用模型链,按顺序尝试
agents.defaults.models存别名与单模型设置;加进去不等于限制覆盖,也不等于注册了新模型
agents.defaults.modelPolicy.allow可选的覆盖白名单,支持 provider/*provider/namespace/* 这类尾部前缀通配
agents.defaults.utilityModel短内部任务用的低成本模型(生成会话标题、进度叙述等),设为空字符串即关闭
agents.defaults.imageModel仅在主模型无法接收图片时使用
agents.defaults.pdfModelpdf 工具用;未设则退到 imageModel,再退到当前会话/默认模型

上下文相关的三个字段容易混:models.providers.*.maxTokens 是供应商级的输出 token 默认值;在 models.providers.*.models[] 每一项上,contextWindow 声明原生窗口,contextTokens 限制活跃输入,maxTokens 覆盖该模型的输出容量。

CLI 侧常用的是这三条:

openclaw onboard
openclaw models list
openclaw models set <provider/model>

还有一个反直觉但文档专门写了的行为:加一个供应商的鉴权,不会改你的主模型openclaw configure 会保留已有的 agents.defaults.model.primaryopenclaw models auth login 也一样,除非显式带 --set-default。供应商插件在鉴权配置补丁里返回的推荐默认模型,OpenClaw 只当作「让这个模型可用」,而不是「替换当前主模型」。想真的换默认,走 openclaw models set <provider/model>models auth login --provider <id> --set-default

官方插件供应商 vs 自己写 models.providers

文档把供应商分成两类,配置方式完全不同:

  • 官方供应商插件(OpenAI、Anthropic、Google、Z.AI、OpenCode、Vercel AI Gateway、DeepSeek、Moonshot、Groq、xAI 等一长串)会自己发布模型目录行,不需要models.providers 的模型条目。启用插件、配好鉴权、选模型就行。只有当你要覆盖 base URL、请求头、模型列表,或者设超时这类窄设置时,才需要显式写 models.providers.<id>
  • 自定义供应商或 OpenAI/Anthropic 兼容代理才用 models.providers(或 models.json)来加。

大部分供应商专属逻辑都住在供应商插件里(registerProvider(...)),OpenClaw 自己只保留通用的推理循环。插件负责 onboarding、模型目录、鉴权环境变量映射、传输与配置归一化、工具 schema 清理、失败分类、OAuth 刷新、用量上报、thinking/reasoning 档位等等。

自定义供应商有几个默认值值得记:省略时 reasoning: falseinput: ["text"]、成本全 0、maxTokens: 8192contextWindow 省略则保持未设置状态,当发现流程和单模型元数据都拿不到上下文信息时,上下文预算按 20 万 token 兜底。如果这个代理模型能吃图片,要显式写 input: ["text", "image"],否则图片会走成纯文本媒体引用而不是原生模型输入。

另外,走非原生端点的 api: "openai-completions" 路由(任何 host 不是 api.openai.com 的非空 baseUrl),OpenClaw 会强制 compat.supportsDeveloperRole: false,并跳过只属于原生 OpenAI 的请求整形;本地慢模型或局域网主机,用 models.providers.<id>.timeoutSeconds 单独加时间,但要注意它撑不开整个运行的超时上限,agents.defaults.timeoutSeconds 更低的话得一起抬。

多把 key 怎么排优先级,什么时候才轮换

同一个供应商配多把 key 是最省事的第一道保险。文档给的来源与优先级,从高到低是:

OPENCLAW_LIVE_<PROVIDER>_KEY   # 单个实时覆盖,优先级最高
<PROVIDER>_API_KEYS            # 逗号或分号分隔的列表
<PROVIDER>_API_KEY             # 主 key
<PROVIDER>_API_KEY_1           # 编号列表

Google 系供应商还会把 GOOGLE_API_KEY 算作兜底。选择顺序保留优先级并去重。

关键在触发条件:只有限流类响应才会用下一把 key 重试——例如 429rate_limitquotaresource exhaustedToo many concurrent requestsThrottlingExceptionconcurrency limit reached、周期性用量上限提示这些。非限流失败会立刻失败,不做任何 key 轮换。所有候选 key 都失败时,返回最后一次尝试的错误。

所以「我配了三把 key 为什么没轮换」多半不是配错了,而是那个错根本不在限流桶里。

故障转移的运行时顺序

文档给的运行时流程是这样一条线:解析会话模型与鉴权档案偏好 → 按当前模型选择和该来源的 fallback 策略构建候选链 → 在当前供应商内按轮换与冷却规则尝试 → 若该供应商以「值得转移的错误」耗尽,则前进到下一个模型候选 → 用胜出的候选跑完这一轮,但不改会话已选的 provider/model

如果所有候选都只是因为供应商过载而失败,且这一轮还没开始执行工具、也没开始输出助手内容,回复运行器会把整条 turn 内候选链重试最多 10 次,退避从 2.5 秒起翻倍、上限 30 秒;等待满 30 秒时发一条状态提示,免得用户干等。全部失败则抛出带结构化逐次尝试详情的 FailoverError,已知时附上最近的冷却到期时间。

要特别记住:fallback 执行是 turn-local 的。回复运行器只持久化 fallback 通知状态,好让 /status 和转换提示能区分「选中的模型」和「实际回答的模型」,它不会把 fallback 记成下一轮的模型选择。

在非群组、非频道会话里,转到 fallback 时会发一条可见通知,恢复到主模型时再发一条:

↪️ Model Fallback: <fallback> (selected <primary>; <reason>)
↪️ Model Fallback cleared: <primary> (was <fallback>)

群组与频道会话保留同样的 fallback 状态和生命周期事件,但不发这两条通知。

同一个 provider/model,来源不同严格程度不同

这是最容易踩的一处。模型是从哪儿来的,决定了它允不允许走 fallback:

来源是否走 fallback
配置默认 agents.defaults.model.primaryagents.defaults.model.fallbacks
Agent 主模型 agents.entries.*.model严格,除非该 agent 的 model 对象自己带 fallbacks;写 fallbacks: [] 是把严格行为显式化
运行时 fallback仅当前轮有效,下一轮从选中的主模型重新开始
用户会话覆盖严格。/model、模型选择器、session_status(model=...)sessions.patch 写入 modelOverrideSource: "user",失败就如实报错,不会拿别的模型糊弄过去
遗留会话覆盖老条目只有 modelOverride 没有 source,一律按用户覆盖处理
cron 任务模型payload.model / --model 是任务主模型,用配置的 fallbacks,除非任务自带 payload.fallbackspayload.fallbacks: [] 让该任务严格

候选链本身的拼法也有明确规则:请求的模型永远排第一;显式配置的 fallbacks 会去重但被模型白名单过滤(当作操作者的明确意图);没有显式 fallback 覆盖时,配置的 fallbacks 会排在配置主模型之前尝试,而配置主模型会被追加到链尾,好让链条最终能落回默认;调用方传了 fallbacksOverride 时,链条就只有请求模型加这个列表,传空列表即关闭模型 fallback,也不会偷偷把主模型追加进来。

冷却、计费禁用与鉴权档案轮换

鉴权档案是 API key 和 OAuth token 的统一抽象。密钥与运行期路由状态存在每个 agent 自己的 ~/.openclaw/agents/<agentId>/agent/openclaw-agent.sqlite 里,配置里的 auth.profiles / auth.order 只是元数据与路由,不含密钥。凭据类型分 api_keyoauthtoken 三种,其中 token 是静态 bearer,OpenClaw 不负责刷新。

没配显式顺序时的轮询排序是:先按档案类型(OAuth,然后静态 token,然后 API key),OAuth 内部再把当前可用 access token 的排在过期的前面(过期的仍然可选,以便运行时刷新),然后按 usageStats.lastUsed 从旧到新,冷却/禁用的挪到最后并按最近到期排序。

自动选中的档案会按会话钉住以保持供应商缓存温度,不是每个请求都轮换;会话重置、压缩完成、档案进入冷却或被禁用时才可能轮换或清除。用 /model …@<profileId> -s 做的手动选择是用户覆盖,能扛过 /new/reset、会话滚动、压缩和冷却窗口。

常规冷却(非计费、非永久鉴权失败)按该档案近期错误次数递增:

  • 第 1 次失败:30 秒
  • 第 2 次失败:1 分钟
  • 第 3 次及以后:5 分钟(封顶)

计数器在该档案内置的失败窗口过去后清零。状态落在每 agent 的 SQLite 鉴权状态里:

{
  "usageStats": {
    "provider:profile": {
      "lastUsed": 1736160000000,
      "cooldownUntil": 1736160600000,
      "errorCount": 2
    }
  }
}

计费类失败(例如 insufficient credits、credit balance too low)走另一条道:它同样值得转移,但通常不是暂时的,所以 OpenClaw 直接把档案标为禁用并配更长的退避,写入 disabledUntildisabledReason。文档特意提醒,不是每个计费形态的响应都是 402,也不是每个 402 都落在这里——临时的用量窗口和组织支出上限错误(例如 weekly usage limit exhausted、daily limit reached)被归类为 rate_limit,走短冷却而不是长禁用。

限流冷却还可以是模型级的:失败模型 id 已知时会记 cooldownModel,同一供应商下的兄弟模型仍可尝试;但计费/禁用窗口是跨模型封住整个档案的。

多把 key 之外,如果你想抑制反复出现的鉴权失败,可以显式开:

OPENCLAW_FALLBACK_SKIP_TTL_MS=60000

它给非主候选记一个会话内、进程内的跳过标记,键包含会话、供应商、模型和所选档案 ID;主候选永远不跳过,网关重启即清空,取值被夹在 1 秒到 10 分钟之间。

哪些错会推进 fallback,哪些不会

会继续走 fallback不会继续
鉴权失败非超时/非转移形态的显式中止
限流与冷却耗尽应留在压缩/重试逻辑里的上下文溢出错误(如 request_too_large、输入超过最大 token 数、ollama error: context length exceeded
过载/供应商繁忙已无候选时的最终未知错误
超时形态的转移错误供应商侧自行处理的安全拒答(按文档口径由供应商级机制接手)
计费禁用
还有剩余候选时的其它未识别错误

有几类错的归类值得单独记:OpenAI 兼容路径上供应商已完成的停止原因,如 Unhandled stop reason: errorProvider finish_reason: error,被归为 server_error(类 HTTP 500),仍然可转移,但诊断里保留供应商原始的 finish-reason 文本,不会被改写成「LLM 请求超时」;而 Provider finish_reason: abortnetwork_errormalformed_response 这类传输形态的,留在超时/转移桶(状态 408)。格式与非法请求错误一般是终态,因为重发同样的载荷还是会失败,所以 OpenClaw 直接把它暴露出来,而不是去轮换鉴权档案。

排查时最有用的是结构化日志:model_fallback_decision 会带上 fallbackStepFromModelfallbackStepToModelfallbackStepFromFailureReasonfallbackStepFromFailureDetailfallbackStepFinalOutcome 这些扁平字段,即便最后一个 fallback 也失败了,也能把最初那个失败原因还原出来。日志与诊断开关怎么打开,见 网关诊断与日志

一个典型的双保险配置是订阅账号加 API key 备份,用 auth.order 定用户可见顺序:

{
  auth: {
    order: {
      openai: ["openai:user@example.com", "openai:api-key-backup"],
    },
  },
}

订阅撞上用量上限时,OpenClaw 会在供应商给出确切重置时间时记下它,转到下一个有序档案,并让这次运行留在同一个 harness 里;重置时间过了,订阅档案重新可选。密钥本身怎么存、怎么用 SecretRef 管理,见 密钥与凭据管理

什么时候这套机制帮不上忙

  • 上下文溢出不归它管。输入超窗是压缩与重试逻辑的事,换个模型并不解决,文档也明确把这类错排除在 fallback 之外。相关机制见 上下文压缩与会话修剪,重试策略的边界见 队列、插话与重试
  • 你自己 /model 选的模型不会被自动救。这是设计如此:显式选择就是严格选择,挂了要看到真实报错。想要自动兜底,就别在会话里手动钉模型,让它走配置默认。
  • 会话中途换模型有代价。文档写得很直白:下一个模型可能有不同的上下文窗口、提示与工具行为、不同的提示缓存实现,中途切换会削弱连续性、可能提前触发压缩、丢掉缓存复用。能新建会话就新建会话。同理,缓存复用要紧时,thinking/reasoning 档位在一个会话内也别来回调——在 OpenAI 上改推理强度会改变可复用的请求状态。
  • 鉴权轮换不会放松模型选择。文档专门加了注:自动钉住和用户钉住的鉴权档案都只是「重试偏好」,同一供应商内的档案轮换不代表模型选择变松了,显式的用户 provider/model 选择在同供应商档案耗尽后照样报失败。
  • 成本这一侧要自己盯。文档提到 Fast 模式属于溢价计费且按模型区分,具体倍率以官方页面为准;把一堆高价模型堆进 fallbacks,出故障时账单形态会和平时不一样。fallbacks 更适合放成本/延迟敏感的任务和低风险对话,这也是官方给的模型策略建议。

配置改完之后,最省事的验证顺序是:openclaw models status 看鉴权候选和 OAuth 到期,/model status 看每个供应商的鉴权候选与端点,/status 看当前选中模型与(有差异时)正在生效的 fallback 模型和原因。三条命令对不上,再回头翻 model_fallback_decision 日志。

延伸阅读


本文依据 OpenClaw 官方仓库(github.com/openclaw/openclawdocs/ 下的官方文档整理,核对日 2026-08-17。 我们没有安装或运行过 OpenClaw,因此不涉及界面外观、操作手感与实测耗时的任何描述; 文中的默认值、命令与配置项均为文档口径,不构成对实际运行结果的保证。 该项目迭代很快,请以仓库最新内容为准。接入即时通讯平台前,请自行确认所在平台的规则与合规要求。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。