OpenClaw 实战 03|架构:3 种部署形态 + Gateway 双 Token + 5 类对接链路原创
# 架构:3 种部署形态 + Gateway 双 Token + 5 类对接链路
这一篇不讲"分层架构、依赖注入"那一套,只讲OpenClaw 在真实使用中表现出的 3 种部署形态 + Gateway 双 Token 鉴权 + 5 类对接链路。
本文所有内容都来自
~/.hermes/workspace/blog-material/notes/claude-code/下的 7 条真实会话记录(2026-06-15 ~ 2026-06-26),分别是:OpenWebUI 接入、容器服务清单、openai 协议网关、FreshRSS 跨实例集成、家用 NAS 后台调用模型、Browser Scope 升级、NAS 实例巡检 Skill。关键认知:OpenClaw 不是单点,是一套多形态、多对接的分布式 Agent 运行时。理解它的"形态 + 鉴权 + 链路"三层结构,才能定位真实的运维问题。
# §1 把 OpenWebUI 对接 OpenClaw 的那次
今年 4 月我想把自托管的 OpenWebUI 对接到已经在跑的 OpenClaw(
真实场景(来自 <session-file>):
我要把 CasaOS 上的 OpenWebUI 接到
踩坑 1:OpenClaw 默认不暴露 OpenAI 接口
OpenClaw 主要协议是 WebSocket/Control UI,/v1/chat/completions 这个 OpenAI 兼容端点默认关闭。必须手动开:
# 1. 开启端点
openclaw config patch --stdin <<'JSON5'
{
"gateway": {
"http": {
"endpoints": {
"chatCompletions": {
"enabled": true
}
}
}
}
}
JSON5
# 2. 重启服务
supervisorctl restart openclaw
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
踩坑 2:root 执行导致 SQLite 权限错误
我用 docker exec 进容器,默认是 root。在 root 下跑 openclaw CLI 后,~/.openclaw/state(SQLite 文件)被创建成 root 拥有,后续普通用户跑就报 unable to open database file。
# 错:root 执行
docker exec -it <CONTAINER_NAME> openclaw config patch --stdin <<...
# 对:指定用户
docker exec -u <USERNAME> -it <CONTAINER_NAME> openclaw config patch --stdin <<...
2
3
4
5
踩坑 3:PATH 找不到命令
容器里用 nvm 装的 node,su - <USERNAME> 不会自动加载 nvm 路径,openclaw 命令找不到。SSH 直连容器时必须显式 export:
export PATH=/home/<USERNAME>/.nvm/versions/node/v22.20.0/bin:/home/<USERNAME>/.local/share/pnpm/bin:$PATH
踩坑 4:Kong 代理 WebSocket 超时
OpenClaw Gateway 走 WebSocket 长连接,Kong 默认 60 秒超时会把连接掐断,导致 Agent 流式输出中断:
# Kong 路由配置里必须调高
read_timeout=3600000
write_timeout=3600000
2
3
结果:调通后,OpenWebUI 的 /v1/models 返回 JSON(不是 HTML),Bearer Token 鉴权生效,Nova 出现在 OpenWebUI 模型列表里,聊天正常。
这个故事揭示了 OpenClaw 真实架构的三个层面:
- 部署形态:容器(Docker + Supervisor)vs 裸机(systemd)vs 源码(pnpm)
- Gateway 鉴权:Token 双源(config vs env)+ Device Scope 升级
- 对接链路:OpenAI 协议 / OpenWebUI / FreshRSS / Telegram / TTS Provider
下面按这三层展开。
# §2 部署形态:3 种并存(你自己就有 2-3 种)
OpenClaw 不是一个装在固定位置的二进制。同一个人维护 3 个 OpenClaw 实例时,3 种部署形态往往并存:
| 形态 | 进程管理 | 典型场景 | 素材来源 |
|---|---|---|---|
| Docker + Supervisor 容器 | supervisorctl | 2026-06-15 检查容器服务 | |
| 裸机 + user systemd | systemctl --user | FNOS 上的家用实例、家用 NAS | 2026-06-26 NAS 实例巡检 Skill |
| 源码 + pnpm | 手动 / node openclaw.mjs | 开发态、临时调试 | 2026-06-15 容器内 OpenClaw 重装 |
为什么会有 3 种并存:
- 远程/公网暴露 → 容器(隔离、可移植)
- 家用 24x7 → 裸机 systemd(开销小、自启稳)
- 开发/调试 → 源码(改一行就能跑)
这意味着什么:
运维命令不通用。同一个 openclaw restart,在容器里是 supervisorctl restart openclaw,在裸机上是 systemctl --user restart openclaw-gateway,在源码态是 kill + node openclaw.mjs。
所以任何 Skill/文档/巡检脚本的开头,都必须先判定当前形态(来自 2026-06-26 肥牛妞巡检):
# 拓扑判定黄金命令
ssh <USER>@<IP> "which openclaw; systemctl --user status openclaw-gateway"
2
判定的本质:
which openclaw找到 → 装了systemctl --user status成功 → systemd 管理的裸机- 上面失败 +
supervisorctl status openclaw成功 → Supervisor 管理的容器 - 上面都失败 + 源码目录有
package.json→ 源码态
复用规则:写 Skill 的第一步永远是"先判定形态",绝不能假设某个命令通用。混用命令会导致:
supervisorctl restart在 systemd 环境下报错"未找到"systemctl --user restart在容器里要么权限错误要么真的重启了宿主机服务(灾难)
# §3 OpenClaw 的 5 个关键目录与文件
不管理论多漂亮,真实 OpenClaw 实例就是 5 个目录 + 1 套配置 + 1 个 SQLite:
| 路径 | 作用 | 关键坑 |
|---|---|---|
~/.openclaw/openclaw.json | 主配置(Gateway、Provider、Model) | 含 Token 引用,别 hardcode |
~/.openclaw/state | SQLite 数据库,存会话、cron_runs、auth_profile | 权限极敏感(root 写过就废了) |
~/.openclaw/settings/*.json | 运行时配置(如 tts.json、gateway.json) | 与主配置可能冲突 |
~/.openclaw/workspace/skills/<name>/ | Skill 安装位置(含 SKILL.md) | 多 Skill 命名冲突要 force |
/workspace/openclaw/ | 源码目录(开发态) | pnpm 缓存单删源码清不干净 |
# 3.1 ~/.openclaw/state — 权限最敏感的一个
这个 SQLite 文件决定整个实例能不能用。从 3 条素材看,它至少 4 次让人翻车:
- 2026-06-15 浏览器素材:root 跑
openclaw→ state 被 root 拥有 → 普通用户报unable to open database file - 2026-06-23 openai 协议素材:Provider 改名 → API key 所在的 agent sqlite(
agents/main/agent/openclaw-agent.sqlite的auth_profile_store/auth_profile_state表,按 provider 名索引)旧名称没迁移 →No API key found - 2026-06-25 skill 安装素材:在容器里 root 跑 → state 写 root → 普通用户开不了
- 2026-06-15 容器内卸载素材:卸载必须
rm -rfstate,否则下次重装读旧数据
铁律:
- 永远用
docker exec -u <USERNAME>进容器跑命令 - state 文件的所有者必须和运行 OpenClaw 的用户一致
- Provider 改名时同步改 SQLite(
auth_profile_store+auth_profile_state两张表)
# 3.2 Skill 目录:~/.openclaw/workspace/skills/<name>/SKILL.md
Skill 不仅是 Agent 的"工具集",也是文档(来自 2026-06-26 肥牛妞):
~/.openclaw/workspace/skills/<skill-name>/
└── SKILL.md ← 含 frontmatter(name/description/metadata)+ 安装事实 + 排障速查
2
真实 Skill 内容必须包含:
- 拓扑判定(容器 vs 裸机 vs 源码)
- 环境变量(PATH / HOME)
- 健康检查命令链(
systemctl --user status+openclaw config validate+openclaw infer model providers+ 端到端 ping) - 排障速查表
# 3.3 源码目录:单删源码清不干净
/workspace/openclaw/ 是开发态目录。从 2026-06-15 容器内 OpenClaw 素材看,彻底卸载要清 4 个地方:
# 1. 沙箱数据
rm -rf /home/<USERNAME>/openclaw-sandboxes
# 2. 源码
rm -rf /workspace/openclaw
# 3. 配置残壳
rm -rf /home/<USERNAME>/.openclaw
# 4. pnpm 缓存(最容易被遗忘)
cd /workspace && pnpm store prune
2
3
4
5
6
7
8
9
10
11
踩坑:只删源码目录不够,pnpm store 里还缓存着包,下次 pnpm install 会拉回旧版本。
# §4 Gateway 鉴权:Token 双源 + Device Scope(最常翻车的部分)
OpenClaw Gateway 是所有外部连接的总入口,它的鉴权机制有两个独立维度:Token 来源 + Device Scope。两个维度同时搞错过才是 Gateway 故障的常见模式。
# 4.1 Token 双源:config vs env
OpenClaw Gateway 在两个地方可以存 Token:
| 来源 | 路径 | 谁读它 |
|---|---|---|
| config | ~/.openclaw/openclaw.json → gateway.auth.token | CLI 客户端直连时 |
| env | Supervisor/systemd 环境变量 OPENCLAW_GATEWAY_TOKEN | Gateway daemon 启动时 |
故障模式(来自 2026-06-22 后台调大模型 + 2026-06-23 openai 协议):
- Gateway 启动读 env 里的 Token
- CLI 客户端读 config 里的 Token
- 两者不一致 → CLI 报
unauthorized: gateway token mismatch - Gateway 服务本身还在跑,Telegram Bot 走降级轮询继续工作
- 表现:Agent "看起来活着"(Telegram 还回消息)但 CLI 完全连不上(
openclaw命令都失败)
诊断步骤(来自 2026-06-23 openai 协议):
# 1. 读 config 里的 token(用 python3 解析避免脱敏)
python3 -c 'import json;print(json.load(open("/home/<USERNAME>/.openclaw/openclaw.json"))["gateway"]["auth"]["token"])'
# 2. 读 env 里的 token
grep OPENCLAW_GATEWAY_TOKEN /etc/supervisor/conf.d/openclaw.conf
# 3. 比对两个值(用 sha256 避免明文比对漏看空格)
python3 -c "import hashlib;print(hashlib.sha256(b'<TOKEN_A>').hexdigest())"
python3 -c "import hashlib;print(hashlib.sha256(b'<TOKEN_B>').hexdigest())"
2
3
4
5
6
7
8
9
修复(推荐 — config 跟随 env):
# 让 config 引用 env,配置变成单一来源
openclaw config set gateway.auth.token '${OPENCLAW_GATEWAY_TOKEN}'
# 重启 Gateway
supervisorctl reread && supervisorctl update && supervisorctl restart openclaw
2
3
4
5
复用规则:永远让 config 跟随 env。不要在两处分别维护 Token——只要一处改,另一处迟早忘。
# 4.2 Device Scope:CLI ≠ Daemon
OpenClaw Gateway 给每个连接发一个 Device Token,CLI 和 Daemon 的 Scope 是独立的(来自 2026-06-15 浏览器)。
典型故障:CLI 调 openclaw browser doctor 报:
GatewayClientRequestError: unauthorized: device token scope mismatch
意思是:当前 CLI 设备的 Scope 只有 operator.write,但 Browser 操作需要 operator.admin。
致命陷阱:用 openclaw devices approve 命令升级 Scope 时——approve 命令本身需要 admin 权限。也就是 CLI 无法自己给自己授权,死循环。
解药:必须从拥有 admin 权限的另一端批准:
- Control UI(网页)
- 已配对的 Telegram Bot
- 管理员手动编辑 SQLite 里的
device_scope表
复用规则:Scope 设计要"最小化"。Browser 这种重操作不该给普通 CLI 自动开,需要时显式批准。
# §5 5 类对接链路(OpenClaw 不是孤岛)
OpenClaw 实例跑起来后,通常要对接 5 类外部系统:
| # | 链路 | 用途 | 真实素材 |
|---|---|---|---|
| 1 | OpenAI 协议兼容网关 | 让 OpenWebUI / 第三方客户端能连 | 2026-06-15 OpenWebUI 接入 + 2026-06-23 openai 协议 |
| 2 | OpenWebUI | 聊天 UI、多模型路由 | 2026-06-15 OpenWebUI 接入 |
| 3 | FreshRSS / 自托管服务 | Agent 读 RSS / 调内部 API | 2026-06-24 FreshRSS 集成 |
| 4 | Telegram Bot | 移动端通道、告警推送 | 2026-06-26 肥牛妞巡检 |
| 5 | TTS Provider | 语音合成(自建 CosyVoice,:8082) | 2026-06-22 后台调大模型 |
# 5.1 OpenAI 协议兼容网关(链路 1)
要让外部工具用 OpenAI 客户端 SDK 直连 OpenClaw,开 2 个配置:
{
"gateway": {
"http": {
"endpoints": {
"chatCompletions": { "enabled": true }
}
},
"bind": "lan",
"auth": { "mode": "token" }
}
}
2
3
4
5
6
7
8
9
10
11
踩坑:
- 默认
chatCompletions关闭 → 必须显式 enable gateway.bind默认localhost→ 容器外连不到- 鉴权用 Bearer Token,不是 API Key(OpenWebUI 配置时要传对)
# 5.2 OpenWebUI 接入(链路 2)
接入通过 OpenWebUI 的 /openai/config/update API(管理员 JWT 鉴权):
{
"ENABLE_OPENAI_API": true,
"OPENAI_API_BASE_URLS": [
"http://<INTERNAL_IP>:8702/v1",
"https://claw.<INTERNAL_BRAND>.ai/v1"
],
"configs": {
"1": {
"enable": true,
"tags": [],
"prefix_id": "openclaw",
"model_ids": []
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
注意:OPENAI_API_BASE_URLS 的每条 URL 都必须能:
- 返回
/v1/models的 JSON(不是 HTML 错误页) - Bearer Token 鉴权通过
# 5.3 FreshRSS / 自托管服务(链路 3)
跨实例接自托管服务(如 FreshRSS)需要 3 层打通(来自 2026-06-24 FreshRSS 集成):
# 1. 网络层:WireGuard 隧道连通
ping <INTERNAL_IP>
# 2. 端口层:服务可达
curl http://<INTERNAL_IP>:8749/i/?a=rss&user=...&token=...
# 3. Skill 层:用 ClawHub CLI 装 Skill 封装 API
npx clawhub install freshrss-reader
2
3
4
5
6
7
8
Skill 内的 API 调用(FreshRSS Google Reader Compatible API):
# 认证:获取 Auth Token
curl -X POST "http://<INTERNAL_IP>:8749/api/greader.php/accounts/ClientLogin" \
-d "Email=<USER>&Passwd=<API_PASSWORD>"
# 数据:带 Token 拿 JSON
curl "http://<INTERNAL_IP>:8749/api/greader.php/reader/api/0/stream/contents" \
-H "Authorization: <AUTH_TOKEN>"
2
3
4
5
6
7
Skill 的真正价值:把复杂的 Auth Token 获取 + JSON 解析封进 freshrss.sh headlines --unread,Agent 只需调一行。
# 5.4 Telegram Bot(链路 4)
Telegram Bot 是 OpenClaw 最常用的移动端通道。从 2026-06-26 肥牛妞 看,巡检 Skill 的端到端测试就是发条 "在线" 看 Agent 回不回 PONG 或文本:
# 健康检查命令链(4 步端到端)
1. systemctl --user status openclaw-gateway.service # 服务活着?
2. openclaw config validate # 配置合法?
3. openclaw infer model providers # Provider 在线?
4. 发送"在线"消息 → Agent 回 PONG / 文本 # 端到端通?
2
3
4
5
踩坑:
- Bot Token 在多处维护容易过期不一致
- Telegram 通道可能"降级轮询"(Token mismatch 时还能用,但慢)
# 5.5 TTS Provider(链路 5)
Nova 当前用自建 CosyVoice(:8082,OpenAI 兼容端点)。⚠️ 早期(≤2026-06-18)用的是 Microsoft Edge TTS,之后切到 CosyVoice——别再按旧版断言。从 2026-06-22 后台调大模型 看,实际用哪个 TTS 不能光看日志:
# 用 ss -tnp 看 OpenClaw 进程的真实出向连接
ss -tnp | grep $(supervisorctl pid openclaw)
# 排除本地回环、Telegram 入站、Kong 控制面
# 锁定真实出向 IP
2
3
4
5
关键调试方法论(详见 §6):日志里有 TTS 请求 ≠ 是当前 Agent 在用。可能是 Kong 转发给了别的服务。
# §6 调试 OpenClaw 架构的 3 条方法论
从 7 条素材里能提炼出 3 条反复出现的方法论:
# 6.1 证伪优于证实
症状:看到本地 TTS 服务 index-tts2 日志有请求,误以为是 OpenClaw 在用。
实际:那个 index-tts2 被 Kong 转发给另一个独立服务使用,跟 OpenClaw 无关。
证伪法(来自 2026-06-22 后台调大模型):
# 1. 触发目标行为(让 OpenClaw 真正调用一次 TTS)
# 2. 观测依赖服务的请求计数/时间戳
grep "POST /v1/audio/speech" /var/log/index-tts2/service.log | wc -l
# 3. 计数没变化 → 该服务没被调用 → 证伪"本地 TTS 被调用"
2
3
4
复用规则:在分布式系统里,"日志存在" ≠ "当前请求经过"。必须"触发-观测-计数变化"建立因果链。
# 6.2 配置考古的局限性
症状:grep 找不到 Gateway Token 配置,瞎改一通没效果。
实际:Token 在 Supervisor env 里,不在文件系统配置里。
解药(来自 2026-06-22 后台):
- 实时网络流量分析(
tcpdump/ss) - 行为侧验证(Thinking 模式:发
23*47=?看响应时间) /proc/<PID>/environ读真实 env
复用规则:配置找不到时,转向"运行时状态"(网络/进程/行为)而非盲目改配置。
# 6.3 区分"业务数据"和"程序残留"
症状:想卸载 OpenClaw,看到 /workspace/ComfyUI/output/openclaw_*.png 以为是程序残留,删了。
实际:那是 ComfyUI 业务产出的图片,跟 OpenClaw 程序无关。
判定法(来自 2026-06-15 容器内卸载):
- 路径在
output/vsbin/→ 业务 vs 程序 - 文件类型
.pngvs.js/.mjs→ 业务 vs 程序 - 目录名含应用名 ≠ 是该应用的残留
复用规则:卸载前严格按路径+类型判定,不能光看文件名。
# §7 真实故障排查流程(综合)
把 7 条素材的故障模式抽象成一套标准排查流程:
症状(Agent 死了/卡了/没回应)
↓
[1] 拓扑判定:which openclaw + supervisor/systemd status
↓
[2] 服务活着吗?supervisor status / systemctl status
↓
[3] 进程在吗?ps + ss(端口 18789 在 listen?)
↓
[4] 配置合法吗?openclaw config validate
↓
[5] Provider 在线吗?openclaw infer model providers
↓
[6] Token 一致吗?config vs env(sha256 比对)
↓
[7] Scope 够吗?openclaw devices list
↓
[8] 端到端 ping:发"在线" → 等 PONG
↓
[9] 看实时日志:supervisorctl tail -f openclaw
↓
[10] 抓包验证:tcpdump vLLM port + grep enable_thinking
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
10 步定位法。前 5 步覆盖 80% 故障。
# §8 下一篇
下一篇 04 OpenClaw 配置管理(待写),会基于 4 条真实素材:
- 06-22 给 provider 新增 model id
- 06-22 配置文件合理性审查
- 06-16 三个 OpenClaw 模型的配置问题
- 06-25 换 telebot token
重点讲 OpenClaw 配置的"3 类入口 + 优先级矩阵"——和本文 §4 的 Token 双源是同一系列的不同切面。
# §9 Agent 可直接解析的元数据块
{
"_meta": {
"doc_version": "2026-06-27",
"article_id": "openclaw-architecture",
"profile_context": "blog",
"estimated_read_time": "12min",
"real_material_count": 7,
"material_sources": [
"notes/claude-code/<session-file>.md",
"notes/claude-code/<session-file>.md",
"notes/claude-code/<session-file>.md",
"notes/claude-code/<session-file>.md",
"notes/claude-code/<session-file>.md",
"notes/claude-code/<session-file>.md",
"notes/claude-code/<session-file>.md"
]
},
"topology": {
"types": ["docker-supervisor", "baremetal-systemd", "source-pnpm"],
"detect_command": "ssh <USER>@<IP> 'which openclaw; systemctl --user status openclaw-gateway'"
},
"critical_paths": {
"config": "~/.openclaw/openclaw.json",
"state_db": "~/.openclaw/state",
"settings_dir": "~/.openclaw/settings/",
"skills_dir": "~/.openclaw/workspace/skills/",
"source_root": "/workspace/openclaw/"
},
"gateway_token_rule": {
"sources": ["config.gateway.auth.token", "env.OPENCLAW_GATEWAY_TOKEN"],
"recommended": "config_follows_env",
"fix_cmd": "openclaw config set gateway.auth.token '${OPENCLAW_GATEWAY_TOKEN}'"
},
"debug_methodology": [
"证伪优于证实:触发-观测-计数变化",
"配置考古的局限性:转向运行时网络/进程/行为",
"区分业务数据 vs 程序残留:路径+类型判定"
],
"safety_rules": [
"永远用 docker exec -u <USERNAME> 跑 openclaw(避免 state.db 权限错)",
"Provider 改名必须同步迁移 SQLite auth_profile_store",
"Skill 第一步必须是拓扑判定(容器 vs 裸机 vs 源码)",
"Token 永远单一来源:config 跟随 env,不要两处分别维护"
],
"verification": {
"config_validate": "openclaw config validate",
"provider_status": "openclaw infer model providers",
"gateway_health": "ss -tlnp | grep 18789",
"end_to_end": "Telegram 发送'在线' 等 PONG"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
AI Agent 使用指南:
- 当用户说"OpenClaw 部署不上去" → 先跑
ssh ... 'which openclaw; systemctl --user status openclaw-gateway'判定形态 - 当用户说"Agent 死了" → 走 §7 的 10 步定位法
- 当用户说"想接 OpenWebUI" → 参考 §5.2 的
/openai/config/update调用,注意 chatCompletions 必须 enable - 当用户说"Token mismatch" → 参考 §4.1,先比对 sha256 再修复