Files
nanoclaw/docs/learning-roadmap.md

9.9 KiB
Raw Blame History

NanoClaw 源码学习路线图

配套答案: 路线图中 21 个问题的详细答案见 docs/answers/ 目录。

前置知识

阅读源码前,确保理解以下核心概念:

  • 两层运行时Host 进程用 Node/pnpmContainer 进程用 Bun。两者不共享代码不共享模块。
  • 唯一的 IO 界面Host 和 Container 之间没有 IPC、没有 stdin pipe。两个 SQLite 文件是唯一的通讯界面:
    • inbound.db — Host 写Container 读
    • outbound.db — Container 写Host 读
  • seq 奇偶规则Host 使用偶数 seqContainer 使用奇数 seq避免双方同时写同一个 DB 时的冲突。
  • 实体模型users → messaging_groups → agent_groups → sessions,中间通过 messaging_group_agents 多对多连接。
  • 会话不等于代理组:一个 agent group 可以有多个 session比如不同线程每个 session 有自己独立的 inbound.db + outbound.db

建议:先快速浏览 docs/architecture.mddocs/architecture-diagram.md,建立一个心智模型再开始。


第一层:全局鸟瞰(先读文档)

顺序 文件 解决什么问题
1 docs/architecture.md 整个系统的架构全貌,消息怎么流转
2 docs/architecture-diagram.md 消息从进到出的可视化链路
3 docs/db.md 三层 DB 模型central + inbound + outbound
4 docs/db-central.md 中央库 data/v2.db 每一张表的职责和字段
5 docs/db-session.md 会话库的 schema + seq 奇偶分配机制 + cross-mount 不变式
6 docs/build-and-runtime.md 为什么 Host 用 NodeContainer 用 Bun两套锁文件互不干扰

第二层:主循环 — 消息从进到出的主干链路

这是整个系统的脊椎。按调用顺序读,每个文件不必求甚解,但要理解数据流向。

顺序 文件 在链路中的角色
7 src/index.ts 程序入口:初始化 DB、运行迁移、启动所有 channel adapter、启动 poll sweep、启动 CLI socket server
8 src/router.ts 入站路由:收到消息 → 解析 messaging group → 解析 agent group → 权限门检查 → 创建/查找 session → 写 inbound.db → 唤醒容器
9 src/session-manager.ts 会话生命周期:管理 session 目录、打开/关闭 DB、心跳检测、跨 mount 的 journal_mode=DELETE 不变式
10 src/delivery.ts 出站投递:轮询 outbound.db → 通过 channel adapter 发送消息 → 处理系统动作(审批、调度等)
11 src/host-sweep.ts 后台清扫60 秒周期 — processing_ack 同步、僵死容器检测、到期消息唤醒、定时任务触发

到这里你应该能回答:一条用户消息从发出来到 agent 回复,经历了哪些步骤。


第三层:容器侧 — Agent 在里面干什么

理解 Host 怎么把消息交给容器之后,钻进去看容器内部。

顺序 文件 在链路中的角色
12 container/agent-runner/src/index.ts 容器入口:加载 /workspace/agent/container.json、构建 MCP server 配置、创建 provider、进入 poll loop
13 container/agent-runner/src/poll-loop.ts 核心大循环:读 inbound.db → 调 providerClaude/OpenCode→ 格式化输出 → 写 outbound.db
14 container/agent-runner/src/providers/factory.ts Provider 工厂:根据 container.json 中的 agent_provider 字段选择 Claude 或 OpenCode
15 container/agent-runner/src/providers/claude.ts Claude Agent SDK 的具体对接实现
16 container/agent-runner/src/formatter.ts 出站消息格式化:按目标 channel 类型做内容适配
17 container/agent-runner/src/mcp-tools/index.ts MCP 工具注册入口agent 能调用的所有工具列表

第四层:带着问题深入子系统

以下问题按系统层次组织,每个问题后面标注了需要读的关键文件。带着问题去读比按文件线性读高效得多。


全局架构

Q1: 一条用户消息从 Slack 发出,到 agent 回复出现在聊天框里,完整路径是什么?

追踪:src/router.tssrc/session-manager.tscontainer/agent-runner/src/poll-loop.tssrc/delivery.ts。动手画一张时序图,标注每个环节读/写了哪个数据库。

Q2: 为什么 Host 用 NodeContainer 用 Bun两套运行时之间的"协议"是什么?

docs/build-and-runtime.md + src/container-runner.ts

Q3: inbound.dboutbound.db 为什么各只能有一个 writerjournal_mode=DELETE 为什么是必须的?

docs/db-session.md + src/session-manager.ts 中的注释块


路由与会话

Q4: 一个 messaging group比如 Slack 频道)怎么决定路由到哪个 agent group没匹配上怎么办

src/router.tsrouteInbound() + src/db/messaging-groups.ts

Q5: Session 什么时候创建?agent-sharedshared、per-thread 三种隔离模式的区别是什么?

docs/isolation-model.md + src/session-manager.ts

Q6: 容器 idle 后被 kill用户再发消息时怎么被唤醒on_wake 消息为什么不会被旧容器偷走?

src/container-runner.tskillContainer() + src/container-restart.ts


权限与安全

Q7: 用户身份怎么确定owner / admin / member 三级的权限检查在哪张表、哪段代码里完成?

src/modules/permissions/access.ts + src/modules/permissions/db/user-roles.ts

Q8: 陌生人在群里 @bot系统怎么决定是忽略、审批、还是直接响应

src/modules/permissions/sender-approval.ts + src/router.ts 中的 access gate 回调

Q9: Agent 在容器里能用 ncl 命令吗?能查其他 agent group 的数据吗?cli_scope 在哪里被检查?

src/cli/dispatch.ts + src/command-gate.ts + src/db/migrations/015-cli-scope.ts


容器生命周期

Q10: 启动 agent 容器时 mount 了哪些东西?/workspace、session DB、CLAUDE.md、skills 分别从哪里来?

src/container-runner.ts 的 mount 参数 + src/group-init.ts

Q11: Agent 的 system prompt 是怎么拼出来的CLAUDE.md + 全局指令 + container config 各自贡献了什么?

src/claude-md-compose.ts + container/agent-runner/src/index.ts

Q12: 容器心跳怎么检测?进程活着但 poll loop 卡死了host 怎么发现?

src/host-sweep.ts + src/session-manager.ts.heartbeat 文件的逻辑


出站投递与系统动作

Q13: Agent 回复消息后delivery.ts 怎么知道用哪个 channel adapter 发送?重试和失败怎么处理?

src/delivery.ts + src/channels/adapter.ts

Q14: Agent 发起 install_packagesadd_mcp_server,从发出请求到容器重建完成,完整的审批-执行链路是什么?

container/agent-runner/src/mcp-tools/self-mod.tssrc/modules/self-mod/request.tssrc/modules/approvals/primitive.tssrc/modules/self-mod/apply.ts

Q15: 定时任务cron怎么实现Agent 在容器里能创建吗?触发时谁写消息进 inbound.db

src/modules/scheduling/recurrence.ts + src/modules/scheduling/db.ts + src/host-sweep.ts 中 recurrence 处理


数据模型

Q16: 中央库 data/v2.db 有哪些表?它们之间的外键关系是怎样的?

src/db/schema.ts + docs/db-central.md。建议自己画一张 ER 图。

Q17: DB 迁移怎么组织?我要加一张表或一个字段该改哪些文件?

src/db/migrations/index.ts + 任意一个 src/db/migrations/0XX-*.ts


Provider 与 MCP

Q18: Claude Agent SDK、OpenCode、Ollama 三个 provider 怎么抽象成统一接口?切换 provider 改什么?

container/agent-runner/src/providers/factory.ts + container/agent-runner/src/providers/types.ts

Q19: 容器里的 MCP server 怎么启动内置工具core、agents、self-mod 等)和外部 MCP server 有什么不同?

container/agent-runner/src/mcp-tools/server.ts + container/agent-runner/src/index.ts 中的 MCP 构建逻辑


Channel 适配器

Q20: 如果要加一个新的 channel比如钉钉需要实现什么接口、改哪些文件

src/channels/adapter.ts(接口定义)+ src/channels/channel-registry.ts(注册表)+ src/channels/chat-sdk-bridge.ts(如果用 Chat SDK 模式)

Q21: Chat SDK bridge 是什么?为什么 Discord/Slack/Telegram 等共用它?

src/channels/chat-sdk-bridge.ts


建议的阅读策略

  1. 先跑通主干第一层→第二层Q1不求甚解但要能画出消息的完整流转路径
  2. 再读容器侧(第三层),理解 agent 内部怎么调 Claude、怎么写回结果
  3. 挑一个子系统深入(第四层),根据你最关心的方向:
    • 关注多租户/权限 → Q7, Q8, Q9
    • 关注容器/部署 → Q6, Q10, Q11, Q12
    • 关注扩展新 provider/channel → Q18, Q19, Q20, Q21
    • 关注运维/稳定性 → Q2, Q3, Q5, Q14, Q15
  4. 最后回到文档,通读 docs/api-details.mddocs/agent-runner-details.md

关键文件速查表

想了解... 先读这个文件
整体启动流程 src/index.ts
消息怎么进来 src/router.ts
消息怎么出去 src/delivery.ts
Session 怎么管理 src/session-manager.ts
容器怎么启动/杀死 src/container-runner.ts
Agent 的主循环 container/agent-runner/src/poll-loop.ts
Agent 能调用哪些工具 container/agent-runner/src/mcp-tools/index.ts
权限检查入口 src/modules/permissions/access.ts
ncl CLI 怎么工作 src/cli/dispatch.ts
DB 有哪些表 src/db/schema.ts
审批流程 src/modules/approvals/primitive.ts
自我修改如何实现 src/modules/self-mod/apply.ts