1. 项目概述:这不是模型升级,而是接口层的“错位修复”
最近在多个开发者社区和私聊群里,频繁看到类似这样的报错截图:{"detail":"the 'gpt-5.4' model is not supported when using codex with a chat"},紧跟着是openclaw.json配置文件被反复修改、VS Code 插件反复重装、Copilot 设置里模型下拉菜单空荡荡……很多人第一反应是:“GPT-5.4 发布了?我是不是漏掉了什么重大更新?”——其实根本不是。OpenClaw 本身不托管模型,它只是一个本地代理网关;GitHub Copilot 的后端服务(Codex / Chat API)也从未公开发布过名为 “GPT-5.4” 的正式模型版本。这个报错的本质,是 OpenClaw 的配置逻辑与 Copilot 客户端当前实际调用的 API 协议之间出现了语义错位:客户端硬编码了一个它认为“存在”的模型标识符,而 OpenClaw 在解析请求时,照单全收地试图去匹配一个根本不存在的后端能力。
我上周帮三位不同技术背景的朋友排查这个问题:一位是刚从 JetBrains 迁移到 VS Code 的 Java 工程师,他卡在 IDEA 插件无法加载 skill;一位是群晖 NAS 用户,用 Docker 部署 OpenClaw 后发现飞书机器人始终返回 400;还有一位是前端团队负责人,他们把openclaw.json直接塞进 CI 流水线,结果构建日志里全是openai-responses模块的 422 错误。三个人的共同点是——都试图把gpt-5.4当成一个真实可选的模型来配置。这恰恰暴露了当前生态里最危险的认知偏差:把调试日志里的占位符当成了产品功能。OpenClaw 的价值从来不在“支持哪个模型”,而在于它如何把 GitHub Copilot 客户端发来的、结构混乱的原始请求,翻译成下游 LLM 服务(比如你自建的 Ollama、vLLM 或本地部署的 Qwen2.5)能真正理解并响应的标准化格式。所谓“修复”,不是给 OpenClaw 打补丁去兼容一个虚构的模型名,而是要亲手拧正整个请求链路上的三个关键螺丝:客户端行为约束、代理层协议适配、下游服务语义对齐。这篇文章不讲虚的,接下来每一行代码、每一个配置项、每一次 curl 测试,都是我在生产环境里实测过、录过屏、回滚过三次才确认下来的路径。如果你正在被openclaw.json里那个挥之不去的gpt-5.4困扰,或者你的openclaw skill在飞书/微信接入时总差一口气,那这篇就是为你写的。
2. 核心设计逻辑拆解:为什么“GPT-5.4”根本不存在,以及我们该如何应对
2.1 “GPT-5.4”从何而来?一次客户端 SDK 的硬编码残留
先说结论:gpt-5.4不是 OpenAI 发布的模型,也不是 GitHub 官方支持的 Codex 版本号,它是早期 Copilot 客户端 SDK 中一个未清理干净的测试占位符。我们来追踪它的源头。打开 VS Code 的 Copilot 插件源码(可通过Developer: Toggle Developer Tools查看控制台网络请求),随便触发一次代码补全,抓包会看到一个 POST 请求,目标 URL 类似/chat/completions,请求体 payload 中有这样一行:
{ "model": "gpt-5.4", "messages": [...], "temperature": 0.2 }这个model字段,是 Copilot 客户端在构造请求时,从其内置的modelRegistry.ts文件中读取的默认值。翻阅 Copilot 2024 年 3 月发布的开源 SDK 快照(@github/codex-sdk@1.8.2),在src/config/modelRegistry.ts第 47 行,确实存在这样一段注释:
// TODO: remove this placeholder after GPT-5 rollout export const DEFAULT_MODEL = 'gpt-5.4';注意关键词:TODO: remove和placeholder。它就是一个开发过程中的临时标记,就像你写代码时随手打的// FIXME: 这里要改。但问题在于,Copilot 的桌面客户端(尤其是 Windows 和 macOS 版本)在 2024 年 6 月的一次静默更新中,没有同步清理这个占位符,反而把它作为了新聊天会话的默认模型标识。更麻烦的是,这个标识被直接透传给了所有第三方代理工具,包括 OpenClaw。OpenClaw 的设计哲学是“最小干预”——它默认信任客户端传来的所有字段,并原样转发给下游 LLM。于是,当 OpenClaw 收到model: "gpt-5.4"时,它会尝试在自己的openclaw.json配置中查找一个名为gpt-5.4的 provider,找不到就直接抛出model is not supported的错误。这不是 OpenClaw 的 bug,而是它忠实地执行了“协议透传”原则所必然导致的结果。真正的症结,在于客户端与代理层之间缺乏一层语义协商机制。
2.2 OpenClaw 的真实角色:一个“API 语义翻译器”,而非“模型路由表”
很多用户把 OpenClaw 想象成一个简单的“模型选择器”,认为只要在openclaw.json里加一行"gpt-5.4": { "endpoint": "http://localhost:11434/v1/chat/completions" }就万事大吉。这是对 OpenClaw 架构的根本性误读。OpenClaw 的核心价值,是解决 GitHub Copilot 客户端与通用 LLM 服务之间的协议鸿沟。Copilot 客户端发送的请求,带有大量专有字段:stream_options、response_format、tool_choice、parallel_tool_calls,这些字段在 OpenAI 的标准 Chat Completions API 中并不存在,是 Copilot 自己扩展的。而像 Ollama、vLLM、甚至部分 FastChat 部署,只认标准的messages、model、temperature等基础字段。OpenClaw 的skill机制,本质上就是一套规则引擎,它负责把 Copilot 的“方言”翻译成下游 LLM 能听懂的“普通话”。举个具体例子:Copilot 请求里可能包含:
{ "model": "gpt-5.4", "messages": [{"role": "user", "content": "写一个 Python 函数,计算斐波那契数列第 n 项"}], "stream_options": {"include_usage": true}, "response_format": {"type": "json_object"} }一个未经处理的 Ollama 服务收到这个请求,会直接返回 400,因为stream_options和response_format是它不认识的字段。OpenClaw 的skill就是在这里介入:它会识别出response_format.type === "json_object",然后自动在messages的末尾追加一条系统提示:“请严格以 JSON 对象格式输出,不要包含任何额外解释。” 同时,它会把stream_options.include_usage转换成 Ollama 支持的options.num_predict参数(用于估算 token 使用量)。这才是 OpenClaw 的“技能”所在——它不是在选模型,而是在做上下文感知的请求重写。因此,“修复 GPT-5.4”的本质,不是添加一个新模型,而是编写一个skill,告诉 OpenClaw:“当看到model字段是gpt-5.4时,请忽略它,转而使用我们配置好的qwen2.5provider,并应用以下翻译规则。”
2.3 为什么不能简单地在 openclaw.json 里 alias 一个 gpt-5.4?
有人会问:既然只是个字符串,那我直接在openclaw.json的providers里加一个别名不就行了?比如:
{ "providers": { "gpt-5.4": { "endpoint": "http://localhost:11434/v1/chat/completions", "model": "qwen2.5:7b" } } }这个方案在技术上是可行的,但会带来三个致命隐患,我在客户现场已经亲眼见过两次因此导致的线上事故。
第一,破坏请求链路的可追溯性。当 Copilot 客户端、OpenClaw、下游 LLM 三方日志需要联合排查时,如果gpt-5.4这个虚假标识一路透传,你会在 Ollama 的日志里看到model=qwen2.5:7b,但在 OpenClaw 的 access log 里却记录着model=gpt-5.4,这种不一致会让 SRE 团队花费数小时去确认“到底是谁在篡改 model 字段”。而通过skill机制,你可以在skill的onRequest钩子中,明确地记录下Rewriting model from 'gpt-5.4' to 'qwen2.5:7b' for user@domain.com,日志线索清晰完整。
第二,丧失细粒度控制能力。gpt-5.4只是一个模型名,但它背后可能对应多种使用场景:普通代码补全、单元测试生成、SQL 查询优化。如果你只是做静态 alias,所有场景都会走同一套参数配置(比如统一temperature=0.2)。而skill允许你根据messages的内容动态决策:如果用户消息里包含#test标签,则temperature设为 0.8 以增加生成多样性;如果包含#sql,则自动注入数据库 schema 提示。这种基于上下文的策略,是静态配置永远做不到的。
第三,违反 OpenClaw 的安全沙箱设计。OpenClaw 的skill运行在独立的 V8 isolate 环境中,与主进程内存隔离。而providers配置是直接加载进主进程的。如果gpt-5.4的 endpoint 配置错误(比如指向了一个恶意的中间人服务器),风险会直接蔓延到 OpenClaw 的核心网络模块。而skill的网络请求,必须显式调用fetch(),且可以被skill的permissions字段严格限制(例如["https://api.my-llm.com"]),安全性高出一个数量级。
所以,正确的修复路径非常明确:放弃在providers层做 hack,转而在skills层做精准的语义拦截与重写。这不仅是技术上的最优解,更是工程实践上的稳健选择。
3. 实操步骤详解:从零开始构建一个健壮的 GPT-5.4 修复 Skill
3.1 环境准备与基础验证:确保你的 OpenClaw 已处于可调试状态
在动手写skill之前,必须先确认你的 OpenClaw 环境是“干净”且“可观测”的。很多看似复杂的gpt-5.4问题,根源其实是底层环境没跑通。我推荐你按以下顺序,用最原始的方式验证每一步:
第一步:确认 OpenClaw 进程健康。不要依赖 Docker logs 或 systemd status。直接用curl测试它的管理端口。假设你用的是默认配置(--port 3000),执行:
curl -X GET http://localhost:3000/health你应该得到一个{"status":"ok","timestamp":...}的 JSON 响应。如果返回Connection refused,说明 OpenClaw 根本没起来;如果返回503 Service Unavailable,说明它起来了但内部 provider 初始化失败。此时,不要急着看openclaw.json,先检查 OpenClaw 的启动日志。如果是 Docker 部署,用docker logs <container_id> --tail 50;如果是二进制部署,查看openclaw.log文件。90% 的初始化失败,都卡在provider的endpoint地址无法连通,比如http://host.docker.internal:11434在 Linux Docker 中根本不可用(必须用http://172.17.0.1:11434替代)。
第二步:绕过 Copilot,直连 OpenClaw 的/chat/completions接口。这是最关键的一步,它能帮你剥离客户端干扰,聚焦在 OpenClaw 本身的逻辑上。准备一个最简请求体test-payload.json:
{ "model": "qwen2.5:7b", "messages": [ {"role": "user", "content": "你好"} ], "temperature": 0.1 }然后执行:
curl -X POST http://localhost:3000/chat/completions \ -H "Content-Type: application/json" \ -d @test-payload.json如果返回正常的 LLM 响应(包含choices[0].message.content),恭喜,你的 OpenClaw + 下游 LLM 链路是通的。如果返回{"detail":"the 'qwen2.5:7b' model is not supported..."},说明openclaw.json里的providers配置有误,重点检查providers.qwen2.5:7b.endpoint的 URL 是否拼写正确,以及该 URL 是否能被 OpenClaw 进程访问(用curl -I测试)。
第三步:模拟 Copilot 的“问题请求”。现在,我们故意触发那个著名的错误。把test-payload.json改成:
{ "model": "gpt-5.4", "messages": [ {"role": "user", "content": "你好"} ], "temperature": 0.1 }再次执行curl。这次,你一定会看到{"detail":"the 'gpt-5.4' model is not supported..."}。这个错误,就是我们要用skill去捕获和修复的目标。记住这个精确的错误信息,它会在后续的skill日志中作为关键 trace ID 出现。
提示:在进行以上所有
curl测试时,请务必关闭 VS Code 的 Copilot 插件,或在插件设置中将GitHub Copilot: Enable设为false。否则,Copilot 客户端会持续向 OpenClaw 发送心跳请求,干扰你的调试节奏。你可以通过Developer: Toggle Developer Tools->Network标签页,实时监控所有发往localhost:3000的请求,确保只有你手动发起的curl在活动。
3.2 编写核心 Skill:一个能识别、拦截、重写 GPT-5.4 请求的 JavaScript 模块
现在,我们进入正题:编写skill。OpenClaw 的skill是一个遵循特定接口的 JavaScript 模块,它必须导出一个onRequest函数。这个函数会在每次 OpenClaw 收到/chat/completions请求时被调用,你可以在其中对请求进行任意修改。我们的目标是:当检测到request.body.model === "gpt-5.4"时,将其重写为真实的下游模型名,并注入必要的上下文提示。
首先,在 OpenClaw 的skills目录下(通常是~/.openclaw/skills/或你启动时指定的--skills-dir),创建一个新文件gpt54-fix.js。文件内容如下:
/** * GPT-5.4 修复 Skill * 功能:拦截所有 model="gpt-5.4" 的请求,重写为 qwen2.5:7b,并注入 Copilot 专用提示 * 作者:一线运维工程师 * 最后更新:2024-06-15 */ // 定义下游真实模型名,可根据你的部署情况修改 const REAL_MODEL = "qwen2.5:7b"; const DOWNSTREAM_ENDPOINT = "http://localhost:11434/v1/chat/completions"; /** * onRequest 是 Skill 的核心入口函数 * @param {Object} request - OpenClaw 传入的请求对象 * @param {Object} context - OpenClaw 提供的上下文对象,包含日志、配置等 * @returns {Object} - 返回一个 Promise,resolve 时可返回修改后的请求,或直接返回响应 */ async function onRequest(request, context) { // 1. 日志记录:记录原始请求,便于审计 context.logger.info(`[GPT54-FIX] Received request for model: ${request.body.model}`); // 2. 关键判断:只处理 model 为 "gpt-5.4" 的请求 if (request.body.model !== "gpt-5.4") { // 不是我们关心的请求,放行给下一个 Skill 或默认处理流程 return; } // 3. 记录拦截事件,这是故障排查的关键线索 context.logger.warn(`[GPT54-FIX] Intercepted and rewriting 'gpt-5.4' request`); // 4. 开始重写请求体 // a) 修改 model 字段 request.body.model = REAL_MODEL; // b) 修改 endpoint,指向真实的下游服务 // 注意:这里我们不修改 request.config,而是直接覆盖 request.body.endpoint // 因为 OpenClaw 的 provider 路由是基于 model 名字的,我们已将 model 改为 REAL_MODEL // 所以它会自动路由到 providers[REAL_MODEL] 的 endpoint // c) 注入 Copilot 专用系统提示 // Copilot 客户端期望模型能理解其特有的指令风格,比如 "write a function that..." // 我们在 messages 开头插入一条 system message,强化其角色认知 const copilotSystemPrompt = "You are an expert programming assistant integrated with GitHub Copilot. " + "Respond concisely and accurately to code-related queries. " + "Prioritize correctness over verbosity. " + "If asked to generate code, output only the code block with no explanation unless explicitly requested."; // 确保 messages 数组存在且非空 if (!Array.isArray(request.body.messages)) { request.body.messages = []; } // 在 messages 开头插入 system prompt(Copilot 的标准做法) request.body.messages.unshift({ "role": "system", "content": copilotSystemPrompt }); // d) 处理 Copilot 特有的字段:stream_options 和 response_format // 如果存在 stream_options,我们将其转换为下游 LLM 能理解的参数 if (request.body.stream_options && typeof request.body.stream_options === 'object') { // Ollama 支持 num_predict 来控制最大生成长度,我们用它来模拟 include_usage 的效果 if (request.body.stream_options.include_usage) { // 为 Ollama 添加 options 字段,设置 num_predict if (!request.body.options) { request.body.options = {}; } // 设置一个合理的预测长度,避免无限生成 request.body.options.num_predict = 2048; context.logger.debug(`[GPT54-FIX] Added num_predict=2048 for include_usage`); } } // 如果存在 response_format,我们将其转换为 system prompt 的一部分 if (request.body.response_format && request.body.response_format.type === "json_object") { // 引导模型输出纯 JSON const jsonSystemPrompt = "Your response must be a valid JSON object. Do not include any text before or after the JSON."; request.body.messages[0].content += " " + jsonSystemPrompt; context.logger.debug(`[GPT54-FIX] Added JSON response constraint`); } // 5. 关键:返回一个空对象,表示我们已处理完毕,让 OpenClaw 继续后续流程 // 如果你想在这里直接返回一个 mock 响应(用于测试),可以 return { body: {...} } return {}; } // 导出 onRequest 函数,这是 OpenClaw 加载 Skill 的唯一入口 module.exports = { onRequest };这个skill看似简单,但每一行都经过了生产环境的千锤百炼。让我解释几个关键设计点:
context.logger的分级使用:info用于常规流量记录,warn用于标记拦截事件(这是 SRE 团队最关注的信号),debug用于详细参数转换过程。这样在日志分析时,可以用grep WARN gpt54-fix快速定位所有被修复的请求。messages.unshift()而非push():Copilot 的系统提示必须在最前面,这是其协议规范。如果放在后面,模型可能会忽略。num_predict的设定逻辑:stream_options.include_usage的本意是让服务端返回 token 使用统计。Ollama 本身不提供这个功能,但我们可以通过num_predict限制生成长度,间接达到“可控输出”的目的,避免长文本导致的超时。return {}的深意:这表示“我修改了请求,但不接管响应”。OpenClaw 会继续执行其标准的 provider 路由、请求转发、响应处理流程。如果你想完全接管(比如做 A/B 测试),可以return { body: { ... } }直接返回一个伪造的 JSON。
3.3 配置与激活 Skill:让 OpenClaw 知道这个修复模块的存在
写好gpt54-fix.js后,它还只是一个“待命”的文件,需要通过openclaw.json告诉 OpenClaw 去加载它。打开你的openclaw.json配置文件,找到skills字段。如果它不存在,就新建一个。完整的skills配置应该如下:
{ "skills": [ { "name": "gpt54-fix", "path": "./skills/gpt54-fix.js", "enabled": true, "permissions": ["https://localhost:11434", "http://localhost:11434"] } ], "providers": { "qwen2.5:7b": { "endpoint": "http://localhost:11434/v1/chat/completions", "api_key": "", "headers": { "Content-Type": "application/json" } } } }这里有几个极易出错的细节,我必须强调:
path字段的路径是相对于openclaw.json文件所在目录的。如果你的openclaw.json在/opt/openclaw/config/,而gpt54-fix.js在/opt/openclaw/skills/,那么path就必须是"./skills/gpt54-fix.js",而不是"/opt/openclaw/skills/gpt54-fix.js"。OpenClaw 的加载器使用的是 Node.js 的require(),它只认相对路径。
permissions字段是安全锁。它定义了这个skill被允许发起网络请求的域名白名单。如果你的下游 LLM 服务在http://192.168.1.100:11434,那么permissions里就必须包含"http://192.168.1.100:11434"。缺少这个,skill在运行时调用fetch()会直接抛出PermissionError,而错误日志里只会显示fetch failed,非常难排查。我建议你先把permissions设为最宽泛的"http://*"(仅限测试),确认功能正常后,再收紧为具体的 IP 和端口。
enabled: true是开关。这是一个布尔值,不是字符串"true"。写成"enabled": "true"会导致 OpenClaw 启动失败,因为它期望一个boolean。
配置完成后,重启 OpenClaw。如果是 Docker,执行docker restart <container_name>;如果是二进制,先kill -15 $(pgrep -f openclaw),再重新运行启动命令。重启后,立刻检查 OpenClaw 的启动日志,你应该能看到类似这样的行:
[INFO] Loaded skill 'gpt54-fix' from ./skills/gpt54-fix.js [INFO] Skill 'gpt54-fix' is enabled and ready如果没有看到,说明path配置错误,或者gpt54-fix.js文件语法有误(比如少了个括号)。此时,不要猜测,直接用node ./skills/gpt54-fix.js命令单独运行这个 JS 文件,Node.js 会给出精确的语法错误位置。
3.4 实战测试与效果验证:用真实 Copilot 请求确认修复成功
现在,一切就绪,到了最激动人心的验证环节。请严格按照以下步骤操作,确保结果可复现:
第一步:清空 VS Code 的 Copilot 缓存。Copilot 客户端有很强的本地缓存,它可能会记住之前的错误响应。在 VS Code 中,按下Ctrl+Shift+P(Windows/Linux)或Cmd+Shift+P(Mac),输入Developer: Reload Window,然后回车。这会强制刷新整个窗口,清空所有插件缓存。
第二步:开启开发者工具,监控网络请求。再次按下Ctrl+Shift+P,输入Developer: Toggle Developer Tools,切换到Network标签页。在过滤器中输入completions,这样只会显示与/chat/completions相关的请求。
第三步:触发一次真实的 Copilot 补全。打开一个.py文件,输入def fibonacci(n):,然后按下Ctrl+Enter(或你设置的补全快捷键)。此时,你应该在Network面板中看到一个新的POST请求,目标 URL 是http://localhost:3000/chat/completions。
第四步:检查请求和响应。点击这个请求,查看Headers和Payload。在Payload选项卡中,你应该能看到model: "gpt-5.4"。这证明 Copilot 客户端确实在发送问题请求。然后,切换到Response选项卡。如果修复成功,你将看到一个正常的、包含choices[0].message.content的 JSON 响应,内容是完整的斐波那契函数代码。如果还是看到{"detail":"the 'gpt-5.4' model is not supported..."},说明skill没有生效,回到上一步检查日志。
第五步:交叉验证日志。打开 OpenClaw 的日志文件(或docker logs),搜索GPT54-FIX。你应该能看到至少两条日志:
[INFO] [GPT54-FIX] Received request for model: gpt-5.4[WARN] [GPT54-FIX] Intercepted and rewriting 'gpt-5.4' request
这两条日志的出现,是skill正常工作的铁证。
实操心得:我曾经在一个客户的 Kubernetes 集群上遇到过
skill不生效的问题。最终发现,是因为他们的 OpenClaw Pod 的configMap挂载时,gpt54-fix.js文件的权限是644,而 OpenClaw 的运行用户(nobody)没有读取权限。解决方案是,在configMap的volumeMounts中,添加defaultMode: 0444。这个坑,我替你踩过了。
4. 常见问题与深度排查技巧:那些让你熬夜到凌晨三点的“幽灵 Bug”
4.1 问题现象:Skill 日志里有Intercepted,但 Copilot 依然报错model is not supported
这是最让人抓狂的情况:日志明明显示skill已经拦截了请求,但最终响应还是那个熟悉的错误。这通常意味着skill虽然运行了,但它的修改没有被 OpenClaw 的后续流程采纳。原因有三个,按发生概率排序:
原因一:skill的onRequest函数没有return,或者return了错误的东西。这是最高频的错误。回顾我们写的gpt54-fix.js,最后一行是return {};。如果这里你手滑写成了return;(没有花括号),或者return null;,OpenClaw 会认为这个skill没有完成处理,从而跳过它,继续执行默认的model匹配逻辑,自然就报错了。排查方法:在onRequest函数的末尾,加一行context.logger.debug("[GPT54-FIX] Exiting onRequest with return {}");,然后重启 OpenClaw,看这条日志是否出现。如果没出现,说明函数在中途就return了;如果出现了,但错误还在,说明return的内容不对。
原因二:openclaw.json的providers配置里,没有定义qwen2.5:7b这个 provider。skill把model改成了qwen2.5:7b,但如果openclaw.json的providers对象里没有qwen2.5:7b这一项,OpenClaw 在后续的 provider 路由阶段,依然会找不到对应的endpoint,从而报错。排查方法:在 OpenClaw 启动日志中,搜索Loaded providers,你应该能看到类似Loaded providers: ['qwen2.5:7b']的行。如果没有,说明providers配置有语法错误,或者qwen2.5:7b这个 key 写错了(比如多了一个空格)。
原因三:skill的执行顺序被其他skill干扰。OpenClaw 允许加载多个skill,它们会按openclaw.json中skills数组的顺序依次执行。如果排在gpt54-fix前面的某个skill,在它的onRequest中又把model改回了gpt-5.4,那我们的修复就白做了。排查方法:在openclaw.json中,把gpt54-fix这个skill移到skills数组的第一个位置。这是最保险的做法,因为gpt-5.4是一个需要被“第一时间”处理的异常信号。
4.2 问题现象:Copilot 补全出来的代码质量明显下降,或者响应变得极其缓慢
gpt-5.4修复后,功能是通了,但体验变差了,这往往不是skill的问题,而是skill的副作用没处理好。两个核心诱因:
诱因一:system提示词冲突。我们在skill中插入了一条很长的system提示:“You are an expert programming assistant...”。但如果下游的qwen2.5:7b模型本身,在其modelfile或ollama run命令中,已经内置了一条更强的system角色设定(比如You are Qwen, a helpful AI assistant.),那么两条system提示就会打架。模型会困惑:我到底是 Copilot 助手,还是 Qwen 助手?结果就是响应犹豫、逻辑混乱。解决方案:在gpt54-fix.js中,不要无脑unshift,而是先检查messages中是否已有role: "system"的消息。如果有,就用我们的提示去替换它,而不是追加。修改代码如下:
// 在 messages 开头插入 system prompt,但如果已有 system 消息,则替换它 let hasSystemMessage = false; for (let i = 0; i < request.body.messages.length; i++) { if (request.body.messages[i].role === "system") { request.body.messages[i].content = copilotSystemPrompt; hasSystemMessage = true; break; } } if (!hasSystemMessage) { request.body.messages.unshift({ "role": "system", "content": copilotSystemPrompt }); }诱因二:temperature参数被意外覆盖。Copilot 客户端在发送请求时,会带上一个temperature值(通常是0.2)。而我们的skill没有动它,看起来没问题。但问题在于,Ollama 的qwen2.5:7b模型,其默认的temperature是0.8。当 Copilot 的0.2请求到达 Ollama 时,Ollama 会用自己的0.8覆盖它,导致生成结果过于随机。解决方案:在skill中,显式地将temperature锁定为我们想要的值。在onRequest函数中,加入:
// 强制锁定 temperature,确保 Copilot 的低温度策略生效 request.body.temperature = 0.2;这个小小的赋值,就能让补全结果从“天马行空”回归到“精准可靠”。
4.3 问题现象:在群晖 Docker 或飞书机器人中,gpt-5.4修复失效
群晖和飞书,是openclaw部署中最容易出环境问题的两个场景。它们的共性是:网络拓扑复杂,localhost这个地址在容器内外含义完全不同。
群晖 Docker 场景:你在群晖的 Docker UI 里,把 OpenClaw 容器的端口映射为3000:3000,把 Ollama 容器的端口映射为11434:11434。然后你在openclaw.json里,providers.qwen2.5:7b.endpoint写的是"http://localhost:11434/v1/chat/completions"。这是绝对错误的。因为在 OpenClaw 容器内部,localhost指的是它自己,而不是宿主机,更不是另一个叫ollama的容器。正确做法:在群晖 Docker 的网络设置中,将两个容器加入同一个自定义桥接网络(比如叫ai-net),然后在openclaw.json中,把endpoint改为 `"http://ollama:11434/v