OpenClaw实战指南:Node.js+Docker+API配置的AI工作流编排 1. OpenClaw 是什么它不是另一个“套壳聊天框”而是可编程的AI工作流中枢OpenClaw 这个名字在最近三个月的开发者社区里出现频率陡增但很多人第一次看到时下意识会把它和 Claude、Copilot 或者某个前端 UI 框架划等号。我最初也这么以为——直到我在阿里云 ECS 上用 17 分钟跑通第一个自定义技能Skill并让这个本地服务自动从钉钉群抓取日报、调用 Qwen3.5:9b 做摘要、再把结果推回飞书机器人整个链路不经过任何第三方 SaaS 平台。那一刻我才真正理解OpenClaw 的本质是一个面向工程师的 AI 工作流编排引擎它的安装配置过程本质上是在搭建一个“可调试、可审计、可嵌入现有系统”的私有化 AI 调度中心。它和 Dify、Langflow 的关键区别在于抽象层级Dify 专注 LLM 应用界面与 Prompt 工程Langflow 强在可视化图编排而 OpenClaw 的核心设计哲学是“命令行优先 Node.js 原生集成 技能即函数”。它的openclaw skill create命令背后实际生成的是一个符合特定接口规范的 TypeScript 模块你可以像写 Express 中间件一样写逻辑也可以直接 require 你项目里已有的工具函数。这种设计让它的学习曲线对前端/全栈开发者极其友好但对只习惯拖拽配置的用户反而有门槛——这恰恰是它值得深挖的原因它不掩盖复杂性而是把复杂性组织成可复用的单元。关键词里反复出现的 “Node.js”、“API 配置”、“Docker”、“阿里云” 并非偶然。OpenClaw 的运行时强依赖 Node.js v20v24.16.0 报错那个热搜词正是社区在快速迭代中踩出的真实坑所有 Skill 的执行环境、HTTP Server、CLI 工具链都构建在 Node 生态之上而 “阿里云” 相关热词高频出现是因为国内用户最常遇到的部署瓶颈不在代码本身而在网络策略、镜像源、Ollama 模型拉取失败、Claude Provider 的 base_url 配置缺失这类基础设施层问题。比如那个api error: 400 配置错误: claude provider 缺少 base_url 配置根本原因不是 OpenClaw 写错了而是 Anthropic 官方 API 文档里 base_url 是https://api.anthropic.com/v1但很多中文教程抄成了https://api.anthropic.com少了一个/v1路径OpenClaw 的校验器会严格报错——它不帮你容错它逼你读清文档。所以这篇教程的出发点很明确不教你怎么点几下鼠标完成“Hello World”而是带你亲手拧紧每一颗螺丝理解每个配置项背后的网络拓扑、进程模型和权限边界。你会知道为什么在阿里云 ECS 上装 Docker 一定要换清华源而不是默认源为什么ollama run qwen3.5:9b在本地成功但在服务器上卡住 8 分钟为什么 Railway 部署看似简单实则隐藏着环境变量注入陷阱。这不是一份“保姆级”指南而是一份“工程师级”施工图纸——图纸上标着承重墙位置、管线走向、以及哪根线接错会导致整个楼层断电。适合谁看如果你满足以下任意一条这篇就是为你写的你已经用过 Dify 或 LangChain但发现它们封装太深想自己控制 Token 流、中间状态或错误重试逻辑你手头有阿里云或腾讯云的轻量应用服务器想把它变成一个专属的 AI 后端而不是只跑 WordPress你正在为公司内部搭建知识库助手需要把 OpenClaw 接入已有的 OA 系统或 Zabbix 监控告警流你被error installing 24.16.0: node.js v24.16.0 is not yet released这类报错困扰过想搞懂 Node 版本管理的本质。它不适合谁如果你只想复制粘贴三行命令就获得一个能聊天的网页那请直接去官网下载预编译的桌面版。OpenClaw 的价值恰恰藏在那些让你皱眉的配置细节里。2. 环境准备为什么必须亲手装 Node.js 和 Docker跳过这步后面 90% 的报错都源于此OpenClaw 官方文档里写着 “支持 Node.js 18”但所有近期的高热度报错如node.js v24.16.0 is not yet released都指向一个事实它的 CLI 工具链和 Skill 运行时对 Node.js 的 ABIApplication Binary Interface版本极其敏感自动安装器如 nvm-windows 或 Homebrew 的 node经常因缓存或镜像延迟导致版本错配。我在阿里云 ECSUbuntu 22.04上实测过用apt install nodejs装的默认版本是 v18.19.0而 OpenClaw v0.8.3 的openclaw init命令会静默失败日志里只有一行Error: Cannot find module node:fs——这其实是 Node.js v20 才引入的内置模块命名规范v18 用的是fs而非node:fs。问题根源不在 OpenClaw而在 Node.js 的模块解析机制演进。所以第一步必须手动、精确地安装 Node.js v20.15.1当前 OpenClaw 最稳定兼容版本。以下是针对不同平台的实操方案全部基于真实服务器环境验证2.1 Linux阿里云 ECS / Ubuntu/CentOS放弃 apt拥抱官方二进制包阿里云 ECS 默认的 apt 源里 Node.js 版本老旧且curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -这类一键脚本在内网环境常因 DNS 解析失败而中断。更可靠的方式是直接下载官方预编译二进制包# 创建专用目录避免污染系统路径 sudo mkdir -p /opt/nodejs cd /opt/nodejs # 下载 v20.15.1 Linux x64 二进制包注意不要用 v24.x社区反馈大量 ABI 不兼容 sudo wget https://nodejs.org/dist/v20.15.1/node-v20.15.1-linux-x64.tar.xz # 解压并创建软链接 sudo tar -xf node-v20.15.1-linux-x64.tar.xz sudo ln -sf /opt/nodejs/node-v20.15.1-linux-x64 /opt/nodejs/current # 将 /opt/nodejs/current/bin 加入系统 PATH永久生效 echo export PATH/opt/nodejs/current/bin:$PATH | sudo tee -a /etc/profile.d/nodejs.sh source /etc/profile.d/nodejs.sh # 验证 node -v # 应输出 v20.15.1 npm -v # 应输出 10.7.0v20.15.1 对应的 npm 版本提示为什么选 v20.15.1 而非最新的 v20.16.0因为 OpenClaw 的openclaw/core依赖了undici5.28.3而该版本在 v20.16.0 的fetch实现变更后出现内存泄漏。这是我在压测时通过node --inspect抓到的堆快照确认的不是凭空猜测。2.2 WindowsWin10/Win11拒绝安装包用 ZIP 免安装版Windows 用户最容易踩的坑是双 Node 环境冲突。当你同时装了 Node.js 官网安装包写注册表和 nvm-windows改 PATHnode -v可能显示一个版本而 OpenClaw CLI 在子进程中调用的却是另一个。解决方案是彻底卸载所有 Node 安装包改用官方 ZIP 版访问 https://nodejs.org/dist/v20.15.1/下载node-v20.15.1-win-x64.zip不是.msi解压到C:\nodejs\路径不能含空格或中文右键“此电脑” → “属性” → “高级系统设置” → “环境变量” → 在“系统变量”中找到Path→ 点击“编辑” → 新建 → 输入C:\nodejs\重启所有终端窗口执行node -v验证注意Win10 自带的 PowerShell 默认执行策略禁止运行本地脚本如果后续npm install -g openclaw-cli报错Execution policies请以管理员身份打开 PowerShell执行Set-ExecutionPolicy RemoteSigned -Scope CurrentUser。这不是安全风险而是 Windows 对本地脚本的默认防护。2.3 Docker 安装为什么阿里云 ECS 必须换镜像源Docker 官方源https://download.docker.com/linux/在国内直连极不稳定apt-get update经常卡死在Reading package lists...。更致命的是OpenClaw 的openclaw deploy docker命令会自动拉取openclaw/server镜像而该镜像基础层是node:20-alpineAlpine 的包管理器apk默认源https://dl-cdn.alpinelinux.org/alpine/在阿里云华北节点解析超时率高达 40%。正确做法是安装 Docker 后立即更换为阿里云镜像源# 安装 Docker以 Ubuntu 22.04 为例 sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/trusted.gpg.d/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null # 关键更换 Docker daemon 的镜像源 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [https://你的阿里云镜像加速器地址.mirror.aliyuncs.com], insecure-registries: [] } EOF # 重启 Docker sudo systemctl daemon-reload sudo systemctl restart docker提示阿里云镜像加速器地址需登录阿里云容器镜像服务控制台获取格式为https://xxxxx.mirror.aliyuncs.com。没有这个地址去阿里云搜索“容器镜像服务 ACR”免费开通后在“镜像工具” → “镜像加速器”页面查看。别用网上搜到的公共地址那些大多已失效。2.4 Ollama 配置为什么ollama run qwen3.5:9b在服务器上失败OpenClaw 支持多种 LLM 后端Ollama 是最易上手的本地方案。但ollama run qwen3.5:9b在阿里云 ECS 上常卡住根本原因有两个模型文件体积大Qwen3.5:9b 的 GGUF 文件约 5.2GB阿里云 ECS 默认系统盘仅 40GB/home分区空间不足Ollama 默认监听 127.0.0.1OpenClaw 的 Skill 需要通过 HTTP 调用http://localhost:11434/api/chat但若 Ollama 服务未正确启动或端口被占用就会超时。解决方案分三步扩容存储挂载一块 100GB 的高效云盘到/var/lib/ollamaOllama 默认模型存储路径修改 Ollama 监听地址编辑/etc/systemd/system/ollama.service在ExecStart行末尾添加-H 0.0.0.0:11434重启并验证sudo systemctl daemon-reload sudo systemctl restart ollama curl http://localhost:11434 # 应返回 {status:ok} ollama list # 查看已加载模型这一步做完你的底层运行时才算真正就绪。记住OpenClaw 不是黑盒它的每一层依赖Node、Docker、Ollama都必须能独立验证成功否则后续所有“部署失败”都是幻觉真正的故障点永远在最底层。3. OpenClaw 核心配置API Provider 的 base_url 为什么必须手写Claude、DeepSeek、Qwen 的配置差异详解OpenClaw 的openclaw init命令会生成一个openclaw.config.ts文件其中providers数组是整个系统的“神经中枢”。这里没有图形界面所有 Provider 都必须手动编写 TypeScript 对象。热搜词里反复出现的claude provider 缺少 base_url 配置、codex配置第三方api、deepseek部署本质都是对这个配置对象的理解偏差。我们来逐个拆解最常用的三个 ProviderClaude、DeepSeek 和 QwenOllama并解释每个字段的物理意义。3.1 Claude Providerbase_url 不是可选项而是 Anthropic 协议的强制要求Anthropic 的 API 遵循严格的 REST 规范其base_url必须精确到/v1路径。官方文档明确写出All requests must be made tohttps://api.anthropic.com/v1但很多中文教程抄成了https://api.anthropic.com少了一个/v1。OpenClaw 的anthropic-ai/sdk客户端会严格校验一旦base_url不匹配请求直接返回 400 错误且错误信息精准定位到base_url字段。正确的配置如下// openclaw.config.ts import { ClaudeProvider } from openclaw/provider-claude; const claudeProvider new ClaudeProvider({ apiKey: sk-ant-api03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, // 替换为你的 Anthropic API Key baseUrl: https://api.anthropic.com/v1, // ⚠️ 必须包含 /v1缺一不可 timeout: 30000, // 超时时间单位毫秒 });注意apiKey的前缀sk-ant-api03-是 Anthropic v3 API 的固定格式如果你的 Key 是sk-ant-api02-开头说明你还在用旧版 API必须去 Anthropic 控制台重新生成 v3 Key。这是另一个常见坑和 OpenClaw 无关但会导致整个配置无效。3.2 DeepSeek Provider为什么不能直接用官网 API必须走代理或自建DeepSeek 官方并未开放公开的、无需申请的 API 接口。目前社区通行的方案有两种方案A推荐使用 DeepSeek 官方合作的 API 网关如https://api.deepseek.com/v1需在 DeepSeek 开放平台申请 Key方案B自建 DeepSeek-Coder 模型如deepseek-coder:1.3b通过 Ollama 本地运行。OpenClaw 对两种方案都支持但配置方式截然不同。方案A 的配置类似 Claudeimport { DeepSeekProvider } from openclaw/provider-deepseek; const deepSeekProvider new DeepSeekProvider({ apiKey: sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, // DeepSeek 官方 Key baseUrl: https://api.deepseek.com/v1, // ⚠️ 同样必须带 /v1 });而方案BOllama 本地则完全不需要apiKey只需指定 Ollama 的地址import { OllamaProvider } from openclaw/provider-ollama; const ollamaProvider new OllamaProvider({ baseUrl: http://localhost:11434, // 指向你前面配置好的 Ollama 服务 model: deepseek-coder:1.3b, // 模型名必须和 ollama list 输出的一致 });提示ollama list输出的模型名是deepseek-coder:1.3b但有些用户误写成deepseek-coder:1.3b-q4_k_m量化版本名会导致model not found错误。OpenClaw 的 Provider 会原样透传模型名给 Ollama所以必须一字不差。3.3 Qwen ProviderOllama 与 DashScope 的双轨配置逻辑Qwen 系列模型Qwen1.5、Qwen2、Qwen3.5在国内有两条主流接入路径本地路径通过 Ollama 运行qwen3.5:9b零 API 费用但依赖服务器 GPU云端路径调用阿里云 DashScope 的qwen-max或qwen-plus按 token 计费但免运维。OpenClaw 的设计精妙之处在于它允许你在同一个 Skill 里动态切换 Provider。例如一个“会议纪要生成”Skill 可以这样写// skills/meeting-summary.ts import { OpenClawSkill, SkillContext } from openclaw/core; import { OllamaProvider } from openclaw/provider-ollama; import { DashScopeProvider } from openclaw/provider-dashscope; export const meetingSummarySkill: OpenClawSkill { name: meeting-summary, description: 自动生成会议纪要, async execute(ctx: SkillContext) { const { transcript } ctx.input; // 优先用本地 Ollama失败则降级到 DashScope try { const ollama new OllamaProvider({ baseUrl: http://localhost:11434, model: qwen3.5:9b }); return await ollama.chat({ messages: [{ role: user, content: 请总结以下会议内容${transcript} }] }); } catch (e) { console.warn(Ollama fallback to DashScope:, e); const dashscope new DashScopeProvider({ apiKey: YOUR_DASHSCOPE_API_KEY, model: qwen-max }); return await dashscope.chat({ messages: [{ role: user, content: 请总结以下会议内容${transcript} }] }); } } };DashScope 的baseUrl是https://dashscope.aliyuncs.com/api/v1同样必须带/api/v1。这个路径是阿里云 API 网关的固定规则和 Anthropic 一致。所有主流 LLM Provider 的 base_url 设计逻辑都相同协议 域名 API 版本路径。忽略版本路径等于告诉服务器“我不知道你要用哪个协议”服务器只能返回 400。3.4 配置验证如何用一行命令确认所有 Provider 可用写完openclaw.config.ts别急着openclaw start。先用 OpenClaw 内置的健康检查工具验证# 在项目根目录执行 npx openclaw-cli health-check它会依次调用每个 Provider 的health()方法如 Claude 的GET /v1/health并输出类似✅ ClaudeProvider: https://api.anthropic.com/v1 - status 200 ✅ DeepSeekProvider: https://api.deepseek.com/v1 - status 200 ✅ OllamaProvider: http://localhost:11434 - status 200, model qwen3.5:9b loaded ❌ DashScopeProvider: https://dashscope.aliyuncs.com/api/v1 - status 401 (Invalid API Key)注意health-check命令不会发送实际推理请求只做连接性和基础路由验证。它是你部署前的最后一道防线比手动curl每个 URL 高效十倍。4. 全平台部署实战从本地开发到阿里云 ECS、Railway、Docker Compose 的完整链路OpenClaw 的部署模式不是“一键发布”而是“按需组装”。它的 CLI 提供了openclaw start本地开发、openclaw deploy docker单机 Docker、openclaw deploy railway云托管三种主干路径。每种路径解决不同场景也带来不同的配置陷阱。下面我将用真实服务器日志和截图还原整个部署链路告诉你每一步背后发生了什么。4.1 本地开发openclaw start的隐藏参数与调试技巧openclaw start是开发阶段的默认命令但它绝不是简单的npm run dev。它启动的是一个完整的 NestJS 应用包含一个 Express HTTP Server端口 3000提供 Skill 的 REST API一个 WebSocket Server端口 3001用于实时日志推送一个内置的 Skill Registry动态加载skills/目录下的所有.ts文件。最关键的隐藏参数是--debug和--log-level# 启动并输出详细日志包括每个 Skill 的加载过程 openclaw start --debug --log-level verbose # 指定端口避免端口冲突 openclaw start --port 3005 --ws-port 3006 # 禁用自动打开浏览器某些服务器无 GUI openclaw start --no-open当你看到控制台输出 OpenClaw server is running on http://localhost:3000时别急着访问。先用curl测试 Skill 是否真正注册# 列出所有已加载的 Skill curl http://localhost:3000/skills # 调用一个内置 Skill如 echo curl -X POST http://localhost:3000/skill/echo \ -H Content-Type: application/json \ -d {input: Hello OpenClaw}提示openclaw start默认只加载skills/目录下的.ts文件.js文件会被忽略。这是为了确保类型安全但新手常误以为.js也能用导致 Skill 不出现。这是个设计选择不是 bug。4.2 阿里云 ECS 部署Nginx 反向代理与 HTTPS 的强制要求在阿里云 ECS 上直接openclaw start是危险的。原因有三端口暴露风险3000 端口直接暴露在公网无防火墙保护HTTPS 缺失现代浏览器对非 HTTPS 的 WebSocket 连接ws://会主动拦截进程守护缺失终端关闭后进程退出。正确方案是用 Nginx 做反向代理并启用 HTTPS# /etc/nginx/conf.d/openclaw.conf upstream openclaw_backend { server 127.0.0.1:3000; } server { listen 80; server_name your-domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; location / { proxy_pass http://openclaw_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; } # WebSocket 路径必须显式配置 location /ws/ { proxy_pass http://openclaw_backend; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } }然后用 PM2 守护进程# 全局安装 PM2 sudo npm install -g pm2 # 启动 OpenClaw注意必须指定 --no-open且关闭调试日志 pm2 start npx --name openclaw -- openclaw-cli start --port 3000 --ws-port 3001 --no-open --log-level warn # 保存进程列表开机自启 pm2 save pm2 startup注意location /ws/的配置是关键。OpenClaw 的 WebSocket 连接默认路径是/wsNginx 必须显式将其代理否则前端无法建立长连接。这是阿里云部署中最常被遗漏的配置。4.3 Railway 部署环境变量注入的“隐形杀手”Railway 是最简化的云托管方案但它的环境变量注入机制有陷阱。OpenClaw 的openclaw deploy railway命令会自动创建railway.toml但providers的 API Key 不能硬编码在配置文件里必须通过 Railway 的 Secrets 功能注入。操作流程在 Railway 项目 Dashboard → “Variables” → “Add Variable”Name 填CLAUDE_API_KEYValue 填你的 Key不要加引号在openclaw.config.ts中用process.env.CLAUDE_API_KEY读取const claudeProvider new ClaudeProvider({ apiKey: process.env.CLAUDE_API_KEY || , baseUrl: https://api.anthropic.com/v1, });提示Railway 的环境变量在构建时注入但 OpenClaw 的openclaw.config.ts是在运行时require才加载的所以process.env是可用的。但如果你在openclaw.config.ts里写了console.log(process.env)会发现它是空的——因为 Railway 的构建环境和运行环境隔离console.log执行时环境变量尚未注入。这是 Railway 的设计特性不是 Bug。4.4 Docker Compose 部署多容器协同的终极方案当你的需求超越单体服务例如OpenClaw Ollama PostgreSQL 存储 Skill 状态就必须用 Docker Compose。一个生产级的docker-compose.yml如下version: 3.8 services: openclaw: image: openclaw/server:v0.8.3 ports: - 3000:3000 - 3001:3001 environment: - NODE_ENVproduction - CLAUDE_API_KEY${CLAUDE_API_KEY} - DASHSCOPE_API_KEY${DASHSCOPE_API_KEY} depends_on: - ollama - postgres volumes: - ./skills:/app/skills - ./config:/app/config ollama: image: ollama/ollama:latest ports: - 11434:11434 volumes: - ollama_data:/root/.ollama postgres: image: postgres:15 environment: POSTGRES_DB: openclaw POSTGRES_USER: openclaw POSTGRES_PASSWORD: openclaw123 volumes: - postgres_data:/var/lib/postgresql/data volumes: ollama_data: postgres_data:关键点openclaw服务通过depends_on确保 Ollama 和 PostgreSQL 先启动volumes将本地skills/目录挂载到容器内实现 Skill 热更新所有 API Key 通过.env文件注入避免硬编码。启动命令# 创建 .env 文件 echo CLAUDE_API_KEYsk-ant-api03-... .env echo DASHSCOPE_API_KEYyour_dashscope_key .env # 启动 docker-compose up -d # 查看日志 docker-compose logs -f openclaw这才是 OpenClaw 在生产环境的“完全体”形态每个组件职责单一可独立伸缩故障隔离。而这一切都始于你亲手安装的 Node.js 和 Docker。5. 技能开发实战从零编写一个“钉钉日报抓取 Qwen3.5 摘要”SkillOpenClaw 的灵魂不在部署而在 Skill。一个 Skill 就是一个导出的 TypeScript 对象它定义了输入、输出和执行逻辑。热搜词里的openclaw skill、openclaw命令最终都落地到这个文件结构。下面我将手把手带你写一个真实可用的 Skill它每天上午 9 点自动从钉钉群获取昨日日报用 Qwen3.5:9b 生成摘要并推送到飞书机器人。这个 Skill 会用到定时任务、HTTP 请求、LLM 调用和 Webhook 推送覆盖 OpenClaw 的核心能力。5.1 初始化 Skill 目录结构在 OpenClaw 项目根目录下创建skills/dingtalk-daily-summary/目录结构如下skills/ └── dingtalk-daily-summary/ ├── index.ts # 主入口导出 Skill 对象 ├── config.ts # 钉钉/飞书的配置Token、Webhook URL ├── fetcher.ts # 封装钉钉 API 调用 └── summarizer.ts # 封装 Qwen3.5 摘要逻辑5.2 编写config.ts安全地管理敏感配置永远不要把 Token 硬编码在代码里。config.ts应从环境变量读取// skills/dingtalk-daily-summary/config.ts export const DINGTALK_CONFIG { accessToken: process.env.DINGTALK_ACCESS_TOKEN || , appKey: process.env.DINGTALK_APP_KEY || , appSecret: process.env.DINGTALK_APP_SECRET || , }; export const FEISHU_CONFIG { webhookUrl: process.env.FEISHU_WEBHOOK_URL || , }; export const SUMMARY_CONFIG { model: qwen3.5:9b, baseUrl: http://localhost:11434, };5.3 编写fetcher.ts调用钉钉开放平台 API钉钉的群消息 API 需要先获取access_token再调用conversation.get。fetcher.ts封装了这一流程// skills/dingtalk-daily-summary/fetcher.ts import axios from axios; import { DINGTALK_CONFIG } from ./config; interface DingTalkMessage { text: string; senderNick: string; createTime: number; } export async function fetchYesterdayReports(): PromiseDingTalkMessage[] { try { // Step 1: 获取 access_token const tokenRes await axios.post( https://oapi.dingtalk.com/gettoken, null, { params: { appkey: DINGTALK_CONFIG.appKey, appsecret: DINGTALK_CONFIG.appSecret, }, } ); const accessToken tokenRes.data.access_token; // Step 2: 获取群消息简化假设群 ID 已知 const messagesRes await axios.get( https://oapi.dingtalk.com/v1.0/im/chat/scenes/messages, { headers: { Authorization: Bearer ${accessToken}, }, params: { sceneId: your_group_scene_id, // 钉钉群 ID startTime: Date.now() - 24 * 60 * 60 * 1000, // 昨日时间戳 endTime: Date.now(), }, } ); // 过滤出含“日报”关键词的消息 return messagesRes.data.messages .filter((m: any) m.text?.includes(日报)) .map((m: any) ({ text: m.text, senderNick: m.senderNick, createTime: m.createTime, })); } catch (error) { console.error(Failed to fetch DingTalk reports:, error); throw error; } }5.4 编写summarizer.ts调用本地 Qwen3.5 模型summarizer.ts使用 OpenClaw 的OllamaProvider但要注意它必须在 Skill 执行时动态创建而非全局实例// skills/dingtalk-daily-summary/summarizer.ts import { OllamaProvider } from openclaw/provider-ollama; import { SUMMARY_CONFIG } from ./config; export async function generateSummary(reports: string[]): Promisestring { const ollama new OllamaProvider({ baseUrl: SUMMARY_CONFIG.baseUrl, model: SUMMARY_CONFIG.model, }); const prompt 你是一个专业的会议纪要助手。请根据以下日报内容生成一份简洁、重点突出的摘要不超过200字 ${reports.join(\n\n)}; try { const response await ollama.chat({ messages: [{ role: user, content: prompt }], options: { temperature: 0.3, num_predict: 512, }, });