Files
nanoclaw/docs/zh/architecture-diagram.md
2026-05-12 13:14:17 +00:00

216 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# NanoClaw 架构图
## 系统概览
```mermaid
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 -> 出站)
```mermaid
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 轮询(活跃)/ 60ssweep
D->>D: hasDestination() 重新验证
D->>B: 通过适配器投递
B->>P: 发送消息 / 编辑 / 反应 / 文件 / 卡片
```
## 命名目的地 + Agent 到 Agent
```mermaid
flowchart LR
subgraph AgentA["Agent Group A (主agent)"]
A_out["输出:<br/>&lt;message to='slack'&gt;...&lt;/message&gt;<br/>&lt;message to='browser-agent'&gt;...&lt;/message&gt;<br/>&lt;internal&gt;scratchpad&lt;/internal&gt;"]
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
```
## 实体模型 + 隔离级别
```mermaid
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 工作频道 |
## 双库拆分(为什么)
```mermaid
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 编号。"]
```