Sparkle CodesSparkle
项目 / Agent记忆

OpenClaw 部署复盘:我们把 AI 员工拆成了 Gateway、状态机和运行环境

x
xpx
Apr 11, 2026
Editorial Insight
#Deployment#OpenClaw

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 首先解决的是这些问题:

TEXT
消息从哪里来
  ↓
由哪个 Gateway 接住
  ↓
路由到哪个 agent/session
  ↓
agent 能看到哪些上下文
  ↓
结果从哪个渠道回去

但“这个 AI 是谁”“它长期记住什么”“它每天几点巡检”“它能不能自动归档邮件”“它能不能调用外部脚本”,这些不是 Gateway 单独解决的,而是配置层和工作流层继续往上叠。

我们后来在内部约定了一个更实用的定义:

OpenClaw 是 AI 助手的运行底座,不是业务工作流本身。
AI 员工 = Gateway + agent 编排 + 记忆状态 + 工具权限 + 触发器 + 业务规则。

这个定义避免了一个常见坑:看到系统能从 Telegram 或 Slack 回复消息,就以为它已经是“员工”。其实这只是入口打通了。真正的员工感,来自它能持续接收事件、判断下一步、调用工具、写回状态,并在下一轮继续接着处理。

架构的核心不是画框图,而是让问题能定位

一开始我们也画过很漂亮的架构图:LLM、Memory、Tools、Scheduler、Agent、Gateway,几个框一连,好像系统就清楚了。

结果第一次排错就暴露问题:一封消息没有按预期触发处理,我们不知道该查哪一层。

是 Gateway 没收到?
是 session 路由错了?
是上下文没有注入?
是模型判断不该处理?
是工具被权限拦了?
还是执行完了但没有写回状态?

这时候才发现,架构不是为了展示“系统由哪些组件组成”,而是为了让我们在出问题时能顺着数据流往下查。

我们最终把 OpenClaw 相关系统画成了这个循环:

TEXT
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。

第二,工具执行不是系统边界的终点。执行之后必须写回状态:处理了什么、结果如何、是否需要下次继续跟进。没有这一步,系统每次都像第一次工作。

我们后来排错时只问一件事<br />

这次失败发生在输入、路由、上下文、决策、工具执行、状态回写里的哪一段?
只要能回答这个问题,AI 系统就不再是一个黑盒。

LLM 只是推理引擎,代理逻辑才负责秩序

我们早期有个错误判断:既然模型足够强,那就把更多决策交给模型。比如“收到消息后自己判断是否要查记忆、是否要调用工具、是否要回复用户”。

这个思路在 demo 阶段很爽,但一接真实工作流就开始飘。

同一类消息,有时模型会先查历史,有时直接回复;有时会调用工具,有时只给建议;有时会把低风险任务说得很严重,有时又把需要人工确认的事情轻轻带过。

后来我们把这部分从“模型自由判断”改成“代理逻辑先定边界,模型在边界内做判断”。

TS
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);

输出大概是:

JSON
{
  "type": "draft",
  "requireReview": true
}

这段代码没有任何“高级 AI 魔法”,但它把系统边界收住了:涉及敏感动作时,只能生成草稿,不能直接执行。

LLM 在这里仍然很重要,它负责理解意图、总结上下文、生成草稿、解释风险。但代理逻辑负责决定:什么能做,什么只能建议,什么必须人工确认。

记忆不是聊天记录,而是状态系统

我们第二个踩坑点是记忆。

最开始我们把 memory 理解成“保存历史对话”。后来发现,这种记忆对长期运行的工作流帮助有限。真正有用的是状态:

TEXT
用户偏好
当前任务进度
上次失败原因
联系人处理规则
某个工作流的最新 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 才是具体业务动作。

配置层通常会把文件拆成这样:

TEXT
AGENTS.md     -> agent 如何思考和行动
SOUL.md       -> 长期人格和行为倾向
USER.md       -> 用户画像和工作偏好
MEMORY.md     -> 始终加载的关键记忆
BOOT.md       -> 启动时检查什么
HEARTBEAT.md  -> 周期检查什么
TOOLS.md      -> 当前机器有哪些工具和限制
IDENTITY.md   -> 快速身份卡片

openclaw-config 的 README 也确实把这些模板文件列在 templates/ 下,并分别赋予身份、用户、记忆、启动例程、周期检查、工具环境等职责。(GitHub)

踩坑预警:不要把记忆写成流水账<br />

“用户昨天说了什么”不一定值得长期保存。
“用户多次纠正我们:涉及付款和合同必须人工确认”才是有价值的长期状态。
记忆层应该服务后续决策,而不是把聊天历史原样堆进去。

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 更适合做轻量巡检:

MARKDOWN
# 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 负责“发现结果并提醒”。

我们后来把周期任务分了三类:

TEXT
Heartbeat:
  轻量巡检、提醒、状态摘要
Scheduled Job:
  可重复、可重试、耗时可控的后台任务
Manual Approval Flow:
  发邮件、删数据、改配置、发布部署等高风险动作

这让系统行为稳定了很多。Heartbeat 不再像一个随时可能乱跑的后台线程,而是一个“定时叫醒 agent 看一眼当前状态”的机制。

工具层要按风险分级,而不是一股脑开放

OpenClaw 这类系统一旦接入工具,就从聊天软件变成了自动化系统。它能读文件、调 API、发消息、更新记录,这些能力越强,越需要边界。

我们最早只做了一个粗暴开关:允许工具 / 不允许工具。后来发现完全不够。

更合理的做法是按风险分级:

TEXT
Level 0: 只读
  - 查询日程
  - 读取状态
  - 搜索文档
  - 生成摘要
Level 1: 可逆写入
  - 创建草稿
  - 添加标签
  - 写入临时文件
  - 更新内部备注
Level 2: 高风险写入
  - 发送外部邮件
  - 删除文件
  - 修改生产配置
  - 触发部署
  - 改权限

然后在代理逻辑里明确处理:

TS
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 });

输出:

JSON
{
  "requireApproval": true
}

这一步看起来保守,但非常必要。因为 AI 系统最大的风险不是“回答错一句话”,而是“带着权限做错一件事”。

本地、Docker、VPS 不是成熟度排序,而是约束不同

我们一开始把部署路径想成了:

TEXT
本地 -> 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)

所以部署方式不是“谁更专业”,而是看我们要解决哪类问题。

本地安装适合把系统跑明白

本地运行的价值是反馈快。

我们能直接看到:

TEXT
配置文件在哪
workspace 在哪
日志怎么写
onboarding 写了什么
Gateway 是否启动
agent 是否拿到了正确上下文

这对初期排错特别重要。尤其是 OpenClaw 这类系统同时涉及模型、Gateway、渠道、session、workspace、bootstrap files 和工具权限,如果一上来就放进容器或 VPS,调试面会变大。

本地的缺点也很明显:机器睡眠、断网、关机,系统就停。它适合验证架构,不适合作为长期在线的最终形态。

我们的判断是:

TEXT
目标是理解系统和跑通第一个 workflow -> 本地
目标是持续在线处理事件 -> 不要停在本地

Docker 解决隔离,不自动解决长期在线

Docker 的优势是环境隔离和可复现。官方 Docker 文档要求 Docker Desktop 或 Docker Engine + Docker Compose v2,构建镜像时至少 2 GB RAM,否则 pnpm install 在 1 GB 主机上可能被 OOM kill。(OpenClaw)

这说明 Docker 不是“无脑更简单”。它引入了另一组问题:

TEXT
volume 怎么挂
状态目录是否持久化
.env 放在哪里
容器网络怎么配
Control UI 是否暴露
日志怎么收集
升级时怎么处理镜像和数据

我们踩过一次很典型的坑:容器重启后 agent 像失忆了一样。最开始以为是 memory 配置没生效,后来发现 workspace 和 state 没有按预期持久化。这个问题和模型无关,是部署层的问题。

踩坑预警:Docker 里最先确认持久化<br />

对 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 不是“把本地命令复制到服务器”这么简单。它立刻带来这些问题:

TEXT
SSH 怎么加固
Gateway 绑定在哪个地址
Control UI 是否暴露公网
token/password 是否启用
状态和 workspace 怎么备份
日志怎么轮转
服务如何自动重启
多用户是否在同一信任边界

官方文档给出的安全默认值也很明确:Gateway 保持 loopback,通过 SSH tunnel 或 Tailscale Serve 访问;如果绑定到 lan 或 tailnet,需要启用 gateway.auth.token 或 gateway.auth.password。(OpenClaw)

这也是我们后来坚持的部署原则:先 loopback,再隧道;先认证,再暴露;先备份,再自动化。

我们最后采用的部署判断表

我们没有把部署方式做成固定路线,而是按目标选型:

TEXT
本地安装
  适合:
    - 初次跑通
    - 调试配置
    - 理解 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 员工,需要我们继续把状态、权限、触发器、工作流和部署边界设计清楚。

BACK TO BLOG
The End of Interaction