OpenClaw 部署复盘:我们把 AI 员工拆成了 Gateway、状态机和运行环境
最近我们把一个“能长期在线处理消息”的 AI 助手原型重新拆了一遍。最开始的问题看起来很简单:OpenClaw 到底应该部署在哪里?本地、Docker,还是 VPS?
但真正动手后发现,部署方式只是表层问题。我们真正没对齐的是:OpenClaw 在这套系统里到底承担什么角色。如果把它直接理解成“AI 员工系统本体”,后面很容易把 Gateway、记忆、人格配置、周期任务、工具权限、业务工作流全部揉在一起,最后系统能跑,但每次出问题都不知道该查哪一层。
后来我们把它拆成三块:OpenClaw 负责 Gateway 和运行底座,配置层负责身份、记忆和周期检查,业务工作流负责真正的自动化动作。这个拆法一出来,架构图、部署路径和排错方式都清楚了很多。
我们先修正了一个误区:OpenClaw 不是“完整 AI 员工本体”
OpenClaw 当前更准确的定位,是一个 self-hosted Gateway:它把 Discord、Google Chat、iMessage、Matrix、Microsoft Teams、Signal、Slack、Telegram、WhatsApp、Zalo 等渠道接到 AI coding agents 上,并让一个 Gateway 进程持有 sessions、routing 和 channel connections。
这句话对架构设计很重要。
它意味着 OpenClaw 首先解决的是这些问题:
消息从哪里来
↓
由哪个 Gateway 接住
↓
路由到哪个 agent/session
↓
agent 能看到哪些上下文
↓
结果从哪个渠道回去
但“这个 AI 是谁”“它长期记住什么”“它每天几点巡检”“它能不能自动归档邮件”“它能不能调用外部脚本”,这些不是 Gateway 单独解决的,而是配置层和工作流层继续往上叠。
我们后来在内部约定了一个更实用的定义:
OpenClaw 是 AI 助手的运行底座,不是业务工作流本身。
AI 员工 = Gateway + agent 编排 + 记忆状态 + 工具权限 + 触发器 + 业务规则。
这个定义避免了一个常见坑:看到系统能从 Telegram 或 Slack 回复消息,就以为它已经是“员工”。其实这只是入口打通了。真正的员工感,来自它能持续接收事件、判断下一步、调用工具、写回状态,并在下一轮继续接着处理。
架构的核心不是画框图,而是让问题能定位
一开始我们也画过很漂亮的架构图:LLM、Memory、Tools、Scheduler、Agent、Gateway,几个框一连,好像系统就清楚了。
结果第一次排错就暴露问题:一封消息没有按预期触发处理,我们不知道该查哪一层。
是 Gateway 没收到?
是 session 路由错了?
是上下文没有注入?
是模型判断不该处理?
是工具被权限拦了?
还是执行完了但没有写回状态?
这时候才发现,架构不是为了展示“系统由哪些组件组成”,而是为了让我们在出问题时能顺着数据流往下查。
我们最终把 OpenClaw 相关系统画成了这个循环:
External Event / User Message / Timer
↓
OpenClaw Gateway
↓
Session + Agent Routing
↓
Bootstrap Config + Memory
↓
LLM Decision Layer
↓
Tool / API / File Action
↓
State Write-back + Logs
↓
Next Trigger
这个循环里有两个关键点。
第一,LLM 不是系统边界的起点。输入可能来自用户消息,也可能来自定时任务、外部事件、新邮件或 webhook。
第二,工具执行不是系统边界的终点。执行之后必须写回状态:处理了什么、结果如何、是否需要下次继续跟进。没有这一步,系统每次都像第一次工作。
这次失败发生在输入、路由、上下文、决策、工具执行、状态回写里的哪一段?
只要能回答这个问题,AI 系统就不再是一个黑盒。
LLM 只是推理引擎,代理逻辑才负责秩序
我们早期有个错误判断:既然模型足够强,那就把更多决策交给模型。比如“收到消息后自己判断是否要查记忆、是否要调用工具、是否要回复用户”。
这个思路在 demo 阶段很爽,但一接真实工作流就开始飘。
同一类消息,有时模型会先查历史,有时直接回复;有时会调用工具,有时只给建议;有时会把低风险任务说得很严重,有时又把需要人工确认的事情轻轻带过。
后来我们把这部分从“模型自由判断”改成“代理逻辑先定边界,模型在边界内做判断”。
type TriggerSource = "message" | "heartbeat" | "webhook";
type Decision =
| { type: "respond"; requireReview: false }
| { type: "draft"; requireReview: true }
| { type: "tool_call"; tool: string; requireReview: boolean }
| { type: "ignore"; reason: string };
type RuntimeInput = {
source: TriggerSource;
channel: "telegram" | "slack" | "email";
text: string;
sender: string;
hasSensitiveKeywords: boolean;
};
function routeInput(input: RuntimeInput): Decision {
if (input.hasSensitiveKeywords) {
return {
type: "draft",
requireReview: true,
};
}
if (input.source === "heartbeat") {
return {
type: "tool_call",
tool: "daily_checklist",
requireReview: false,
};
}
if (input.channel === "slack" && input.text.includes("部署")) {
return {
type: "draft",
requireReview: true,
};
}
return {
type: "respond",
requireReview: false,
};
}
// input
const event: RuntimeInput = {
source: "message",
channel: "slack",
text: "这个服务今晚能不能直接部署?",
sender: "teammate@example.com",
hasSensitiveKeywords: true,
};
// operation
const decision = routeInput(event);
// output
console.log(decision);
输出大概是:
{
"type": "draft",
"requireReview": true
}
这段代码没有任何“高级 AI 魔法”,但它把系统边界收住了:涉及敏感动作时,只能生成草稿,不能直接执行。
LLM 在这里仍然很重要,它负责理解意图、总结上下文、生成草稿、解释风险。但代理逻辑负责决定:什么能做,什么只能建议,什么必须人工确认。
记忆不是聊天记录,而是状态系统
我们第二个踩坑点是记忆。
最开始我们把 memory 理解成“保存历史对话”。后来发现,这种记忆对长期运行的工作流帮助有限。真正有用的是状态:
用户偏好
当前任务进度
上次失败原因
联系人处理规则
某个工作流的最新 checkpoint
哪些动作曾被人工纠正过
openclaw-config 这类配置层项目已经把这个方向拆得比较清楚:它把自己定位成 OpenClaw 的 shared configuration layer,用 Markdown 和 Python 脚本提供 persistent memory、real-world tools,以及 scheduled autonomous workflows;README 里还明确提到三层记忆结构:always-loaded essentials、daily context files、deep knowledge with semantic search。(GitHub)
这也解释了为什么我们不能把 OpenClaw 的 Gateway 和 openclaw-config 混成一个东西。
Gateway 更像运行时底座。
Config 更像长期行为和状态管理。
Workflow 才是具体业务动作。
配置层通常会把文件拆成这样:
AGENTS.md -> agent 如何思考和行动
SOUL.md -> 长期人格和行为倾向
USER.md -> 用户画像和工作偏好
MEMORY.md -> 始终加载的关键记忆
BOOT.md -> 启动时检查什么
HEARTBEAT.md -> 周期检查什么
TOOLS.md -> 当前机器有哪些工具和限制
IDENTITY.md -> 快速身份卡片
openclaw-config 的 README 也确实把这些模板文件列在 templates/ 下,并分别赋予身份、用户、记忆、启动例程、周期检查、工具环境等职责。(GitHub)
“用户昨天说了什么”不一定值得长期保存。
“用户多次纠正我们:涉及付款和合同必须人工确认”才是有价值的长期状态。
记忆层应该服务后续决策,而不是把聊天历史原样堆进去。
Heartbeat 解决的是“周期巡检”,不是万能后台任务
当我们第一次看到 HEARTBEAT.md,直觉上很容易把它理解成 cron:到了时间就执行任务。
但 OpenClaw 的 Heartbeat 文档讲得更细:Heartbeat 是 main session 里的 scheduled agent turn,用来让模型周期性地 surface anything that needs attention;它不会创建 background task records,后台任务记录是给 detached work、subagents 或 isolated cron jobs 用的。(OpenClaw)
这个区别很关键。
Heartbeat 更适合做轻量巡检:
# HEARTBEAT.md
## Morning Check
When heartbeat runs during workday mornings:
1. Check pending user-visible items.
2. Summarize only things that need attention.
3. Do not send external messages.
4. Do not modify files unless explicitly asked.
5. If nothing needs attention, return HEARTBEAT_OK.
它不适合承载所有后台自动化。比如长时间采集、批量迁移、复杂数据处理,这类任务最好拆成独立 job 或脚本,再由 heartbeat 负责“发现结果并提醒”。
我们后来把周期任务分了三类:
Heartbeat:
轻量巡检、提醒、状态摘要
Scheduled Job:
可重复、可重试、耗时可控的后台任务
Manual Approval Flow:
发邮件、删数据、改配置、发布部署等高风险动作
这让系统行为稳定了很多。Heartbeat 不再像一个随时可能乱跑的后台线程,而是一个“定时叫醒 agent 看一眼当前状态”的机制。
工具层要按风险分级,而不是一股脑开放
OpenClaw 这类系统一旦接入工具,就从聊天软件变成了自动化系统。它能读文件、调 API、发消息、更新记录,这些能力越强,越需要边界。
我们最早只做了一个粗暴开关:允许工具 / 不允许工具。后来发现完全不够。
更合理的做法是按风险分级:
Level 0: 只读
- 查询日程
- 读取状态
- 搜索文档
- 生成摘要
Level 1: 可逆写入
- 创建草稿
- 添加标签
- 写入临时文件
- 更新内部备注
Level 2: 高风险写入
- 发送外部邮件
- 删除文件
- 修改生产配置
- 触发部署
- 改权限
然后在代理逻辑里明确处理:
type ToolRisk = "read" | "reversible_write" | "dangerous_write";
type ToolRequest = {
name: string;
risk: ToolRisk;
reason: string;
};
function shouldRequireApproval(tool: ToolRequest): boolean {
if (tool.risk === "dangerous_write") return true;
if (tool.name.includes("send_email")) return true;
if (tool.name.includes("deploy")) return true;
return false;
}
// input
const request: ToolRequest = {
name: "send_email_to_client",
risk: "dangerous_write",
reason: "回复客户合同问题",
};
// operation
const requireApproval = shouldRequireApproval(request);
// output
console.log({ requireApproval });
输出:
{
"requireApproval": true
}
这一步看起来保守,但非常必要。因为 AI 系统最大的风险不是“回答错一句话”,而是“带着权限做错一件事”。
本地、Docker、VPS 不是成熟度排序,而是约束不同
我们一开始把部署路径想成了:
本地 -> Docker -> VPS
好像越往后越高级。
后来查官方文档和实际部署后,我们把这个理解改掉了。OpenClaw 当前安装文档推荐的是安装脚本:它会检测 OS、安装 Node、安装 OpenClaw,并启动 onboarding;系统要求是 Node 24 推荐,或 Node 22.14+,支持 macOS、Linux、Windows,且 WSL2 更稳定。(OpenClaw)
Docker 并不是默认必经路径。官方 Docker 文档明确写着 Docker is optional,只在需要 containerized gateway 或验证 Docker flow 时使用;如果是在自己机器上追求最快 dev loop,应该用 normal install flow。(OpenClaw)
所以部署方式不是“谁更专业”,而是看我们要解决哪类问题。
本地安装适合把系统跑明白
本地运行的价值是反馈快。
我们能直接看到:
配置文件在哪
workspace 在哪
日志怎么写
onboarding 写了什么
Gateway 是否启动
agent 是否拿到了正确上下文
这对初期排错特别重要。尤其是 OpenClaw 这类系统同时涉及模型、Gateway、渠道、session、workspace、bootstrap files 和工具权限,如果一上来就放进容器或 VPS,调试面会变大。
本地的缺点也很明显:机器睡眠、断网、关机,系统就停。它适合验证架构,不适合作为长期在线的最终形态。
我们的判断是:
目标是理解系统和跑通第一个 workflow -> 本地
目标是持续在线处理事件 -> 不要停在本地
Docker 解决隔离,不自动解决长期在线
Docker 的优势是环境隔离和可复现。官方 Docker 文档要求 Docker Desktop 或 Docker Engine + Docker Compose v2,构建镜像时至少 2 GB RAM,否则 pnpm install 在 1 GB 主机上可能被 OOM kill。(OpenClaw)
这说明 Docker 不是“无脑更简单”。它引入了另一组问题:
volume 怎么挂
状态目录是否持久化
.env 放在哪里
容器网络怎么配
Control UI 是否暴露
日志怎么收集
升级时怎么处理镜像和数据
我们踩过一次很典型的坑:容器重启后 agent 像失忆了一样。最开始以为是 memory 配置没生效,后来发现 workspace 和 state 没有按预期持久化。这个问题和模型无关,是部署层的问题。
对 OpenClaw 这种 Gateway + workspace + state 的系统来说,容器能启动不代表部署正确。
先确认配置、凭据、session、workspace、logs 是否都落在可备份的持久化路径里。
VPS 解决持续在线,但把安全问题摆上桌
如果目标是让 AI 助手 24/7 等待事件、处理消息、跑周期检查,VPS 或常开服务器才更接近最终形态。
OpenClaw 的 VPS 文档对这个模型说得很直接:Gateway 跑在 VPS 上,并持有 state + workspace;我们从 laptop 或 phone 通过 Control UI、Tailscale 或 SSH 访问;VPS 应该被当成 source of truth,并定期备份 state + workspace。(OpenClaw)
但 VPS 不是“把本地命令复制到服务器”这么简单。它立刻带来这些问题:
SSH 怎么加固
Gateway 绑定在哪个地址
Control UI 是否暴露公网
token/password 是否启用
状态和 workspace 怎么备份
日志怎么轮转
服务如何自动重启
多用户是否在同一信任边界
官方文档给出的安全默认值也很明确:Gateway 保持 loopback,通过 SSH tunnel 或 Tailscale Serve 访问;如果绑定到 lan 或 tailnet,需要启用 gateway.auth.token 或 gateway.auth.password。(OpenClaw)
这也是我们后来坚持的部署原则:先 loopback,再隧道;先认证,再暴露;先备份,再自动化。
我们最后采用的部署判断表
我们没有把部署方式做成固定路线,而是按目标选型:
本地安装
适合:
- 初次跑通
- 调试配置
- 理解 workspace 和 bootstrap
- 快速验证 workflow
不适合:
- 长期在线
- 多渠道稳定接入
- 无人值守自动化
Docker
适合:
- 隔离依赖
- 团队复现环境
- 验证容器化 Gateway
- 限制 agent 可见的运行环境
不适合:
- 完全不熟悉容器的新手直接上生产
- 没有规划 volume/state/log 的长期部署
VPS
适合:
- 24/7 运行
- 远程访问
- 持续监听消息和事件
- 接近生产的自动化工作流
不适合:
- 没有安全加固意识
- 不做备份
- 把个人账号和团队 agent 混在同一运行环境
这个表不是教条。比如一个人用的轻量系统,本地常开机器也能跑;团队内部实验,也可以先 Docker;真正生产化,也未必只靠单台 VPS,后面可能会拆成多实例、多 workspace、多 agent 隔离。
关键是不要把部署方式当成炫技,而要看它解决了什么约束。
更真实的架构边界:谁拥有状态,谁承担风险
这次复盘后,我现在看 OpenClaw 系统,会先问三个问题。
第一,谁拥有状态?
如果 Gateway 在本地,状态就在本机。
如果 Gateway 在 VPS,VPS 就是 source of truth。
如果 Docker 没挂好 volume,状态可能会随着容器生命周期丢失。
第二,谁能调用工具?
Agent 能看到哪些目录?
能运行哪些命令?
能访问哪些 API key?
能否接触个人浏览器、邮件、云盘、生产配置?
第三,谁负责复核?
哪些动作自动执行?
哪些动作只生成草稿?
哪些动作必须人工确认?
失败后怎么重试?
误操作后怎么回滚?
这些问题比“用不用 Docker”更重要。因为 OpenClaw 这类系统一旦跑起来,它不是一个静态应用,而是一个会不断接收输入、产生判断、调用工具、更新状态的运行体。
目前还没完全解决的问题
这套拆法让我们把 OpenClaw 从“神秘 AI 员工平台”拆回了可维护系统,但还有几个问题没有完全解决。
记忆治理仍然很难。哪些信息应该长期保留,哪些应该过期,哪些应该变成规则,哪些只是一次性上下文,现在还需要人工定期整理。否则 memory 会慢慢变成第二个垃圾堆。
工具权限还需要更细。现在按只读、可逆写入、高风险写入来分,已经比一个总开关好很多,但还不够。后面更理想的方式,是给每个 workflow 单独定义 tool policy,而不是让所有 agent 共用一组权限。
可观测性也要继续补。我们需要更清楚地记录:这次输入来自哪里,注入了哪些上下文,模型做了什么判断,调用了哪些工具,写回了什么状态。没有这些记录,系统越自动化,排错越像猜谜。
所以我现在不会把 OpenClaw 简单介绍成“装上就有 AI 员工”。更准确的说法是:OpenClaw 给了我们一个自托管、多渠道、agent-native 的 Gateway 底座;真正可靠的 AI 员工,需要我们继续把状态、权限、触发器、工作流和部署边界设计清楚。