从单兵到平台:把 OpenClaw 容器改造成多项目 Agent 工厂
在上一篇中,我们构建了 Hephaestus——一个自动解剖 C++ 代码并产出技术参考文档的三 Agent 流水线。它跑得不错,但有一个问题:整个容器栈和 Hephaestus 项目深度耦合。
如果明天我想用同样的 OpenClaw 基础设施跑一个完全不同的 Agent——比如一个自动监控 GitHub Trending 并写周报的 Agent,或者一个定时扫描安全漏洞数据库的 Agent——我得从头搭一套几乎一样的 Docker 环境。
这不合理。OpenClaw Gateway 本身是通用的,项目特异的部分其实很少。本文记录如何用最小改动,把一个单项目容器改造成可以一行命令切换项目的通用平台。
耦合点分析
先诊断,再动手。当前的 Hephaestus 栈有三个层面的耦合:
层1:镜像(Dockerfile)
FROM alpine/openclaw:latest
# Hephaestus 专用:Rust + Go 工具链
RUN curl https://sh.rustup.rs | sh -s -- -y
RUN curl -sSL https://go.dev/dl/go1.24.1.linux-amd64.tar.gz | tar -C /usr/local -xz
Rust 和 Go 是 Hephaestus 的编译验证需要的,换个项目可能需要 Python + Node,也可能什么都不需要。把语言工具链烧进基础镜像是浪费。
层2:编排(docker-compose.yml)
container_name: hephaestus-gateway # 硬编码项目名
ports: ["18790:18790"] # 硬编码端口
volumes:
- ./data/openclaw:/home/node/.openclaw # 单一数据目录
容器名、端口、数据路径全部写死,跑第二个项目就会冲突。
层3:数据(data/ 目录)
data/
├── openclaw/
│ ├── openclaw.json ← Hephaestus 的三 Agent 配置
│ └── workspace/
│ ├── SOUL.md ← Hephaestus 的写作规则
│ ├── HEARTBEAT.md ← Hephaestus 的流水线
│ └── TOPIC_INDEX.md ← Hephaestus 的选题
└── ssh/ ← Hephaestus 的 Git 凭证
所有运行时数据混在一个目录里,无法隔离不同项目的状态。
改造方案:三层解耦
第一层:镜像分层
把 Dockerfile 拆成基础镜像和工具链扩展镜像:
# Dockerfile — 基础镜像,所有项目共用
FROM alpine/openclaw:latest
USER root
RUN apt-get update -qq && \
apt-get install -y -qq --no-install-recommends ripgrep && \
rm -rf /var/lib/apt/lists/*
USER node
# dockerfiles/Dockerfile.dev — 开发镜像,需要编译验证的项目用
FROM openclaw-base:latest
USER root
# Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
sh -s -- -y --no-modify-path
# Go
RUN curl -sSL https://go.dev/dl/go1.24.1.linux-amd64.tar.gz | \
tar -C /usr/local -xz
# Python (如果需要)
# RUN apt-get install -y python3 python3-pip
USER node
不需要编译的项目(纯文本处理、API 调用类)直接用基础镜像,构建快、体积小。
第二层:编排参数化
用 .env 文件驱动 docker-compose.yml,所有项目特异值都走变量:
# .env — 切换项目只改这个文件
PROJECT=hephaestus
CONTAINER_PREFIX=hephaestus
GATEWAY_PORT=18790
OPENCLAW_IMAGE=openclaw-dev:latest
# docker-compose.yml — 参数化
services:
tailscale:
container_name: ${CONTAINER_PREFIX}-ts
ports:
- "${GATEWAY_PORT}:${GATEWAY_PORT}"
# ...其余不变
openclaw-gateway:
image: ${OPENCLAW_IMAGE}
container_name: ${CONTAINER_PREFIX}-gateway
volumes:
- ./projects/${PROJECT}/openclaw:/home/node/.openclaw
- ./projects/${PROJECT}/ssh:/home/node/.ssh
command: ["node", "dist/index.js", "gateway", "--bind", "lan",
"--port", "${GATEWAY_PORT}"]
# ...其余不变
openclaw-cli:
image: ${OPENCLAW_IMAGE}
container_name: ${CONTAINER_PREFIX}-cli
volumes:
- ./projects/${PROJECT}/openclaw:/home/node/.openclaw
- ./projects/${PROJECT}/ssh:/home/node/.ssh
# ...其余不变
关键变化:./data/openclaw → ./projects/${PROJECT}/openclaw。数据目录跟着项目走。
第三层:项目目录隔离
每个项目是一个独立的目录,包含完整的运行时状态:
projects/
├── hephaestus/
│ ├── openclaw/
│ │ ├── openclaw.json ← 3 Agent: Scanner/Analyzer/Writer
│ │ ├── agents/ ← Agent 会话状态
│ │ ├── devices/ ← 设备配对
│ │ └── workspace/
│ │ ├── SOUL.md ← C++ 代码分析规则
│ │ ├── HEARTBEAT.md ← 每日写作流水线
│ │ └── TOPIC_INDEX.md ← 35 个选题
│ └── ssh/
│ ├── id_ed25519
│ └── config
│
├── github-digest/ ← 未来项目示例
│ ├── openclaw/
│ │ ├── openclaw.json ← 1 Agent: Digest Writer
│ │ └── workspace/
│ │ ├── SOUL.md ← 周报写作规则
│ │ └── HEARTBEAT.md ← 每周触发
│ └── ssh/
│
└── vuln-scanner/ ← 另一个示例
├── openclaw/
│ ├── openclaw.json ← 2 Agent: Scanner/Reporter
│ └── workspace/
│ └── SOUL.md
└── ssh/
每个项目目录是完全自包含的。切换项目不会影响其他项目的状态、会话历史、配对信息。
操作流程
切换项目
# 编辑 .env,改一行
PROJECT=github-digest
# 重启
docker compose down openclaw-gateway
docker compose up -d
创建新项目
# 脚手架脚本
./new-project.sh my-new-project
# 它做的事情:
mkdir -p projects/my-new-project/{openclaw/workspace,ssh}
# 复制一份最小 openclaw.json 模板
# 创建空的 SOUL.md
# 生成 SSH 密钥(可选)
并行运行多个项目
如果需要同时跑两个项目(不同端口),用 compose profiles 或 override:
# 方案1:不同 .env 文件
docker compose --env-file .env.hephaestus up -d
docker compose --env-file .env.digest up -d
# 方案2:override 文件
docker compose -f docker-compose.yml -f projects/hephaestus/override.yml up -d
不过大多数场景下,一次跑一个项目就够了。OpenClaw Gateway 不是重量级服务,启停很快。
attach.sh 的适配
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
# 从 .env 读取当前项目
source .env 2>/dev/null || true
PREFIX="${CONTAINER_PREFIX:-openclaw}"
case "${1:-shell}" in
shell|"") docker exec -it "${PREFIX}-gateway" bash ;;
root) docker exec -it -u root "${PREFIX}-gateway" bash ;;
cli) docker compose run --rm openclaw-cli ;;
logs) docker compose logs -f openclaw-gateway ;;
status) docker compose ps ;;
project) echo "Current: ${PROJECT:-unset}" ;;
*) echo "Usage: $0 [shell|root|cli|logs|status|project]" ;;
esac
./attach.sh project 显示当前活跃项目,避免进错容器。
新项目脚手架脚本
#!/usr/bin/env bash
# new-project.sh — 创建新项目骨架
set -euo pipefail
NAME="${1:?Usage: $0 <project-name>}"
DIR="projects/$NAME"
[ -d "$DIR" ] && { echo "Project '$NAME' already exists"; exit 1; }
mkdir -p "$DIR"/{openclaw/workspace,ssh}
cat > "$DIR/openclaw/openclaw.json" << 'EOF'
{
"gateway": {
"mode": "local",
"bind": "lan",
"port": 18790,
"controlUi": { "enabled": true, "allowInsecureAuth": true }
},
"agents": {
"defaults": {
"model": { "primary": "google/gemini-3-flash", "fallbacks": ["openai/gpt-4o"] },
"sandbox": { "mode": "off" }
},
"list": [
{
"id": "main",
"name": "Main Agent",
"default": true
}
]
}
}
EOF
cat > "$DIR/openclaw/workspace/SOUL.md" << 'EOF'
# Soul
你的身份和规则写在这里。
EOF
echo "Created project: $DIR"
echo "Next steps:"
echo " 1. Edit $DIR/openclaw/openclaw.json — 配置 agents"
echo " 2. Edit $DIR/openclaw/workspace/SOUL.md — 写规则"
echo " 3. Set PROJECT=$NAME in .env"
echo " 4. docker compose up -d"
迁移清单
从当前的 Hephaestus 单项目结构迁移到多项目结构,需要的改动:
| 步骤 | 操作 | 风险 |
|---|---|---|
| 1 | mv data/ projects/hephaestus/ |
低——重命名目录 |
| 2 | 拆 Dockerfile 为 base + dev | 低——需要重新 build |
| 3 | 参数化 docker-compose.yml | 中——需要测试变量替换 |
| 4 | 创建 .env 文件 | 低 |
| 5 | 更新 .gitignore(projects/ 替换 data/) |
低 |
| 6 | 更新 attach.sh | 低 |
整个迁移可以在 30 分钟内完成,Gateway 停机时间不超过 1 分钟。
设计权衡
为什么不用 Kubernetes? 这是跑在家里 Mac 上的个人项目,一个 docker-compose 就够了。K8s 的声明式配置确实更适合多项目编排,但引入它的运维成本远大于收益。
为什么不在一个 Gateway 里混跑多个项目的 Agent? OpenClaw 的 Agent 共享同一个 workspace。不同项目的 SOUL.md 会互相覆盖,心跳调度也会混乱。项目间隔离最干净的方式就是各用各的 .openclaw 目录。
SSH 密钥要不要共享? 看场景。如果多个项目都要推送到同一个 Gitea,共享 SSH 密钥是合理的(可以用符号链接)。如果项目需要访问不同的 Git 服务,各自维护更安全。
总结
这次改造的核心思路很简单:找到耦合点,参数化它。
- 镜像耦合 → Dockerfile 分层
- 编排耦合 →
.env参数化 - 数据耦合 → 项目目录隔离
最终的效果是:切换项目只需改 .env 里一行 PROJECT=xxx,然后 docker compose up -d。每个项目的 Agent 配置、工作区状态、SSH 凭证完全独立,互不干扰。
OpenClaw Gateway 本身就是一个通用的 AI Agent 运行时。我们要做的不是改造它,而是不要让自己的部署方式限制了它的通用性。