6.7 KiB
6.7 KiB
NanoClaw 架构图
系统概览
flowchart TB
subgraph Platforms["消息平台"]
P1[Discord]
P2[Telegram]
P3[Slack]
P4[GitHub / Linear]
P5[WhatsApp / iMessage / Teams / GChat / Matrix / Webex / Email]
end
subgraph Host["宿主机进程 (Node)"]
direction TB
Bridge["Chat SDK 桥接<br/>(src/channels/chat-sdk-bridge.ts)"]
Router["路由器<br/>(src/router.ts)<br/>platformId + threadId -> messaging_group -> agent_group -> session"]
SessMgr["Session 管理器<br/>(src/session-manager.ts)<br/>创建 inbound.db + outbound.db"]
Runner["容器运行器<br/>(src/container-runner.ts)<br/>OneCLI ensureAgent + 启动"]
Delivery["投递轮询器<br/>(src/delivery.ts)<br/>活跃时 1s / sweep 时 60s"]
Sweep["宿主机 Sweep<br/>(src/host-sweep.ts)<br/>心跳、重试、重复执行"]
Central[("中央库<br/>data/v2.db<br/>agent_groups<br/>messaging_groups<br/>messaging_group_agents<br/>sessions<br/>pending_approvals")]
end
subgraph OneCLI["OneCLI 网关 (0.3.1)"]
Vault["Agent Vault<br/>秘密 + OAuth"]
Approvals["configureManualApproval<br/>-> pending_approvals"]
end
subgraph Session["每个 Session 的容器 (Docker / Apple Container)"]
direction TB
PollLoop["轮询循环<br/>(container/agent-runner)"]
Provider["Agent 提供程序<br/>(claude、opencode、mock;待办: codex)"]
MCP["MCP 工具<br/>send_message, send_file, edit_message,<br/>add_reaction, send_card, ask_user_question,<br/>schedule_task, create_agent,<br/>install_packages, add_mcp_server"]
Skills["容器技能<br/>(container/skills/)"]
InDB[("inbound.db<br/>宿主机写入<br/>偶数 seq<br/>messages_in<br/>destinations<br/>processing_ack")]
OutDB[("outbound.db<br/>容器写入<br/>奇数 seq<br/>messages_out<br/>心跳文件")]
end
subgraph Groups["Agent Group 文件系统 (groups/*)"]
Folder["CLAUDE.md<br/>记忆<br/>每个 group 的技能<br/>container_config"]
end
P1 & P2 & P3 & P4 & P5 --> Bridge
Bridge --> Router
Router --> Central
Router --> SessMgr
SessMgr --> InDB
SessMgr --> Runner
Runner --> OneCLI
Runner --> PollLoop
PollLoop --> InDB
PollLoop --> Provider
Provider --> MCP
Provider --> Skills
MCP --> OutDB
OutDB --> Delivery
Delivery --> Central
Delivery --> Bridge
Bridge --> P1 & P2 & P3 & P4 & P5
Sweep --> InDB
Sweep --> OutDB
Sweep --> Central
Runner -.挂载.-> Folder
MCP -.审批.-> Approvals
Approvals --> Central
Provider -.API 调用.-> Vault
消息流程 (入站 -> agent -> 出站)
sequenceDiagram
participant P as 平台 (例如 Telegram)
participant B as Chat SDK 桥接
participant R as 路由器
participant SM as Session 管理器
participant IDB as inbound.db
participant C as 容器 (agent-runner)
participant ODB as outbound.db
participant D as 投递轮询器
P->>B: 新消息
B->>R: routeInbound(platformId, threadId, msg)
R->>R: 解析 messaging_group -> agent_group -> session<br/>(agent-shared | shared | per-thread)
R->>SM: 确保 session + DB 存在
R->>IDB: INSERT messages_in (偶数 seq)
R->>C: 唤醒容器 (docker run / 已在运行)
C->>IDB: 轮询 messages_in
C->>C: 格式化 xml, 流式传输到选定的 provider
C->>ODB: INSERT messages_out (奇数 seq)<br/>解析 <message to="name"> 块
D->>ODB: 1s 轮询(活跃)/ 60s(sweep)
D->>D: hasDestination() 重新验证
D->>B: 通过适配器投递
B->>P: 发送消息 / 编辑 / 反应 / 文件 / 卡片
命名目的地 + Agent 到 Agent
flowchart LR
subgraph AgentA["Agent Group A (主agent)"]
A_out["输出:<br/><message to='slack'>...</message><br/><message to='browser-agent'>...</message><br/><internal>scratchpad</internal>"]
end
subgraph Dests["inbound.db.destinations (每个 agent)"]
D1["slack -> messaging_group 42"]
D2["browser-agent -> agent_group 7<br/>(双向行)"]
D3["github -> messaging_group 13"]
end
subgraph AgentB["Agent Group B (浏览器子agent)"]
B_session["自己的 inbound.db / outbound.db<br/>继承了返回到 A 的目的地"]
end
Slack[Slack 频道]
GitHub[GitHub PR 线程]
A_out -->|解析 + 查找| Dests
D1 -->|投递| Slack
D2 -->|写入 B 的 inbound.db| B_session
D3 -->|投递| GitHub
B_session -.通过 'parent' 回复.-> Dests
实体模型 + 隔离级别
erDiagram
agent_groups ||--o{ messaging_group_agents : 已连接
messaging_groups ||--o{ messaging_group_agents : 已连接
agent_groups ||--o{ sessions : 运行
messaging_groups ||--o{ sessions : 上下文
agent_groups ||--o{ agent_destinations : 拥有
agent_groups ||--o{ pending_approvals : 请求
agent_groups {
int id
string name
string folder
string agent_provider
json container_config
}
messaging_groups {
int id
string channel_type
string platform_id
string name
bool is_group
string unknown_sender_policy "strict | request_approval | public"
}
users {
string id PK "命名空间 <channel>:<handle>"
string kind
string display_name
}
user_roles {
string user_id FK
string role "owner | admin"
string agent_group_id FK "null = 全局"
}
agent_group_members {
string user_id FK
string agent_group_id FK
}
user_dms {
string user_id FK
string channel_type
string messaging_group_id FK
}
messaging_group_agents {
int messaging_group_id
int agent_group_id
string session_mode "agent-shared | shared | per-thread"
json trigger_rules
int priority
}
sessions {
int id
int agent_group_id
int messaging_group_id
string sdk_session_id
string status
}
隔离级别速查表
| 级别 | session_mode |
共享内容 | 示例 |
|---|---|---|---|
| 1. 共享 session | agent-shared |
工作区 + 记忆 + 对话 | Slack + GitHub webhooks 在同一线程 |
| 2. 相同 agent,独立 session | shared / per-thread |
仅工作区 + 记忆 | 一个 agent 跨 3 个 Telegram 聊天 |
| 3. 独立的 agent group | (不同的 agent_group_id) |
无 | 个人 vs 工作频道 |
双库拆分(为什么)
flowchart LR
subgraph Mount["/workspace (挂载到容器中的卷)"]
In[("inbound.db")]
Out[("outbound.db")]
HB["/.heartbeat (文件 touch)"]
end
Host[宿主机进程] -->|"仅写入<br/>(偶数 seq)"| In
Host -->|读取| Out
Container[agent-runner] -->|读取| In
Container -->|"仅写入<br/>(奇数 seq)"| Out
Container -->|每次轮询 touch| HB
HostSweep[宿主机 sweep] -->|stat mtime| HB
HostSweep -->|读取 processing_ack| In
note1["每个文件有且仅有一个写入者。<br/>消除了 SQLite 跨进程写入竞争。<br/>无冲突的 seq 编号。"]