2026-02-15 · 架构
32
架构 · 2026-02-15

我愚蠢的省钱实验:从 cliproxy 中转到 OpenClaw 原生 Failover 的踩坑全记录

起因:Token 焦虑症

跑 AI Agent 最大的开销是什么?Token。

我的 OpenClaw 实例每天定时执行 4 个 Cron 任务:Gmail 摘要、垃圾评论清理、爬虫数据分析、服务器安全巡检。每个任务都需要调用大模型,带 tool use 能力。主会话用的是 Claude Opus 级别的模型,Cron 任务本来也在跟着跑——一个早上 9 点的 Gmail digest 就干掉了当天一半的用量。

这让我动了歪脑筋:能不能用免费的中转站来跑这些非关键任务?

答案是可以,但我选错了路。

第一个坑:cliproxy 大一统中转方案

思路

cliproxy 是一个本地 API 代理,跑在 127.0.0.1:8317。它的核心功能是 round-robin——你给它配一堆后端,它轮流转发请求。

我的如意算盘:

OpenClaw → cliproxy (本地代理)
              ├── 后端 A (公益站 1)
              ├── 后端 B (公益站 2)
              ├── 后端 C (公益站 3)
              └── 后端 D (付费站兜底)

只需要在 OpenClaw 里配一个 provider(cliproxy),cliproxy 自己搞定多后端轮换。简洁、优雅。

配置

cliproxy 的配置很简单。在 /opt/homebrew/etc/cliproxyapi.conf 里加后端,OpenClaw 这边只需要一个 provider:

{
  "cliproxy": {
    "baseUrl": "http://127.0.0.1:8317/v1",
    "apiKey": "sk-free-xxxx",
    "api": "openai-completions",
    "models": [
      { "id": "claude-sonnet-4-5-20250929" },
      { "id": "deepseek-v3.2" },
      { "id": "gpt-5.3-codex" }
    ]
  }
}

看起来没毛病。上线。

翻车

第一天,deepseek-v3.2 通过 cliproxy 发了 17 个请求,全部失败。后端返回 500 和 520。

问题出在哪?cliproxy 的 round-robin 没有熔断机制

它就像一个不长记性的挂号员:明知道某个医生今天没来,还是把你的号挂给他。你说"这个医生不在",他说"好的下一位",然后下一轮又把别人挂给这个不在的医生

具体来说:

  1. 无状态轮换:cliproxy 不记录后端健康状态。A 挂了,下一轮还是会轮到 A
  2. 错误不透明:OpenClaw 只看到 cliproxy 返回的错误,不知道背后是哪个后端出的问题
  3. 冷却失效:OpenClaw 把 cliproxy 整个 provider 标记为冷却,但问题可能只是某一个后端
  4. 无法精准恢复:某个后端恢复了,OpenClaw 不知道;整个 cliproxy 还在冷却期

这是架构上的根本缺陷:在 OpenClaw 和实际后端之间加了一层不透明的代理,破坏了 OpenClaw 原生故障感知的精度

数据说话

跑了一天的结果:

指标
cliproxy 方案
直连方案

deepseek-v3.2 成功率
0/17 (0%)
N/A(后端本身不稳定)

错误定位时间
无法定位
即时(per-provider)

冷却精度
provider 级(误伤)
provider 级(精确)

恢复速度
等整个冷却期结束
单个 provider 独立恢复

第二个坑:OpenRouter 免费模型

cliproxy 方案挂了之后,我想到另一条路:OpenRouter。

OpenRouter 是一个模型聚合平台,上面有不少标记为 "free" 的模型。OpenClaw 有个内置命令 openclaw models scan,可以扫描 OpenRouter 上可用的免费模型。

openclaw models scan

注册了 OpenRouter 的 API key(sk-or-v1-xxxx...),开扫。

结果很快出来了:能找到好几个免费模型,看起来挺美好。

但全部不支持 tool use。

这就要命了。OpenClaw Agent 的核心能力依赖 tool use——读文件、执行命令、调 API、发消息。一个不支持 tool use 的模型对 Agent 来说就是个废物,只能做纯文本 Q&A。

免费模型不支持 tool use 不是巧合,是经济规律:tool use 需要模型理解复杂的函数签名、生成结构化的调用参数、处理多轮工具交互。这些能力需要更大的模型、更多的推理计算,免费额度覆盖不了这个成本。

这条路也死了。

正确的路:OpenClaw 原生 Failover

两条捷径都走不通,我老老实实回来看 OpenClaw 自己的故障转移机制。

架构

OpenClaw 的 failover 分两级:

请求失败
  │
  ├─ 第一级:Auth Profile 轮换(同一 provider 内)
  │   同一 provider 有多个 API key → 逐个尝试
  │
  └─ 第二级:Model Fallback(跨 provider)
      当前 provider 所有 key 都失败 → 跳到 fallbacks 列表下一个

关键区别在于:每个 provider 是独立管理的。OpenClaw 精确知道哪个 provider 挂了、挂了多久、什么时候该重试。

配置方案

我把之前塞在 cliproxy 后面的公益站拆出来,每个作为独立 provider 注册:

{
  "models": {
    "providers": {
      "linuxdo": {
        "baseUrl": "https://free-api-1.example.com",
        "apiKey": "sk-free-xxxx",
        "api": "anthropic-messages",
        "models": [{
          "id": "claude-sonnet-4-5-20250929",
          "name": "Claude Sonnet 4.5 (站点A)"
        }]
      },
      "huan": {
        "baseUrl": "https://free-api-2.example.com",
        "apiKey": "sk-free-yyyy",
        "api": "anthropic-messages",
        "models": [{
          "id": "claude-sonnet-4-5-20250929",
          "name": "Claude Sonnet 4.5 (站点B)"
        }]
      },
      "runanytime": {
        "baseUrl": "https://free-api-3.example.com",
        "apiKey": "sk-free-zzzz",
        "api": "anthropic-messages",
        "models": [{
          "id": "claude-sonnet-4-5-20250929",
          "name": "Claude Sonnet 4.5 (站点C)"
        }]
      },
      "hotaru": {
        "baseUrl": "https://paid-api.example.com",
        "apiKey": "sk-paid-xxxx",
        "api": "anthropic-messages",
        "models": [{
          "id": "claude-sonnet-4-5-20250929",
          "cost": { "input": 0.003, "output": 0.015 }
        }]
      }
    }
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "anyrouter/claude-opus-4-6",
        "fallbacks": [
          "foxcode/claude-sonnet-4-5-thinking",
          "linuxdo/claude-sonnet-4-5-20250929",
          "huan/claude-sonnet-4-5-20250929",
          "runanytime/claude-sonnet-4-5-20250929",
          "hotaru/claude-sonnet-4-5-20250929",
          "zai/glm-4.7",
          "openai-codex/gpt-5.3-codex"
        ]
      }
    }
  }
}

注意:模型名称必须用 provider/model-id 完整格式,不能用别名。这也是踩过的坑。

探测过程

配之前得先确认这些公益站真的能用。不能假设"支持 OpenAI 格式的站也支持 Anthropic Messages 格式"。我用 curl 逐个探测:

# 探测某个公益站是否支持 anthropic-messages 格式
curl -s -w "\nHTTP %{http_code} in %{time_total}s\n" \
  https://free-api-1.example.com/v1/messages \
  -H "x-api-key: sk-free-xxxx" \
  -H "anthropic-version: 2023-06-01" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-sonnet-4-5-20250929",
    "max_tokens": 64,
    "messages": [{"role":"user","content":"hi"}]
  }'

一共测了 7 个站,3 个通过。有的站超时,有的站返回 401,有的站只支持 OpenAI Chat Completions 格式。实测是唯一可靠的验证方式。

深入 OpenClaw Failover 机制

既然决定依赖 OpenClaw 原生能力,就有必要彻底搞懂它的运作逻辑。

冷却机制(Cooldown)

当一个 provider 的请求失败(auth 错误、429 限流、超时),OpenClaw 用指数退避标记冷却:

连续失败次数
冷却时长

1
1 分钟

2
5 分钟

3
25 分钟

4+
1 小时(上限)

冷却状态存在 ~/.openclaw/agents/<agentId>/agent/auth-profiles.json

{
  "usageStats": {
    "linuxdo:default": {
      "lastUsed": 1739577600000,
      "cooldownUntil": 1739577660000,
      "errorCount": 1
    }
  }
}

如果错误原因是账单/额度不足(insufficient credits),退避策略更激进:5 小时起步,翻倍递增,上限 24 小时。因为余额不足不是重试能解决的。

会话粘性(Session Stickiness)

OpenClaw 不会每次请求都重新选 provider。它为每个会话锁定一个 auth profile,保持不变直到:

为什么要锁定?缓存。很多 API provider 对同一个 session 有 prompt caching,频繁换 provider 会丢失缓存,导致每次请求都重新计算整个上下文。这在长会话里是巨大的浪费。

错误分类与触发

不是所有错误都会触发 failover:

错误类型
HTTP 状态码
触发 Failover?
处理方式

认证失败
401/403

冷却 + 轮换

速率限制
429

冷却 + 轮换

格式/参数错误
400

冷却 + 轮换

账单不足
402/payment

长禁用 + 轮换

超时
timeout

冷却 + 轮换

服务器内部错误
500

不推进(可能是临时抖动)

模型不支持
404

报错给用户

这里有个细节值得注意:HTTP 400 在最近的版本中被纳入了 failover 触发范围。以前 400 被认为是"你的请求格式有问题",不该换 provider。但实际上很多中转站对特定请求格式的兼容性不同,同一个请求在 A 站 400 在 B 站可能正常。这个改动让跨 provider 兼容性好了很多。

Fallback 链的执行逻辑

用户消息到达
  │
  ├─ 取 primary model (anyrouter/claude-opus-4-6)
  │   ├─ 取该 provider 的 auth profiles
  │   │   ├─ profile 1 → 成功 → 返回
  │   │   ├─ profile 1 → 失败 → 标记冷却
  │   │   ├─ profile 2 → 成功 → 返回
  │   │   └─ 所有 profiles 失败 → 进入第二级
  │   │
  │   └─ 第二级:取 fallbacks[0] (foxcode/...)
  │       ├─ 同样尝试所有 profiles
  │       ├─ 失败 → 取 fallbacks[1] (linuxdo/...)
  │       └─ ... 直到 fallbacks 耗尽
  │
  └─ 所有 fallback 耗尽 → 报错给用户

对 Cron 任务,我给每个任务单独指定了 provider,避免和主会话抢资源:

任务
指定模型
为什么

Gmail digest (09:00)
linuxdo/claude-sonnet-4-5
免费,能力足够

垃圾评论清理 (11:00)
huan/claude-sonnet-4-5
免费,分散负载

爬虫分析 (11:10)
runanytime/claude-sonnet-4-5
免费,分散负载

服务器巡检 (11:40)
linuxdo/claude-sonnet-4-5
复用,间隔够长

cliproxy vs OpenClaw 原生:本质区别

回头看,cliproxy 方案和 OpenClaw 原生 failover 的本质区别在于信息对称性

cliproxy 是个黑盒。OpenClaw 往里扔请求,cliproxy 内部怎么轮、轮到谁、谁挂了——OpenClaw 一无所知。这导致:

OpenClaw 原生方案,每个后端都是独立 provider,故障信息完全透明:

用一句话总结:不要在 Agent 框架和模型 API 之间插入不透明的代理层。除非这个代理层本身有完善的健康检查和熔断机制。

公益站使用心得

最后说几句关于公益站的实践经验。

不稳定是常态。 公益站靠社区维护,没有 SLA,随时可能跑路或者限流。"随时跑路公益站"——人家自己名字都写得很诚实。

分散比集中好。 配 3-5 个公益站,每个独立 provider,让 OpenClaw failover 自动切换。比把所有鸡蛋放在一个 cliproxy 篮子里强。

API 格式不能假设。 有的站支持 OpenAI Chat Completions,有的支持 Anthropic Messages,有的两个都支持但模型名前缀不同。必须 curl 实测。

付费站兜底。 免费的东西总有代价,关键时刻需要一个稳定的付费站保底。我的 hotaru 就是这个角色——输入 $0.003/1K tokens,输出 $0.015/1K tokens,便宜但稳。

成本对比。 切换到多 provider 直连方案后,Cron 任务的主力消耗从主 account 的高端模型转移到了免费公益站。每天 4 个任务的 token 开销从"半天用量"降到了几乎为零。偶尔公益站不稳定时 failover 到付费站,一天也就几分钱。

结论

这次折腾的核心教训:

  1. 中间代理层破坏信息对称——OpenClaw 的 failover 依赖对每个 provider 精确的健康状态感知,cliproxy 把这些信息全挡了
  2. 免费模型不支持 tool use——OpenRouter 的免费模型对 Agent 场景没用
  3. 分散风险比集中管理好——多个独立 provider 各自冷却,比一个代理统一管理更可靠
  4. 实测是唯一验证方式——API 兼容性不能靠文档推断

省钱没有捷径。但正确的架构选择可以在保持可靠性的前提下把成本压到最低。OpenClaw 原生 failover + 多公益站独立注册,就是目前最优解。

目录 最新
← 左侧翻上一屏 · 右侧翻下一屏 · 中间唤出菜单