当前位置: 首页 > news >正文

OpenClaw二次开发实战:从插件定制到国产大模型适配

1. 为什么必须亲手改 OpenClaw?——从“开箱即用”到“专属智能体”的真实分水岭

你第一次打开 OpenClaw,点几下就调通了大模型、执行了命令、连上了天气 API,心里一热:“这工具真香!”但很快,现实就给你泼了盆冷水:企业内网系统没开放公网接口,AI 回复里总夹带英文术语,UI 上那个“重置会话”按钮位置反人类,更别说想让 AI 主动推送每日销售简报——这些需求,官方文档里连影子都找不到。这时候你才真正意识到:OpenClaw 不是“用完即走”的玩具,而是一套可塑性极强的AI 智能体底座。它的价值不在于预设功能多炫酷,而在于你能否把它拧成自己业务流水线里的一颗精密螺丝。我带过 7 个不同行业的二次开发项目,从制造业设备巡检助手,到律所合同风险初筛工具,再到高校科研文献摘要生成器,无一例外,最终交付的都不是“OpenClaw 加了个插件”,而是“一个叫‘智巡通’‘法眼初筛’‘研读快’的独立产品”。这背后的核心动作,就是二次开发。它不是程序员的专利,而是所有想把 AI 真正落地进业务毛细血管的人,必须掌握的“最后一公里”手艺。本文讲的不是“如何看懂源码”,而是“如何在不破坏系统稳定性的前提下,精准地、可维护地、可升级地,在关键节点上打上自己的补丁”。你会看到,一个天气插件的注册,背后是整个插件生命周期管理的设计哲学;一条自定义 CLI 指令的添加,牵扯着命令解析器与日志系统的耦合逻辑;而适配通义千问,远不止是换掉一个 URL,而是要理解 OpenClaw 如何用统一的ModelRequest/ModelResponse接口,把千差万别的大模型 API “翻译”成自己能听懂的语言。这不是教科书式的理论推演,而是我在 Gitee 仓库提交了 217 次代码、回滚过 3 次主干、被测试环境凌晨三点的告警电话叫醒过 5 次之后,亲手验证过的每一步。接下来的内容,没有一句废话,每一个命令、每一行代码、每一个配置项,都对应着一个真实踩过的坑和一次成功的交付。

2. 开发环境:别让第一步就卡死在依赖地狱里(2026 年实测最稳方案)

很多人卡在第一步,不是因为技术不行,而是败给了环境。Node.js 版本冲突、pnpm 镜像失效、Git 子模块拉取失败……这些看似琐碎的问题,足以消耗掉新手一整天的热情。我见过太多人,在pnpm install卡在 87% 时选择放弃。所以这一节,我只讲 2026 年当下最可靠、最省心的方案,所有步骤均在 macOS Sonoma 14.5、Ubuntu 24.04 LTS 和 Windows 11 23H2 上实测通过。

2.1 核心依赖:版本不是“≥”,而是“必须等于”

OpenClaw 的 monorepo 架构对依赖版本极其敏感。node -v ≥ 22.0.0这句话,很多教程一笔带过,但实际中,v22.14.0v22.15.0可能就因为 V8 引擎的一个小更新,导致packages/ui的 Vue Devtools 插件热更新完全失效。我的建议是:严格锁定为v22.14.0。这不是保守,而是基于血泪教训。pnpm -v ≥ 9.0.0同理,v9.12.2是目前最稳定的版本,它能完美处理packages/corepackages/runtime之间复杂的 peerDependencies 关系。至于git -v ≥ 2.40.0,重点在于其对 sparse-checkout 的支持,这是国内镜像加速的关键。下面给出一套零失误的安装脚本:

# macOS (使用 Homebrew) brew install node@22 brew unlink node && brew link --force node@22 npm install -g pnpm@9.12.2 brew install git@2.40 # Ubuntu/Debian (使用 NodeSource 官方源) curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - sudo apt-get install -y nodejs sudo npm install -g pnpm@9.12.2 sudo apt-get install -y git # Windows (使用 Scoop) scoop install nodejs-lts@22.14.0 scoop install pnpm@9.12.2 scoop install git

提示:执行完后务必验证node -v输出为v22.14.0pnpm -v9.12.2。任何偏差都可能导致后续构建失败,不要心存侥幸。

2.2 源码拉取:为什么 Gitee 镜像比 GitHub 官方库更值得信赖?

官方 GitHub 仓库(https://github.com/OpenClaw/OpenClaw.git)在 2026 年上半年已基本停止维护,所有新功能、Bug 修复和安全补丁都发布在 Gitee 镜像(https://gitee.com/OpenClaw-CN/openclaw-cn.git)上。这个镜像并非简单同步,而是由国内核心贡献者团队进行的增强型维护,包括:

  • 预编译了packages/uinode_modules缓存,避免pnpm install时反复下载@vue/dev-server
  • packages/gateway中所有外部 API 调用(如天气、股票)替换为国内可用的免费替代服务;
  • scripts/目录下新增了sync-upstream.sh脚本,可一键将 Gitee 分支与 GitHub 官方最新 commit 对齐。

拉取命令必须加上--depth=1参数,这是提速的关键:

git clone --depth=1 https://gitee.com/OpenClaw-CN/openclaw-cn.git cd openclaw-cn

注意:不要用git clone --recursive。OpenClaw 的子模块(如packages/ui/node_modules)在 monorepo 下是通过 pnpm 的 workspace 功能管理的,手动拉取子模块只会制造混乱。

2.3 依赖安装与开发启动:pnpm install的隐藏开关

pnpm install看似简单,但它背后有三个决定成败的隐藏开关,官方文档从未提及:

  1. --no-frozen-lockfile:首次安装时,必须加此参数。它会强制 pnpm 忽略pnpm-lock.yaml中的哈希值,重新计算所有依赖的精确版本。这是因为 Gitee 镜像的 lock 文件可能与你的 Node.js 版本不完全兼容。
  2. --reporter ndjson:加上这个,安装过程会输出结构化日志,便于排查卡在哪个包。当安装卡住时,你可以tail -f pnpm-debug.log实时查看。
  3. --filter:如果你只关心coreui的开发,可以pnpm install --filter @openclaw/core --filter @openclaw/ui,跳过cliruntime,节省 40% 时间。

完整的、最稳妥的安装命令如下:

pnpm install --no-frozen-lockfile --reporter ndjson

启动开发服务时,pnpm dev:uipnpm dev:gateway必须在两个独立的终端窗口中运行。这是因为dev:ui启动的是 Vite 的前端开发服务器(端口 5173),而dev:gateway启动的是 Express 的后端网关(默认端口 3000)。它们之间通过proxy配置进行通信。如果你在一个终端里用&&连接,dev:gateway进程会因dev:ui的热重载而被意外终止。

实操心得:我习惯在 VS Code 中使用Terminal: Split Terminal功能,左边跑 UI,右边跑 Gateway。这样,当 UI 报错时,我能立刻在右边看到 Gateway 是否也抛出了异常,从而快速判断是前端渲染问题,还是后端数据返回问题。这个简单的操作,能帮你把 80% 的“白屏”问题定位时间从 30 分钟缩短到 3 分钟。

3. 源码结构解剖:一张图看懂 OpenClaw 的“五脏六腑”与手术刀该往哪下

很多开发者面对packages/目录时,第一反应是“这么多文件,从哪下手?”。其实,OpenClaw 的架构设计得非常清晰,它遵循的是经典的“关注点分离”原则,每一层都只做一件事,并且这件事做得非常极致。把它想象成一家现代化的工厂:ui是面向客户的展厅,gateway是前台接待处,core是中央调度室,runtime是车间里的机械臂,而cli则是厂长随身携带的对讲机。理解了这个类比,你就知道,想改客户看到的界面,就去ui;想控制谁能进厂、能进哪个车间,就去gateway;想改变生产计划(AI 意图解析),就去core;想给机械臂加个新夹具(新命令),就去runtime;想让厂长用语音直接下达指令,就去cli。下面这张表,是我根据 2026 年最新版源码(commit:a1b2c3d)整理出的“手术指南”,它不仅告诉你每个目录是干什么的,更告诉你哪些文件是绝对不能碰的“心脏”,哪些是你可以自由发挥的“四肢”

目录核心作用开发重点(可安全修改)绝对禁区(严禁修改)修改后影响范围
packages/ui前端用户界面,Vue3 + TypeScript + Piniasrc/views/(页面组件)、src/components/(通用组件)、src/assets/styles/(CSS 变量)src/main.ts(Vue 实例初始化)、src/router/index.ts(路由守卫逻辑)、src/stores/useSessionStore.ts(会话状态管理核心)仅影响 UI 层,不影响任何业务逻辑和数据流
packages/gatewayHTTP 网关,负责请求路由、鉴权、日志、跨域src/middleware/auth.ts(自定义鉴权逻辑)、src/routes/api/v1/(新增 API 接口)、src/config/server.ts(端口、HTTPS 配置)src/app.ts(Express 应用实例创建)、src/middleware/cors.ts(CORS 头设置)、src/utils/proxy.ts(反向代理核心)影响所有进出 OpenClaw 的网络请求,修改不当会导致整个服务不可用
packages/coreAI 智能体大脑,负责意图识别、任务规划、插件调度、模型调用src/plugins/(所有插件实现)、src/model/modelManager.ts(模型适配器注册)、src/scheduler/taskPlanner.ts(任务编排算法)src/index.ts(Core 模块导出入口)、src/types/(所有类型定义文件)、src/utils/llmUtils.ts(LLM 通用工具函数)影响所有 AI 决策和执行,是整个系统最核心、最敏感的部分
packages/runtime执行引擎,负责调用系统命令、脚本、HTTP 请求等src/commands/(内置命令实现)、src/executors/(执行器抽象)、src/permissions/(权限检查策略)src/index.ts(Runtime 入口)、src/executor/ExecutorFactory.ts(执行器工厂模式核心)影响所有“行动”类操作,比如openclaw run script.shopenclaw exec curl ...
packages/cli命令行工具,提供openclaw全局命令src/commands/(自定义指令)、src/utils/logger.ts(日志格式化)、src/config/configManager.ts(配置文件读写)src/index.ts(CLI 入口)、src/program.ts(Commander 初始化)仅影响命令行交互体验,不影响 Web UI 和后台服务

这张表的价值,远不止于“知道改哪”。它揭示了一个重要事实:OpenClaw 的可扩展性,是通过“约定优于配置”来实现的。比如,你想加一个新插件,不需要去改core的任何一行核心代码,只需要在packages/core/src/plugins/下新建一个.ts文件,实现Plugin接口,然后在index.tsregister一下。这个过程,就像给工厂的调度室(core)增加一份新的《标准作业指导书》(SOP),而不用去拆解调度室的电脑主板。这种设计,保证了你在深度定制的同时,依然能平滑地接收上游的版本更新。我曾帮一家银行客户将 OpenClaw 改造成内部合规审查助手,他们要求所有插件必须经过内部安全审计。我们就是严格遵循这个结构,在packages/core/src/plugins/下开发了 12 个插件,每个插件都独立打包、独立审计、独立部署,最终成功上线,且后续 OpenClaw 官方发布了 3 个大版本,我们的插件一个都没改,全部兼容。

4. 实战 1:开发第一个自定义插件——不只是“Hello World”,而是理解插件生命周期的起点

“天气查询”插件,是 OpenClaw 二次开发的“Hello World”,但它的意义远不止于此。它是一个完美的教学案例,因为它完整地覆盖了插件开发的四个核心阶段:定义(Definition)、注册(Registration)、初始化(Initialization)和执行(Execution)。很多新手只关注handler函数,却忽略了initactions数组的精妙设计。下面,我将带你逐行拆解weather.plugin.ts,并告诉你,为什么这样写,才是“生产环境友好”的写法。

4.1 插件定义:parameters不是摆设,而是 AI 的“输入说明书”

parameters: [ { name: 'city', type: 'string', required: true, description: '城市名称,如北京、上海' } ],

这段代码,表面上是在告诉 OpenClaw “这个插件需要一个叫 city 的字符串参数”,但它的深层作用,是为 OpenClaw 的core层的意图识别引擎提供训练数据。当你在 UI 里输入“查一下深圳的天气”,OpenClaw 的 LLM 会将这句话解析成一个 JSON 结构,其中action字段会被识别为get-weather,而parameters字段则会被填充为{ "city": "深圳" }。这个过程之所以能成功,正是因为parameters数组里的description字段,为 LLM 提供了足够的上下文语义。如果description写成“城市”,LLM 就无法区分“城市”是指“北京市”还是“城市级别”。所以,description的写作,是一门“给 AI 写说明书”的艺术。我建议的黄金法则:名词 + 示例 + 限制。例如,对于一个需要邮箱的插件,description应该是:“用户的电子邮箱地址,如user@example.com,必须包含 @ 符号”。

4.2 插件执行:handler函数里的“防御式编程”

handler: async (params) => { const { city } = params; try { const res = await axios.get( `https://api.vvhan.com/api/weather?city=${encodeURIComponent(city)}` ); return { success: true, data: res.data, message: `查询到${city}的天气:${res.data.info}` }; } catch (e) { return { success: false, message: `查询失败:${(e as Error).message}` }; } }

这段代码,新手常犯的错误是直接throw e。这在开发环境没问题,但在生产环境,一个未捕获的 Promise Rejection 会直接导致整个gateway服务崩溃。正确的做法,是像上面这样,永远返回一个结构化的、符合ActionResponse接口的对象success: true/false是给core层的调度器看的,它决定了后续是继续执行下一个动作,还是进入错误处理流程。message字段,则是给最终用户看的,它必须是人类可读的、有温度的提示,而不是冰冷的AxiosError: Request failed with status code 404。我在线上环境见过最惨的案例,一个插件的message返回了Error: timeout of 5000ms exceeded,结果客服人员拿着这个报错去跟客户解释,客户以为是他们的网络有问题……所以,message的文案,一定要经过产品经理的审核。

4.3 插件注册:index.ts里的“单例陷阱”

import { PluginManager } from '../manager/plugin'; import WeatherPlugin from './weather.plugin'; const pluginManager = new PluginManager(); pluginManager.register(WeatherPlugin); export default pluginManager;

这段代码,藏着一个巨大的陷阱:new PluginManager()PluginManager是一个单例管理器,它的职责是全局唯一地管理所有插件的生命周期。如果你在index.ts里每次都new一个,那么core层其他地方(比如taskPlanner.ts)通过import { pluginManager } from '../plugins'获取到的,就是一个全新的、空的实例,你的WeatherPlugin就永远不会被发现。正确的写法,是确保PluginManager的实例在整个应用生命周期内只有一个。OpenClaw 的标准做法,是在packages/core/src/index.ts中创建并导出这个单例:

// packages/core/src/index.ts import { PluginManager } from './manager/plugin'; import { initPlugins } from './plugins'; // 创建单例 export const pluginManager = new PluginManager(); // 初始化所有插件(这个函数会自动导入并注册 index.ts 里的所有插件) initPlugins(pluginManager); export { pluginManager };

然后,在你的weather.plugin.ts里,注册方式就变成了:

// packages/core/src/plugins/weather.plugin.ts import { pluginManager } from '../index'; // 导入全局单例 import { Plugin, Action } from '../types/plugin'; import axios from 'axios'; const WeatherPlugin: Plugin = { // ... 其他定义 }; // 直接注册到全局单例 pluginManager.register(WeatherPlugin); // 注意:这里不再 export default,因为注册动作已经完成

实操心得:我养成了一个习惯,在每次开发完一个新插件后,都会在packages/core/src/plugins/index.ts里加一行console.log('Plugin [name] registered')。然后启动pnpm dev:gateway,在终端日志里搜索registered。如果没看到,说明注册失败,立刻回头检查import路径和register调用。这个小小的日志,帮我避开了 90% 的“插件不生效”问题。

5. 实战 2:扩展自定义系统指令——让 OpenClaw 成为你终端里的“瑞士军刀”

CLI(命令行界面)是 OpenClaw 与操作系统最直接的桥梁。openclaw my-command这样的指令,看起来只是加了一条命令,但它背后打通了从用户输入、参数解析、权限校验到最终执行的完整链路。很多开发者只满足于“能跑起来”,却忽略了这条链路上的每一个安全关卡。本节,我将以my-command为例,带你深入packages/cli的底层,看看如何写出一个既强大又安全的自定义指令。

5.1 指令逻辑:commander的高级用法与权限边界

import { Command } from 'commander'; import { logger } from '../../utils/logger'; export const myCommand = new Command('my-command') .description('我的自定义指令:打印指定信息') .argument('<message>', '要打印的信息') .option('-r, --repeat <times>', '重复次数', '1') .action((message, options) => { const repeat = parseInt(options.repeat); for (let i = 0; i < repeat; i++) { logger.info(`[自定义指令] ${message}`); } logger.success('自定义指令执行完成!'); });

这段代码,展示了commander库的两个核心能力:.argument()定义必填参数,.option()定义可选参数。但真正的精髓,在于.action()回调函数里的内容。注意,我在这里没有使用console.log,而是用了logger.infologger.success。这是因为packages/cli的日志系统是统一的,它会将所有日志按等级(info, warn, error, success)分类,并写入~/.openclaw/logs/cli.log。这对于线上问题排查至关重要。想象一下,当客户说“我的自定义指令不工作了”,你只需要grep "my-command" ~/.openclaw/logs/cli.log,就能看到完整的执行轨迹。

更重要的是,my-command.action()函数,是运行在当前用户权限下的。这意味着,如果你在action里写了execSync('rm -rf /'),那后果不堪设想。所以,OpenClaw 的cli层有一个隐含的、但极其重要的设计:所有自定义指令,都必须通过runtime层的Executor来执行危险操作my-command这个例子,它只是打印信息,属于安全操作,所以可以直接用logger。但如果你要写一个openclaw backup-db指令,它的action函数里,就应该调用runtime提供的executeCommand方法:

// packages/cli/src/commands/backup-db.ts import { Command } from 'commander'; import { executeCommand } from '@openclaw/runtime'; // 正确引入 import { logger } from '../../utils/logger'; export const backupDbCommand = new Command('backup-db') .description('备份数据库') .argument('<db-name>', '数据库名称') .action(async (dbName) => { try { // 通过 runtime 执行,它会进行权限检查和沙箱隔离 const result = await executeCommand({ command: 'mysqldump', args: ['-u', 'root', '-p', dbName], // 这里可以配置超时、工作目录、环境变量等 timeout: 30000, cwd: '/var/backups' }); logger.success(`数据库 ${dbName} 备份成功!`); } catch (e) { logger.error(`数据库备份失败:${e.message}`); } });

5.2 指令注册:program.parse()的时机与顺序

import { program } from 'commander'; import { myCommand } from './commands/my-command'; program.addCommand(myCommand); program.parse(process.argv);

这段注册代码,看似简单,但顺序至关重要。program.parse(process.argv)是整个 CLI 的“启动开关”,它会解析process.argv(即你在终端输入的openclaw my-command "Hello"),然后根据addCommand注册的指令列表,找到匹配的myCommand并执行其action。因此,addCommand必须在parse之前调用。这是一个典型的“注册-启动”模式。我曾经遇到一个诡异的 Bug:my-command总是报error: unknown command 'my-command'。排查了整整一天,最后发现,是因为在packages/cli/src/index.ts里,program.parse()被写在了addCommand的前面。这个错误非常隐蔽,因为 TypeScript 编译不会报错,只有运行时才会暴露。

另一个容易被忽视的点是:program是一个全局单例。packages/cli/src/index.ts是整个 CLI 的入口,它只应该被node dist/cli/index.js执行一次。如果你在my-command.ts里也import { program } from 'commander'并尝试addCommand,就会导致指令被注册两次,从而引发不可预知的副作用。所以,所有addCommand的调用,都必须集中在packages/cli/src/index.ts这一个文件里。这是一个硬性规范,也是保证 CLI 行为可预测的基础。

实操心得:为了防止忘记注册,我创建了一个packages/cli/src/commands/_registry.ts文件,里面只有一行代码:export * from './my-command';。然后在index.ts里,我用import { myCommand } from './commands/_registry';。这样,当我开发完一个新指令后,只需要在_registry.ts里加一行export * from './new-command';,就能确保它一定会被index.ts导入并注册。这个小小的“注册中心”,让我再也没错过任何一个自定义指令。

6. 实战 3:适配国产大模型——不是“换个 API Key”,而是重构你的 AI 通信协议

适配通义千问,是本教程里技术含量最高的一环。它绝不是“把 OpenAI 的 URL 换成阿里云的 URL”那么简单。它是一次对 OpenClawAI 通信协议的深度解构与重构。OpenClaw 的core层,本质上是一个“AI 协议转换器”。它向上,为uicli提供统一的、简洁的ask()接口;向下,为各种大模型(OpenAI, Anthropic, Tongyi)提供标准化的request()方法。ModelAdapter接口,就是这个转换器的“插槽”。本节,我将带你亲手打造这个插槽,并告诉你,为什么TongyiAdapterrequest方法里,要对res.data.choices[0].message.content进行如此精确的提取。

6.1 模型适配器:ModelRequestModelResponse的契约精神

// packages/core/src/types/model.ts export interface ModelRequest { messages: Array<{ role: string; content: string }>; temperature?: number; max_tokens?: number; } export interface ModelResponse { content: string; finishReason: string; raw: any; }

这两个接口,是 OpenClaw 的“宪法”。ModelRequest规定了core层向任何大模型发起请求时,必须提供的数据格式;ModelResponse则规定了任何大模型返回的数据,必须被“翻译”成的格式。TongyiAdapter的核心任务,就是在这两者之间架起一座桥。我们来看request方法的关键部分:

async request(req: ModelRequest): Promise<ModelResponse> { try { const res = await axios.post( 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions', { model: 'qwen-turbo', messages: req.messages, temperature: req.temperature || 0.7 }, { headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json' } } ); // 关键:将通义千问的原始响应,映射到 ModelResponse 接口 return { content: res.data.choices[0].message.content, finishReason: res.data.choices[0].finish_reason, raw: res.data }; } catch (e) { throw new Error(`通义千问请求失败:${(e as Error).message}`); } }

这段代码的精妙之处,在于return语句。res.data.choices[0].message.content是通义千问 API 的标准返回路径,但ModelResponse.content是 OpenClaw 的标准。这个映射,就是“协议转换”的全部意义。如果通义千问的 API 发生变更(比如把choices改成data),你只需要修改TongyiAdapter里的这一行,core层的其他所有代码(taskPlanner.ts,pluginManager.ts)都无需改动。这就是“面向接口编程”的威力。

6.2 适配器注册:createModelAdapter的“工厂模式”实践

// packages/core/src/model/modelManager.ts export function createModelAdapter(type: string, config: any) { switch (type) { case 'openai': return new OpenAIAdapter(config); case 'tongyi': // 新增 return new TongyiAdapter(config); default: throw new Error(`不支持的模型类型:${type}`); } }

这个switch语句,是一个典型的“工厂模式”(Factory Pattern)实现。它把“创建什么类型的适配器”这个决策,从具体的业务代码中抽离出来,集中到了一个统一的工厂函数里。这样做有两个巨大好处:

  1. 可扩展性:未来要加百川、智谱、月之暗面,你只需要在switch里加一个case,写一个新Adapter类,就完成了全部工作。
  2. 可测试性:你可以为createModelAdapter写单元测试,模拟传入'tongyi',断言它返回的是TongyiAdapter的实例,而无需启动真实的网络请求。

提示:config参数的类型是any,这在 TypeScript 里是不推荐的。在生产项目中,你应该为每种模型定义一个具体的配置接口,比如TongyiConfig,并在createModelAdapterswitch分支里进行类型断言,以获得更好的类型安全。

6.3 配置与验证:config.yaml里的“信任链”

model: type: tongyi config: apiKey: 你的通义千问API Key

这个 YAML 配置,是 OpenClaw 启动时加载模型适配器的“钥匙”。type: tongyi告诉modelManager去工厂里找TongyiAdapterconfig.apiKey则是打开这把锁的密码。但这里有一个至关重要的安全细节:apiKey绝对不能硬编码在源码里~/.openclaw/config.yaml是用户家目录下的文件,它默认是600权限(只有文件所有者可读写),这是保护密钥的第一道防线。在企业环境中,我们通常会把这个配置文件放在一个受控的、加密的配置中心(如 HashiCorp Vault),然后在pnpm dev:gateway启动时,通过环境变量OPENCLAW_CONFIG_PATH指向它。这样,密钥就永远不会出现在 Git 仓库或开发者的本地磁盘上。

验证适配是否成功,最直接的方法,不是看 UI 是否能对话,而是gateway的日志里搜索TongyiAdapter。当你启动服务后,gateway会在初始化时打印Using model adapter: TongyiAdapter。如果没看到,说明createModelAdapter没有被正确调用,或者config.yamltype写错了。这个日志,是你调试模型适配问题的“生命线”。

7. 打包发布:从本地开发到可交付产品的最后一步

开发完成,只是万里长征的前半程。如何把你的定制版 OpenClaw,变成一个可以分发给同事、客户甚至上架到公司内网的“产品”,这才是二次开发的终极目标。pnpm build看似一个简单的命令,但它背后是一整套现代前端/后端工程化流水线。本节,我将为你揭开build脚本的神秘面纱,并分享几个在企业交付中屡试不爽的“打包技巧”。

7.1 全量构建:pnpm build做了什么?

执行pnpm build,它会依次触发以下步骤:

  1. build:ui: 运行vite build,将packages/ui编译成静态资源(HTML/CSS/JS),输出到packages/ui/dist
  2. build:gateway: 运行tsc,将packages/gateway的 TypeScript 代码编译成 JavaScript,输出到packages/gateway/dist
  3. build:core: 同样运行tsc,编译packages/core,输出到packages/core/dist
  4. build:runtime: 编译packages/runtime
  5. build:cli: 编译packages/cli,并生成可执行的dist/cli/index.js

这个过程,是高度并行化的。pnpm会利用workspace的依赖关系图,自动确定编译顺序。例如,gateway依赖core,所以build:core一定会在build:gateway之前完成。你不需要手动干预。

7.2 打包 CLI:生成真正的“可执行文件”

pnpm build:cli是一个关键步骤。它不仅仅编译代码,还会执行pkg工具(一个 Node.js 应用打包器),将dist/cli/index.js及其所有依赖,打包成一个无需安装 Node.js 即可运行的二进制文件。这对于交付给 IT 部门或非技术人员至关重要。

# 打包为 macOS 可执行文件 pnpm build:cli --target macos-x64 # 打包为 Windows 可执行文件 pnpm build:cli --target win-x64 # 打包为 Linux 可执行文件 pnpm build:cli --target linux-x64

--target参数指定了目标平台。pkg会自动下载对应平台的 Node.js 运行时,并将其与你的代码一起打包。最终生成的文件,比如openclaw-macos,就是一个独立的、双击即可运行的程序。它的大小通常在 80MB 左右,包含了整个 Node.js 运行时。虽然体积不小,但换来的是极致的易用

http://www.jsqmd.com/news/1178046/

相关文章:

  • PyScript不是JS替代品,而是零基础Python用户的应急交互工具
  • 合肥家长别骂娃!上课走神多动,可能是专注力短板
  • 打破行业套路!千元级全功能知识产权管理系统,重构知产数字化性价比标准!!!
  • 你的AI账单,你自己看得懂吗?
  • 亨得利官方名表服务中心|全新维修地址及客服热线权威信息通告(2026年7月最新) - 亨得利官方博客
  • LinkAGI:面向 Claude Code、Codex 与 Gemini CLI 的统一 API 接入服务
  • 系统架构师必知:3种最大流算法对比(Ford-Fulkerson vs Edmonds-Karp vs Dinic)
  • 九大网盘高速下载终极指南:LinkSwift 直链助手完整使用教程
  • AMD EPYC 9004系列与ARM服务器CPU:5项关键指标实测与混合云选型分析
  • PyTorch模型生产可观测性实战:Prometheus+Loki+Jaeger四层架构
  • 2026年7月哈尔滨玻璃/黑龙江钢化玻璃品牌选择策略_黑龙江屿泰玻璃制品有限公司 - 行业平台推荐
  • 宝玑中国官方售后服务中心|最新官方地址和维修热线权威信息通告(2026年7月更新) - 亨得利钟表维修中心
  • Granger因果检验:预测增强性检验而非真实因果推断
  • 2026年聚氨酯同步带厂家口碑榜:哪家质量更靠谱?
  • 2026年7月惠州快充适配器电源ATE测试设备/惠州工业控制电源ATE测试设备选厂核心技巧_扶光科技(惠州)有限公司 - 行业平台推荐
  • 产后私密清爽护理泡沫使用注意
  • 物理AI:打破虚拟边界,解锁AI落地物理世界的下一代革命
  • 差分隐私与可信执行环境协同实现数据可用不可见
  • Python遗传算法实战:100皇后问题工程化求解与调优
  • 多Agent设计与工程
  • Win10专业工作站版极简一键重装方案详解
  • 深度学习算法实战指南:从CNN到Transformer的场景选择与代码实现
  • SVD 手眼标定法:从 9 组数据求解 AX=XB 的 5 步核心推导
  • 亲身到店探访杭州亨得利官方名表服务中心|服务热线及全部维修地址(2026年7月更新) - 亨得利官方
  • 2026年7月江苏吨袋倒袋拆包机/吨袋拆包机工厂评估指南_南京共赢粉体设备有限公司 - 行业平台推荐
  • 为什么药品冷藏箱不能只看最低温度?——基于PMG MP8 6L压缩机药品冷藏箱的一次完整工程测试解析
  • Excel 2021/365 数据透视表实战:5分钟重构千行住宿表,实现多维度统计
  • 亲身到店探访杭州亨得利官方名表服务中心|官方热线及详细网点地址(2026年7月更新) - 亨得利官方
  • Windows 11终极清理指南:用Win11Debloat快速优化你的系统性能
  • 2026年7月六驱消防洒水车/六驱越野客车工厂行业评估_湖北神宇专用汽车有限公司 - 行业平台推荐