claude-swap 自动切换引擎源码解析:滞后、冷却与账号隔离的设计思路
【免费下载链接】claude-swapSwitch between multiple Claude Code accounts, with automatic rate-limit rotation, usage dashboard, and parallel sessions项目地址: https://gitcode.com/gh_mirrors/cl/claude-swap
claude-swap 是 Claude Code 的多账号切换工具:它备份每个账号的 OAuth 凭据,在撞上速率限制之前自动轮换到余量最大的账号,并提供实时用量仪表盘与并行会话。本文拆解它内置的自动切换引擎——阈值触发、滞后(hysteresis)、冷却(cooldown)与账号隔离(quarantine)这四个核心机制的设计思路,帮你理解一个"不翻车"的自动切换器该长什么样。
一、自动切换要解决什么问题:告别限速手动切号
Claude Code 的订阅配额有 5 小时和 7 天两个滑动窗口,任何一个烧满就要等恢复。手动切换的痛点在于:你很难精确判断"还剩多少余量、现在切是否划算"。引擎的答案是主动式切换——不等撞墙,而是在余量接近耗尽时提前换人,换过去的瞬间旧账号仍然有效,正在运行的 Claude Code 会自动捡到新凭据。
上图是cswap watch打开的实时仪表盘:每个账号的 5h / 7d 配额条、重置倒计时、当前活跃账号一目了然——自动切换引擎读的就是同一份用量数据。
二、引擎总览:一个 tick 循环与四类触发器
引擎核心是 AutoSwitchEngine 类,它刻意做到与界面无关:不打印、不依赖 TUI,每轮tick()只做一件事——取用量、做决策、通过类型化事件(PollEvent/SwitchEvent/NoSwitchEvent…)向上汇报。CLI、TUI、macOS 菜单栏共用同一个事件流。
每次 tick 先给当前账号定性,得到四种触发器之一:
- proactive(主动):绑定窗口(5h/7d 中更高的那个)越过阈值,且当前账号还有余量;
- at-limit(撞线):当前账号余量为 0,必须立刻走人;
- failover(故障转移):当前账号用量连续多个 tick 读不到(如 token 失效),迁到健康账号;
- consume-first(先消耗):
--strategy consume-first模式下,主动迁到"周窗口重置最早"的账号,把快过期的配额先花掉。
决策骨架在 _tick_inner,执行落地在 _perform。
三、阈值与滞后:自动切换为什么默认 90% 触发
策略参数集中在 AutoSwitchSettings,两个关键值值得细看:
- threshold = 90:为什么不设 95?源码注释说得很直白——要给 macOS 钥匙串约 30 秒的凭据拾取延迟、以及"重负载回合烧过阈值才来得及换人"留出缓冲。主动切换的意义就在于换人时旧账号还没死。
- hysteresis_pct = 10:这是防抖的灵魂。主动切换的候选账号不仅要低于阈值,还必须比当前账号好出整整 10 个百分点(见 滞后排他判断)。
滞后解决的是经典问题:两个账号都卡在 89%/91% 时,"严格更大"的比较会让引擎在两者之间无限乒乓——A 好 1 个点就切过去,烧掉那 1 个点又切回来。加了 10 点迟滞后,跨线的单向移动永远放行,贴线的往返移动永远拒绝,余量明确更大的账号则一次到位。
四、冷却时间:给切换装上刹车
即使阈值和滞后都放行,_in_cooldown 还会检查:距离上次切换是否满cooldown_seconds(默认300 秒)。冷却时间戳持久化在备份根目录的autoswitch_state.json里,且整个"复查→切换→记录"序列持有文件锁——前台cswap auto循环和 cron 里的cswap auto --once两个进程因此只会做出一次被序列化的决策,输家读到赢家写下的lastSwitchAt就自动退避,不会双重切换。
注意刹车的作用域:只有主动类触发(proactive / consume-first)受冷却约束。at-limit 和 failover 是"逃离死亡"的逃生通道,任何冷却都不该挡住你离开一个已经撞墙或读不到用量的账号。
五、账号隔离:死令牌、身份冲突与存活会话
多账号系统最阴险的故障不是"切不过去",而是"切到了错误的账号"。引擎在激活候选账号前有一道 _freshen_target 前置检查:
- 令牌保鲜:若候选账号的访问令牌将在 10 分钟内过期(恰好是 Claude Code 自身 5 分钟刷新缓冲的两倍),引擎先用备份的刷新令牌换新令牌,保证激活时新令牌一定"够老够新";
- invalid_grant → 隔离:刷新令牌已死的账号不会反复重试,而是被写入隔离名单(_quarantine 持久化到状态文件),从轮转中除名并报告原因。用户重新登录并
cswap add后,凭据指纹变化会被自动检测,账号自动解除隔离回到轮转; - identity-conflict → 隔离:令牌还活着,但认证出来的是另一个组织/账号——切过去会让所有仪表读数正常、人却在错误账号上干活,比死令牌更危险,同样隔离;
- skip-live-session:该账号正被
cswap run的会话占用(令牌在独立 profile 里自转),自动激活默认登录会造成一个刷新令牌两处竞写,直接跳过,让会话自己消耗完配额。
六、自适应轮询:把 API 流量压到 O(1)
引擎不能"每分钟把 N 个账号全查一遍"——Anthropic 的用量接口对非第一方客户端有约 60 分钟窗口、约 28~30 次请求的预算。poll_policy.py 把这条预算翻译成一组节奏常数,plan_after_fetch 据此为每个账号单独排程:
| 状态 | 轮询间隔 | 逻辑 |
|---|---|---|
| 活跃账号,正在消耗且逼近阈值 | 60s | 紧急模式,最坏一集内不超过 15 次 |
| 活跃账号,消耗中 | 180s 起减半 | 用量在动就加密 |
| 活跃/候选账号,用量静止 | 300s~600s | 不动就退避 |
| 已耗尽账号 | 约 10min | 服务方可能提前放额度,不能真睡到重置时刻 |
| 遭遇 429 后 | ×1.5 指数退避至 30min | TCP 式拥塞控制,多机器共享同一令牌也能公平退让 |
调度器本身还有两个巧妙约束:每个 tick 的基线请求量是O(1)——只取当前账号 + 一个"最久没查"的候选(_collect_scheduled_usage),其余全部从本地用量存储读取;仅当当前账号进入阈值下方 15 个百分点的"升级带"、或用量不可读需要故障转移时,才升级为全员刷新。所有排程还叠加 ±10% 抖动,让多机/多进程永不同步踩踏接口。
七、防抖的极致:no-return bar 如何避免来回切换
阈值滞后只防"贴线乒乓",还有一类更隐蔽的抖动:引擎切到 B 之后,C 因数据燃烧一点点变化又被排到第一,引擎回头切回 C——而 C 其实和离开时一模一样。
引擎的解法是"不许撤销上一步"(_no_return_account):每次成功切换都会记录lastSwitchFrom(从哪来)以及离开那一刻的离场快照(leftHeadroom/leftRecoveryAt,见 _perform)。下一 tick 里,刚离开的账号默认被禁止成为候选,除非它比离开时真的变好了——余量显著回升、或绑定窗口的重置时间明显提前。这两个信号都只能由"窗口滚动"这类真实事件产生,靠用量燃烧伪造不出来。
八、快速上手:常用自动切换配置清单
所有旋钮都可用cswap config修改,例如cswap config set autoswitch.threshold 80。常用项:
| 配置键 | 默认值 | 作用 |
|---|---|---|
autoswitch.threshold | 90 | 绑定窗口利用率达到多少即寻找更优账号 |
autoswitch.cooldownSeconds | 300 | 两次主动切换的最短间隔(秒) |
autoswitch.hysteresisPct | 10 | 候选账号须领先当前账号的百分比 |
autoswitch.strategy | best | best(余量最大)或consume-first(周窗口重置最早) |
autoswitch.unhealthyTicks | 3 | 用量连续不可读多少次后故障转移 |
autoswitch.model | 无 | 把指定模型(如 Fable)的周限额并入决策 |
想先观察再实切?cswap auto --dry-run会完整走一遍决策、只记录不切换;cswap auto --once --json输出单行 JSON 事件,方便接入 cron。
结语
claude-swap 自动切换引擎的设计哲学可以浓缩成一句话:每一次"不切换"都必须有名字。低于阈值不切、冷却中不切、滞后不够不切、候选全耗尽不切——每种拒绝都对应一个明确的NoSwitchEvent原因,配合隔离、离场快照、自适应轮询,把一个"看起来一行 if 就能写完"的循环,做成了长时间运行也不翻车的状态机。想继续深挖,建议从 tests/test_autoswitch.py 的场景化测试入手,几乎每个防抖机制都有对应的回归用例。
【免费下载链接】claude-swapSwitch between multiple Claude Code accounts, with automatic rate-limit rotation, usage dashboard, and parallel sessions项目地址: https://gitcode.com/gh_mirrors/cl/claude-swap
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考