前言 链接到标题
Raspberry Pi 4(4GB 内存)跑了小半年的 OpenClaw(目前是 5.7),一直当我的个人助理使用。现在要开放给客户使用,但面临两个核心问题:
- 数据隔离:每个人的对话、记忆不能混在一起
- 执行安全:不能让客户在宿主机上乱跑命令
OpenClaw 从 6.x 开始引入了原生多 Agent 架构 + Docker 沙盒子系统,正好解决这两个问题。OpenClaw 的镜像也从 5.7 的 3.2GB 精简到 6.5 的 1.71GB,体积几乎减半。这篇文章记录从 5.7 升级到 2026.6.5-beta.12(代码与正式版几乎相同,GitHub 上稳定版标记为 2026.6.5、Docker Hub 上的 beta.12 镜像与正式版代码一致,可以放心使用)并配置多用户沙盒隔离的全过程。
为什么升级到 6.5 链接到标题
跟上节奏,避免大跳跃 链接到标题
5.7 到 6.5 跨越了多个大版本,核心架构有不少变化:
- 多 Agent 系统:
openclaw agents子命令,一个 Gateway 承载多个独立 Agent - Docker 沙盒子系统:
openclaw sandbox,不用自己写容器编排 - CardKit 卡片 API:飞书从旧消息 API 迁移到卡片 API,支持更丰富的交互
- SQLite 持久化:状态数据从 JSON 文件迁移到 SQLite,更可靠
性能和多用户场景 链接到标题
6.x 的性能改善对多用户场景是实打实的——Gateway 容器本身很轻量(~300MB 内存),多 Agent 不走额外进程,只是一个命名空间和路由逻辑。Pi 4 的 4GB 内存跑 1 个主人 + 3-4 个沙盒用户绰绰有余。
CardKit 的"美丽"与"遗憾" 链接到标题
6.x 引入的 CardKit 卡片 API 理论上支持流式打字效果——LLM 每生成一个 token,飞书卡片上就多一个字。实际体验上,如果流式正常,用户能看到 AI 实时"打字",心理等待感会大大降低。这是 5.7 时代没有的能力。
但有一个遗留 bug:飞书 CardKit streaming 的一个回归(PR #87896 引入)导致每个 streaming chunk 到达时替换整张卡片内容,而不是追加。最终卡片只显示 1-2 个字符。
根因在飞书插件的 FeishuStreamingSession.update() 方法——它发送增量文本而非累积文本给 CardKit API,而 CardKit API 的 PUT elements/content 是替换语义,不是追加语义。
当前临时方案:在 openclaw.json 中设 streaming: false,回复一次性出现。虽然失去了打字效果,但内容完整。其他渠道插件(Discord、Telegram、WhatsApp 等)使用整条消息编辑 API,不存在此问题,所以只有飞书用户受影响。
这个 bug 的核心修复(PR #90181)已在 main 分支合入,但仍有几个边缘 case(卡片显示不全、提前关闭、不自动撑高)在等待后续版本修复。等这些都关闭后,把 streaming 改回 true 即可恢复流式体验——那时候的用户体验将比 5.7 时代好很多。一次性出消息在当前场景下影响不算大,但确实有时让人以为机器人卡住了,如果能流式输出,用户体验会提升一大截。
部署架构总览 链接到标题
node1(Pi 4)上跑一个 OpenClaw Gateway Docker 容器,通过 DooD(Docker-out-of-Docker)模式在宿主机上创建 sibling 沙盒容器。主人(我)直接走 host 模式,其他用户每人一个独立的 Docker 沙盒容器。
2026.6.5
(~300MB 内存)"] DS["/var/run/docker.sock"] DC["/usr/bin/docker"] subgraph 数据卷_volume CFG[".openclaw/
openclaw.json
credentials/
state/openclaw.sqlite"] W0["workspace/
主人 workspace"] W1["workspace-my_test/
测试用户 workspace"] W2["workspace-xxx/
用户 workspace"] end subgraph 沙盒容器_懒加载 S1["openclaw-sbx-my_test
debian:bookworm-slim
/workspace -> host path
(~80-150MB idle)"] end end subgraph 飞书 FB["飞书 Bot
cli_xxxx..."] U0["主人
ou_1c1666d8..."] U1["测试用户
ou_93db2e6d..."] U2["用户
ou_8d6e942c..."] end U0 -- "DM" --> FB --> OC U1 -- "DM" --> FB --> OC --> S1 OC --> DS --> Docker_daemon OC --> DC --> Docker_daemon Docker_daemon --> S1
核心要点 链接到标题
| 组件 | 说明 |
|---|---|
| 单 Gateway + 多 Agent | 一个容器,agents.list[] 里每个用户一个条目,bindings[] 路由 |
| DooD 模式 | 容器内挂 /var/run/docker.sock + /usr/bin/docker,创建 sibling 容器 |
| FS bridge 映射 | .openclaw 目录必须按宿主路径同名映射进容器(踩坑重点!) |
| 沙盒懒加载 | 用户首次发消息才创建容器,不占用 idle 资源 |
| workspace 需用宿主路径 | DooD 模式下 Docker daemon 按宿主命名空间解析路径 |
配置文件详解 链接到标题
docker-compose.yml 链接到标题
这是整个部署的入口。核心是 volumes 块——挂载了什么决定了沙盒能访问什么。
services:
openclaw-gateway:
image: ghcr.nju.edu.cn/openclaw/openclaw:2026.6.5-slim
# 镜像与正式版 2026.6.5 代码几乎一致,放心使用
volumes:
- /home/user/openclaw/.openclaw:/home/node/.openclaw
- /home/user/openclaw/.openclaw/workspace:/home/node/.openclaw/workspace
- /home/user/.ssh:/home/node/.ssh:ro
- /home/user/.config:/home/node/.config:ro
- /var/run/docker.sock:/var/run/docker.sock
- /usr/bin/docker:/usr/bin/docker:ro
- /home/user/openclaw/.openclaw:/home/user/openclaw/.openclaw # FS bridge!
ports:
- "18789:18789"
- "18790:18790"
init: true
restart: unless-stopped
特别注意:最后一行 FS bridge 映射——Docker daemon 在创建沙盒容器时,按宿主机的文件系统解析 bind mount 源路径。如果 workspace 路径是 /home/node/...,宿主机上不存在此路径,沙盒容器的 /workspace 会挂载到空目录。必须将 .openclaw 再按宿主路径映射一次。
openclaw.json 三段核心配置 链接到标题
第一段:飞书认证 链接到标题
"channels": {
"feishu": {
"enabled": true,
"dmPolicy": "allowlist",
"allowFrom": [
"ou_1c1666d8...",
"ou_93db2e6d...",
"ou_8d6e942c..."
],
"streaming": false,
"renderMode": "card"
}
}
dmPolicy: "allowlist" 是关键——只有 allowFrom 列表中的用户能发消息,且重启后持久有效。OpenClaw 默认的 pairing 模式在容器重启后会丢失配对状态,让用户反复验证,体验很差。
第二段:Agent 列表 链接到标题
"agents": {
"list": [
{ "id": "main" },
{
"id": "my_test",
"workspace": "/home/user/openclaw/.openclaw/workspace-my_test",
"sandbox": {
"mode": "all",
"scope": "agent",
"workspaceAccess": "rw",
"docker": {
"image": "m.daocloud.io/docker.io/debian:bookworm-slim",
"network": "openclaw-sandbox"
}
},
"tools": {
"sandbox": {
"tools": {
"alsoAllow": ["group:web"]
}
},
"allow": [
"read", "write", "edit", "apply_patch",
"exec", "process",
"group:messaging", "group:memory", "group:web", "group:fs"
],
"deny": ["browser", "canvas", "nodes", "gateway", "cron"]
}
}
]
}
- workspace 路径用宿主路径(
/home/user/...),不是容器路径 sandbox.mode: "all"表示所有请求都在沙盒内执行sandbox.scope: "agent"每用户一个容器- 必须显式设置(默认 完全无网),设为 让沙盒能联网
- 是沙盒下的额外工具放行层, 让 web_fetch/web_search 对沙盒 LLM 可见
sandbox.docker.network必须显式设置(默认"none"完全无网),设为"openclaw-sandbox"让沙盒能联网tools.sandbox.tools.alsoAllow是沙盒下的额外工具放行层,group:web让 web_fetch/web_search 对沙盒 LLM 可见- tools.allow 放开了 group:web(含 web_search/web_fetch)让沙盒用户也能联网
第三段:路由绑定 链接到标题
"bindings": [
{
"type": "route",
"agentId": "my_test",
"match": {
"channel": "feishu",
"accountId": "default",
"peer": {
"kind": "dm",
"id": "ou_93db2e6d..."
}
}
}
]
peer 过滤是关键——不加 peer 会匹配所有飞书 DM,导致主人消息也被路由到测试用户。openclaw agents add --bind 命令不支持 peer 级绑定,必须停 gateway 后手改配置。
沙盒用户邀请流程 链接到标题
docker compose down + up -d User->>LH: 手机上飞书搜索应用 User->>OC: 发送第一条消息 OC->>OC: dmPolicy: allowlist 检查通过 OC->>OC: bindings 路由到对应 agent OC->>Docker: 懒加载创建沙盒容器 Docker-->>OC: 容器就绪 OC-->>User: 回复消息
找用户 OpenID 的踩坑记 链接到标题
这是整个过程中最折磨人的环节。
飞书管理后台的坑:应用可见性必须设为"全部用户"。我之前设为"部分用户"可见,结果新用户始终无法通过飞书 API 查到。卡了整整一天。
OpenID 格式:飞书用户 ID 有两种——短名和长名(ou_xxx 格式)。API 只认长名格式。
尝试失败的方法:
- 飞书管理后台翻半天找不到 open_id 字段
- 调试界面命令各种查不到
- API Explorer 返回空列表
最终方案(两种方法):
方法一(最快):让新用户给 bot 发一条消息,bot 会自动回复 pairing 消息,其中就包含了用户的 open_id:
Your Feishu user id: ou_xxx...
这种方法最简单,不需要任何 API 权限,适用于邀请任何用户。
方法二(AI 辅助):使用 AI 编程助手(如 OpenCode),给它提供飞书应用的 appId + appSecret。AI 通过飞书 API 工具(feishu_get_user),设置 user_id_type=open_id 列出所有用户,再根据姓名或手机号匹配目标用户。在飞书管理后台中,必须确保应用可见性设为"全部用户"(而不是"部分用户"),否则 API 会返回空列表。
拿到 open_id 后还不能直接使用,需要将 open_id 加入 channels.feishu.allowFrom[] 列表。
配置检查清单 链接到标题
拿到 open_id 后,检查以下三项:
- 飞书管理后台:应用可见性 > 全部用户(设为"部分用户"会导致 API 查不到人)
- openclaw.json:
channels.feishu.allowFrom[]追加 open_id - openclaw.json:
agents.list[]新增 agent 条目 +bindings[]路由(含 peer 过滤)
沙盒隔离细节 链接到标题
网络 链接到标题
沙盒容器默认网络为 "none"(完全断网),必须显式设置 sandbox.docker.network 才能联网。将 network 设为 "openclaw-sandbox"(专用隔离网络 ),沙盒就能访问内外网。如果不设置,web_search/web_fetch 会因无网络而失败。
镜像 链接到标题
选用 debian:bookworm-slim(138MB),在保证基本工具链可用的前提下尽量轻量。用户如果需要 curl/wget 等网络工具,可在容器内自行安装,web_search 工具即可正常使用。
挂载隔离 链接到标题
沙盒容器只挂入了 workspace 目录(rw),其他数据不可见:
| 数据 | 沙盒容器内 | 说明 |
|---|---|---|
| 当前用户的 AGENTS.md/USER.md/MEMORY.md | ✅ /workspace (rw) | 可读写 |
| 内置技能文件 | ✅ /workspace/.openclaw/…/skills (ro) | 只读 |
| 其他用户的 workspace | ❌ 不可见 | 数据隔离 |
| Gateway 配置(openclaw.json) | ❌ 不可见 | 防配置篡改 |
| 凭据(credentials/) | ❌ 不可见 | 防凭据泄露 |
| 对话历史(sessions/) | ❌ 不可见 | 隐私保护 |
懒加载 链接到标题
沙盒容器不会在 Gateway 启动时就创建。用户在飞书发送第一条消息时,OpenClaw 自动执行 docker run 创建容器。之后 persist,直到用户长时间不活跃或手动 sandbox recreate。
资源评估 链接到标题
Pi 4 的 4GB 内存,实测情况:
| 组件 | 内存 |
|---|---|
| openclaw-gateway | ~300MB |
| alloy(日志采集) | ~50MB |
| 系统 + 其他 | ~500MB |
| 每个沙盒容器(idle) | ~80-150MB |
| 4 用户场景 | 余量充足 |
避坑总结 链接到标题
| 坑 | 原因 | 解决方案 |
|---|---|---|
| 配对重启丢失 | pairing 状态仅存内存 | 改 dmPolicy: "allowlist" + allowFrom |
| binding 拦截所有消息 | agents add --bind 不支持 peer 过滤 |
停 gateway 后手改 JSON 加 peer |
| 沙盒 workspace 为空 | DooD 路径用容器路径 | workspace 改宿主路径 + FS bridge 映射 |
| 容器内无 docker 命令 | OpenClaw 沙盒 spawn docker 子进程 | 挂载 /usr/bin/docker:/usr/bin/docker:ro |
| 飞书 streaming 异常 | CardKit API 回归 bug | streaming: false 临时关闭 |
| 找不到用户 open_id | 应用可见性 = “部分用户” | 改"全部用户",让用户发条消息即可 |
docker image ls 显示两个大小 |
Size 是完整镜像大小(5.7: 3.2GB, 6.x: 1.71GB),第二列是实际磁盘占用 |
看 Size 列,6.x 体积减半 |
结语 链接到标题
OpenClaw 6.5 的多 Agent + 沙盒架构非常实用。一个 Pi 4 就能跑起来,把 AI 助手开放给多个用户,还能确保数据隔离和执行安全。虽然 CardKit streaming 有个暂时的 bug,但一次性出消息在当前场景下影响不大。
最期待的改进还是飞书流式输出——现在回复一次性出现,用户体验不太好,有时让人以为机器人卡住了。等后续版本把 streaming 修好,飞书卡片上能看到 AI 实时"打字",体验会好很多。