- wire SQLite control-plane access/session paths into Rust web local-folder routes - preserve local Markdown attachment semantics across upload, reload, and secondary-pane resource tabs - refresh design governance docs, Reasonix task templates, and bug records - retire root .mcp.json local MCP config
722 lines
15 KiB
Markdown
722 lines
15 KiB
Markdown
# 1 [reference] Tree-First Graph 内核方案 v1
|
||
|
||
> 更新时间:2026-04-22
|
||
>
|
||
> 当前状态:`reference`。本文是 tree-first graph kernel 的长期架构边界,不是当前可直接执行的 checklist;执行入口见 `design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`。
|
||
>
|
||
> 当前优先级入口:
|
||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
|
||
>
|
||
> 关联文档:
|
||
> - `/mnt/Data1T/mnote/design/03-rust-web/reference/3-rust-web-long-term-architecture-v1.md`
|
||
> - `/mnt/Data1T/mnote/design/03-rust-web/reference/3-1-rust-web-long-term-checklist-v2.md`
|
||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||
|
||
## 1. 文档目的
|
||
|
||
本文回答的不是“页面如何加速”,而是更底层的问题:
|
||
|
||
> **mnote 长期到底应该以什么作为统一对象内核。**
|
||
|
||
在前一轮讨论里,方向经历了一个重要修正:
|
||
|
||
- 不是让 `Mindmap` 成为系统主投影
|
||
- 也不是让“导图页面”变成新的系统中心
|
||
- 而是让 **`tree-first graph` 成为统一结构内核**
|
||
- `Mindmap` 只是这个内核的一种可视化挂件
|
||
|
||
这份文档的目标,是把这个判断正式固定下来。
|
||
|
||
---
|
||
|
||
## 2. 先给结论
|
||
|
||
结论只有一句:
|
||
|
||
> **mnote 的长期核心不应是 `BlockNote-first`,也不应是 `Mindmap-first`,而应是 `tree-first graph kernel`。**
|
||
|
||
也就是说:
|
||
|
||
- **树** 是主骨架
|
||
- **图** 是横向引用扩展
|
||
- **Mindmap** 是树/图的一种空间化视图
|
||
- **Sidebar / 页面树 / 文件树 / 文档阅读页 / 搜索结果 / AI 面板** 都只是同一内核的不同投影
|
||
|
||
因此,长期正确方向不是:
|
||
|
||
- “把所有东西都画成导图”
|
||
|
||
而是:
|
||
|
||
- “让所有对象共享同一份结构内核,再让不同视图各自投影”
|
||
|
||
---
|
||
|
||
## 3. 为什么不是 Mindmap-first
|
||
|
||
虽然 Mindmap 在结构表达上很强,但它不适合被定义为主中心。
|
||
|
||
原因有四个。
|
||
|
||
### 3.1 导图 UI 不是所有场景的最佳交互
|
||
|
||
下面这些场景不适合被强行导图化:
|
||
|
||
- Sidebar 导航
|
||
- 文件树浏览
|
||
- 文档阅读
|
||
- 搜索结果浏览
|
||
- 历史版本查看
|
||
- AI 工具面板
|
||
|
||
这些场景里,很多时候:
|
||
|
||
- 列表更合适
|
||
- 树表更合适
|
||
- 阅读流更合适
|
||
- 搜索结果卡片更合适
|
||
|
||
所以:
|
||
|
||
> **导图是一种强表达能力的视图,不是所有结构都应默认进入的主视图。**
|
||
|
||
### 3.2 如果 Mindmap 成为主中心,系统会被导图交互绑架
|
||
|
||
一旦把 Mindmap 当主投影,后面很容易出现:
|
||
|
||
- 结构建模被导图控件的数据形状反向约束
|
||
- 页面/文件/章节/引用都被迫适配导图编辑器
|
||
- 视图层规则污染对象层规则
|
||
|
||
这会让“结构内核”被“某种 UI 控件”夺走主导权。
|
||
|
||
### 3.3 当前本地代码已经说明 Mindmap 更像重操作壳
|
||
|
||
当前:
|
||
|
||
- [`MindmapBlock.tsx`](/mnt/Data1T/mnote/wolai-frontend/src/components/editor/blocks/MindmapBlock.tsx)
|
||
|
||
同时承担:
|
||
|
||
- 内嵌块
|
||
- 独立页
|
||
- 工具栏
|
||
- 右键菜单
|
||
- 导航器
|
||
- 缩略图
|
||
- 本地全屏视图
|
||
|
||
这说明它当前的本质更接近:
|
||
|
||
- 一个前端重交互操作层
|
||
|
||
而不是:
|
||
|
||
- 一个稳定、极简、内核级结构模型
|
||
|
||
### 3.4 Mindmap 已经适合退到“挂件层”
|
||
|
||
真正更合理的位置是:
|
||
|
||
- 它继续保留
|
||
- 但作为 `tree-first graph kernel` 的挂件和投影
|
||
- 而不是事实源
|
||
|
||
---
|
||
|
||
## 4. 为什么是 Tree-First Graph
|
||
|
||
这是因为 mnote 当前最稳定、最通用、最可渐进迁移的共同语义,本质上是:
|
||
|
||
- **父子层级**
|
||
- **局部子树**
|
||
- **对象引用**
|
||
|
||
这正对应:
|
||
|
||
- 树
|
||
- 子树
|
||
- 图边
|
||
|
||
### 4.1 树是最自然的主骨架
|
||
|
||
下面这些天然就是树:
|
||
|
||
- 工作区结构
|
||
- 页面树
|
||
- 文件树
|
||
- 文档大纲
|
||
- PDF 章节结构
|
||
- 页面内部结构
|
||
- 导图节点结构
|
||
|
||
因此“树优先”不是一种美学偏好,而是数据现实。
|
||
|
||
### 4.2 图是必须存在的扩展层
|
||
|
||
但系统又不可能只有树,因为还存在:
|
||
|
||
- 双向引用
|
||
- 页面引用页面
|
||
- 块引用块
|
||
- 摘要引用原文
|
||
- PDF 章节引用页码/附件
|
||
- AI 生成节点引用证据节点
|
||
|
||
这些都不是父子关系,而是横向边。
|
||
|
||
所以系统最终一定是:
|
||
|
||
- 树作为主骨架
|
||
- 图作为引用扩展
|
||
|
||
也就是:
|
||
|
||
> **tree-first graph**
|
||
|
||
### 4.3 这种结构最适合 Rust 内核化
|
||
|
||
因为它天然适合:
|
||
|
||
- typed node
|
||
- typed edge
|
||
- subtree query
|
||
- graph traversal
|
||
- object projection
|
||
- CLI / AI tool 直接操作
|
||
|
||
这比“以某个前端编辑器数据格式为真相”更适合进入 Rust core。
|
||
|
||
---
|
||
|
||
## 5. 内核定义
|
||
|
||
## 5.1 统一内核
|
||
|
||
长期建议把 mnote 的结构真相定义为:
|
||
|
||
- `WorkspaceKernel`
|
||
- `Node`
|
||
- `Edge`
|
||
- `Projection`
|
||
|
||
### 5.1.1 当前已落地的 Rust 类型
|
||
|
||
2026-04-16 这轮已经在 Rust `core-protocol` 中补入统一 kernel 类型定义,入口文件为:
|
||
|
||
- `/mnt/Data1T/mnote/rust/crates/core-protocol/src/kernel.rs`
|
||
|
||
当前已落下的主类型包括:
|
||
|
||
- `KernelNode`
|
||
- `KernelEdge`
|
||
- `KernelProjectionRequest`
|
||
- `KernelProjectionResult`
|
||
- `KernelSubtreeRef`
|
||
- `KernelSubtreeResult`
|
||
- `KernelGetNode`
|
||
- `KernelGetSubtree`
|
||
- `KernelListChildren`
|
||
- `KernelListEdges`
|
||
- `KernelTraverseGraph`
|
||
- `KernelCreateNode`
|
||
- `KernelUpdateNode`
|
||
- `KernelMoveSubtree`
|
||
- `KernelAttachEdge`
|
||
- `KernelDetachEdge`
|
||
|
||
这意味着这份文档里的 kernel 语义已经不再只是概念,而是进入了 Rust 可复用协议层。
|
||
|
||
### 5.2 Node
|
||
|
||
每个对象都是带类型的节点。
|
||
|
||
候选节点类型包括:
|
||
|
||
- `workspace`
|
||
- `folder`
|
||
- `page`
|
||
- `section`
|
||
- `paragraph`
|
||
- `asset`
|
||
- `book`
|
||
- `pdf`
|
||
- `mindmap`
|
||
- `mindmap_node`
|
||
- `table`
|
||
- `query_view`
|
||
- `summary`
|
||
- `ai_note`
|
||
- `reference_anchor`
|
||
|
||
当前 Rust 中的最小首批节点类型已经固定为:
|
||
|
||
- `workspace`
|
||
- `folder`
|
||
- `page`
|
||
- `section`
|
||
- `asset`
|
||
- `book`
|
||
- `pdf`
|
||
- `mindmap`
|
||
- `mindmap_node`
|
||
- `summary`
|
||
- `ai_note`
|
||
- `reference_anchor`
|
||
- `content_node`
|
||
- `index_node`
|
||
|
||
注意:
|
||
|
||
> **这里的关键不是名字,而是“页面、文件、书籍、摘要、AI 结果不再分属不同系统,而是同一内核里的 typed node”。**
|
||
|
||
### 5.3 Edge
|
||
|
||
边分成两类:
|
||
|
||
#### A. 骨架边
|
||
|
||
- `parent_of`
|
||
- `child_of`
|
||
- `contains`
|
||
|
||
#### B. 扩展边
|
||
|
||
- `references`
|
||
- `backlinks_to`
|
||
- `source_of`
|
||
- `derived_from`
|
||
- `summarizes`
|
||
- `indexes`
|
||
- `points_to`
|
||
|
||
当前 Rust 中的最小首批边类型已经固定为:
|
||
|
||
- `parent_of`
|
||
- `child_of`
|
||
- `contains`
|
||
- `references`
|
||
- `backlinks_to`
|
||
- `source_of`
|
||
- `derived_from`
|
||
- `summarizes`
|
||
- `indexes`
|
||
- `points_to`
|
||
|
||
这样:
|
||
|
||
- 树结构靠骨架边维持
|
||
- 网状关系靠扩展边表达
|
||
|
||
### 5.4 Projection
|
||
|
||
Projection 不是数据真相,只是同一内核的不同投影。
|
||
|
||
长期主要投影包括:
|
||
|
||
- Sidebar tree
|
||
- 页面树
|
||
- 文件树
|
||
- 文档阅读流
|
||
- Mindmap
|
||
- 搜索结果页
|
||
- AI 操作视图
|
||
- 未来可能的关系图 / 时间线 / 表格视图
|
||
|
||
当前 Rust 中已经固定的投影种类包括:
|
||
|
||
- `sidebar_tree`
|
||
- `page_tree`
|
||
- `file_tree`
|
||
- `mindmap`
|
||
- `read_view`
|
||
- `search_results`
|
||
- `rag_index`
|
||
|
||
### 5.5 四层边界
|
||
|
||
为了避免后续又把前端页面壳误当事实源,当前主线边界固定为四层:
|
||
|
||
1. **事实源**
|
||
- 统一 `tree-first graph kernel`
|
||
- 只承载 `node / edge / subtree / audit`
|
||
- 不承载具体前端 UI 状态
|
||
2. **投影**
|
||
- `sidebar tree`
|
||
- `page tree`
|
||
- `file tree`
|
||
- `mindmap projection`
|
||
- `read view`
|
||
- `search / rag projection`
|
||
3. **编辑器**
|
||
- `BlockNote`
|
||
- `Mindmap canvas`
|
||
- `OnlyOffice`
|
||
- 未来其他专用内容编辑器
|
||
4. **外挂 / 挂件**
|
||
- AI 面板
|
||
- 评论
|
||
- 历史
|
||
- 回链
|
||
- 右侧辅助信息面板
|
||
|
||
固定规则是:
|
||
|
||
- 事实源只在 kernel
|
||
- 投影不拥有对象真相
|
||
- 编辑器不等于对象模型
|
||
- 外挂只消费 kernel 或 projection,不再私自定义第二套对象真相
|
||
|
||
### 5.6 术语冻结
|
||
|
||
这一轮同时把后续文档和任务要共用的术语冻结如下:
|
||
|
||
- `node`
|
||
指统一内核中的 typed object
|
||
- `edge`
|
||
指节点之间的 typed relation
|
||
- `projection`
|
||
指从 kernel 派生出的视图结果,不是事实源
|
||
- `subtree`
|
||
指从某个 root node 出发的一段有界层级结构
|
||
- `content node`
|
||
指以正文载荷为主的节点,适合交给 BlockNote 之类编辑器处理
|
||
- `reference edge`
|
||
指非父子关系的引用边,例如页面引用、证据引用、来源引用
|
||
- `summary node`
|
||
指对某段 subtree 或 source node 做摘要后的节点
|
||
- `index node`
|
||
指为搜索/RAG 建立的结构索引节点
|
||
|
||
### 5.7 现阶段并入策略
|
||
|
||
当前主线按下面的口径迁移:
|
||
|
||
- 暂时继续存在,但应逐步并入 kernel 的对象:
|
||
- 页面
|
||
- Sidebar 数据集
|
||
- 搜索结果对象
|
||
- 导图树数据
|
||
- 当前明确不是事实源、只保留为编辑或展示壳:
|
||
- BlockNote 文档结构
|
||
- Mindmap 前端画布状态
|
||
- OnlyOffice 页面壳
|
||
- 各类 AI host / panel 本地状态
|
||
|
||
---
|
||
|
||
## 6. 与当前系统的关系
|
||
|
||
## 6.1 与 Sidebar / 页面树 / 文件树的关系
|
||
|
||
这些都不应再视为独立系统。
|
||
|
||
长期应改成:
|
||
|
||
- 同一份结构内核
|
||
- 在 Sidebar 中投影为导航树
|
||
- 在文件页中投影为文件树
|
||
- 在某些对象页中投影为结构树
|
||
|
||
所以:
|
||
|
||
> **Sidebar 不是“一个前端导航组件”,而是 kernel 的树投影。**
|
||
|
||
## 6.2 与 Mindmap 的关系
|
||
|
||
Mindmap 不再是中心,而是:
|
||
|
||
- kernel 的空间化图形视图
|
||
- 适合做结构浏览、重组、章节展开、节点重排
|
||
|
||
但它不再是:
|
||
|
||
- 唯一主视图
|
||
- 唯一对象真相
|
||
|
||
### 6.3 与 BlockNote 的关系
|
||
|
||
长期上,`BlockNote` 应从“系统底座”降级为:
|
||
|
||
- 内容编辑挂件
|
||
- 某类页面内容编辑器
|
||
|
||
而不是:
|
||
|
||
- 页面结构本体
|
||
- 工作区结构内核
|
||
|
||
也就是说,未来不是:
|
||
|
||
- 页面 = BlockNote 文档
|
||
|
||
而更接近:
|
||
|
||
- 页面 = kernel 子树
|
||
- BlockNote = 某类内容节点的编辑器
|
||
|
||
### 6.4 与 AI 的关系
|
||
|
||
AI 不应直接面对“页面壳”和“前端控件”,而应直接面对 kernel。
|
||
|
||
长期上 AI 更适合操作:
|
||
|
||
- 节点
|
||
- 子树
|
||
- 引用边
|
||
- 结构索引
|
||
- 节点摘要
|
||
|
||
例如:
|
||
|
||
- 创建节点
|
||
- 拆分章节为子树
|
||
- 为节点补 refs
|
||
- 生成 summary 节点
|
||
- 把 PDF 章节树挂到 book 节点下
|
||
|
||
这比“模拟导图 UI 操作”或“模拟 BlockNote 操作”更稳定。
|
||
|
||
### 6.5 与 RAG 的关系
|
||
|
||
RAG 不再只是:
|
||
|
||
- 文本块检索
|
||
|
||
而应演进为:
|
||
|
||
- 结构索引检索 + 正文证据回查
|
||
|
||
例如:
|
||
|
||
- 一本书先变成书籍节点
|
||
- 再生成章节树
|
||
- 再生成章节子树摘要
|
||
- 再用节点 refs 指回原始页码、附件、正文块
|
||
|
||
这样检索时可以:
|
||
|
||
1. 先命中结构层
|
||
2. 再下钻子树
|
||
3. 再回查原文证据
|
||
|
||
这正适合复杂文档。
|
||
|
||
---
|
||
|
||
## 7. 结合 BookRAG 的启发
|
||
|
||
用户提到的 `BookRAG` 给出的核心启发,不是“做一个导图页面”,而是:
|
||
|
||
> **复杂文档应该先被抽成层级结构索引,再做检索与生成。**
|
||
|
||
这和 mnote 非常契合。
|
||
|
||
### 7.1 书籍对象的建议形态
|
||
|
||
长期上可以采用:
|
||
|
||
- 一个 `book` 节点
|
||
- 一个 canonical 章节树
|
||
- 每章是一个 subtree
|
||
- 每小节是更深层节点
|
||
- 节点 refs 指向:
|
||
- 页码
|
||
- PDF
|
||
- 原文块
|
||
- 摘要
|
||
- AI 生成说明
|
||
|
||
### 7.2 不建议直接复制很多份 chapter mindmap 实体
|
||
|
||
更好的方式是:
|
||
|
||
- 逻辑上是一棵 canonical tree
|
||
- 章节导图只是 subtree projection
|
||
- 需要时再做缓存或派生视图
|
||
|
||
否则会出现:
|
||
|
||
- 多份结构副本
|
||
- 同步成本
|
||
- 版本冲突
|
||
|
||
### 7.3 这和当前 Rust Mindmap 协议是兼容的
|
||
|
||
当前 Rust 已经有:
|
||
|
||
- `MindmapTreeNode`
|
||
- `MindmapOp`
|
||
- `mindmap_get_subtree`
|
||
- `mindmap_outline_to_mindmap`
|
||
|
||
这意味着:
|
||
|
||
- 以树为真相
|
||
- 以子树为检索和投影单位
|
||
|
||
并不是从零开始。
|
||
|
||
---
|
||
|
||
## 8. 这条路线和“主 Mindmap”有什么本质差异
|
||
|
||
两者差异很大。
|
||
|
||
### 8.1 错误路线
|
||
|
||
错误路线是:
|
||
|
||
- 整个工作区变成一个超级导图页面
|
||
- 所有东西都围绕导图控件组织
|
||
- UI 形态决定对象语义
|
||
|
||
### 8.2 正确路线
|
||
|
||
正确路线是:
|
||
|
||
- 整个工作区共享一份结构内核
|
||
- Mindmap 只是可视化挂件
|
||
- 列表、树表、阅读流、搜索结果也都是合法投影
|
||
- 对象语义先于 UI 形态存在
|
||
|
||
---
|
||
|
||
## 9. 长期分层建议
|
||
|
||
## 9.1 Kernel Layer
|
||
|
||
Rust 内核负责:
|
||
|
||
- typed node
|
||
- typed edge
|
||
- subtree query
|
||
- graph traversal
|
||
- projection query
|
||
- 权限
|
||
- trace
|
||
- 版本
|
||
- 审计
|
||
|
||
## 9.2 Service Layer
|
||
|
||
Rust Web 层负责:
|
||
|
||
- API
|
||
- SSR 页面壳
|
||
- SSE / WS
|
||
- 结构查询
|
||
- AI bridge
|
||
- projection 请求分发
|
||
|
||
## 9.3 Projection Layer
|
||
|
||
不同前端视图负责:
|
||
|
||
- Sidebar tree projection
|
||
- 阅读页 projection
|
||
- Mindmap projection
|
||
- 搜索 projection
|
||
- AI 操作 projection
|
||
|
||
## 9.4 Editor Layer
|
||
|
||
编辑器只是挂件:
|
||
|
||
- BlockNote
|
||
- Mindmap canvas
|
||
- OnlyOffice
|
||
- 未来别的专用编辑器
|
||
|
||
它们都不再是系统底座。
|
||
|
||
---
|
||
|
||
## 10. 为什么这条路线更适合替代 BlockNote 世界
|
||
|
||
因为它不是“再造一个更大的前端编辑器”,而是:
|
||
|
||
- 先把页面结构、对象结构和引用结构收口
|
||
- 再让 BlockNote 退化成一个专用内容编辑挂件
|
||
|
||
长期上,页面不再被定义成:
|
||
|
||
- 一个 block 文档
|
||
|
||
而更接近:
|
||
|
||
- 一个子树容器
|
||
|
||
这样未来才可能逐步实现:
|
||
|
||
- 页面结构独立于 BlockNote
|
||
- 页面中的某些内容节点仍可用 BlockNote 编辑
|
||
- 某些结构节点则改用别的编辑/操作方式
|
||
|
||
这比一次性整体替掉 BlockNote 更现实。
|
||
|
||
---
|
||
|
||
## 11. 风险与约束
|
||
|
||
### 11.1 不要把整个 workspace 真存成一条超大 JSON 树
|
||
|
||
逻辑上统一成一棵树,不等于物理上只能是一条大对象。
|
||
|
||
长期更合理的是:
|
||
|
||
- 逻辑统一
|
||
- 物理分片
|
||
- 子树加载
|
||
- 局部版本
|
||
- 局部缓存
|
||
|
||
### 11.2 不要让 Projection 反向定义内核
|
||
|
||
例如:
|
||
|
||
- Mindmap 控件的数据格式
|
||
- BlockNote 的块数据结构
|
||
- Sidebar 某次渲染需要的 rows
|
||
|
||
这些都不能反过来定义 kernel 真相。
|
||
|
||
### 11.3 不要过早把所有内容节点都树化成同一种文本节点
|
||
|
||
结构树适合表达:
|
||
|
||
- 层级
|
||
- 目录
|
||
- 引用
|
||
- 摘要
|
||
- 索引
|
||
|
||
但富文本正文仍然可能需要自己的内容模型。
|
||
|
||
所以长期更合理的是:
|
||
|
||
- 树/图内核负责结构
|
||
- 内容节点负责正文
|
||
- 两者通过 typed node 接口连接
|
||
|
||
---
|
||
|
||
## 12. 最终结论
|
||
|
||
最终结论可以固定成下面这句话:
|
||
|
||
> **mnote 的长期方向不是 Mindmap-first,而是 Tree-First Graph Kernel;Mindmap 只是其中一种挂件、投影和操作器。**
|
||
|
||
这意味着:
|
||
|
||
- 页面树、文件树、Mindmap、RAG 结构索引、AI 结构操作,本质上都应收口到同一结构内核
|
||
- `BlockNote` 不再是系统定义页面的唯一方式
|
||
- Rust 最终不只是承接 API 或导图对象,而是承接整个统一结构真相
|
||
|
||
如果后续继续推进,真正该优先做的不是“先重写导图 UI”,而是:
|
||
|
||
1. 定义统一 kernel node / edge 模型
|
||
2. 定义 subtree / projection / reference 查询协议
|
||
3. 让 Sidebar、搜索、AI、Mindmap 开始直接消费 kernel
|
||
4. 最后再逐步边缘化 `BlockNote`
|