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

8.5 KiB
Raw Permalink Blame History

NanoClaw 数据库架构 — 概述

数据模型的概览三个数据库database它们如何协同工作以及跨数据库保持不变的那些约束。表级 schema 请参阅下面的链接。

  • db-central.mddata/v2.db 中的每一张表身份标识、连接关系、审批、Chat SDK 状态)以及迁移系统。
  • db-session.md — 每个 session会话inbound.db + outbound.db 对、seq 奇偶规则以及 session 目录布局。

相关文档:architecture.md(高层设计);api-details.md(出入站消息内容格式);isolation-model.md(频道到 agent 的连接模式)。


1. 三个数据库

NanoClaw 使用三种 SQLite 数据库,全部位于宿主机文件系统上:

DB 位置 写入者 读取者 用途
中央库Central data/v2.db 宿主机 宿主机 身份、权限、路由、连接——管理平面
Session 入站库 data/v2-sessions/<agent_group_id>/<session_id>/inbound.db 宿主机 宿主机(同步)、容器(只读) 宿主机 → 容器消息 + 路由投影
Session 出站库 data/v2-sessions/<agent_group_id>/<session_id>/outbound.db 容器 宿主机(轮询)、容器 容器 → 宿主机消息 + 处理状态

单一写入者规则。 每个 SQLite 文件有且仅有一个写入者。宿主机写入中央库和每个 inbound.db;容器只写入自己的 outbound.db。这消除了跨越 Docker/Apple Container 挂载边界的写入竞争——SQLite 锁在跨挂载场景下不可靠。

一切皆为消息。 宿主机和容器之间没有 IPC、stdin 管道或文件监视器。两个 session DB 是唯一的 IO 接口。心跳heartbeat是对 .heartbeat 的文件 touch(2),而非数据库写入。

日志模式。 Session DB 使用 journal_mode = DELETE(而非 WAL。跨挂载的 WAL 可见性是个 bug 温床DELETE 模式加上 open-write-close 会强制刷新页面缓存,使对端能看到变更。


2. 数据库映射

data/
  v2.db                                   ← 中央库(宿主机 ↔ 宿主机)
  v2-sessions/
    <agent_group_id>/
      .claude-shared/                     ← agent group 共享的 Claude 状态
      agent-runner-src/                   ← 每个 agent group 的 agent-runner 覆盖层
      <session_id>/
        inbound.db                        ← 宿主机写入,容器读取
        outbound.db                       ← 容器写入,宿主机读取
        .heartbeat                        ← 容器触碰的 mtime
        inbox/<message_id>/               ← 已解码的用户附件
        outbox/<message_id>/              ← agent 生成的附件

路径辅助函数:sessionDir()inboundDbPath()outboundDbPath()heartbeatPath()——均在 src/session-manager.ts 中。


3. 中央库 vs. session数据存放规则

数据类型 存放位置 原因
身份、角色、成员关系 中央库 稳定、跨 session、极少写入
频道连接、路由规则 中央库 管理平面
目的地 ACL 中央库(+ 每个 session 的投影) 中央为真源session 本地快速查找
Session 注册表id、状态 中央库 宿主机编排生命周期
审批与待处理问题 中央库 能经受容器重启、管理员可见
丢弃消息审计 中央库 全局运维视图
入站消息、重试状态 session inbound.db 每个 session 的工作负载;宿主机为唯一写入者
出站消息、agent 状态 session outbound.db 容器为唯一写入者;宿主机轮询
投递结果 session inbound.dbdelivered 宿主机在成功时写入;容器读取以定位编辑目标
处理状态 session outbound.dbprocessing_ack 容器不能写入 inbound.db

启发式原则:如果一个值是消息、路由投影或运行时确认,则放入 per-session 的 DB。其他一切都在中央库。


4. 跨挂载可见性

Session DB 被 bind mount 到容器中。在修改 DB 代码之前,需要了解几条规则:

  • journal_mode = DELETE,而非 WAL。 WAL 文件不能可靠地跨越挂载边界容器可能读到过时的页面。DELETE 模式强制每个写入者刷新主文件。
  • 宿主机侧 open-write-close。 宿主机对 inbound.db 的写入是打开连接、写入、然后关闭。保持句柄打开会使缓存的页面对容器不可见。
  • 容器读取为只读。 容器以 readonly: true 打开 inbound.db,且从不写入——所有容器→宿主机的状态通过 outbound.db 传递(见 db-session.md)。
  • 心跳是文件 touch。 .heartbeat 的 mtime 是存活信号,而非 DB 列。每次心跳做 DB 写入会串行化在其他写入者后面。

这些规则在 src/session-manager.tscontainer/agent-runner/src/db/ 中通过约定强制执行。如果你要修改 DB 的打开方式,请先重新阅读这些代码。


5. 设计模式一览

  1. 两库 session 拆分。 inbound.dboutbound.db 各有一个写入者,一个数据流向——无跨挂载锁竞争。
  2. Seq 奇偶规则。 偶数 = 宿主机,奇数 = 容器。两张表之间的不相交命名空间让 agent 可以仅凭 seq 引用任何消息。详情见 db-session.md §3
  3. 投影模式。 agent_destinationssession_routing 在容器唤醒时从中央库投影到每个 session 的 inbound.db 中——容器获得快速、本地的读取路径,无需跨挂载查询。
  4. 通过反向通道确认。 容器从不写入 inbound.db。状态同步通过 outbound.db 中的 processing_ack 实现,由宿主机轮询并协调。
  5. 带外心跳。.heartbeat 的文件 touch,而非 DB 写入,因此存活检查不串行在其他写入者后面。
  6. 惰性 session-DB 迁移。 中央库使用编号迁移per-session 的 DB 使用 IF NOT EXISTS + 针对旧 session 目录的临时 ALTER TABLE 辅助函数。
  7. ACL = 行的存在。 agent_destinations 的成员关系本身就是权限——没有单独的 permissions 表。

6. 读取者与写入者 — 一览

DB 写入者 读取者
agent_groups central src/db/agent-groups.ts session 解析器、投递、路由
messaging_groups central src/db/messaging-groups.ts、频道设置 路由、投递、session 解析器
messaging_group_agents central src/db/messaging-groups.ts 路由
users central src/db/users.ts、认证流程 权限检查
user_roles central src/db/user-roles.ts src/access.ts、所有权限关卡
agent_group_members central src/db/agent-group-members.ts 成员资格检查
user_dms central src/user-dm.tsensureUserDm 审批 + 配对投递
sessions central src/db/sessions.tssrc/session-manager.ts 投递、sweep、容器运行器
pending_questions central src/db/sessions.ts(通过 ask_user_question 容器响应匹配器
agent_destinations central src/db/agent-destinations.ts、迁移 004 回填 writeDestinations()、投递 ACL
pending_approvals central src/db/sessions.tssrc/onecli-approvals.ts 管理员卡片投递、sweep
unregistered_senders central src/db/dropped-messages.ts 运维工具
chat_sdk_* central src/state-sqlite.ts Chat SDK 桥接
schema_version central src/db/migrations/index.ts 迁移运行器
messages_in inbound src/db/session-db.ts container/agent-runner/src/db/messages-in.ts
delivered inbound src/db/session-db.tsmarkDelivered 容器编辑/反应定位
destinations inbound writeDestinations()src/session-manager.ts 容器路由 / ACL
session_routing inbound writeSessionRouting()src/session-manager.ts 容器 send_message 默认值
messages_out outbound container/agent-runner/src/db/messages-out.ts src/delivery.ts 轮询循环
processing_ack outbound container/agent-runner/src/db/messages-in.ts src/host-sweep.tssyncProcessingAcks
session_state outbound container/agent-runner/src/db/session-state.ts 容器启动时