1. 为什么我要把 OpenClaw 塞进 Docker 里跑OpenClaw 是一个开源 AI 智能体框架核心用 TypeScript 编写能通过 Gateway、Agent、Skills、Memory 四层结构主动规划并执行任务而不是像普通聊天机器人那样只被动回答问题。它适合想搭建个人 AI 助手、又希望数据留在本地的开发者。我最初是在一台旧笔记本上直接跑源码结果 Node 版本冲突、Python 依赖打架、系统里被写进一堆全局包卸载时差点把环境搞崩。后来改用 Docker 隔离才算真正把 OpenClaw 变成一个能随手启停、随时回滚的服务。这篇内容聚焦两件事一是用 Docker Compose 把 OpenClaw 的 Gateway 和运行环境完整跑起来二是用 TypeScript 写一个自定义技能插件让 AI 助手能真正调用你自己的业务逻辑。整个过程我会给出可直接复制的配置、插件骨架和验证命令你照着做就能得到一个能执行日常任务的本地智能体。先说清楚 OpenClaw 的定位。它不是一个模型而是一个调度层你给它一个目标它拆解步骤、选择技能、调用工具、汇总结果。模型可以是 GPT 系列、Claude 系列也可以是本地部署的开源模型。技能Skills是它真正“干活”的手脚而 TypeScript 是官方推荐的技能开发语言类型系统能帮你在编译期就发现参数错误。Docker 则解决环境一致性问题——你的 OpenClaw 在笔记本、服务器、NAS 上跑出来的行为应该完全一样。我踩过的坑是一开始觉得 Docker 只是“打包”没必要。但 OpenClaw 的技能插件会调用系统命令、读写文件、访问网络裸机运行时权限边界很模糊。容器化之后我可以精确控制它能碰哪些目录、能连哪些端口这对一个“能主动执行任务”的智能体来说是安全底线。所以下面的步骤Docker 是主线TypeScript 技能是重点两者结合才是完整的 OpenClaw 实战。2. TaoToken 前置准备给 OpenClaw 接上模型能力OpenClaw 本身不带模型它需要你配置一个兼容 OpenAI 接口的模型服务。我实测下来用 TaoToken 的 API 接入最省事因为它同时支持 GPT 和 Claude 系列OpenClaw 的模型配置里只需要填 Base URL、API Key 和 Model ID 三个字段就能跑通。先到 TaoToken 官网注册账号然后进入控制台创建 API Key。地址是 https://taotoken.net/api-keys 创建时建议给 Key 起个名字比如openclaw-local方便以后在多个项目里区分。Key 只显示一次复制后先存到密码管理器里。接下来确认你要用的模型。OpenClaw 的 Agent 对模型的指令遵循能力要求较高我建议先用 Claude 系列做任务规划因为它在多步推理和工具调用上比较稳。你可以在模型对话页面先试一下模型是否可用https://taotoken.net/model-chat 随便问一句“帮我规划一个三步的文件整理任务”看它能不能给出结构化的步骤。如果能说明这个模型适合接进 OpenClaw。然后记下三个关键值Base URL 填https://taotoken.net/apiAPI Key 填你刚创建的那串Model ID 填你在模型对话里验证过的模型名。这三个值后面会写进 OpenClaw 的配置文件里。注意 Base URL 不要加 UTM 参数OpenClaw 的 HTTP 客户端对 URL 查询参数比较敏感加了可能导致签名校验失败。如果你打算长期跑编码类或 Agent 类任务可以看一下 Coding Planhttps://taotoken.net/coding-plan 它的额度模型更适合高频调用。普通日常任务用按量计费就够了。接入文档在 https://taotoken.net/doc 里面有完整的接口说明和错误码对照排障时会用到。这里要提醒一点OpenClaw 的 Gateway 会以容器内进程的身份发起请求所以你的 API Key 是通过环境变量注入容器的不要硬编码在 TypeScript 源码里。下面 Docker Compose 部分我会用.env文件来管理这些敏感值。3. 可复制配置Docker Compose 与 TypeScript 技能骨架这一节是全文的核心我会给出完整的docker-compose.yml、.env模板以及一个 TypeScript 技能插件的骨架。你新建一个目录openclaw-lab把下面文件按路径放好即可。先建目录结构mkdir -p openclaw-lab/skills/file-organizer/src cd openclaw-lab然后是.env文件放在openclaw-lab/.env# 模型服务配置 OPENCLAW_MODEL_BASE_URLhttps://taotoken.net/api OPENCLAW_MODEL_API_KEYsk-你的Key替换这里 OPENCLAW_MODEL_IDclaude-3-5-sonnet-20241022 # Gateway 配置 OPENCLAW_GATEWAY_PORT18789 OPENCLAW_LOG_LEVELinfo # 技能目录挂载 OPENCLAW_SKILLS_DIR./skills注意OPENCLAW_MODEL_ID要换成你在模型对话里验证过的那个模型名不要照抄。OPENCLAW_MODEL_API_KEY换成你自己的 Key。接着是docker-compose.yml放在openclaw-lab/docker-compose.ymlservices: openclaw-gateway: image: node:20-alpine container_name: openclaw-gateway working_dir: /app env_file: - .env environment: - NODE_ENVproduction - OPENCLAW_SKILLS_DIR/app/skills ports: - ${OPENCLAW_GATEWAY_PORT}:18789 volumes: - ./skills:/app/skills - ./data:/app/data - ./gateway:/app/gateway command: sh -c npm install -g openclawlatest openclaw gateway start --port 18789 --skills-dir /app/skills restart: unless-stopped healthcheck: test: [CMD, wget, -qO-, http://127.0.0.1:18789/api/v1/health] interval: 30s timeout: 5s retries: 3这里我用node:20-alpine作为基础镜像因为 OpenClaw 的 Gateway 是 Node 服务Alpine 体积小、启动快。volumes把本地的skills目录挂进容器这样你改 TypeScript 技能后重启容器就能生效不用重新构建镜像。data目录用来持久化 Memory 和任务日志。然后是 TypeScript 技能插件骨架。OpenClaw 的技能本质是一个导出execute函数的模块接收参数对象返回结果对象。新建openclaw-lab/skills/file-organizer/src/index.tsimport { readdir, mkdir, rename } from fs/promises; import { join, extname } from path; interface SkillInput { targetDir: string; dryRun?: boolean; } interface SkillOutput { moved: number; details: Array{ from: string; to: string }; } const CATEGORY_MAP: Recordstring, string { .jpg: images, .png: images, .pdf: documents, .docx: documents, .xlsx: spreadsheets, .csv: spreadsheets, .ts: code, .js: code, }; export async function execute(input: SkillInput): PromiseSkillOutput { const { targetDir, dryRun false } input; const entries await readdir(targetDir, { withFileTypes: true }); const details: SkillOutput[details] []; for (const entry of entries) { if (!entry.isFile()) continue; const ext extname(entry.name).toLowerCase(); const category CATEGORY_MAP[ext]; if (!category) continue; const from join(targetDir, entry.name); const destDir join(targetDir, category); const to join(destDir, entry.name); if (!dryRun) { await mkdir(destDir, { recursive: true }); await rename(from, to); } details.push({ from, to }); } return { moved: details.length, details }; }再建openclaw-lab/skills/file-organizer/package.json{ name: openclaw-skill-file-organizer, version: 1.0.0, main: dist/index.js, scripts: { build: tsc }, devDependencies: { typescript: ^5.4.0, types/node: ^20.11.0 } }以及openclaw-lab/skills/file-organizer/tsconfig.json{ compilerOptions: { target: ES2022, module: commonjs, outDir: dist, rootDir: src, strict: true, esModuleInterop: true }, include: [src/**/*.ts] }这套配置的关键点是技能编译后输出到dist/index.jsOpenClaw 的 Gateway 会扫描skills目录下的package.json读取main字段加载技能。dryRun参数让你可以先预览会移动哪些文件确认无误再真正执行。这就是一个能“真正干活”的最小技能。4. 验证请求从启动到任务触发配置写好后先构建技能再启动容器。在openclaw-lab目录下执行cd skills/file-organizer npm install npm run build cd ../.. docker compose up -d第一次启动会拉取node:20-alpine并全局安装openclaw可能需要一两分钟。用下面命令看日志docker compose logs -f openclaw-gateway当你看到类似Gateway listening on 18789和Loaded skill: file-organizer的输出说明 Gateway 和技能都加载成功了。如果只看到 Gateway 启动但没看到技能加载检查skills/file-organizer/dist/index.js是否存在以及package.json的main字段是否指向它。接着验证健康检查接口curl http://127.0.0.1:18789/api/v1/health返回{status:healthy}就说明服务正常。然后验证模型连通性OpenClaw 提供了一个诊断接口curl -X POST http://127.0.0.1:18789/api/v1/diagnose \ -H Content-Type: application/json \ -d {check:model}如果返回里包含model: ok说明 TaoToken 的 Base URL、Key、Model ID 三件套配置正确。如果返回model: failed先检查.env里的 Key 有没有多余空格再确认 Model ID 拼写。现在触发一个真实任务。先准备测试文件mkdir -p data/test-files touch data/test-files/a.jpg data/test-files/b.pdf data/test-files/c.ts data/test-files/readme.md然后调用任务接口让 OpenClaw 用file-organizer技能整理data/test-filescurl -X POST http://127.0.0.1:18789/api/v1/tasks \ -H Content-Type: application/json \ -d { goal: 整理 data/test-files 目录下的文件按类型归类, skill: file-organizer, input: { targetDir: /app/data/test-files, dryRun: false } }注意targetDir要用容器内路径/app/data/test-files因为data目录挂载到了/app/data。返回结果里会有moved字段和details数组列出每个文件从哪移到哪。执行完再看目录ls -R data/test-files你应该看到images/a.jpg、documents/b.pdf、code/c.ts而readme.md因为不在映射表里保持原位。这就是一个完整的“AI 助手真正为你干活”的链路自然语言目标 → Gateway 解析 → 技能执行 → 结果返回。如果你想验证多步任务可以再发一个 goal“先整理文件然后统计每个分类的文件数量”。OpenClaw 会先调file-organizer再根据返回的details做聚合。这一步能跑通说明 Agent 的规划能力和技能调用链路都正常。5. 本篇常见错排查401、local proxy failed 与技能加载失败这一节列几个我实际遇到过的报错以及对应的排查路径。你大概率会碰到其中一两个。第一个是401 Unauthorized。完整报错通常长这样{error:{code:invalid_api_key,message:Authentication failed}}原因一般是.env里的OPENCLAW_MODEL_API_KEY不对或者 Key 被复制时带了换行。排查方法进入容器内部打印环境变量确认docker compose exec openclaw-gateway printenv | grep OPENCLAW_MODEL如果 Key 末尾有%或空格说明.env文件里有多余字符。另外确认 Base URL 是https://taotoken.net/api不要写成带/v1的路径OpenClaw 会自己拼接。如果 Key 确认无误还是 401去 TaoToken 控制台看这个 Key 是否被禁用或额度耗尽。第二个是local proxy failed或ECONNREFUSED。报错类似Error: connect ECONNREFUSED 127.0.0.1:18789这通常不是模型问题而是 Gateway 没起来或端口没映射。先docker compose ps看容器状态如果是Exit状态用docker compose logs openclaw-gateway看退出原因。常见原因是openclaw全局安装失败比如网络超时。可以进容器手动装一次docker compose exec openclaw-gateway npm install -g openclawlatest如果容器根本起不来检查docker-compose.yml里ports的变量${OPENCLAW_GATEWAY_PORT}是否在.env里定义了。变量没定义时 Docker 会报端口格式错误。第三个是技能加载失败日志里出现Skill not found: file-organizer或Cannot find module。先确认dist/index.js存在ls skills/file-organizer/dist/如果没有回到skills/file-organizer目录执行npm run build。如果存在但还报错检查package.json的main字段是不是dist/index.js以及tsconfig.json的outDir是否一致。还有一个容易忽略的点execute函数必须是export的且参数和返回值的字段名要和调用时一致。OpenClaw 用反射读取导出函数如果写成export default可能识别不到。第四个是reading choices报错完整信息类似TypeError: Cannot read properties of undefined (reading choices)这说明模型返回体里没有choices字段通常是 Base URL 指向了一个不兼容 OpenAI 格式的端点。确认你用的是https://taotoken.net/api并且 Model ID 是对话模型而不是嵌入模型。如果你在.env里把 Model ID 写成了text-embedding-3-small这类嵌入模型就会触发这个错误。换成claude-3-5-sonnet-20241022或gpt-4o再试。第五个是 OAuth 相关报错比如OAuth token expired。如果你用的是某些需要 OAuth 的模型服务OpenClaw 的静态 Key 模式会失败。TaoToken 的 API Key 模式不需要 OAuth所以确认你没有在配置里混入 OAuth 字段。如果之前配过其他服务清空.env重新填三件套即可。排障时记住一个原则先确认 Gateway 活着health 接口再确认模型通diagnose 接口最后确认技能加载logs 里搜Loaded skill。这三层依次排查90% 的问题能定位到。接入文档在 https://taotoken.net/doc 里面有错误码对照表遇到没见过的报错可以去查。6. 把 OpenClaw 变成你的长期编码与任务搭档跑通上面的链路后你可以做几件事让它更实用。第一把file-organizer扩展成支持自定义映射表通过技能输入参数传入categoryMap这样不同项目可以复用同一个技能。第二给技能加上日志输出OpenClaw 的 Memory 会记录每次任务的结果你可以在data目录里看到历史记录方便回溯。如果你打算让 OpenClaw 长期跑编码类任务比如自动整理代码仓库、生成提交信息、批量重命名文件建议用 Coding Plan 的额度因为这类任务调用频率高、上下文长。地址是 https://taotoken.net/coding-plan 。普通日常整理任务用按量计费就够。另外OpenClaw 的技能生态是它真正强大的地方。你可以用同样的 TypeScript 骨架写一个“邮件摘要”技能、一个“日报生成”技能甚至一个“调用内部 API 查数据”的技能。每个技能就是一个独立的 npm 包通过 Docker 的 volume 挂载进容器互不干扰。这种隔离性让你可以放心让 AI 助手去碰真实文件因为最坏情况也只是容器内的目录被改宿主机其他部分不受影响。最后给一个实用技巧在docker-compose.yml里加一个openclaw-cli服务用profiles控制它只在需要时启动专门用来跑一次性任务比如openclaw-cli: image: node:20-alpine profiles: [cli] env_file: - .env volumes: - ./skills:/app/skills - ./data:/app/data command: sh -c npm install -g openclawlatest openclaw task run --goal 整理 /app/data/test-files --skill file-organizer这样你可以用docker compose --profile cli run openclaw-cli触发一次性任务而 Gateway 保持常驻。两者共享同一套技能和配置维护成本很低。到这一步你的 OpenClaw 就不再是一个玩具而是一个能真正执行日常任务的本地 AI 助手。