title: 搭建我的专属AI助手:OpenClaw云服务器部署实战指南 author: Gamehu tags:
📢 更新日志:
- 2026-03-09 补充
HEARTBEAT.md抢占主会话的排障记录,修正文中 Docker Compose 版本与记忆机制描述,新增“升级是否成功”的核验方法。- 2026-03-06 新增
Step-3.5-Flash接入与会话级/model切换说明,补充默认模型不切全局、只切当前会话的使用方式。- 2026-03-03 新增"记录系统优化"章节,解决会话文件膨胀问题,添加自动归档和结构化存储方案。
最近刷到不少 OpenClaw 的文章,演示零成本安装、智能体集群、国内部署经验。我手头正好有台配置不高的云服务器闲置着,决定动手搭建一个专属 AI 助手。
但这篇文章不是推广,是真实的部署踩坑记录。整个过程历时数小时,遇到了 OOM 内存不足、配置验证失败、防火墙规则冲突等一系列问题,把完整的解决过程记录下来。
先说结论:OpenClaw 确实能跑起来,但需要对 Docker、网络和 Linux 有一定了解,且内存配置要充足。
OpenClaw 是一个开源的个人 AI 助手框架,核心价值在于:它不是只聊天的 AI,而是能动手干活的 AI。
核心能力:
最重要的是——运行在自己的服务器上,数据完全掌控。
搬瓦工买的一台机器,配置不高,平时捣鼓各种东西的。
| 配置项 | 实际环境 |
|---|---|
| 系统 | Ubuntu 24.04.2 LTS |
| CPU | 3 核 |
| 内存 | 2GB RAM |
| 磁盘 | 39GB SSD |
| Docker | 29.2.1 |
| Docker Compose | v5.1.0 |
| 公网 IP | your-server-ip |
⚠️ 重要提示:2GB 内存对于源码构建来说不够,需要配置 Swap。
Ubuntu 24.04 下安装 Docker 很顺利:
# 更新系统
apt update && apt upgrade -y
# 安装 Docker
curl -fsSL https://get.docker.com | sh
# 验证安装
docker --version # Docker version 29.2.1
docker compose version # Docker Compose version v5.1.0
国内用户建议配置镜像加速,编辑 /etc/docker/daemon.json:
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://dockerhub.icu"
]
}
官方提供了 Docker 镜像,但我选择从源码构建以获得最新功能:
mkdir -p /opt/openclaw
cd /opt/openclaw
git clone https://github.com/openclaw/openclaw.git
cd openclaw
开始构建:
docker build -t openclaw:local -f Dockerfile .
🚨 第一个大坑:构建到 pnpm install 时容器被杀
查看日志发现是 OOM(内存不足)。2GB RAM 在构建 Node.js 项目时不够用,pnpm install 直接占满内存导致进程被系统杀死。
解决方案:添加 Swap 空间
# 创建 5GB Swap 文件
dd if=/dev/zero of=/swapfile bs=1G count=5
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
# 验证
free -h
# 显示:Swap: 5.0Gi
添加 Swap 后重新构建,成功完成。
📷 安装过程 - 添加 Swap 解决 OOM:
创建 docker-compose.yml:
version: '3.8'
services:
openclaw-gateway:
image: openclaw:local
container_name: openclaw
restart: unless-stopped
environment:
- HOME=/home/node
- NODE_ENV=production
volumes:
- ./.openclaw:/home/node/.openclaw
- ./.openclaw/workspace:/home/node/.openclaw/workspace
ports:
- "127.0.0.1:18789:18789"
- "127.0.0.1:18790:18790"
command: ["node", "dist/index.js", "gateway", "--bind", "lan", "--port", "18789"]
🚨 第二个坑:配置验证极其严格
OpenClaw 对 openclaw.json 配置文件的校验非常严格,很多字段在官方文档中没有明确说明,但缺失会导致启动失败。
我遇到的错误:
✖ Configuration validation failed:
- gateway.mode is required
- gateway.controlUi.allowedOrigins is required when using non-loopback bind
正确的最小配置(openclaw.json):
{
"gateway": {
"port": 18789,
"mode": "local",
"controlUi": {
"allowedOrigins": ["http://localhost:18789", "http://127.0.0.1:18789"]
},
"auth": {
"token": "your-secure-token-here"
}
}
}
📷 OpenClaw Web UI 界面:
关键注意点:
gateway.mode必须显式设置为"local"gateway.controlUi.allowedOrigins在使用非 localhost 绑定时是必需的gateway.bind不能写在 JSON 中(无效字段),必须通过 CLI 参数--bind lan设置
我选择使用阿里云百炼的 Coding Plan,配置 kimi-k2.5 作为主力模型:
{
"models": {
"mode": "merge",
"providers": {
"bailian": {
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"apiKey": "YOUR_BAILIAN_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "kimi-k2.5",
"name": "kimi-k2.5",
"input": ["text", "image"],
"contextWindow": 262144,
"maxTokens": 32768
},
{
"id": "qwen3.5-plus",
"name": "qwen3.5-plus",
"input": ["text", "image"],
"contextWindow": 1000000,
"maxTokens": 65536
}
]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "bailian/kimi-k2.5"
}
}
}
}
在 @BotFather 创建 Bot 并获取 Token。
🚨 第三个坑:配对码过期与 dmPolicy 冲突
{
"channels": {
"telegram": {
"enabled": true,
"botToken": "YOUR_BOT_TOKEN_HERE",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"]
}
}
}
初始配置时我加了 "dmPolicy": "open",结果与 allowFrom 白名单冲突,报错:
dmPolicy: open requires allowFrom: ["*"]
解决方案:删除 dmPolicy 字段,直接使用 allowFrom 白名单。
启动服务:
docker compose up -d
docker compose logs -f
看到日志显示 Telegram channel started 且没有错误,说明 Bot 已上线。
📷 Telegram Bot 配置成功:
这是 OpenClaw 真正强大的地方——通过 Google Workspace 集成,AI 可以帮你管理邮件、日历、文档。
OpenClaw 使用 gog skill 来访问 Google 服务(Gmail、Calendar、Drive、Docs、Sheets)。
openclaw-integration)OpenClaw 通过 MCP (Model Context Protocol) 集成 Google Workspace。
在服务器上创建 MCP 配置文件:
mkdir -p /opt/openclaw/.openclaw/mcp
cat > /opt/openclaw/.openclaw/mcp/google-workspace.json << 'EOF'
{
"mcpServers": {
"google-workspace": {
"command": "npx",
"args": ["-y", "@gongchangio/mcp-google-workspace"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id.apps.googleusercontent.com",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"GOOGLE_REDIRECT_URI": "http://localhost:18790/oauth2callback"
}
}
}
}
EOF
cd /opt/openclaw
docker compose restart
# 查看日志等待授权提示
docker compose logs -f | grep -i google
首次启动会显示 OAuth 授权链接,在浏览器中打开并授权后,会获得一个 code,将其填回即可。
配置完成后,你可以通过 Telegram 让 AI 帮你:
管理邮件:
"帮我查看今天的重要邮件,并回复需要处理的"
操作日历:
"帮我查一下下周的会议安排,把周五下午的会议改到周四"
读写文档:
"读取 Drive 里的 '项目计划.docx',帮我总结要点并更新状态"
⚠️ 注意:Google OAuth 有权限范围限制
🚨 第四个坑:Docker 绕过 INPUT 链的防火墙规则
我最初的防火墙配置:
iptables -I INPUT 1 -p tcp --dport 18789 -s 127.0.0.1 -j ACCEPT
iptables -I INPUT 2 -p tcp --dport 18789 -j DROP
测试发现:公网仍然可以访问 Web UI!
原因:Docker 在 DOCKER 链中插入了 ACCEPT 规则,且该链在 INPUT 链之前被处理。
正确的解决方案:使用 DOCKER-USER 链
# 在 DOCKER-USER 链中插入规则(在 Docker 规则之前执行)
iptables -I DOCKER-USER 1 -p tcp -s 127.0.0.1 --dport 18789 -j ACCEPT
iptables -I DOCKER-USER 2 -p tcp --dport 18789 -j DROP
iptables -I DOCKER-USER 3 -p tcp -s 127.0.0.1 --dport 18790 -j ACCEPT
iptables -I DOCKER-USER 4 -p tcp --dport 18790 -j DROP
# 保存规则
iptables-save > /root/iptables-rules.txt
最终安全策略:
| 项目 | 状态 |
|---|---|
| 配置文件权限 | 容器运行用户可读(避免 EACCES) |
| 公网 Web UI | 通过 DOCKER-USER 链完全阻止 |
| 本地 Web UI | 127.0.0.1 可访问 |
| Telegram 访问 | 仅白名单用户 ID 可用 |
| 外部访问方式 | SSH 隧道 |
📷 最后安全检查 - 安全加固结果:
SSH 隧道访问命令:
ssh -N -L 18789:127.0.0.1:18789 root@your-server-ip
# 然后在本地浏览器访问 http://localhost:18789
OpenClaw 的持久记忆系统让它能跨会话记住你的偏好、项目信息和个人习惯。这是从"工具"升级为"助手"的关键。
| 路径 | 用途 | 文件类型 |
|---|---|---|
/home/node/.openclaw/memory/ |
数据库存储(自动) | main.sqlite - 对话历史 |
/home/node/.openclaw/workspace/ |
结构化记忆文件 | MEMORY.md, USER.md |
/home/node/.openclaw/workspace/memory/ |
每日记忆归档 | YYYY-MM-DD.md |
# USER.md - About Your Human
- **Name:** Gamehu
- **What to call them:** Gamehu
- **Timezone:** Asia/Shanghai (UTC+8)
## Context
- **职业:** IT 行业,编程 + 团队管理
- **需求:** 个人助手
- **风格偏好:** 简单直接,不绕弯子
# MEMORY.md - Long-term Memory
## Technical Stack
- **Primary Languages:** Java, JavaScript, Python, Go
- **Frameworks:** Spring Boot, React
- **Cloud:** Alibaba Cloud, AWS
### Preferences
- **Code Style:** Clean, well-documented
- **Learning Style:** Hands-on, project-based
- **Dislikes:** Repetitive small talk
### Current Projects
- Building personal AI assistant
- Managing development team
给 Bot 发送:
"记住我喜欢用 Go 语言写后端服务"
过一段时间再问:
"我之前告诉你我喜欢用什么语言?"
如果配置正确,Bot 应该能回答出 Go。
在解决 compaction 卡死问题后,我意识到 OpenClaw 的会话记录管理还有很多优化空间。之前的经历是:会话文件在两天内膨胀到 1.4MB(603 条消息),导致系统卡死。为了防止类似问题再次发生,我设计并部署了一套结构化的记录管理系统。
| 问题 | 优化前 | 优化后 |
|---|---|---|
| 会话文件膨胀 | 1.4MB(603条消息) | 每日自动归档,单文件 < 100KB |
| 数据管理 | 所有文件混在一起 | 结构化分类存储 |
| 查找历史 | 翻找 JSONL 文件 | Markdown 摘要,一目了然 |
| 数据备份 | 手动复制 | 自动归档 + 一键导出 |
我在 workspace 下创建了完整的分类存储结构:
/root/.openclaw/workspace/
├── memory/ # 记忆类数据
│ ├── daily/ # 每日摘要 (YYYY-MM-DD.md)
│ ├── weekly/ # 每周汇总
│ └── archived/ # 长期归档
├── records/ # 记录类数据
│ ├── conversations/ # 对话记录 (按月归档)
│ │ └── 2026-03/
│ │ ├── 2026-03-03/ # 当日完整会话
│ │ └── 2026-03-03-sessions.tar.gz
│ ├── tasks/ # 任务记录
│ │ ├── completed/ # 已完成任务
│ │ └── failed/ # 失败任务
│ ├── browser/ # 浏览器操作记录
│ │ ├── screenshots/ # 网页截图
│ │ └── page-dumps/ # 页面快照
│ └── files/ # 文件操作日志
├── outputs/ # 输出类数据
│ ├── generated/ # AI 生成的文档
│ ├── exports/ # 导出文件
│ └── reports/ # 定时报告
└── logs/ # 日志类
├── system/ # 系统日志 (archive-YYYY-MM-DD.log)
├── model/ # 模型调用记录
└── errors/ # 错误日志
创建 /root/.openclaw/workspace/archive-daily.sh:
#!/bin/bash
WORKSPACE="/root/.openclaw/workspace"
SESSIONS_DIR="/home/node/.openclaw/agents/main/sessions"
DATE=$(date +"%Y-%m-%d")
MONTH=$(date +"%Y-%m")
# 创建归档目录
mkdir -p "$WORKSPACE/records/conversations/$MONTH/$DATE"
mkdir -p "$WORKSPACE/memory/daily"
# 归档会话文件
if [ -d "$SESSIONS_DIR" ]; then
cp "$SESSIONS_DIR"/*.jsonl "$WORKSPACE/records/conversations/$MONTH/$DATE/" 2>/dev/null || true
SESSION_COUNT=$(ls -1 "$SESSIONS_DIR"/*.jsonl 2>/dev/null | wc -l)
SIZE=$(du -sb "$SESSIONS_DIR" 2>/dev/null | cut -f1)
else
SESSION_COUNT=0
SIZE=0
fi
# 生成每日摘要 Markdown
cat > "$WORKSPACE/memory/daily/${DATE}.md" << DAILYMD
---
date: ${DATE}
type: daily-summary
sessions_count: ${SESSION_COUNT}
sessions_size_kb: $((${SIZE} / 1024))
---
# 📅 ${DATE} 对话摘要
## 📊 今日统计
- **日期**: ${DATE}
- **星期**: $(date +%A)
- **会话数**: ${SESSION_COUNT}
- **数据大小**: $((${SIZE} / 1024)) KB
## 🏷️ 重要标签
-
## 💡 今日收获
-
## 📌 待办事项
- [ ]
---
DAILYMD
# 记录归档日志
echo "[$(date)] 归档完成: ${DATE}, ${SESSION_COUNT} 个会话" >> "$WORKSPACE/logs/system/archive-${DATE}.log"
赋予执行权限:
chmod +x /root/.openclaw/workspace/archive-daily.sh
添加每天 23:00 自动执行归档:
# 添加定时任务
(crontab -l 2>/dev/null | grep -v archive-daily; echo "0 23 * * * /root/.openclaw/workspace/archive-daily.sh >> /root/.openclaw/workspace/logs/system/cron.log 2>&1") | crontab -
# 查看已配置的定时任务
crontab -l
创建 /root/.openclaw/.aliases.sh,方便快速查看记录:
# 查看今日记录
alias oc-today='cat /root/.openclaw/workspace/memory/daily/$(date +%Y-%m-%d).md 2>/dev/null || echo "今日记录不存在"'
# 查看昨日记录
alias oc-yesterday='cat /root/.openclaw/workspace/memory/daily/$(date -d yesterday +%Y-%m-%d 2>/dev/null || date -v-1d +%Y-%m-%d).md 2>/dev/null || echo "昨日记录不存在"'
# 手动执行归档
alias oc-archive='/root/.openclaw/workspace/archive-daily.sh'
# 查看统计
alias oc-stats='echo "=== OpenClaw 统计 ===" && echo "会话文件: $(ls -1 /home/node/.openclaw/agents/main/sessions/*.jsonl 2>/dev/null | wc -l)" && echo "每日摘要: $(ls -1 /root/.openclaw/workspace/memory/daily/*.md 2>/dev/null | wc -l)"'
# 查看目录大小
alias oc-size='du -sh /root/.openclaw/workspace/*/ 2>/dev/null | sort -h'
使用方法:
# 加载快捷命令
source /root/.openclaw/.aliases.sh
# 查看今日记录
oc-today
# 手动归档
oc-archive
# 查看统计
oc-stats
为了让 OpenClaw Bot 也能执行归档,在 Docker 容器内创建归档脚本:
# 在容器内创建归档命令
docker exec openclaw-openclaw-gateway-1 bash -c 'cat > /tmp/oc-archive.sh << "EOF"
#!/bin/bash
WORKSPACE="/home/node/.openclaw/workspace"
SESSIONS="/home/node/.openclaw/agents/main/sessions"
DATE=$(date +"%Y-%m-%d")
MONTH=$(date +"%Y-%m")
mkdir -p "$WORKSPACE/records/conversations/$MONTH/$DATE"
COUNT=0
if [ -d "$SESSIONS" ]; then
cp "$SESSIONS"/*.jsonl "$WORKSPACE/records/conversations/$MONTH/$DATE/" 2>/dev/null || true
COUNT=$(ls -1 "$SESSIONS"/*.jsonl 2>/dev/null | wc -l)
fi
cat > "$WORKSPACE/memory/daily/$DATE.md" << DAILY
---
date: $DATE
type: daily-summary
sessions_count: $COUNT
---
# 📅 $DATE 对话摘要
## 📊 统计
- 会话数: $COUNT
## 🏷️ 标签
-
## 💡 收获
-
DAILY
echo "✅ 归档完成: $DATE, $COUNT 个会话"
EOF
chmod +x /tmp/oc-archive.sh'
配置完成后,你可以直接在 Telegram 中对 Bot 发送:
| 指令 | 功能 |
|---|---|
| "归档今天的对话" | 手动触发归档 |
| "查看今天的记录" | 显示今日摘要 |
| "查看昨天的记录" | 显示昨日摘要 |
| "统计我的对话数量" | 显示统计报告 |
生成的 Markdown 文件 (memory/daily/2026-03-03.md):
---
date: 2026-03-03
type: daily-summary
sessions_count: 5
sessions_size_kb: 45
---
# 📅 2026-03-03 对话摘要
## 📊 今日统计
- **日期**: 2026-03-03
- **星期**: Tuesday
- **会话数**: 5
- **数据大小**: 45 KB
## 🏷️ 重要标签
- OpenClaw 配置
- 技能开发
## 💡 今日收获
- 配置了自动归档系统
- 解决了会话文件膨胀问题
## 📌 待办事项
- [ ] 测试归档功能
- [ ] 配置 Web 画廊
这种结构化的记录管理方式,让你可以:
OpenClaw 的 Skill 系统让你可以扩展 AI 的能力。我把自己的 Hexo 博客做成了 Skill,让 Bot 可以直接帮我创建博客文章。
在服务器上创建文章生成脚本 /opt/hexo-blog/create-post.sh:
#!/bin/bash
# Hexo Blog Post Creator
BLOG_DIR="/opt/hexo-blog"
POSTS_DIR="$BLOG_DIR/source/_posts"
# 参数
TITLE="${1:-新建文章}"
CATEGORY="${2:-随笔}"
TAGS="${3:-日常}"
DATE=$(date +"%Y-%m-%d %H:%M:%S")
FILENAME=$(echo "$TITLE" | sed 's/ /-/g' | tr '[:upper:]' '[:lower:]')-$(date +"%Y%m%d").md
# 创建目录
mkdir -p "$POSTS_DIR"
# 创建文章
cat > "$POSTS_DIR/$FILENAME" << EOF
---
title: $TITLE
author: Gamehu
date: $DATE
tags:
EOF
# 添加标签
IFS=',' read -ra TAG_ARRAY <<< "$TAGS"
for tag in "${TAG_ARRAY[@]}"; do
echo " - $(echo $tag | xargs)" >> "$POSTS_DIR/$FILENAME"
done
# 添加分类和正文
cat >> "$POSTS_DIR/$FILENAME" << EOF
categories:
- $CATEGORY
---
## 写在前面
$TITLE
<!-- more -->
## 正文
开始写作...
---
EOF
echo "✅ 文章创建成功: $POSTS_DIR/$FILENAME"
赋予执行权限:
chmod +x /opt/hexo-blog/create-post.sh
OpenClaw 使用 SKILL.md 文件(不是 YAML),创建 ~/.openclaw/skills/hexo-blog/SKILL.md:
---
name: hexo-blog
description: Create and manage Hexo blog posts via shell script
---
# Hexo Blog Skill
Create new blog posts with proper front matter for Hexo.
## Usage
Ask me to create a blog post:
- "帮我创建一篇博客文章,标题是《XXX》,分类是技术,标签是 OpenClaw,AI"
- "写一篇新文章,关于 Docker 使用心得"
## Tools
This skill uses the `shell` tool to execute `/opt/hexo-blog/create-post.sh`.
## Example
bash /opt/hexo-blog/create-post.sh "文章标题" "分类" "标签1,标签2"
docker restart openclaw-openclaw-gateway-1
验证 Skill 是否加载:
openclaw skills list
# 应该显示: ✓ ready │ 📦 hexo-blog │ Create and manage Hexo blog posts...
在 @gamehu_kb_bot 里直接说:
"帮我创建一篇博客文章,标题是《OpenClaw 使用心得》,分类是工具,标签是 OpenClaw,AI"
Bot 会自动:
openclaw-shi-yong-xin-de-20260302.md| 组件 | 作用 | 示例 |
|---|---|---|
| Script | 执行实际任务 | Shell/Python 脚本 |
| SKILL.md | Skill 定义和说明 | Markdown 文件(带 YAML frontmatter) |
| Directory | Skill 存放位置 | ~/.openclaw/skills/<skill-name>/ |
OpenClaw 内置了浏览器控制功能,让 AI 能够操作真实浏览器执行网页任务,比如搜索信息、填写表单、抓取数据等。
🚨 坑位 14:Docker 镜像默认不包含浏览器
Docker 部署后尝试使用浏览器功能时,报错:
[tools] browser failed: Error: No supported browser found
(Chrome/Brave/Edge/Chromium on macOS, Linux, or Windows).
默认的 Dockerfile 不会安装 Chromium/Playwright 浏览器,需要通过 --build-arg OPENCLAW_INSTALL_BROWSER=1 参数构建带浏览器的镜像。
cd /opt/openclaw
docker compose down
# 构建时间约 15-30 分钟,增加约 300MB
docker build -t openclaw:local-browser -f Dockerfile \
--build-arg OPENCLAW_INSTALL_BROWSER=1 \
--build-arg OPENCLAW_DOCKER_APT_PACKAGES="chromium xvfb" \
.
编辑 .env 文件:
# 修改前
OPENCLAW_IMAGE=openclaw:local
# 修改后
OPENCLAW_IMAGE=openclaw:local-browser
docker compose up -d
# 进入容器检查
docker exec openclaw-openclaw-gateway-1 \
ls -la /home/node/.cache/ms-playwright/
# 应该看到:
# chromium-1208/
# chromium_headless_shell-1208/
# ffmpeg-1011/
| 功能 | 说明 |
|---|---|
| 网页浏览 | 访问指定 URL,获取页面内容 |
| 表单填写 | 自动填写并提交网页表单 |
| 信息搜索 | 在搜索引擎或电商网站搜索 |
| 数据抓取 | 提取网页中的结构化数据 |
| 截图保存 | 截取网页画面 |
在 Telegram 中直接发送以下指令:
搜索信息:
"打开浏览器,访问 GitHub,搜索 'openclaw',告诉我前 3 个结果"
商品比价:
"帮我在京东上搜索 '机械键盘',找到评分最高的 3 个产品,记录价格"
数据抓取:
"访问 https://news.ycombinator.com/,提取首页前 10 条新闻的标题和链接"
用户指令 → OpenClaw Agent → 浏览器控制服务 → 无头 Chrome → 网页操作 → 结果返回
浏览器控制服务默认监听:http://127.0.0.1:18791/
尝试让 Bot 完成以下任务:
OpenClaw 支持配置多个 Telegram Bot,每个 Bot 可以绑定不同的 Agent,实现分工协作。
注意:仅配置 channels.telegram.accounts 还不够,想要真正隔离上下文,必须再加 agents.list + bindings。
| Bot | 用户名 | 职责 | 对应 Agent |
|---|---|---|---|
| 主助手 | @GameHuOpenclaw_bot | 通用对话、代码、写作 | main |
| 新闻助手 | @gamehu_news_bot | 科技新闻、每日简报 | news |
| 成长助手 | @gamehu_growth_bot | 学习计划、习惯追踪 | growth |
| 知识库助手 | @gamehu_kb_bot | 文档查询、笔记整理 | kb |
每个 Bot 都需要独立的 Token:
/main_bot - 主助手
/news_bot - 新闻助手
/growth_bot - 成长助手
/kb_bot - 知识库助手
{
"channels": {
"telegram": {
"enabled": true,
"accounts": {
"main": {
"botToken": "YOUR_MAIN_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"]
},
"news": {
"botToken": "YOUR_NEWS_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"]
},
"growth": {
"botToken": "YOUR_GROWTH_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"]
},
"kb": {
"botToken": "YOUR_KB_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"]
}
}
}
}
}
{
"agents": {
"list": [
{ "id": "main", "default": true, "workspace": "/home/node/.openclaw/workspace" },
{ "id": "news", "workspace": "/home/node/.openclaw/workspace-news" },
{ "id": "growth", "workspace": "/home/node/.openclaw/workspace-growth" },
{ "id": "kb", "workspace": "/home/node/.openclaw/workspace-kb" }
]
},
"bindings": [
{ "agentId": "main", "match": { "channel": "telegram", "accountId": "main" } },
{ "agentId": "news", "match": { "channel": "telegram", "accountId": "news" } },
{ "agentId": "growth", "match": { "channel": "telegram", "accountId": "growth" } },
{ "agentId": "kb", "match": { "channel": "telegram", "accountId": "kb" } }
]
}
⚠️
workspace字段必须显式指定:每个 agent 需要独立的 workspace 路径,否则多个 agent 共享同一个 workspace,记忆和配置会互相干扰。
这样每个 bot 会落到不同 agent,会话文件分目录保存,记忆文件也完全隔离。
每个 agent 对应一个独立目录,在服务器上创建:
mkdir -p /root/.openclaw/workspace-news
mkdir -p /root/.openclaw/workspace-growth
mkdir -p /root/.openclaw/workspace-kb
每个目录下的结构:
workspace-news/
├── SOUL.md # Agent 的身份定义
├── MEMORY.md # 长期记忆
├── USER.md # 用户信息
├── AGENTS.md # Agent 行为规范
└── memory/ # 每日记忆文件
每个 AGENT.md 定义该 Bot 的专业领域和行为:
# News Agent
## Role
你是一个科技新闻助手,专注于提供最新、最有价值的科技资讯。
## Capabilities
- 追踪科技行业动态
- 总结技术文章要点
- 提供每日新闻简报
## Style
- 简洁明了,重点突出
- 提供信息来源链接
- 主动推送重要消息
docker compose restart
查看日志确认所有 Bot 启动:
[telegram] [main] starting provider (@GameHuOpenclaw_bot)
[telegram] [news] starting provider (@gamehu_news_bot)
[telegram] [growth] starting provider (@gamehu_growth_bot)
[telegram] [kb] starting provider (@gamehu_kb_bot)
⚠️ 注意事项
botToken,全部放在 accounts 下accountId(如 main、news、growth、kb)必须是唯一的default,会和系统默认账号冲突配置好多 Bot 之后,你可能会发现一个问题:OpenClaw 里设置的定时任务(cron job),推送结果全都跑到了 main bot,而不是对应的 bot。
🚨 坑位 17:定时任务 delivery 缺少 accountId
在 OpenClaw Web UI 里配了新闻推送任务,时间一到,消息却出现在了 @GameHuOpenclaw_bot(main bot),而不是 @gamehu_news_bot。
OpenClaw 的定时任务存储在 ~/.openclaw/cron/jobs.json,每个任务的 delivery 字段控制推送目标。如果只写了 to(用户 ID),没有写 accountId,OpenClaw 不知道用哪个 bot 账户推送,默认走 main。
另外,to 只负责“推给谁”(用户 / 群 / 频道),accountId 负责“用哪个 bot 发”。这两个字段职责不同,缺一不可。
有问题的配置:
"delivery": {
"mode": "announce",
"channel": "telegram",
"to": "YOUR_USER_ID"
}
在每个 job 的 delivery 中加上 accountId,同时在 job 顶层加上 agentId。建议把执行超时提到 120 秒(字段 timeoutSeconds):
{
"id": "xxx",
"agentId": "news",
"name": "每日新闻推送",
"timeoutSeconds": 120,
"payload": {
"kind": "agentTurn",
"message": "执行新闻脚本并发送结果给用户"
},
"delivery": {
"mode": "announce",
"channel": "telegram",
"accountId": "news",
"to": "YOUR_USER_ID"
}
}
这种方式最适合你当前“4 个 Bot 各司其职”的场景。to 全部写你的用户 ID,accountId 分别写 main/news/growth/kb,消息会显示来自对应 bot。
{
"agentId": "news",
"timeoutSeconds": 120,
"delivery": {
"mode": "announce",
"channel": "telegram",
"accountId": "news",
"to": "YOUR_USER_ID"
}
}
如果要发到频道,不要填 bot 用户名(如 @gamehu_news_bot),而是填频道 chat id(通常是 -100...)。并确保对应 bot 已加入目标群/频道且有发言权限。
{
"agentId": "news",
"timeoutSeconds": 120,
"delivery": {
"mode": "announce",
"channel": "telegram",
"accountId": "news",
"to": "-1001234567890"
}
}
| 任务 | agentId | accountId | 推送到 |
|---|---|---|---|
| 每日新闻推送 | news | news | @gamehu_news_bot |
| 每日成长提醒 | growth | growth | @gamehu_growth_bot |
| 每周知识库总结 | kb | kb | @gamehu_kb_bot |
| 其他通用任务 | main | main | @GameHuOpenclaw_bot |
修改完 jobs.json 后重启服务即可生效:
cd /opt/openclaw && docker compose restart
最近我把底层模型又补了一档:Step-3.5-Flash。但这里有个非常容易搞混的点,我一开始也踩了坑:
🚨 坑位 18:想要“随时切模型”,不等于要改全局默认模型
如果你直接把 agents.defaults.model.primary 全局切到 Step,看起来省事,实际上会把别的会话、别的 Bot,甚至定时任务一起带偏。
更稳的做法是:
bailian/kimi-k2.5step/step-3.5-flash 注册成一个可选 provider/model ... 切换{
"models": {
"mode": "merge",
"providers": {
"bailian": {
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"apiKey": "YOUR_BAILIAN_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "kimi-k2.5",
"name": "kimi-k2.5",
"contextWindow": 256000,
"maxTokens": 8192
}
]
},
"kimi": {
"baseUrl": "https://api.moonshot.cn/v1",
"apiKey": "YOUR_KIMI_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "moonshot-v1-128k",
"name": "moonshot-v1-128k",
"contextWindow": 131072,
"maxTokens": 8192
}
]
},
"step": {
"baseUrl": "https://api.stepfun.com/v1",
"apiKey": "YOUR_STEP_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "step-3.5-flash",
"name": "Step 3.5 Flash",
"contextWindow": 256000,
"maxTokens": 8192
}
]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "bailian/kimi-k2.5"
},
"contextTokens": 131072
}
}
}
contextTokens 调到 131072这不是说 kimi-k2.5 或 step-3.5-flash 只能吃 128k,而是为了兼容我现在保留的第三档模型 moonshot-v1-128k。
如果你想做到:
那最稳的双档/三档基线,就是把 contextTokens 先压在 131072。这样:
bailian <-> step 切换时,几乎不会因为窗口变化引发额外压缩moonshot-v1-128k 时,也不会立刻超限直接在 Telegram 里发:
/models
/models step
/model status
/model step
/model bailian/kimi-k2.5
/model kimi/moonshot-v1-128k
这里要强调 3 个边界:
也就是说,这种切换本质上是“会话级模型覆盖”,不是“全局改配置”。
长期记忆不会丢,工作区画像也不会丢。真正会受影响的是短期上下文压缩:
bailian/kimi-k2.5 切到 step/step-3.5-flash,因为两边都是大窗口,风险很低bailian 或 step 切到 moonshot-v1-128k,因为窗口更小,下一轮可能触发更激进的 compaction所以我现在的原则很简单:
bailian/kimi-k2.5/model step/model bailian/kimi-k2.5/model kimi/moonshot-v1-128k⚠️ 如果你想支持“切换模型 step”这种中文自然语言,而不是
/model step,那是另外一层 parser 改造,不是 OpenClaw 开箱即用能力。
openclaw.json(多 Bot 生产版本,已脱敏,包含 compaction 修复):
{
"gateway": {
"port": 18789,
"mode": "local",
"controlUi": {
"allowedOrigins": ["http://localhost:18789", "http://127.0.0.1:18789"]
},
"auth": {
"token": "your-secure-token-here"
}
},
"models": {
"mode": "merge",
"providers": {
"bailian": {
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"apiKey": "YOUR_BAILIAN_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "kimi-k2.5",
"name": "kimi-k2.5",
"reasoning": false,
"input": ["text", "image"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 256000,
"maxTokens": 8192
}
]
},
"kimi": {
"baseUrl": "https://api.moonshot.cn/v1",
"apiKey": "YOUR_KIMI_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "moonshot-v1-128k",
"name": "moonshot-v1-128k",
"reasoning": false,
"input": ["text", "image"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 131072,
"maxTokens": 8192
}
]
},
"step": {
"baseUrl": "https://api.stepfun.com/v1",
"apiKey": "YOUR_STEP_API_KEY",
"api": "openai-completions",
"models": [
{
"id": "step-3.5-flash",
"name": "Step 3.5 Flash",
"reasoning": false,
"input": ["text", "image"],
"cost": { "input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0 },
"contextWindow": 256000,
"maxTokens": 8192
}
]
}
}
},
"agents": {
"defaults": {
"model": {
"primary": "bailian/kimi-k2.5"
},
"contextTokens": 131072,
"compaction": {
"mode": "safeguard",
"reserveTokensFloor": 24000,
"identifierPolicy": "strict",
"memoryFlush": {
"enabled": true,
"softThresholdTokens": 6000
}
}
},
"list": [
{ "id": "main", "default": true },
{ "id": "news" },
{ "id": "growth" },
{ "id": "kb" }
]
},
"bindings": [
{ "agentId": "main", "match": { "channel": "telegram", "accountId": "main" } },
{ "agentId": "news", "match": { "channel": "telegram", "accountId": "news" } },
{ "agentId": "growth", "match": { "channel": "telegram", "accountId": "growth" } },
{ "agentId": "kb", "match": { "channel": "telegram", "accountId": "kb" } }
],
"session": {
"scope": "per-sender",
"reset": {
"mode": "idle",
"idleMinutes": 30
},
"maintenance": {
"mode": "enforce",
"pruneAfter": "7d",
"maxEntries": 120
}
},
"channels": {
"telegram": {
"enabled": true,
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"historyLimit": 30,
"accounts": {
"main": {
"botToken": "YOUR_MAIN_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"],
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"streaming": "off"
},
"news": {
"botToken": "YOUR_NEWS_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"],
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"streaming": "off"
},
"growth": {
"botToken": "YOUR_GROWTH_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"],
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"streaming": "off"
},
"kb": {
"botToken": "YOUR_KB_BOT_TOKEN",
"allowFrom": ["YOUR_TELEGRAM_USER_ID"],
"dmPolicy": "pairing",
"groupPolicy": "allowlist",
"streaming": "off"
}
}
}
}
}
⚠️ 关键提示:
- 如果你保留多档模型可切换,
contextTokens不要盲目拉满;我现在用131072作为三档模型共存时的安全基线。- 浏览器功能:默认镜像不包含 Chromium,需要使用
--build-arg OPENCLAW_INSTALL_BROWSER=1构建openclaw:local-browser镜像- 多 Bot 场景必须配置
bindings,否则 direct chat 默认会落到主会话桶,容易串上下文。- 如果你临时切到
moonshot-v1-128k,继续保持contextTokens <= 131072,避免再次出现 compaction 超限。
单 Bot 简化版(含 compaction 修复):
{
"gateway": {
"port": 18789,
"mode": "local",
"controlUi": {
"allowedOrigins": ["http://localhost:18789", "http://127.0.0.1:18789"]
},
"auth": {
"token": "your-secure-token"
}
},
"models": {
"providers": {
"bailian": {
"baseUrl": "https://coding.dashscope.aliyuncs.com/v1",
"apiKey": "YOUR_API_KEY",
"models": [{
"id": "kimi-k2.5",
"contextWindow": 256000,
"maxTokens": 8192
}]
}
}
},
"agents": {
"defaults": {
"model": { "primary": "bailian/kimi-k2.5" },
"contextTokens": 131072,
"compaction": {
"mode": "safeguard",
"reserveTokensFloor": 24000
}
}
},
"session": {
"reset": {
"mode": "idle",
"idleMinutes": 30
},
"maintenance": {
"mode": "enforce",
"pruneAfter": "7d",
"maxEntries": 120
}
},
"channels": {
"telegram": {
"enabled": true,
"historyLimit": 30,
"botToken": "YOUR_BOT_TOKEN",
"allowFrom": ["YOUR_USER_ID"]
}
}
}
| 坑位 | 问题 | 解决方案 |
|---|---|---|
| 1 | 构建时 OOM | 添加 5GB Swap 空间 |
| 2 | gateway.mode 缺失 |
必须显式设置 "mode": "local" |
| 3 | controlUi.allowedOrigins 缺失 |
非 localhost 绑定时必须配置 |
| 4 | gateway.bind 配置无效 |
必须使用 CLI 参数 --bind lan |
| 5 | Telegram 配对失败 | 删除 dmPolicy,使用 allowFrom 白名单 |
| 6 | 防火墙不生效 | 使用 DOCKER-USER 链而非 INPUT 链 |
| 7 | Google OAuth 授权失败 | 确保 Redirect URI 与配置完全一致 |
| 8 | MCP 服务器无法启动 | 检查 npx 是否可用,网络是否通畅 |
| 9 | 多 Bot 配置时 default 账号冲突 |
避免使用 default 作为 accountId,改用 main、news 等 |
| 10 | 多 Bot 顶层 botToken 被覆盖 |
使用 accounts 时,顶层不要设置 botToken |
| 11 | Skill 脚本权限不足 | 确保脚本有执行权限 chmod +x script.sh |
| 12 | Skill 中文字符处理 | 使用 `echo |
| 13 | Compaction 卡死导致无响应 | 配置合理 contextTokens + 设置 historyLimit + 启用 session.maintenance |
| 14 | Docker 镜像无浏览器 | 使用 --build-arg OPENCLAW_INSTALL_BROWSER=1 重新构建镜像 |
| 15 | 镜像体积过大 (5.7GB) | 使用多阶段构建 + node:22-bookworm-slim 基础镜像 |
| 16 | 会话记录膨胀失控 | 部署结构化记录系统 + 每日自动归档 + 分类存储 |
| 17 | 定时任务推送全到 main bot | jobs.json 每个 job 加 agentId + delivery.accountId |
| 18 | 想切 Step 却误改全局默认模型 | 保持默认模型不动,使用 /model ... 做会话级切换 |
| 19 | HEARTBEAT.md 抢占 main 主会话 |
保留 heartbeat,但改成低风险静默版;主动提醒交给 cron 或其他 bot |
OpenClaw 默认构建的镜像体积巨大:
openclaw:local: 4.4GBopenclaw:local-browser: 5.7GB在 2GB 内存的服务器上,光是镜像就占满了磁盘。
| 问题 | 说明 | 占用 |
|---|---|---|
| 单阶段构建 | 构建依赖和生产代码混在一起 | ~1GB |
| 未清理 devDependencies | 开发依赖未删除 | ~500MB |
| bookworm 基础镜像 | 完整 Debian 系统 | ~1GB |
| Chromium + Playwright | 浏览器全套 | ~1.7GB |
创建 Dockerfile.optimized:
# ============================================================================
# 阶段1:构建阶段
# ============================================================================
FROM node:22-bookworm AS builder
WORKDIR /app
RUN corepack enable
# 复制依赖文件
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile
# 复制源码并构建
COPY . .
RUN pnpm build && pnpm ui:build
# 关键:清理开发依赖,只保留生产依赖
RUN pnpm prune --prod
# ============================================================================
# 阶段2:生产阶段 - 使用精简基础镜像
# ============================================================================
FROM node:22-bookworm-slim AS production
# 只安装必需的系统依赖
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# 从构建阶段只复制必要文件
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./
COPY --from=builder /app/openclaw.mjs ./
# 使用非 root 用户运行
RUN groupadd -r nodeuser && useradd -r -g nodeuser nodeuser
RUN chown -R nodeuser:nodeuser /app
USER nodeuser
ENV NODE_ENV=production
EXPOSE 18789 18790
CMD ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"]
cd /opt/openclaw
# 构建轻量级版本 (2-3GB)
docker build \
--target production \
-t openclaw:slim \
-f Dockerfile.optimized \
.
# 构建浏览器版本 (3-4GB)
docker build \
--target browser \
-t openclaw:slim-browser \
-f Dockerfile.optimized \
.
| 镜像 | 原体积 | 优化后 | 节省 |
|---|---|---|---|
openclaw:local |
4.4GB | openclaw:slim 2.5GB |
43% ↓ |
openclaw:local-browser |
5.7GB | openclaw:slim-browser 3.5GB |
39% ↓ |
修改 .env 文件:
# 轻量级版本(推荐日常使用)
OPENCLAW_IMAGE=openclaw:slim
# 或浏览器版本(需要网页自动化时)
# OPENCLAW_IMAGE=openclaw:slim-browser
然后重启服务:
docker compose up -d
使用 Alpine 基础镜像(极致精简)
FROM node:22-alpine
# 可将体积降至 < 1.5GB
# 但可能有兼容性问题
启用 Docker 镜像压缩
docker build --compress ...
定期清理无用镜像
docker image prune -a
部署运行几天后,我遇到了一个极其隐蔽但致命的问题:Telegram Bot 突然不响应了,或者响应需要等 5 分钟以上。
embedded run compaction start 不动深度诊断后发现了两个根本原因:
OpenClaw 默认上下文预算与实际模型窗口不一致时,会导致系统误判可用空间,compaction 后仍然超限(日志显示使用了 145.7%)。
所以一定要注意不同的模型一定要修改响应的配置。
所有历史消息(603 条)全部进入上下文,而不是只取最近的几十条。两天内会话文件膨胀到 1.4MB,每次 compaction 都要扫描全部历史。
maxTokens 配得过小会让长任务反复截断并补发,间接加速会话膨胀。它不是唯一根因,但会放大卡顿问题。
需要添加三组关键配置:
{
"agents": {
"defaults": {
"contextTokens": 131072,
"compaction": {
"mode": "safeguard",
"reserveTokensFloor": 24000,
"identifierPolicy": "strict",
"memoryFlush": {
"enabled": true,
"softThresholdTokens": 6000
}
}
}
},
"session": {
"scope": "per-sender",
"reset": {
"mode": "idle",
"idleMinutes": 30
},
"maintenance": {
"mode": "enforce",
"pruneAfter": "7d",
"maxEntries": 120
}
},
"channels": {
"telegram": {
"enabled": true,
"historyLimit": 30,
"accounts": {
...
}
}
}
}
| 配置项 | 值 | 作用 |
|---|---|---|
agents.defaults.contextTokens |
131072 | 多档模型共存时更稳,切回 moonshot-v1-128k 也不容易超限 |
agents.defaults.compaction.mode |
safeguard | 启用保护模式,预留安全空间 |
session.maintenance.mode |
enforce | 强制清理 7 天前的旧会话 |
session.maintenance.maxEntries |
120 | 最多保留 120 个会话 |
channels.telegram.historyLimit |
30 | 每次请求最多带 30 条历史 |
上面的 historyLimit 和 session 配置放在 channels.telegram 级别,会对所有 4 个 Bot 统一生效。如果需要不同 Bot 不同策略,可以在每个 account 下单独设置 historyLimit。
后来我又把 step/step-3.5-flash 接进来了。这里最重要的经验是:
别为了切模型去改全局默认模型,直接用 /model ... 切当前会话就够了。
原因很简单:
我现在的实际用法:
/model step
/model bailian/kimi-k2.5
/model kimi/moonshot-v1-128k
/model status
其中:
bailian <-> step 风险最低moonshot-v1-128k 时,最容易触发短期上下文压缩后来我又遇到过一次比 compaction 更隐蔽的问题:Telegram 能收到消息,但主 Bot 要么完全不回,要么要等很久才回。
@GameHuOpenclaw_bot 发消息,长时间没有任何回复lane=session:agent:main:mainsendMessage这次不是 Telegram 渠道挂了,也不是容器死了,而是 HEARTBEAT.md 的主动任务把 main 主会话占掉了。
排查时我在 sessions.json 里发现,agent:main:main 这条映射的 origin.provider 已经不是 Telegram,而是 heartbeat。结果就是:
mainmain 会话上下文已经被 heartbeat 任务污染这里有个很容易误解的点:HEARTBEAT.md 很有用,但它不是核心运行时依赖。OpenClaw 的 heartbeat 机制本身会启动,但如果你把 HEARTBEAT.md 写成“主动检查 git、主动提醒发博客、主动输出长段内容”的详细任务单,它就可能在重启、升级尝试或异常恢复后占用 main session。
我这次现场看到的情况是:
update available (latest): v2026.3.7 (current v2026.2.27)v2026.2.27main 会话映射被 heartbeat 改写了所以这次故障更准确地说,是升级尝试触发重启后,暴露了 heartbeat 抢占主会话的问题,而不是“新版本本身有 bug”。
如果你遇到“消息进来了但 Bot 不回”,重点看两处:
docker compose logs -f openclaw-gateway~/.openclaw/agents/main/sessions/sessions.json如果日志里反复出现主会话执行,但没有正常发消息,同时 sessions.json 里 agent:main:main.origin.provider = heartbeat,基本就能确定是这个坑。
sessions.json 和当前 session 文件agent:main:main 映射openclaw-gatewayHEARTBEAT.md 改成低风险版低风险版的原则是:
HEARTBEAT_OK如果你的 main bot 是日常高频对话入口,我现在的建议很明确:
HEARTBEAT.md 可以保留这样既能保留记忆整理能力,又不会再次把主聊天打挂。
不要只看自己执行过 git pull 或 docker compose restart,要以启动日志里的版本号为准。
如果日志仍然显示:
update available (latest): v2026.3.7 (current v2026.2.27)
那结论很简单:你还在跑旧版本,升级并没有真正完成。
部署完成后,通过 Telegram 与 Bot 对话:
配置 contextTokens、historyLimit 和 session.maintenance 后,OpenClaw 从"两天必挂"变成"长期稳定运行"。这是生产环境部署的必选项,不是可选项。
📷 Telegram 实际对话效果:
openclaw.json 提交到 GitallowFrom 列表