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

6.7 KiB
Raw Blame History

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 轮询(活跃)/ 60ssweep
  D->>D: hasDestination() 重新验证
  D->>B: 通过适配器投递
  B->>P: 发送消息 / 编辑 / 反应 / 文件 / 卡片

命名目的地 + Agent 到 Agent

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

实体模型 + 隔离级别

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 编号。"]