起因:改了三次都没变
我在 ccswitch(v3.19.2,Windows 端的 Claude Code 配置切换工具)里点"编辑供应商",把 Opus 档的模型改掉、保存。切回 Claude Code 重开,跑的模型还是原来那个。再改、再保存、再重启,依旧没变。
“改不了"这类问题最怕瞎猜。下面是用日志和数据库逐字段对比、一步步逼出根因的过程。结论比直觉绕得多——写入是成功的,毛病出在三个地方叠加。
先排除两个看起来最像的嫌疑
嫌疑一:写入失败(原子写 os error 50)。 WSL2 经 9P 桥写 Windows 侧文件,历史上 rename 操作会被拒,报 os error 50。但我看 ~/.claude/settings.json 的修改时间,刚被成功写过;ccswitch 自己的数据库也在同一秒写了,还做了切换前备份。写入链路是通的,排除。
嫌疑二:WSL 和 Windows 是两份文件。 检查发现 ~/.claude/settings.json 是个符号链接,指向 Windows 侧的同一份文件。ccswitch 在 Windows 端写的,就是 Claude Code 在 WSL 里读的,同一份,排除。
先证伪这两个,能省掉大量在错误方向上折腾的时间。这也是排查这类"看起来没生效"问题的第一步:先确认写入到底成没成,别在没坏的地方修。
真正的根因一:模型配置的三层覆盖
Claude Code 决定"实际跑哪个模型”,不是看一个字段,是三层叠在一起,优先级从高到低:
ANTHROPIC_MODEL环境变量——最高优先级。一旦设了,强制全程跑这个模型,盖过下面两层。ANTHROPIC_DEFAULT_<档位>_MODEL——“Opus/Sonnet/Haiku/Fable 档分别映射到哪个具体模型”。model字段——当前用哪个档位(opus/sonnet/haiku/fable)。
我的配置里三层互相打架:model: "opus"(选 Opus 档),Opus 档映射到模型 A,但 ANTHROPIC_MODEL 又硬钉成模型 B。最高优先级那条一压,下面两层全失效——不管你改 Opus 档的映射,还是改 model 字段切档,实际都跑模型 B。
这就是"改了跟没改一样"的第一层原因:你以为改的是档位映射,但硬钉压着,根本没轮到档位映射说话。
真正的根因二:ccswitch 的"编辑"和"应用"是两个分离的动作
这是日志逼出来的。ccswitch 的日志在这次操作的时间窗里只记了这些:
| |
也就是说,“点编辑、改模型、保存"这个动作,ccswitch 只把它写进了自己的数据库 providers 表(模板更新成功,连云端同步都触发了),但不会因此就把新模板推写到 Claude Code 实际读的那个 .claude/settings.json。要推到 live,得另外点"切换/应用供应商”。ccswitch 启动时也只做反向检查(要不要从 live 回灌进数据库),不会主动把数据库推到 live。
所以:数据库里你的编辑生效了,live 文件纹丝未动,Claude Code 读的还是旧的。这是第二层原因——你做了"编辑",没做"应用"。
真正的根因三:数据库模板与 live 文件漂移
把数据库里这个供应商的模板和 live 文件的实际值拉出来逐字段对比,四个档位映射整组错位了一个位置:
| 档位 / 字段 | 数据库模板(编辑后存的) | live 文件(实际跑的) |
|---|---|---|
| Opus 档 | qwen3.8-max | glm-5.2-fast-preview |
| Sonnet 档 | glm-5.2-fast-preview | deepseek-v4-flash-0731 |
| Fable 档 | deepseek-v4-flash-0731 | qwen3.8-max |
ANTHROPIC_MODEL | glm-5.2-fast-preview | qwen3.8-max |
这不是一个字段没改对,是多次编辑数据库、live 却一直停在老切换点,两头越漂越远。即使你回去点"切换"让 ccswitch 推 live,推过去的也是这套错位的模板,还是会乱。另外数据库模板只含 env 块、不含 model 和 effortLevel 顶层字段——这是 ccswitch 的设计,但它确实让"通过 ccswitch 完整管理模型"变得别扭。
修复:删掉硬钉,让档位说话
最小修复:删掉 ANTHROPIC_MODEL 这条最高优先级的硬钉。删掉之后,model 字段选档位 → 档位映射 → 实际模型,这条链才真正通。配合理顺过的档位映射(Opus=主力、Sonnet=难任务、Haiku=轻活、Fable=满分),删硬钉后档位分工才生效。
改完必须重启 Claude Code——env 是启动时注入进程的,改文件不影响已经跑起来的会话,当前会话仍是旧值,完全退出重开才读到新配置。
验证方法:重启后用 /model 切到 Sonnet 档,看实际模型是不是变成了 Sonnet 档映射的那个。变了,就证明档位映射这条链通了。
日常怎么办:绕开 ccswitch,直接改 settings.json
诊断清楚之后,最省心的路反而是最朴素的:日常不用 ccswitch,直接改 ~/.claude/settings.json。因为这次"改不了"的唯一原因就是 ccswitch 切换会拿数据库旧模板盖掉 live 文件;你不碰它,这条覆盖路径就不存在了,settings.json 就是你说了算的单一真相源。
唯一铁律:别再打开 ccswitch 点"切换/应用供应商"。一点就触发整块覆盖(错位的档位映射、硬钉、丢键全回来)。不点它,改完重启即可。日常切档用 Claude Code 自带的 /model 命令也行,它本质就是改 settings.json 的 model 字段,在"不用 ccswitch"的前提下同样持久。
诚实边界
- 三层覆盖的优先级排序,是静态推断加系统回显佐证(“settings.json pins X — applies on restart”)。重启后三层里究竟哪层最终拍板,我没逐字段实测重启,这部分待你自己重启验证。
- 结论针对 ccswitch v3.19.2。写文当天检测到 v3.20.0 更新,是否修了"编辑不推 live"的行为,未知。
settings.local.json优先级比settings.json高。如果 local 里也设了同名模型字段,会盖过 settings.json——排查时别忘了看 local。
怎么自己查
复现这套诊断,核心就四条命令(路径和密钥请换成你自己的):
| |
第三条查出来的模板,和第二条查出来的 live 值,逐字段对比——对不上的字段就是漂移点。第四条日志里如果没有"应用到 live"之类的事件,就坐实了"编辑不推 live"。
排查这类"配置改了不生效"问题,思路其实通用:先把数据流上每一段都点亮——写进去了没?写的和读的是同一份吗?读的时候哪层优先级最高盖过了你改的那层?三步走完,根因自己就浮出来了。


