Files
nanoclaw/docs/learning-roadmap.md

195 lines
9.9 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 源码学习路线图
> **配套答案:** 路线图中 21 个问题的详细答案见 [`docs/answers/`](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.md` 和 `docs/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.ts` → `src/session-manager.ts` → `container/agent-runner/src/poll-loop.ts` → `src/delivery.ts`。动手画一张时序图,标注每个环节读/写了哪个数据库。
#### Q2: 为什么 Host 用 NodeContainer 用 Bun两套运行时之间的"协议"是什么?
> `docs/build-and-runtime.md` + `src/container-runner.ts`
#### Q3: `inbound.db` 和 `outbound.db` 为什么各只能有一个 writer`journal_mode=DELETE` 为什么是必须的?
> `docs/db-session.md` + `src/session-manager.ts` 中的注释块
---
### 路由与会话
#### Q4: 一个 messaging group比如 Slack 频道)怎么决定路由到哪个 agent group没匹配上怎么办
> `src/router.ts` 的 `routeInbound()` + `src/db/messaging-groups.ts`
#### Q5: Session 什么时候创建?`agent-shared`、`shared`、per-thread 三种隔离模式的区别是什么?
> `docs/isolation-model.md` + `src/session-manager.ts`
#### Q6: 容器 idle 后被 kill用户再发消息时怎么被唤醒`on_wake` 消息为什么不会被旧容器偷走?
> `src/container-runner.ts` 的 `killContainer()` + `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_packages` 或 `add_mcp_server`,从发出请求到容器重建完成,完整的审批-执行链路是什么?
> `container/agent-runner/src/mcp-tools/self-mod.ts` → `src/modules/self-mod/request.ts` → `src/modules/approvals/primitive.ts` → `src/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.md``docs/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` |