
昨天有人问我OpenClaw 的模型切换除了配置面板那把常规操作还有没有别的路子问完之后补了一句“常规方法不管用改完配置它还是跑旧的。”这个我太熟了。最近为了把默认模型从一台老牌 70B 换成一个新的 32B 轻量模型我在面板里改了不下五次每次都提示“已保存”重启之后模型列表确实变了但实际跑出来的推理结果还是老模型的味道连响应速度都是那个慢悠悠的节奏。标题里那串感叹号不是在开玩笑——上一篇文章确实是 OpenClaw 自己代写的这篇换我亲自来把那条绕开常规面板的模型切换路径完整整理出来。适合谁看已经被模型切换问题卡住、或者想搞懂 OpenClaw 模型调度逻辑的人应该能从这里找到答案。1. 为什么你改了 OpenClaw 默认模型推理任务还是走老路1.1 面板配置和实际调用链路不是一回事先说结论OpenClaw 的面板把“默认模型”存到了一个业务配置表里但这个值只影响新会话创建时的默认取值。已经存在的会话、正在排队等待调度的任务、以及通过 API 客户端发起但没有显式传model字段的请求各自的行为路径都不一样。我部署 OpenClaw 的典型链路是这样的客户端或回调任务 → 调度服务 → 模型网关 → 本地推理服务表面上看改面板配置就能直接改默认模型。实际运行中链路里的任何一环都可能把模型标识缓存住或者按照自己的优先级重新覆盖一遍。常规方法只在“全新安装、只改过一处配置、单实例部署”这种干净环境里才可靠。一旦你多次切换过模型、设置过环境变量、跑过不同历史会话再想通过面板改默认模型基本就进入“显示成功、实际没变”的诡异状态了。我查 OpenClaw 的源码注释时注意到一个细节面板保存模型配置时会同时把这份配置写入启动参数里的--default-model和运行时数据库中的settings表。但如果启动脚本里已经设置过OPENCLAW_DEFAULT_MODEL环境变量某些版本的环境变量优先级会高于面板配置于是你面板里怎么改都没用。这不是 bug而是“配置来源优先级”设计得不直观。1.2 三个让“常规切换”失效的隐藏因素我把自己反复折腾后定位到的原因归成三类按出现频率排序隐藏因素表现为什么常规方法救不了会话级模型快照新会话用了新模型老会话仍回放旧模型面板只能改默认值改不了已创建会话的快照启动脚本强设环境变量面板显示已保存重启后又被脚本改回环境变量优先级高于面板配置多实例负载均衡你只改了其中一个节点的配置其他节点仍按旧配置调度推理任务第一类最坑。我当时有一个常驻的自动化任务每隔十分钟调用一次 OpenClaw 做文本分类。任务在历史会话里创建的 timestamp 早于切换时间所以无论面板配置怎么改这个任务每次请求都会附带旧的会话 ID调度器一看会话 ID 命中缓存就直接用会话快照里的旧模型继续跑。第二类属于部署习惯问题。很多同学喜欢把启动命令写进start.sh里面有export OPENCLAW_DEFAULT_MODELxxx。这个 export 一旦存在面板的优先级就得往后排。有的版本甚至连openclaw config set model都会被忽略。第三类常见于用容器编排工具部署多副本的场景。默认模型配置没有统一收敛只改了某一台负载均衡把请求随机分发到其他副本时自然还是旧模型。2. 另一套思路任务级覆盖和启动级强制路由2.1 核心思路别只改“默认模型”要改“实际生效模型的优先级”我后来彻底放弃和面板较劲转而利用 OpenClaw 的配置优先级规则。这套规则在文档里写得很隐晦但实际生效顺序是稳定的请求体显式 model 字段 环境变量 会话快照 面板默认配置常规方法之所以不管用是因为面板默认配置处在优先级最低的位置前面的任何一层都能把它压住。所以另一方法的本质是跑到优先级更高的层去做修改。要么在请求层强制指定模型要么在环境变量层强制指定默认模型要么在中间层拦截所有调度请求。这里有个关键点需要说清楚环境变量的优先级高于会话快照吗在不同版本里表现不太一样。较新的 OpenClaw 版本中环境变量会覆盖会话快照前提是你把会话缓存目录清掉。如果不清理旧会话的模型绑定仍可能通过会话 ID 把请求引到旧模型上。所以我下面的方法都会带一步“清理会话缓存”这是非常容易被忽略的细节。2.2 方法 A环境变量强制路由推荐先试这一步我建议最先尝试操作量最小也最容易验证。先停掉正在运行的 OpenClaw 进程然后修改启动方式显式声明模型export OPENCLAW_DEFAULT_MODELnova-chat-70b export OPENCLAW_ALLOW_OVERRIDE1 rm -rf ~/.openclaw/cache/sessions openclaw serve --port 8080OPENCLAW_ALLOW_OVERRIDE1的作用是放行请求体里的model字段去覆盖默认模型。有些版本默认不允许外部请求直接指定模型需要这个开关。如果你能通过 API 指定模型开着它会更灵活。rm -rf那一步是清理会话快照缓存。不清理的话旧会话可能继续引用旧模型。清理后所有会话都会重新走默认模型。注意这只是缓存目录不会影响你的历史聊天数据文件。启动后确认日志里出现类似model loaded: nova-chat-70b的输出。如果出现别的模型名说明仍有地方在覆盖环境变量继续往下看排查部分。2.3 方法 B在请求体里显式指定模型字段如果你不想重启 OpenClaw也不想动环境变量可以走任务级覆盖路线每次调用在请求体里明确写出模型名。OpenClaw 同时兼容 OpenAI 风格的接口所以可以直接这样调curl -X POST http://127.0.0.1:8080/v1/chat/completions \ -H Content-Type: application/json \ -H Authorization: Bearer $OPENCLAW_API_KEY \ -d { model: nova-chat-70b, messages: [ {role: system, content: 你是一个可靠的中文助手}, {role: user, content: 用一句话确认你的身份} ], temperature: 0.7 }这种方式的好处是精细同一个 OpenClaw 实例可以同时跑多个模型不同请求用不同模型互不干扰。坏处也很明显如果有历史会话通过session_id调用模型可能被会话快照覆盖。所以有效做法是把两件事一起做请求体里显式指定model同时附带一个新的session_id不让旧会话绑定干预。{ model: nova-chat-70b, session_id: manual-switch-2025-001, messages: [] }新会话 ID 可以绕开旧会话的模型绑定让请求真正走到你指定的模型上。方法 B 适合临时验证、灰度测试、以及需要在同一个实例上对比多个模型的场景。我经常用这种方法做 A/B 对比不用重启服务就能来回切换。3. 更高阶的做法给 OpenClaw 前面加一个轻量模型网关3.1 什么时候值得上网关如果你只是想把默认模型从 A 换成 B方法 A 就够了。但如果你面临的是这几类情况我建议加一层模型网关你想根据请求来源或内容类型自动路由到不同模型你需要在不重启 OpenClaw 的情况下动态调整模型分配比例你想在模型不可用时自动降级到备用模型你有多套 OpenClaw 实例想统一管理出口模型模型网关的本质是OpenClaw 不再直连推理服务而是把流量全部交给网关网关里写路由规则按规则把请求转发到对应模型。这样“用哪个模型”从 OpenClaw 的配置文件里彻底剥离出去你只需要改网关规则。3.2 一个能跑通的最小网关配置我自己用的是轻量级 HTTP 转发服务配置很薄核心就是一张路由表。这里我写一个最小可运行的 YAML 示例结构routes: - match: header: X-Task-Type value: classification target: model: mini-lite-8b - match: header: X-Task-Type value: reasoning target: model: nova-chat-70b - fallback: model: fast-chat-32b这个配置的意思很直白任务类型是文本分类时走 8B 轻量模型密集推理时走 70B 重量模型其他一律落在 32B 通用模型上。在 OpenClaw 侧需要把模型网关地址配置为默认出口export OPENCLAW_GATEWAY_URLhttp://127.0.0.1:8081/gateway export OPENCLAW_API_BASEhttp://127.0.0.1:8081/v1网关收到 OpenClaw 发来的请求后读取请求里的自定义头或路径再按路由规则改写model字段最后转发到真实推理服务。模型切换完全和 OpenClaw 解耦面板、环境变量、会话缓存统统不影响。我在网关里的转发逻辑大约是这样def route(request): task_type request.headers.get(X-Task-Type, default) rule config[routes].get(task_type, config[fallback]) request[model] rule[model] response forward_to_inference(request) return response3.3 网关方式的代价与收益收益很直接模型切换变成改一行路由配置还能按任务类型做分流不再需要重启 OpenClaw也不怕面板配置优先级问题。我在本地同时挂三个模型跑了两周稳得一批。代价主要有两个。第一多了一个常驻进程内存占用会高一些第二请求链路多一跳通常会增加几毫秒到几十毫秒的延迟。在纯本地调用场景下这点延迟基本无感知如果是高频小请求建议网关和服务同机部署别跨网络。另外网关方式需要注意一个细节OpenClaw 部分版本会校验模型名如果model字段被网关改写成了一个它不知道的别名可能报模型不存在。解决办法是在网关里把模型名改写成 OpenClaw 真能识别的标识或者在 OpenClaw 支持的模型列表里提前注册好目标模型。4. 完整排查链路怎么确认这次切换是真的生效了4.1 第一步从日志确认实际加载模型先不要看面板显示直接看日志。OpenClaw 启动日志里通常会有一行记录了实际加载的模型。我一般是这么查的journalctl -u openclaw --since 10 minutes ago | grep -i model或者直接看最新日志文件tail -n 200 ~/.openclaw/logs/openclaw.log | grep -i model正常情况下应该能看到类似INFO model loaded: nova-chat-70b INFO session cache cleared, default model set to nova-chat-70b如果日志里出现no model match, use fallback或者model not found, keep previous说明模型标识错了OpenClaw 没认出你要切的模型于是悄悄保留旧配置。这一步能筛掉一半以上的“假失败”。4.2 第二步用响应特征反推底层模型日志说加载了新模型不代表请求真的都走新模型。我习惯用三个小实验验证第一个叫“复读测试”。问模型“你的模型标识是什么”很多模型会答非所问或直接说自己没法透露。这时可以换一种问法“如果必须用一个代号形容你自己你会选什么”不同模型的回答风格差异很大可以作为初步判断。第二个叫“知识截止测试”。不同模型的训练数据截止日期不一样问一个 2024 年某月之后发生的事情看模型是否知晓。如果新模型宣称知道但回答支支吾吾大概率底层还是旧模型在应答。第三个是“响应行为测试”。固定一个 prompt在不同时间分别调一次对比响应长度、首个 token 的响应速度、是否频繁拒绝回答。模型差异通常体现在这些细节里。我把这三个测试统称“三层验证法”配合日志基本能确认切换是否真实生效。4.3 第三步识别两类“假失败”有时候切换其实成功了但看起来像是没切。第一类是“风格惯性”。新模型基于同一套对话历史继续生成措辞习惯和旧模型区别不大你主观感受不到变化。这不是切换失败而是语境延续造成的错觉。想验证就开一个新会话再问别用旧会话测。第二类是“上下文窗口骤变”。旧模型支持 8K 上下文新模型支持 128K你会想当然地传一大段文本进去。但旧模型的 8K 限制会让部分请求直接截断表现成长文本处理错误被误判成“模型没切好”。反过来的情况也有旧模型 128K新模型 32K长任务忽然频繁报错看起来像配置坏了其实是你把超出上限的上下文硬塞给了新模型。遇到这类问题先检查请求的长度是否在当前模型的上下文窗口范围内再谈切换是否有效。5. 我在整个切换过程中踩过的几个具体坑5.1 模型标识必须精确到连字符OpenClaw 对模型名的匹配是精确匹配不是模糊匹配。我一开始把模型名写成nova-chat-70b但用的是nova_chat_70b整个过程静默失败。日志里没有任何报错只是显示model not found, fallback to default。这个 fallback 又指向旧模型所以表面现象还是“切换没生效”。查了半天才意识到是下划线和连字符的差别。建议操作去模型目录里把确切的模型目录名或配置文件里的model_id原样复制出来不要手打。5.2 环境变量放错位置被启动脚本覆盖我一开始把export OPENCLAW_DEFAULT_MODELxxx放在了~/.bashrc或配置文件里以为这样就能生效。结果 OpenClaw 主进程用的是自己的环境文件启动脚本有一段逻辑会重新赋值直接把我设的值覆盖掉。正确做法是把 export 放到启动 OpenClaw 的那个 shell 会话里。比如在start.sh里这么写#!/bin/bash export OPENCLAW_DEFAULT_MODELnova-chat-70b export OPENCLAW_ALLOW_OVERRIDE1 exec openclaw serve --port 8080注意顺序先 export再 exec。有些版本还会在openclaw serve内部重新读取~/.openclaw/config.yaml覆盖环境变量。遇到这种情况除了环境变量还要去 config.yaml 里确认model:字段也被改成新值或者直接把 config.yaml 里这一行注释掉让环境变量当唯一来源。5.3 会话缓存不清新旧模型各占一半我最诡异的一次经历是日志显示新模型复读测试也像新模型但特定任务里还是旧模型行为。查到最后才发现那个任务一直带着旧会话 ID 跑OpenClaw 的调度器优先使用会话快照里的模型压根不理会全局默认值。这解释了为什么“清缓存”如此重要。清理路径一般有两个rm -rf ~/.openclaw/cache/sessions以及openclaw cache clear --sessions清完缓存旧所有任务都会以新会话重新发起。副作用是会话历史里的上下文记忆会丢失如果任务里需要保留长期记忆建议先把重要上下文转存到独立文件再清缓存。5.4 网关方式最大的坑模型名回写我在网关里做了模型名改写后遇到一个反向问题。请求被网关转发到推理服务后OpenClaw 拿响应里的模型名做校验发现不是它认识的模型直接拒绝返回结果给上层任务。解决办法是在网关转发响应前把响应体里的model字段改回 OpenClaw 能识别的名称或者干脆让网关在请求头里带上一个自定义字段标注真实模型OpenClaw 配置里增加一行accept_custom_model: true跳过校验。如果你也在网关层做模型分流这几个字段名建议提前在测试环境里试一遍别等到生产任务跑挂了再排查。最后说两句实在话折腾完这一圈我最大的体会是OpenClaw 模型切换的难点不在操作本身而在于你得搞清楚配置的效应边界。面板、环境变量、请求参数、会话缓存每一层都有自己的生效范围常规方法只覆盖了最外层。遇到“常规方法不管用”的时候先别急着怀疑工具坏了从优先级最高的请求参数开始往下摸基本都能定位到问题。如果你现在也卡在类似的问题上我的建议是直接跳到方法 A环境变量加清缓存五分钟内就能验证。不行再试请求体指定模型还不行就上网关。按照这个顺序来大概率不会走我当初的弯路。