对齐 Wolai 侧栏体验并收拢设计入库

This commit is contained in:
lix-2026
2026-04-30 16:18:54 +08:00
parent 8c895b3dc0
commit afb2a5b8a0
89 changed files with 23188 additions and 84 deletions
+57
View File
@@ -0,0 +1,57 @@
# Rust Design Index
更新时间:2026-04-13
适用范围:`/mnt/Data1T/mnote/rust/design`
---
## 1. 当前状态
当前目录只承载已经复制进 `/mnt/Data1T/mnote/rust/` 的核心稳定设计,用于支撑单仓收口后的 Rust 内核落位。
当前已复制:
- `core/01-domain-model-v0.md`
- `core/02-command-query-tool-protocol-v0.md`
- `core/03-storage-event-indexing-v0.md`
- `core/04-onlyoffice-integration-boundary-v0.md`
这些文档对应当前已复制进 `/mnt/Data1T/mnote/rust/crates/` 的 P0 crate
- `core-domain`
- `core-protocol`
- `event-log`
- `storage-convex-bridge`
- `index-fts`
---
## 2. 阅读顺序
后续在 `mnote` 主仓处理 Rust 内核时,建议按以下顺序阅读:
1. `core/01-domain-model-v0.md`
2. `core/02-command-query-tool-protocol-v0.md`
3. `core/03-storage-event-indexing-v0.md`
4. `core/04-onlyoffice-integration-boundary-v0.md`
---
## 3. 当前不在本目录的历史资料
以下资料仍保留在 `/mnt/Data1T/mnote-rust/design/`,当前作为参考,不属于本次第一阶段强制复制范围:
- `blueprint/`
- `phases/`
- `execution/`
- `UI/`
如果后续需要继续把历史阶段文档并入主仓,应在复制前先做去历史化整理,避免把旧阶段叙事原样带入 `mnote` 主仓。
---
## 4. 约定
- `/mnt/Data1T/mnote/design/` 继续承担全局架构与跨系统路线说明。
- `/mnt/Data1T/mnote/rust/design/` 只承载 Rust 内核专属设计。
- 新增 Rust 设计文档时,优先补充这里的索引,再补充 `/mnt/Data1T/mnote/design/old/08-legacy-rust-kernel/process/rust-kernel-backport-plan.md` 的实施状态。
+665
View File
@@ -0,0 +1,665 @@
# 01. Domain Model v0
更新时间:2026-04-11
适用范围:`/mnt/Data1T/mnote-rust`
---
## 1. 目标
本文件定义 `mnote-rust` 的**核心领域模型**。
它要解决的问题不是“前端怎么渲染”,而是:
- 系统里到底有哪些一等对象
- 哪些对象是事实层真相,哪些只是派生视图
- AI / CLI / Web / Desktop 应该围绕什么稳定对象工作
- OnlyOffice、Mindmap、OCR、RAG 这类能力应挂在哪一层
本文件优先保证:
- 长期稳定
- 脱离 UI 依赖
- 适合 Rust 内核实现
- 适合 AI 通过结构化协议读写
---
## 2. 建模原则
### 2.1 事实与视图分离
以下对象属于**事实层**
- Workspace
- Page
- Block
- Asset
- Reference
- Task
- CommandLog
- Event
- AgentSession
以下对象默认属于**派生层 / 视图层**:
- 目录(TOC
- 反链聚合
- 搜索结果列表
- Mindmap 视图树
- OnlyOffice 当前光标/当前页/当前选区
- AI 面板当前消息列表 UI 状态
原则:
> 派生层可以重建;事实层必须稳定、可审计、可迁移。
### 2.2 人与 AI 共用同一套领域对象
不能做人类一套模型、AI 一套模型。
要求:
- 人类编辑页面,本质是修改 `Page / Block / Asset / Reference`
- AI 编辑页面,本质也是修改同样的对象
- CLI 批处理、同步任务、导入器也修改同样的对象
### 2.3 第三方编辑器不是事实源
Block editor、OnlyOffice、mindmap editor 都不是系统真相。
它们只能是:
- 某类对象的视图/交互适配器
- 输入输出变换器
- 外部文档能力宿主
### 2.4 结构化修改优先
优先通过:
- 显式对象
- 显式字段
- 显式命令
- 显式 ops
避免通过:
- UI 状态猜测
- 整文全文字符串替换
- 第三方编辑器内部瞬时结构直接落库
---
## 3. 对象总览
```text
Workspace
├─ Page
│ ├─ DocumentBody
│ │ └─ Block*
│ ├─ PageProperty*
│ ├─ PageLink*
│ └─ Snapshot*
├─ Asset*
├─ Task*
├─ AgentSession*
├─ CommandLog*
└─ Event*
Reference
├─ block -> block
├─ block -> page
├─ block -> asset-fragment
├─ page -> asset
└─ task -> page/block/asset
```
---
## 4. Workspace
`Workspace` 是系统的顶层容器。
建议字段:
- `workspace_id`
- `slug`
- `title`
- `created_at`
- `updated_at`
- `owner_actor_id`
- `default_locale`
- `storage_policy`
- `sync_policy`
- `feature_flags`
- `archived_at`
职责:
- 隔离页面、资产、任务、日志
- 挂载存储策略与同步策略
- 作为权限边界与导出边界
不负责:
- 具体页面内容
- UI 布局
- 当前用户会话态
---
## 5. Page
`Page` 是笔记系统的主内容容器。
### 5.1 Page 的定位
它不是“某个编辑器文件”,而是:
- 一条知识对象
- 一个文档入口
- 一个块树正文容器
- 一个可被引用、索引、审计的实体
### 5.2 建议字段
- `page_id`
- `workspace_id`
- `parent_page_id`(允许页面树)
- `title`
- `slug`
- `icon`
- `cover_asset_id`
- `body_root_block_id`
- `page_type``note | doc | database_record | inbox | template | system`
- `status``active | archived | deleted`
- `created_at`
- `updated_at`
- `last_edited_at`
- `last_edited_by`
- `current_revision`
### 5.3 PageProperty
页面属性不应和前端表格视图绑死。
建议独立对象:
- `property_key`
- `value_type`
- `value`
- `display_hint`
- `source`
可用于:
- 标签
- 时间
- 状态
- 优先级
- 自定义结构化元数据
---
## 6. DocumentBody 与 Block
### 6.1 为什么以 Block 为正文主模型
因为从既有 `mnote` / `mnote-next` 的稳定人层语义看,真正稳定的是:
- 单块编辑
- 在某块后插入块
- 删除块
- 重排块
- 缩进层级
- 页面引用与块引用占位
- 复杂块占位
这说明“块”比“整篇富文本 JSON”更接近系统真实操作单元。
### 6.2 Block 建议字段
- `block_id`
- `workspace_id`
- `page_id`
- `parent_block_id`
- `prev_block_id`
- `next_block_id`
- `sort_key`
- `block_type`
- `content`
- `props`
- `annotations`
- `status`
- `created_at`
- `updated_at`
- `created_by`
- `updated_by`
- `revision`
### 6.3 BlockType 建议
最小集合建议:
- `paragraph`
- `heading`
- `bulleted_list_item`
- `numbered_list_item`
- `todo`
- `quote`
- `code_block`
- `divider`
- `callout`
- `page_reference`
- `block_reference`
- `embed_asset`
- `embed_view`
- `embed_onlyoffice`
- `embed_mindmap`
- `embed_table`
- `system_placeholder`
原则:
- 类型应描述语义,不描述某个前端组件名
- `embed_*` 表示“外部能力挂件”,不是事实层自己变成那个系统
### 6.4 Block.content
建议:
- 文本类块使用结构化 inline span 序列
- 避免只存单纯 HTML
- 避免直接依赖第三方编辑器私有 schema
建议形态:
```json
{
"spans": [
{ "type": "text", "text": "hello" },
{ "type": "page_ref", "page_id": "page_xxx", "title": "项目计划" },
{ "type": "text", "text": " world" }
]
}
```
### 6.5 Block.props
只放结构化、有限、可校验字段,例如:
- `level`
- `checked`
- `collapsed`
- `indent`
- `language`
- `align`
- `width`
- `height`
- `asset_id`
- `view_id`
避免把任意 UI 状态塞进 props。
---
## 7. Asset
`Asset` 是所有非正文主块树内容的统一挂载对象。
### 7.1 Asset 范围
包括:
- 图片
- PDF
- docx
- xlsx
- pptx
- 音频
- 视频
- 导出文件
- OCR 中间产物
- 结构提取结果
### 7.2 建议字段
- `asset_id`
- `workspace_id`
- `storage_key`
- `original_name`
- `mime_type`
- `size_bytes`
- `checksum`
- `origin`upload / import / generated / synced
- `asset_kind`image / pdf / office / audio / video / binary / extracted_text
- `created_at`
- `updated_at`
- `created_by`
- `status`
- `latest_version_id`
### 7.3 AssetVersion
附件应支持版本化。
建议:
- `asset_version_id`
- `asset_id`
- `version_no`
- `storage_key`
- `checksum`
- `derived_from_version_id`
- `created_at`
- `created_by`
- `change_reason`
这样才能支撑:
- OnlyOffice 编辑回写
- OCR 重跑
- 导入转换
- AI 修改资产派生内容
---
## 8. Reference
`Reference` 是知识型系统的关键对象,不能只做正文里的临时 token。
### 8.1 Reference 范围
- 块引用块
- 页面引用
- 资产片段引用
- PDF 页码引用
- Office 书签/段落锚点引用
- URL 引用
- 检索结果引用
### 8.2 建议字段
- `reference_id`
- `workspace_id`
- `source_object_type`
- `source_object_id`
- `target_object_type`
- `target_object_id`
- `anchor`
- `label`
- `snippet`
- `confidence`
- `created_at`
- `created_by`
- `ref_kind`
### 8.3 Anchor
`anchor` 建议作为独立结构:
```json
{
"kind": "pdf_page",
"page": 12,
"bbox": null,
"text_quote": "..."
}
```
或:
```json
{
"kind": "office_bookmark",
"bookmark": "Heading_3",
"text_quote": "..."
}
```
原则:
- 锚点是系统对象,不是编辑器私有游标
- 锚点失效时要能标记 stale,而不是静默消失
---
## 9. SelectionAnchor
`SelectionAnchor` 不是长期事实主对象,但在 Agent / 编辑器桥接中有价值。
它表示:
- 当前选区
- 当前光标
- 当前活动页
- 当前资产中的定位点
建议定位为:
- **短生命周期上下文对象**
- 可存入 AgentSession / UI Session
- 默认不直接作为主事实落库
因为:
- 它变化太频繁
- 容易和编辑器宿主耦合
- 更适合作为命令输入上下文
---
## 10. Task
`Task` 既是用户任务,也是 AI 工作单元。
### 10.1 建议字段
- `task_id`
- `workspace_id`
- `title`
- `description`
- `task_type`
- `status`
- `priority`
- `assignee_actor_id`
- `source_page_id`
- `source_block_id`
- `input_payload`
- `output_payload`
- `created_at`
- `updated_at`
- `due_at`
- `completed_at`
### 10.2 为什么纳入领域模型
因为 AI 长期介入后,很多能力不是即时编辑,而是:
- 导入任务
- 提取任务
- OCR 任务
- 摘要任务
- 索引重建任务
- 同步任务
这些都应进入系统内核,而不是散在外部脚本里。
---
## 11. AgentSession
`AgentSession` 表示某次 AI 介入的上下文单元。
### 11.1 建议字段
- `agent_session_id`
- `workspace_id`
- `provider`
- `model`
- `initiator_actor_id`
- `status`
- `started_at`
- `updated_at`
- `ended_at`
- `tool_policy`
- `confirmation_policy`
- `summary`
### 11.2 不应存什么
不应把整份聊天 UI 状态直接当作领域模型核心。
建议分开:
- 领域层只保留会话元信息、工具调用摘要、事件链路
- 详细消息可作为附属日志或导出工件保存
---
## 12. CommandLog 与 Event
### 12.1 CommandLog
代表一次显式操作请求。
建议字段:
- `command_id`
- `workspace_id`
- `command_name`
- `actor`
- `source`
- `target`
- `payload_hash`
- `reason`
- `refs`
- `idempotency_key`
- `dry_run`
- `status`
- `requested_at`
- `finished_at`
- `error_code`
- `error_message`
### 12.2 Event
代表命令执行后产生的事实变化。
建议字段:
- `event_id`
- `workspace_id`
- `command_id`
- `event_type`
- `object_type`
- `object_id`
- `before_revision`
- `after_revision`
- `payload`
- `created_at`
原则:
- 命令与事件分离
- 先有意图,再有变化
- 审计必须能回答“谁因为什么改了什么”
---
## 13. View / Derived Object
以下对象建议明确标注为派生对象,不直接当真相:
- TOCView
- BacklinkView
- SearchHit
- KnowledgeGraphView
- MindmapProjection
- OnlyOfficeProjection
- PagePreview
- DailyDigest
它们的共同特点:
- 来自事实层投影
- 可以缓存
- 可以失效
- 可以重建
---
## 14. Mindmap 的正确定位
从既有设计看,Mindmap 很适合走结构化 `ops` 协议。
但在 `mnote-rust` 中,建议将其定义为:
- `Asset``ViewProjection` 的一种
-`Page / Block / Reference / Asset` 投影生成或承载
- 支持独立存储其节点视图结构
- 但不让它成为全系统唯一知识主模型
建议:
- 若是“页面内导图块”,则作为 `embed_mindmap` block 挂载
- 若是“独立导图资产”,则作为 `Asset(kind=mindmap)`
- AI 修改导图时,走 `MindmapOp[]`,但最终仍写入系统命令日志
---
## 15. OnlyOffice 的正确定位
OnlyOffice 不应被建模为“Page 本体”,应被建模为:
- `Asset(kind=office)`
- `AssetVersion`
- `OnlyOfficeAdapterSession`
- `SelectionAnchor(kind=office_*)`
- `Reference(anchor=office_*)`
也就是说:
- Office 文档是系统资产
- OnlyOffice 是该资产的一种编辑适配器
- 编辑产生新版本与事件
- AI 可以通过插件/适配器读当前选区、插入内容、替换内容
- 但系统真相依然在你的领域模型与资产版本链里
---
## 16. v0 必须避免的错误
### 16.1 不要把前端 store 当领域模型
例如:
- 当前页面 UI 选中态
- 面板展开态
- 聊天输入框草稿
这些不应进入核心领域。
### 16.2 不要把第三方编辑器 JSON 当系统法典
否则未来:
- 替换编辑器会非常痛苦
- AI 只能学编辑器私有结构
- CLI 难以稳定操作
### 16.3 不要把全文字符串替换当主修改方式
必须保留结构化修改能力。
### 16.4 不要把向量库结果当事实对象
向量结果是检索派生物,不是知识真相。
---
## 17. 一句话结论
`mnote-rust` 的领域模型应以 **Workspace / Page / Block / Asset / Reference / Task / AgentSession / CommandLog / Event** 为核心;
Mindmap、OnlyOffice、搜索结果、AI 面板都只是围绕这些核心对象形成的投影、适配器或任务系统,而不能反客为主。
@@ -0,0 +1,717 @@
# 02. Command / Query / Tool Protocol v0
更新时间:2026-04-11
适用范围:`/mnt/Data1T/mnote-rust`
---
## 1. 目标
本文件定义 `mnote-rust` 的统一协议面。
目标只有一个:
> 让 Web、Desktop、CLI、Agent、批处理任务都通过**同一套正式入口**操作系统。
它要解决的问题:
- 怎样避免“每个页面、每个组件、每个 API route 都有自己的写法”
- 怎样让 AI 不依赖 UI 模拟,而能直接操作笔记软件
- 怎样保证命令可审计、可回放、可测试、可维护
---
## 2. 基本原则
### 2.1 写与读必须分离
- **Command**:会改变事实层
- **Query**:只读,不改变事实层
- **Tool**:面向 AI / CLI 的能力暴露层,内部映射到 Command / Query / Job
### 2.2 Tool 不是第三套业务逻辑
禁止出现:
- Web 走一套逻辑
- CLI 走一套逻辑
- AI Tool 再写一套逻辑
正确关系:
```text
Tool -> Command / Query / Job -> Domain + Storage + EventLog
```
### 2.3 每条写命令都要可审计
至少要记录:
- 谁发起
- 从哪里发起
- 改了什么
- 为什么改
- 影响了哪些对象
- 是否成功
### 2.4 AI 默认只能调用系统工具
AI 不应默认获得:
- 任意 SQL
- 任意文件写入
- 任意 UI 按钮点击
- 任意内部未定型方法
AI 应调用:
- 稳定的 Tool API
- 明确的参数模型
- 明确的权限模型
---
## 3. 三层协议面
```text
Human UI / CLI / Agent Runtime
Tool API
┌───────┴────────┐
▼ ▼
Query API Command API
│ │
└───────┬────────┘
Job / Workflow API
Rust Note Core
```
说明:
- Tool API 是面向使用者的稳定能力面
- Command / Query API 是内核的正式操作面
- Job / Workflow API 负责长任务、异步流程、外部能力编排
---
## 4. Actor 模型
所有命令、查询、工具调用都应带 `actor`
建议结构:
```json
{
"actor": {
"type": "human",
"id": "user_123",
"session_id": "ui_session_xxx"
}
}
```
或:
```json
{
"actor": {
"type": "agent",
"id": "agent_session_456",
"provider": "openai",
"model": "gpt-5.4-mini"
}
}
```
或:
```json
{
"actor": {
"type": "system",
"id": "job_runner"
}
}
```
用途:
- 审计
- 权限判断
- 限流
- 错误追踪
---
## 5. Command API
## 5.1 通用结构
建议所有命令统一壳:
```json
{
"command": "insert_block",
"command_id": "cmd_01",
"idempotency_key": "idem_01",
"actor": {
"type": "agent",
"id": "agent_session_1"
},
"source": {
"channel": "tool",
"client": "mcp"
},
"target": {
"workspace_id": "ws_1",
"page_id": "page_1"
},
"payload": {},
"reason": "补充总结段落",
"refs": ["ref_1"],
"dry_run": false,
"validate_only": false,
"requested_at": "2026-04-11T12:00:00Z"
}
```
### 5.2 通用返回
```json
{
"ok": true,
"command_id": "cmd_01",
"event_ids": ["evt_1", "evt_2"],
"affected_objects": [
{ "type": "block", "id": "block_123" }
],
"revision": {
"workspace_id": "ws_1",
"page_id": "page_1",
"value": 42
},
"warnings": [],
"rollback_hint": {
"kind": "compensating_command",
"command": "delete_block",
"target_id": "block_123"
}
}
```
### 5.4 观测字段约束
从 task-034 开始,所有正式 `Command / Query / Tool / Job` 面都要默认带上并统一解释下面这些字段:
- `request_id`:一次入口请求级别的稳定编号,用于串起 route、runtime、日志和回查接口
- `trace_id`:一次完整调用链的稳定编号,用于跨 query / command / tool / job 串联同一条执行路径
- `command_id`:写命令的唯一编号,也是 `command_log_id``event_id` 推导的基础
- `idempotency_key`:CLI、AI、Web 共享的幂等语义主键,不能三端各自解释
- `command_log_id` / `event_id`:进入审计与回放链后的稳定对象编号,供 request/trace/command 回查与恢复命令复用
如果某条能力不能带出这组字段,它就还不能算“可供 CLI 与 AI 稳定运行的正式协议面”。
### 5.3 命令分类
#### 页面类
- `create_page`
- `rename_page`
- `move_page`
- `archive_page`
- `restore_page`
- `set_page_property`
#### 块类
- `insert_block`
- `update_block`
- `delete_block`
- `move_block`
- `batch_apply_block_ops`
- `replace_block_content`
#### 引用类
- `create_reference`
- `remove_reference`
- `rebind_reference_anchor`
#### 资产类
- `attach_asset`
- `replace_asset_version`
- `set_asset_metadata`
- `extract_asset_outline`
#### 工作区 / 系统类
- `create_task`
- `cancel_task`
- `rebuild_search_index`
- `run_import_job`
- `run_export_job`
---
## 6. Command 设计约束
### 6.1 命令应小而明确
优先:
- `insert_block`
- `move_block`
- `set_page_property`
避免:
- `save_everything`
- `update_editor_state`
- `mutate_page_with_ui_payload`
### 6.2 支持幂等键
AI 可能重试、网络可能重放,因此:
- 所有外部写命令建议支持 `idempotency_key`
- 同一作用域内重复提交应可去重
### 6.3 支持 dry-run / validate-only
这样 AI 可以先问:
- 这个操作是否合法?
- 会影响哪些对象?
- 是否需要确认?
这对 Agent 很重要。
### 6.4 支持批量 ops,但要有边界
对于 Mindmap、结构调整、导入转换,可以提供:
- `batch_apply_block_ops`
- `apply_mindmap_ops`
- `apply_document_transform_ops`
但要求:
- ops 必须结构化
- 单条失败策略明确
- 返回逐条结果
---
## 7. Query API
## 7.1 通用结构
```json
{
"query": "page.get",
"actor": {
"type": "human",
"id": "user_1"
},
"scope": {
"workspace_id": "ws_1"
},
"params": {
"page_id": "page_1"
}
}
```
### 7.2 查询分类
#### 页面与块
- `page.get`
- `page.tree`
- `page.list_children`
- `block.get`
- `block.subtree`
#### 检索
- `search.text`
- `search.reference`
- `search.asset`
- `search.page_by_title`
#### 资产
- `asset.get`
- `asset.list_versions`
- `asset.resolve_anchor`
#### 日志与会话
- `command_log.list`
- `command_log.get`
- `event.list`
- `agent_session.get`
- `task.list`
### 7.3 输出要求
- 输出稳定、字段可预期
- 支持分页、游标、过滤
- 避免直接返回前端组件所需临时形态
- 对大型对象支持摘要与展开模式
---
## 8. Tool API
## 8.1 Tool API 的职责
Tool API 是给:
- AI Agent
- CLI
- 自动化脚本
- 外部集成方
用的能力层。
它的原则是:
- 工具名清晰
- 参数结构明确
- 返回格式稳定
- 明确确认策略与权限策略
## 8.2 工具命名风格
建议按领域分组:
- `note.read_page`
- `note.list_children`
- `note.insert_block`
- `note.update_block`
- `note.move_block`
- `note.search`
- `asset.attach_file`
- `asset.extract_outline`
- `office.get_selection`
- `office.replace_selection`
- `mindmap.apply_ops`
- `system.get_recent_commands`
也可以对外映射为 MCP 风格扁平命名,但内核层建议保留层级语义。
## 8.3 Tool 与 Command/Query 的映射
例如:
- `note.read_page` -> `page.get`
- `note.insert_block` -> `insert_block`
- `note.search` -> `search.text`
- `asset.attach_file` -> `attach_asset`
- `mindmap.apply_ops` -> `apply_mindmap_ops`
### 8.4 Tool 返回
建议统一包含:
- `ok`
- `data`
- `warnings`
- `requires_confirmation`
- `confirmation_reason`
- `next_actions`
例如:
```json
{
"ok": true,
"data": {
"page_id": "page_1",
"title": "项目计划"
},
"warnings": [],
"requires_confirmation": false,
"next_actions": []
}
```
---
## 9. Job / Workflow API
有些操作不是同步命令,而是长任务。
例如:
- 导入大型 PDF
- OCR
- 构建索引
- 批量转换文档
- Office 文件解析
- 全库引用修复
- AI 长链路整理
因此需要 `Job API`
### 9.1 Job 通用结构
```json
{
"job": "import_document",
"job_id": "job_1",
"actor": { "type": "agent", "id": "agent_1" },
"input": {
"asset_id": "asset_1"
}
}
```
### 9.2 Job 状态
- `queued`
- `running`
- `waiting_confirmation`
- `succeeded`
- `failed`
- `cancelled`
### 9.3 Job 输出
- 结果对象
- 生成的 page / asset / reference
- 日志摘要
- 错误详情
---
## 10. Confirmation Policy
不是所有工具都应直接执行。
建议分级:
### 10.1 无需确认
- 只读查询
- 本地摘要生成
- 小范围插入草稿块
### 10.2 建议确认
- 批量删除
- 批量重排
- 覆盖资产版本
- 导出到外部位置
### 10.3 强制确认
- 大范围页面删除
- 全库重建/迁移
- 覆盖性导入
- 对外同步/发布
确认策略应由内核或 policy 层判断,不能完全由前端决定。
---
## 11. Error Model
错误必须结构化。
建议格式:
```json
{
"ok": false,
"error": {
"code": "BLOCK_NOT_FOUND",
"message": "目标块不存在",
"details": {
"block_id": "block_xxx"
},
"retryable": false,
"suggested_action": "请先刷新页面树或重新读取块子树"
}
}
```
分类建议:
- `VALIDATION_ERROR`
- `PERMISSION_DENIED`
- `NOT_FOUND`
- `CONFLICT`
- `STALE_REVISION`
- `RATE_LIMITED`
- `EXTERNAL_ADAPTER_ERROR`
- `INTERNAL_ERROR`
这样 AI 才能根据错误做下一步,而不是只看到一段字符串。
---
## 12. Revision / Concurrency
### 12.1 为什么必须有 revision
AI 与人可能同时修改。
如果没有 revision
- 很容易覆盖彼此内容
- 工具重试会写乱
- 无法做冲突判断
### 12.2 建议
- `Page` 级 revision
- `Block` 级 revision
- `AssetVersion` 级 version_no
命令可支持:
- `expected_revision`
- `on_conflict` 策略(fail / merge_if_possible / create_patch
---
## 13. OnlyOffice 协议边界
OnlyOffice 不应直接暴露为“让 AI 操控 iframe”。
建议拆成两层:
### 13.1 Office Adapter Query
- `office.get_active_asset`
- `office.get_selection`
- `office.get_current_page`
- `office.list_bookmarks`
### 13.2 Office Adapter Command
- `office.insert_text`
- `office.replace_selection`
- `office.insert_comment`
- `office.save_as_new_version`
- `office.extract_outline`
原则:
- Tool 面暴露的是“Office 文档能力”
- 不是浏览器点击坐标或 iframe DOM
- 若插件不可用,应返回结构化不可用错误
---
## 14. Mindmap 协议边界
继续保留既有设计里最有价值的部分:`ops`
建议:
- `mindmap.get`
- `mindmap.apply_ops`
- `mindmap.expand_node`
- `mindmap.attach_refs`
其中 `mindmap.apply_ops` 入参可以继续沿用:
- `addChild`
- `addSiblingAfter`
- `updateText`
- `setHyperlink`
- `setRefs`
- `appendNote`
- `deleteNode`
但要求:
- 所有调用都写入统一命令日志
- mindmap 不再走单独一套野生落盘体系
---
## 15. CLI 协议面
CLI 不是开发附属品,而是系统正式壳层。
建议 CLI 优先覆盖:
- `mnote page get <page-id>`
- `mnote page tree`
- `mnote block insert`
- `mnote block move`
- `mnote search text`
- `mnote asset attach`
- `mnote office selection`
- `mnote job run import-document`
- `mnote log recent`
CLI 应满足:
- 输出 JSON 模式
- 退出码稳定
- 适合 shell / AI / 自动化脚本调用
---
## 16. MCP / Agent Runtime 暴露面
如果后续提供 MCP
- MCP 工具层应直接包装 Tool API
- 不应重新发明一套独立业务逻辑
- 返回值尽量和 CLI JSON 输出接近
这样好处是:
- Web agent、桌面 agent、本地 Hermes/Codex 都共用一套系统能力
- 文档里只需维护一份工具协议
---
## 17. 最小 v0 清单
建议 v0 最先固化以下协议:
### Command
- `create_page`
- `rename_page`
- `insert_block`
- `update_block`
- `delete_block`
- `move_block`
- `attach_asset`
- `create_reference`
### Query
- `page.get`
- `page.tree`
- `block.subtree`
- `search.text`
- `asset.get`
- `command_log.list`
### Tool
- `note.read_page`
- `note.insert_block`
- `note.update_block`
- `note.move_block`
- `note.search`
- `asset.attach_file`
- `system.get_recent_commands`
这套足以先打通:
- 人类基础编辑
- CLI 基础操作
- AI 基础介入
- 审计与回放链路
---
## 18. 一句话结论
`mnote-rust` 的协议设计应坚持:
> **Command 负责改,Query 负责读,Tool 负责给 AI/CLI 用,Job 负责长流程;所有入口最终收敛到同一 Rust 内核,不再允许页面私有接口、编辑器私有接口、临时胶水接口长期并存。**
@@ -0,0 +1,622 @@
# 03. Storage / Event / Indexing v0
更新时间:2026-04-11
适用范围:`/mnt/Data1T/mnote-rust`
---
## 1. 目标
本文件定义 `mnote-rust` 在当前路线下的持久化、事件日志、索引体系。
它要回答:
- 当前系统事实层应落在哪里
- Rust 内核怎样接入既有事实层而不重复造库
- 写操作如何进入事件链路
- 搜索、引用、RAG、回放、审计依赖什么数据面
- 如何既保证性能,又保证 AI 可观测、可维护、可回放
这部分的前提必须先讲清:
> 对当前 `mnote` / `mnote-next` 主线来说,**Convex 仍是主事实层**。
因此,这份文档不再讨论“用 SQLite 取代 Convex 做主库”,而是讨论:
- 如何在 **Convex 主事实层** 之上建立 Rust 内核
- 如何把索引、事件、导出、离线缓存、本地处理组织清楚
- 如何避免再次引入第二套事实真相
如果这层设计不好,系统最终仍会退化成:
- 页面组件自己维护真相
- 各类能力各写各的缓存
- AI 看不到完整上下文
- Rust 内核与现有系统各管一套数据
- 回滚、审计、重建索引都变得困难
---
## 2. 总原则
### 2.1 主事实层、事件日志、索引必须分层
三者职责不同:
- **主事实层**:保存系统当前真相
- **事件日志**:保存“如何变成现在”的过程
- **索引层**:为检索、聚合、推荐、RAG 提供加速读模型
禁止让任一层越权:
- 不能拿索引当真相
- 不能只靠事件流而没有可直接读取的当前状态
- 不能让页面缓存成为事实层
- 不能让本地缓存演化成第二主库
### 2.2 当前主事实层应继续落在 Convex
基于 `mnote``mnote-next` 已有主线,当前阶段应坚持:
- **Convex 是唯一主事实层**
- Rust 内核通过协议与 bridge 接入 Convex,而不是绕开它再建一套主库
- 旧仓与新仓共享的核心对象,应优先复用既有 Convex schema / deployment / caller 体系
这不是保守,而是避免推翻已经跑通的主链路。
### 2.3 本地文件系统与本地嵌入式存储仍然重要
虽然主事实层继续用 Convex,但本地层仍然需要:
- **文件系统**:资产原文件、导出文件、缓存、派生产物、临时工作目录
- **可选本地嵌入式存储**:离线缓存、索引快照、dry-run、调试态数据、临时队列
但这些都不能升级为新的主事实层。
### 2.4 写入必须原子化到“主事实层 + 事件落账”
一次命令成功后,至少要保证:
- 主事实层写入完成
- 事件写入成功
- 命令日志有记录
索引允许异步追平,但必须可检测 lag。
### 2.5 索引必须可重建
搜索索引、向量索引、聚合视图都必须满足:
- 可从主事实层 + 事件重新构建
- 可做全量重建
- 可做增量追平
否则后期会不可维护。
---
## 3. 三层数据面
```text
[主事实层]
Convex + FileSystem
[事件与日志层]
CommandLog + DomainEvent + JobLog + AuditTrail
[索引与派生层]
FTS / Reference Graph / Backlink View / Outline View / Vector Index
```
### 3.1 主事实层负责
- Workspace / Page / Block / Asset / Reference / Task / AgentSession 当前状态
- 事务性写入
- 读取当前真相
- 提供稳定 schema
- 承担权限与主对象约束
### 3.2 事件与日志层负责
- 记录命令与事件
- 记录 actor、reason、scope、结果
- 支持回放、审计、问题追踪
- 作为索引增量更新输入
### 3.3 索引与派生层负责
- 全文搜索
- 页面大纲
- 反链
- 资产片段搜索
- RAG 检索读模型
- 推荐 / 关系聚合
---
## 4. 主事实层选型
## 4.1 当前阶段的明确结论
推荐:
- **Convex**:结构化主事实层
- **File System**:存原始资产与大对象
- **Rust bridge / protocol layer**:作为统一命令、查询、工具接入面
原因:
-`mnote` 中“Convex 已完全替换 Supabase”的现实一致
-`mnote-next` 中“默认复用现有 Convex deployment / project”的路线一致
- 复用现有 deployment、schema、自建与 AI 开发经验,避免重建第二套数据库主线
- 更符合“复用成熟能力,重构边界,不做无意义重建”的迁移原则
## 4.2 本地嵌入式存储的正确定位
可以保留本地嵌入式存储,但只限于:
- CLI / Agent 的离线缓存
- 全文索引引擎的本地数据文件
- dry-run / scaffold / 调试态数据
- 单机导入处理中的临时工作库
不能把它写成:
- 第二套主事实层
- 与 Convex 并列的正式写入真相
- 长期双写的核心业务库
## 4.3 不建议的中心方案
- 用 SQLite 取代 Convex 成为主真相
- OnlyOffice / 第三方编辑器内部状态作为主真相
- 仅事件溯源、无当前态表
- 单纯 Markdown 文件散落 + 大量 sidecar 作为唯一结构源
- 长期维护 Convex + SQLite 双事实源
这些都会让系统再次陷入边界混乱。
---
## 5. 主事实对象结构建议
这里讨论的是**领域对象结构**,不是要求新建第二套数据库。
## 5.1 结构原则
- 主对象必须能映射到现有 Convex schema
- 单对象一组稳定字段或有限关联表
- 避免过度 EAV
- JSON 仅用于局部扩展字段,不替代主 schema
## 5.2 建议主对象
至少包含:
- `workspaces`
- `pages`
- `page_versions`(可选)
- `blocks`
- `block_relations`(可选,若不全放在 block 表中)
- `assets`
- `asset_versions`
- `references`
- `tasks`
- `agent_sessions`
- `command_logs`
- `domain_events`
- `jobs`
- `job_logs`
这些对象应优先映射或扩展到现有 Convex 主线,而不是在新仓先落一套平行本地表。
## 5.3 blocks 对象建议
关键字段:
- `block_id`
- `workspace_id`
- `page_id`
- `parent_block_id`
- `sort_key`
- `block_type`
- `content_json`
- `props_json`
- `annotations_json`
- `revision`
- `deleted_at`
说明:
- `sort_key` 建议支持稀疏排序,避免频繁全量重排
- 软删除优于直接硬删,方便审计与恢复
- `revision` 用于乐观并发控制
## 5.4 assets 对象建议
- `asset_id`
- `workspace_id`
- `asset_kind`
- `mime_type`
- `current_version_id`
- `storage_strategy`
- `status`
- `metadata_json`
## 5.5 references 对象建议
- `reference_id`
- `source_object_type`
- `source_object_id`
- `target_object_type`
- `target_object_id`
- `anchor_json`
- `ref_kind`
- `status`
---
## 6. 文件系统布局建议
资产不要全塞数据库 blob。
建议:
```text
workspace-data/
├─ assets/
│ ├─ asset_xxx/
│ │ ├─ v1/original.docx
│ │ ├─ v2/original.docx
│ │ ├─ extracted/outline.json
│ │ ├─ extracted/text.md
│ │ └─ derived/preview.png
├─ indexes/
│ ├─ fts/
│ └─ vector/
├─ runtime/
│ ├─ jobs/
│ ├─ sessions/
│ └─ temp/
└─ export/
```
原则:
- 结构化真相仍以 Convex 为主
- 原始大文件入文件系统
- 派生产物有明确目录归属
- 临时文件与正式资产分离
---
## 7. Command Log
`CommandLog` 是所有写命令的正式记录。
### 7.1 最低字段
- `command_log_id`
- `command_name`
- `actor_type`
- `actor_id`
- `source`
- `workspace_id`
- `target_objects`
- `payload_summary`
- `refs_json`
- `idempotency_key`
- `status`
- `created_at`
- `finished_at`
### 7.2 设计要求
- 能关联到一次真实主写入
- 能关联后续 domain events
- 能关联 tool call / job / rollback
- 能为 AI 回放与人类审计提供最小闭包
### 7.3 与 Convex 的关系
命令日志可以:
- 直接进入 Convex 主线对象
- 或通过 bridge 落到与主对象同一事实层
但不要单独把命令日志只记在本地、主对象却记在远端;那会破坏统一审计链路。
---
## 8. Domain Event
`DomainEvent` 不是为了炫技,而是为了:
- 给索引层提供标准增量输入
- 给回放和审计提供结构化事件
- 给异步任务提供稳定订阅源
### 8.1 事件最低字段
- `event_id`
- `workspace_id`
- `aggregate_type`
- `aggregate_id`
- `event_type`
- `event_version`
- `payload_json`
- `command_log_id`
- `actor_type`
- `created_at`
### 8.2 事件边界
建议记录领域事件,而不是 UI 手势。
例如:
- `page_created`
- `page_renamed`
- `block_inserted`
- `block_moved`
- `block_content_replaced`
- `asset_version_added`
- `reference_created`
- `task_status_changed`
不建议记录:
- 弹窗打开
- 光标移动
- hover 展示
- 面板折叠
---
## 9. Job 与异步链路
不是所有事情都应进主事务。
应把这些放入 Job
- OCR
- OnlyOffice 提取/转换
- 向量切片与嵌入
- 全量重建索引
- 大文件导入
- 外部同步
### 9.1 Job 最低字段
- `job_id`
- `job_type`
- `workspace_id`
- `target_objects`
- `input_json`
- `status`
- `progress`
- `error`
- `created_at`
- `started_at`
- `finished_at`
### 9.2 不能放进主事务的内容
- 大模型推理
- OCR
- 大文件转换
- 向量重建
- 远程同步
这些必须走 Job。
---
## 10. 索引体系
## 10.1 Full Text Search
最低必做:
- 页面标题全文检索
- 块内容全文检索
- 资产提取文本全文检索
- 引用 snippet 检索
建议:
- 初期直接用本地全文索引引擎(如 SQLite FTS5、Tantivy 或兼容方案)
- 索引文档单位统一为“可定位对象”
例如:
- page
- block
- asset_fragment
- reference_snippet
### 10.2 为什么块级索引重要
因为你的产品最终不是“整篇文档搜索”,而是:
- 找到某一段
- 跳回块位置
- 让 AI 只编辑局部
- 给引用与上下文最小闭包
## 10.3 Reference Graph
必须维护引用图读模型:
- page -> page
- block -> block
- block -> asset fragment
- task -> source object
这对:
- 反链
- 影响面分析
- AI 上下文组装
- 知识导航
都很关键。
## 10.4 Outline View
页面大纲、Office 文档提取大纲、PDF 目录、Mindmap 树都应是派生读模型。
优点:
- 不污染主事实层
- 允许多种提取策略
- 容易重建
## 10.5 Vector Index
向量索引建议延后,但结构上预留。
原则:
- 向量索引只是检索加速层
- 向量 chunk 必须能回指 `page_id / block_id / asset_id / anchor`
- 向量索引失效可重建,不影响系统主真相
---
## 11. 索引更新策略
## 11.1 起步建议:异步增量 + 可全量重建
每次事件提交后:
- 生成索引任务
- 按对象粒度增量更新
- 记录 lag 与最后处理 event id
## 11.2 索引一致性要求
- 主事实层一致性 > 索引实时性
- 查询可返回“索引处理中”状态
- 对必须强一致的局部场景,可同步刷新小范围索引
## 11.3 失败恢复
索引 worker 挂了也不能影响主写入。
必须支持:
-`last_processed_event_id` 继续追平
- 指定对象重建
- 指定 workspace 全量重建
### 11.4 回放与索引重建的正式入口
从 task-034 开始,回放和重建不再只是“库里有个纯函数”,而要成为统一命令面的一部分。
最小要求:
- `event_replay`:从统一事件列表读取,输出 replay 后的 `cursor` 与受影响 event 摘要
- `index_rebuild`:基于 `last_processed_event_id` / `last_processed_at` 重建索引批次,并返回新的 cursor
- 两者都复用同一套 `request_id``trace_id``workspace_id``command_id` 语义,不允许临时脚本私有解释
这意味着后续无论是 CLI、AI 还是 Web 排障,都应先调用同一条 Rust 恢复命令面,而不是各自写一套索引补数脚本。
---
## 12. AI 可观测性
AI 全流程介入后,系统必须能回答:
- 某次回答引用了哪些对象
- 某次编辑基于哪些上下文
- 某个工具为什么失败
- 当前搜索索引是否滞后
- 哪些对象正在被长任务处理
因此建议额外维护:
### 12.1 AgentSession 关联表
- `agent_session_objects`
- `agent_session_tool_calls`
- `agent_session_outputs`
### 12.2 Tool Call Log
记录:
- tool name
- input
- output summary
- duration
- error
- command_log_id / query trace id
这样后期 AI 排障、回放、评估都会容易很多。
---
## 13. 性能关键点
### 13.1 不要整页全文重写
编辑块时,只更新相关块与局部派生视图。
### 13.2 不要同步重建全索引
任何全库重建都必须后台化。
### 13.3 资产处理走异步
OCR、Office 转换、预览图生成、向量切片都不能阻塞主编辑事务。
### 13.4 读写模型分离
高频 UI 读需求可通过派生视图优化,不要污染主事实 schema。
---
## 14. OnlyOffice 在存储层的正确位置
OnlyOffice 相关对象建议这样落地:
- 原始 `docx/xlsx/pptx/pdf`:作为 `Asset` + `AssetVersion`
- 文档编辑结果:生成新 `AssetVersion`
- 提取结构(目录、批注、书签、文本片段):作为派生读模型或 `Reference`
- 当前选区、当前页码:作为短生命周期 `SelectionAnchor`
不要把:
- OnlyOffice 内部文档状态
- 插件瞬时 UI 状态
- callback 的临时 payload
直接当成主事实层。
---
## 15. 迁移建议
先做:
1. 盘点现有 Convex schema 与 `mnote-rust` 领域对象的映射
2.`CommandLog / DomainEvent` 的主事实层方案
3.`Page / Block / Asset / Reference` 的 bridge / repo 接口
4. 建立全文索引最小实现
5. 建立索引 worker 骨架
再做:
6. AgentSession / ToolCallLog
7. Outline / Backlink 读模型
8. 向量索引占位
9. Office / OCR / Mindmap 派生索引
10. 必要的离线缓存 / dry-run 本地层
---
## 16. 一句话结论
`mnote-rust` 当前应采用 **Convex 主事实层 + FileSystem 资产存储 + CommandLog/DomainEvent 事件链路 + 可重建全文/引用/向量索引** 的分层结构;
Rust 内核负责把命令、查询、工具、索引、异步任务与外部能力重新组织清楚,而不是再造一套与现有主线并列的数据库事实源。
@@ -0,0 +1,491 @@
# 04. OnlyOffice Integration Boundary v0
更新时间:2026-04-11
适用范围:`/mnt/Data1T/mnote-rust`
---
## 1. 目标
本文件定义 `mnote-rust` 中 OnlyOffice 的正式边界。
它要解决的问题:
- OnlyOffice 在系统里到底是什么
- 可以接多深
- 哪些能力该放进插件/编辑器侧
- 哪些能力必须留在 Rust 内核
- 官方现状已经支持到哪一步,哪些不是猜想
---
## 2. 基于官方资料的确定事实
以下判断基于 ONLYOFFICE 官方文档与官方 API 文档,而不是推测。
### 2.1 ONLYOFFICE 已有官方 AI 插件能力
官方文档已明确:
- ONLYOFFICE 提供 **AI 插件**
- 可接入多种模型提供商,例如 **OpenAI、DeepSeek**
- 支持的能力包括但不限于:
- 文本生成
- 文本编辑
- 总结
- 自动创建宏
- 面向编辑器的 AI 辅助操作
这说明:
> OnlyOffice 不是一个“完全不懂 AI 的富文档编辑器”,而是已经具备官方 AI 扩展体系。
### 2.2 ONLYOFFICE 已有插件系统
官方文档明确支持:
- 自定义插件
- 插件 UI 集成
- 插件调用外部服务
- 插件与编辑器文档内容交互
- 插件注册、安装、配置与运行
这说明后续你完全可以:
- 做自有 AI 插件
- 做面向 mnote-rust 的桥接插件
- 把系统命令面引入编辑器环境
### 2.3 Office JS API 已能做深度文档操作
官方 API 文档与样例表明,插件/脚本可进行:
- 获取文档对象 `Api.GetDocument()`
- 获取当前选区/范围
- 获取文本内容
- 插入文本
- 替换内容
- 创建段落
- 插入内容控件
- 处理表格、图片、表单等对象
这意味着:
> “AI 在 OnlyOffice 内获取当前选区并改写当前文档片段”这条能力链,官方已经支持。
### 2.4 ONLYOFFICE 支持自建部署与集成
官方文档明确把 ONLYOFFICE Docs 作为可集成到自有系统中的 office suite / Docs API / 插件宿主来提供。
结合你当前仓库里已有:
- Docker 自建 `onlyoffice/documentserver`
- callback / proxy / 外网地址改写
- 前端侧 OnlyOffice 工具桥
可以判断:
> 自建部署这条路是成立的,而且值得继续做。
---
## 3. 由这些事实推出的边界判断
## 3.1 能做深集成,但不能反客为主
结论:
- **OnlyOffice 适合深集成**
- **OnlyOffice 不适合做系统主内核**
原因不是它能力不够,而是职责不同。
OnlyOffice 擅长:
- 编辑 office 文档
- 处理复杂版式文档
- 承载编辑器插件与文档内 AI
- 管理编辑器态的操作命令
但它不擅长天然承担:
- 笔记块树真相
- 全局页面树真相
- 跨页面引用网络真相
- AI 全局任务编排
- 统一命令总线
- 全系统事件日志中心
## 3.2 在 mnote-rust 中的正式定位
OnlyOffice 应定位为:
> **Office Asset Editor Adapter + Office AI Subsystem**
而不是:
- 主页面编辑器内核
- 主数据模型
- 主命令入口
- 主任务系统
---
## 4. 正式职责划分
## 4.1 OnlyOffice 负责什么
### A. Office 资产编辑
负责编辑:
- `docx`
- `xlsx`
- `pptx`
- 部分 `pdf` 相关工作流
### B. 编辑器内局部 AI 能力
例如:
- 基于当前选区总结
- 重写/润色选区文本
- 插入生成内容
- 提取 action items
- 生成标题/批注/宏
### C. 编辑器上下文采集
例如:
- 当前文档 ID
- 当前资产版本 ID
- 当前选区
- 当前页/书签/活动对象
- 编辑器保存状态
### D. 将编辑结果回写主系统
例如:
- 保存为新 `AssetVersion`
- 产出书签/目录/片段锚点
- 触发提取文本与索引任务
## 4.2 Rust 内核负责什么
### A. 主事实层
- Workspace
- Page
- Block
- Asset
- AssetVersion
- Reference
- Task
- Event
- CommandLog
### B. 系统级命令与权限
- 谁可以编辑哪个资产
- 谁可以覆盖哪个版本
- 哪些操作需要确认
- 哪些任务需要后台执行
### C. 全局 AI 调度
- Agent Session
- Tool Registry
- CLI / MCP / Tool API
- 长任务编排
- 审计与回放
### D. 跨资产与跨页面知识组织
- 引用图
- 搜索索引
- 反链
- RAG
- 页面/块/资产统一导航
---
## 5. 最推荐的集成方式
## 5.1 推荐结构
```text
AI / CLI / Web / Desktop
Rust Command / Query / Tool API
├─ Asset Service
├─ Agent Service
├─ Search / Reference Service
└─ OnlyOffice Adapter Service
├─ Docs API
├─ Callback Handler
├─ Plugin Bridge
└─ Selection / Save / Anchor Bridge
```
核心含义:
- 所有人和 AI 都先找 Rust 内核
- Rust 内核再协调 OnlyOffice
- OnlyOffice 不是上帝,只是一个能力域
## 5.2 不推荐结构
```text
AI -> OnlyOffice Plugin -> 直接改系统数据库/页面状态
```
或者:
```text
Web 页面逻辑 -> 直接控制 OnlyOffice -> 顺手写一部分业务真相
```
这种结构会再次回到胶水冲突。
---
## 6. 官方 AI 能力在新系统中的用法
## 6.1 可以直接利用的部分
可以利用官方插件/Office API 做:
- `office.get_selection`
- `office.get_document_text`
- `office.replace_selection`
- `office.insert_after_selection`
- `office.create_comment_from_ai`
- `office.generate_outline`
- `office.extract_action_items`
这些能力适合做成:
- 插件内命令
- 本地桥接命令
- Tool API 的 office 子域工具
## 6.2 不应依赖的部分
不应把以下能力外包给 OnlyOffice
- 页面树管理
- 笔记块模型
- 全局知识链接
- Agent 全局工作记忆
- 系统统一任务编排
- 跨类型对象权限控制
---
## 7. 推荐的 Tool API 分工
## 7.1 Rust 内核对外工具
例如:
- `asset.open_in_office`
- `asset.list_versions`
- `asset.commit_new_version`
- `reference.resolve_anchor`
- `office.create_edit_session`
- `office.get_capabilities`
## 7.2 OnlyOffice 插件桥工具
例如:
- `office_plugin.get_selection`
- `office_plugin.get_document_text`
- `office_plugin.replace_selection`
- `office_plugin.insert_text`
- `office_plugin.get_outline`
- `office_plugin.list_bookmarks`
## 7.3 正确的调用链
```text
Agent / CLI
-> note/asset/office Tool API
-> Rust 内核
-> OnlyOffice Adapter / Plugin Bridge
-> Office JS API / Docs API
-> 返回结构化结果
```
而不是:
```text
Agent 直接知道 OnlyOffice 内部细节并自行编排保存流程
```
---
## 8. 版本回写策略
这是边界里最关键的一块。
### 8.1 正确做法
OnlyOffice 编辑完成后:
- 生成新的 `AssetVersion`
- 更新 `assets.current_version_id`
-`command_log` / `event`
- 触发:
- 文本提取
- 大纲提取
- 锚点更新
- 索引更新
### 8.2 不正确做法
- 只在编辑器里“看起来改了”但系统无版本记录
- callback 到了就直接覆盖文件,不产生日志
- 直接把插件临时状态写成业务真相
---
## 9. Anchor / 选区 / 书签策略
OnlyOffice 的一个真正价值,是它能提供相对更强的文档内部定位。
建议区分两类定位:
### 9.1 短期编辑上下文
例如:
- 当前选区
- 当前光标
- 当前页
- 当前激活对象
这类只作为:
- `SelectionAnchor`
- AgentSession 上下文
- Tool 输入
### 9.2 长期可引用锚点
例如:
- bookmark
- 标题路径
- 批注 id
- 内容控件 id
- 提取片段 hash
这类才适合进正式 `Reference.anchor`
原则:
- 选区是瞬时上下文
- 书签/内容控件/稳定片段才是长期引用对象
---
## 10. 插件开发建议
## 10.1 建议做自有插件桥
理由:
- 官方插件体系已经成熟到可用
- 你需要的是“把 OnlyOffice 接进自己的命令系统”
- 自有桥插件比零散页面 hack 更稳
## 10.2 插件职责建议
插件只做:
- 获取编辑器上下文
- 执行局部编辑命令
- 请求 mnote-rust Tool API
- 接收结构化结果并落到文档
- 回传选择/书签/状态
插件不要做:
- 自己维护主业务状态
- 自己做全局权限判断
- 自己做全局任务调度
- 自己缓存长期知识图谱
---
## 11. 源码部署 / 自建部署的实际建议
## 11.1 结论
**值得继续推进自建部署。**
## 11.2 原因
- 网络、callback、鉴权策略可控
- 便于把插件、配置、回调、存储策略收归自己管理
- 更方便未来与桌面版、本地模式、离线模式衔接
- 减少外部部署差异导致的集成复杂度
## 11.3 但不建议的误区
- 不要把“自建部署”误解为“去重写编辑器引擎”
- 不要把“插件可改内容”误解为“OnlyOffice 可以替代整个笔记内核”
- 不要把“官方已有 AI 插件”误解为“你的 Agent 架构可以省掉”
---
## 12. 对 mnote-rust 的直接影响
这份结论会直接影响后续架构文档:
1. `office.*` 作为独立工具域存在
2. `AssetVersion` 必须是一等对象
3. `Reference.anchor` 必须支持 office bookmark / content control / text quote
4. `SelectionAnchor` 只做短期上下文,不升格为主事实
5. Agent 与 OnlyOffice 的关系必须经由 Rust Tool API,而不是页面硬连
---
## 13. 最小落地路线
### Phase A
- 自建 ONLYOFFICE Docs 持续使用
- 补正式 adapter 文档
-`AssetVersion` / callback / save 流程
### Phase B
- 做自有桥插件
- 打通选区读取、文本替换、结构化命令执行
- 统一插件与系统鉴权
### Phase C
- 把 office 工具域接入统一 Tool API / CLI / Agent Runtime
- 让 AI 通过系统命令操作 Office 资产
- 建立锚点、版本、索引、审计闭环
### Phase D
- 再评估是否要更深度定制 UI / 插件体验
- 但不进入“重写 OnlyOffice 内核”路线
---
## 14. 一句话结论
基于 ONLYOFFICE 官方当前能力,最合理的路线不是回避它,也不是围绕它建系统,而是:
> **把 ONLYOFFICE 作为可自建、可插件化、可 AI 深接入的 Office 资产编辑子系统;同时把主事实层、命令层、日志层、任务层牢牢放在 Rust 内核里。**