Files
mnote/design/07-ai/process/7-14-online-local-ai-markdown-editing-convergence-v1.md
T
lix-2026 cdff672aa5 feat: align local-first workspace direction
Document the VSCode-like local-first product shape, demote Convex to a control-plane role, and retire stale architecture drafts.

Add local workspace migration/export references plus smoke coverage for no-Convex managed workspace startup, local markdown title/body/options persistence, asset upload behavior, and Convex fixture export.

Verification: git diff --cached --check; node scripts/check-local-first-convex-guard.js --staged; node scripts/task444-convex-workspace-export-local-fixture-smoke.js; node scripts/task166-local-first-managed-workspace-no-convex-smoke.js; node scripts/task167-local-markdown-title-body-options-no-convex-smoke.js
2026-05-19 08:11:58 +08:00

38 KiB
Raw Blame History

7-14 [process] 在线 / 本地文档 AI Markdown 编辑路径收敛 v2

创建时间:2026-05-16

更新时间:2026-05-16v3:深度参考 CLI Main skill 系统,补全成熟度采纳清单)

2026-05-18 口径补充:

  • local-first workspace 已成为早期产品默认形态;本地 .md 是默认 AI 编辑目标,在线 Convex 文档降级为可选 cloud / sync / share source。
  • 本文早期把 mnote.doc.markdown_edit 描述为统一主路径;最新口径改为:local-first 普通 Markdown 编辑优先给 agent 授权文件引用,由 agent 使用自身成熟的 diff / apply_patch / 文件编辑能力完成;mnote.doc.markdown_edit 保留为 cloud / remote agent / compat fallback。
  • 页面内图片 / 附件上传已在 local source 下写入 {mdBase}.assets/ 并保存相对 Markdown 路径,AI 后续处理附件引用时也应保留相对路径,不改写为 Convex media asset。

当前状态:PROCESS

本稿目的:

  1. 纠正 7-9 / 7-10 / 7-12 / 7-13 中隐含的「块级编辑是 AI 唯一写入路径」假设
  2. 基于 CLI Main 参考实现,确立 mnote 的「agent 原生文件 patch/diff 为 local-first 默认路径,MNote 文本级兼容工具 + 块级结构性操作为 fallback / 辅助」两层模型
  3. 规划 BlockNote AI 流式/review 能力的远期方向(当前不实施)
  4. 统一 cloud / remote / compat 文档和本地 .md 文件的 AI 读取、权限、冲突与回读口径

关联文档:

  • /mnt/Data1T/mnote/design/07-ai/done/7-9-page-block-ai-tooling-roadmap-v1.md
  • /mnt/Data1T/mnote/design/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md
  • /mnt/Data1T/mnote/design/07-ai/process/7-12-page-ai-hermes-tool-routing-and-review-surface-v1.md
  • /mnt/Data1T/mnote/design/07-ai/done/7-13-page-block-editor-runtime-actor-v1.md
  • /mnt/Data1T/mnote/design/03-rust-web/process/3-13-rust-web-local-markdown-gfm-ast-parser-migration-v1.md
  • /mnt/Data1T/mnote/design/04-tree-domain/done/4-22-local-folder-convex-unified-tree-execution-checklist-v1.md
  • /mnt/Data1T/mnote/bugs/07-ai/process/page-ai-block-edit-model-fallback-missing-content.md

参考实现(已分析):

  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main/主参考skill 系统、scope/detail 上下文、两层操作、工作流、参考文件模式、CI 校验
    • skills/lark-doc/SKILL.md + references/
    • skill-template/master-skill-template.md、skill-template.md
    • shortcuts/doc/internal/skillscheck/
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/blocknote-ai/packages/xl-ai/ — 远期参考:流式/review
  • /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-ai-autocomplete/ — 独立功能:内联补全

上位依据:

  • /mnt/Data1T/mnote/ARCHITECTURE.md
  • /mnt/Data1T/mnote/design/01-05-current-priority-overview.md

1. 结论

在线 Convex 文档和本地 .md 文件本质上是同一个东西:一段 markdown 文本,Tiptap 只是块级 UI 表现层。 进一步切到 local-first 后,本地 .md 已经是普通文件,因此不需要再为常规正文编辑发明一套 MNote 专用工具。当前 07-ai 设计把 AI 编辑契约钉死在 EditorBlockDocument 的块级操作上,导致三个连锁问题:

  1. AI 被迫在块级操作:对「改写这段话」「补充一段总结」「调整语气」「把所有 TODO 改成 DONE」等自然请求,AI 必须产出 { op: "replace", blockId: "block_1", ... } 格式的块操作,而不能直接产出修改后的文本或搜索替换对。这增加了 AI 的认知负担和出错概率。

  2. 本地 .md 文件没有 AI 能力:本地文件没有稳定的 blockId(每次解析重新分配),因此块级工具无法应用于本地文件。当前 Hermes 的 13 个工具没有一个是面向本地文件的。

  3. 两套口径维护:在线文档(块操作)和本地文档(无 AI 路径)长期走两套路径,维护成本翻倍。

参考实现的验证

分析了三个参考实现后,确认 mnote 的方向是正确的:

参考实现 AI 编辑模型 mnote 采纳
CLI Main (Lark Doc) 文本级 str_replace + 块级 block_replace/insert_after/delete/move_after 两层操作 主参考:两层模型 + AI skill 合同设计
BlockNote AI 纯块级 add/update/delete(依赖 blockId),流式 apply + suggest/review 远期参考:流式/review 能力,Phase C 规划
Tiptap AI Autocomplete 纯文本补全,单句接龙,无工具 独立功能:内联 AI 补全(非本文讨论范围)

CLI Main 最关键的设计决策:它不把 AI 钉死在某一层。str_replace 用于文本级修改(不需要 blockId),block_* 用于精确块结构调整。格式上,XML 用于精确编辑场景,Markdown 用于整篇导入/导出。mnote 的不同之处在于:local-first 之后,默认对象就是本地 .md 文件,因此可以直接复用 Codex / Hermes / Reasonix 自身成熟的 diff、apply_patch、文件编辑能力;MNote 的职责收口为权限沙箱、文件引用解析、审计和刷新。

正确方向

mnote 的 AI 编辑路线:本地授权文件 + agent 原生 patch/diff 为主;MNote 兼容工具为 cloud/remote/结构化辅助;BlockNote AI 的流式/review 只作为远期交互参考。

具体:

  • 当前实施Phase A/B):local-first 页面 AI 只传当前文件引用、可选 selection 和用户指令;MNote 校验 AiAccessScope 后让 agent 在受限目录中使用原生 patch/diff 编辑 .mdmnote.doc.markdown_edit 作为兼容 / 远端代理 fallback,mnote.block.* 作为复杂结构辅助。
  • 远期规划Phase C):BlockNote AI 的流式增量 apply + suggest/review 层。当前先设计,不实施。

2. 参考实现详解

2.1 CLI MainLark Doc)— 两层操作模型

CLI Main 的 skills/lark-doc/SKILL.md 是飞书文档的 AI skill 定义。它把文档操作分成两层:

文本级操作(不需要 blockId):

docs +update --command str_replace --search "原文片段" --replace "新文本"

AI 只需要匹配文本,不需要知道块在哪儿。适用于所有内容修改场景。

块级操作(需要 blockId):

docs +update --command block_replace --block-id "block_1" --content "<xml>...</xml>"
docs +update --command block_insert_after --block-id "block_1" --content "<xml>...</xml>"
docs +update --command block_delete --block-id "block_1"
docs +update --command block_move_after --block-id "block_3" --target-block-id "block_1"

适用于精确块结构调整。AI 需要知道 blockId,因此依赖前置的 docs +fetch 返回带 id 的内容。

格式选择规则CLI Main 的 skill 中内联):

场景 格式 原因
精确块编辑(替换/插入/删除指定块) XML XML 保留块 id、类型、属性
整篇文档导入/导出 Markdown 人类可读,和 .md 文件互通
AI 对话中的内容引用 Markdown 模型理解 markdown 远好于 XML

对 mnote 的启示

  1. 两层操作模型直接适用。mnote 的 markdown_edit = CLI Main 的 str_replace(文本级),apply_block_ops = CLI Main 的 block_*(块级)。
  2. 但 mnote 的主格式是 markdown 而非 XML。在线文档的持久化格式(Convex documents.content)可以投影为 markdown,本地文件本身就是 markdown。所以我们不需要 XML 这一层——markdown 既是 AI 编辑格式,也是人类可读格式。
  3. CLI Main 的 skill 格式YAML frontmatter + Markdown body,定义 tool shortcuts + 上下文格式规范)值得学习。mnote 的 Hermes mnote plugin 可以用类似方式组织。

2.1.1 Skill YAML 前端元数据

参考文件:skill-template/master-skill-template.mdskill-template/skill-template.mdscripts/skill-format-check/index.js

每个 skill 文件以 YAML frontmatter--- 包裹)开头:

---
name: lark-doc
description: "飞书云文档 / Docx / 知识库 Wiki 文档(v2):创建、打开、读取..."
metadata:
  requires:
    bins: ["lark-cli"]
  cliHelp: "lark-cli docs --api-version v2 --help; ..."
---

CI 自动化校验(skill-format-check.yml + scripts/skill-format-check/index.js)确保 namedescription 必填。

→ mnote 采纳Hermes 的 mnote plugin 已采用 YAML frontmatter/home/lix/.hermes/plugins/mnote/plugin.yaml)。在此基础上规范化:

  • namemnote
  • description:一段完整的 skill 描述,含覆盖的工具和适用场景
  • metadata.domainmnote-doc(文档域),后续可扩展 mnote-treemnote-file
  • CI 校验:确保每个 plugin 的 plugin.yaml 含必填字段

2.1.2 Scope / Detail 上下文控制

参考文件:skills/lark-doc/references/lark-doc-fetch.mdshortcuts/doc/docs_fetch_v2.go

CLI Main 的 docs +fetch 支持五个 --scope 级别,精确控制注入 AI 上下文的文档量:

--scope 返回内容 AI 场景
outline 标题树(h1-hN+ blockId "给我看目录"
section 指定标题下的完整节 "精读第三章"
range blockId 区间 "给我 100-200 行"
keyword 关键词周围最小片段 "找提到 deployment 的地方"
full(默认) 全文 最后手段

三个 --detail 级别控制元数据量:

--detail blockId 样式属性 AI 场景
simple 只读、总结
with-ids 需要精确寻址时
full 即将编辑该节

片段包装:<fragment requested-start="..." requested-end="..."><excerpt top-block-id="..." parent-block-path="..."> 标记告知 AI"这是部分视图,非完整块"。

→ mnote 采纳(高优先级):

mnote.doc.fetch({
  scope: "section" | "outline" | "keyword" | "full",
  detail: "simple" | "with_ids" | "full",
  format: "markdown",
  maxChars: 8000
})
  • scope 的四级映射:outline(标题树)、section(当前节)、keyword(搜索关键词)、full(全文)
  • detail 的三级映射:simple(纯文本)、with_ids(带块标记)、full(带属性)
  • 片段包装:markdown 中用 <!-- fragment: section "标题" --> 注释标记部分视图边界
  • 实现位置:mnote-webmnote.doc.fetch handler

2.1.3 更新工作流:Code-Act Loop

参考文件:skills/lark-doc/references/style/lark-doc-update-workflow.md

CLI Main 的文档编辑遵循 Plan → Execute → Observe → Iterate 四步循环:

1. Plan(先读后改):
   docs +fetch --scope section --detail full
   → 分析当前状态 → 制定操作序列

2. Execute(精准手术,不全量覆盖):
   默认用 str_replace / block_insert_after / block_delete
   block_move_after 用于重排
   overwrite 仅在有明确指令时使用
   append + block_delete 组合优于 overwrite

3. Observe(每次写后回读):
   docs +fetch --scope section
   → 确认修改正确

4. Iterate(修复差异):
   如发现偏差 → 回到 Plan

更新命令决策树:

需要修改文本内容(不改块结构)?
  → str_replace(文本级,不需要 blockId
需要整段替换?
  → block_replace --block-id xxx(需要先 fetch --detail with_ids
需要插入/删除?
  → block_insert_after / block_delete
需要重排结构?
  → block_move_after / block_copy_insert_after

→ mnote 采纳(当前 Hermes 的 mnote plugin 应内置此工作流):

  • Hermes mnote skill 的 SKILL.md 中内联 Code-Act Loop 指导
  • mnote.doc.markdown_edit(文本级搜索替换)优先于 mnote.doc.apply_block_ops(块级精确操作)
  • 模型 system prompt 追加:"永远不要在不确定时使用全文覆盖;优先搜索替换;每次写入后回读确认"

2.1.4 参考文件分离模式

参考文件:skills/lark-doc/references/24 个独立 .md 文件)

CLI Main 把详细工具规格从主 SKILL.md 中分离到 references/ 子目录:

skills/lark-doc/
  SKILL.md              ← 技能概述 + 决策表 + 路由规则
  references/
    lark-doc-fetch.md   ← fetch 工具详细规格
    lark-doc-update.md  ← update 工具详细规格
    lark-doc-xml.md     ← XML 块语法参考
    lark-doc-md.md      ← Markdown 格式规则
    style/
      lark-doc-update-workflow.md  ← 编辑工作流
      lark-doc-style.md            ← 写作风格指南

→ mnote 采纳Hermes mnote plugin 应采用相同结构:

~/.hermes/plugins/mnote/
  plugin.yaml              ← YAML 前端元数据
  SKILL.md                 ← 技能概述 + 工具决策表
  references/
    mnote-doc-fetch.md     ← fetch 详细规格(scope/detail/format
    mnote-doc-update.md    ← 更新命令规格(search/replace + block ops
    mnote-doc-workflow.md  ← Code-Act Loop
    mnote-doc-context.md   ← 上下文冻结与格式化规则

2.1.5 跨域资源路由

参考文件:skills/lark-doc/SKILL.md(嵌入式资源路由表)

CLI Main 的 lark-doc skill 定义了嵌入式资源的显式路由表:

| 嵌入标签                     | 提取字段                  | 代理 skill    |
|-----------------------------|--------------------------|---------------|
| <sheet token="..." ...>     | token → spreadsheet_token | lark-sheets   |
| <bitable token="..." ...>   | token → app_token        | lark-base     |
| <whiteboard token="...">    | board_token              | lark-whiteboard|

→ mnote 采纳(远期,当文档内嵌入其他资源类型时):

  • 在线文档中嵌入的思维导图 → 路由到 mnote-mindmap skill
  • 嵌入的附件/文件 → 路由到 mnote-file skill
  • 当前 Phase A 不实施,预留路由表字段

2.1.6 格式策略:XML vs Markdown

参考文件:skills/lark-doc/references/lark-doc-xml.mdlark-doc-md.md

CLI Main 的格式选择规则:

场景 格式 原因
精确块编辑 XML(默认) 保留 blockId、样式属性、结构
整篇导入/导出 Markdown 人类可读,和 .md 互通
AI 对话引用 Markdown 模型理解 markdown 远好于 XML

→ mnote 采纳CLI Main 用 XML 做默认格式是因为飞书文档本身是 XML 存储。mnote 的在线文档持久化格式(Convex documents.content)和本地文件都是 markdown,因此 markdown 是 mnote 的默认和唯一 AI 格式。这是正确的差异化决策。

2.1.7 Skill CI 校验

参考文件:.github/workflows/skill-format-check.ymlscripts/skill-format-check/

CLI Main 的 CI 自动检查每个 SKILL.md

  1. ---\n 开头
  2. YAML frontmatter 语法有效
  3. namedescription 必填
  4. metadata 缺失仅为警告

→ mnote 采纳:对 Hermes mnote plugin 和所有 ~/.hermes/skills/note-taking/mnote-*/SKILL.md 执行同等校验。在 CI 中增加 skill-format-check 步骤。

2.2 BlockNote AI — 流式 apply + suggest/review

BlockNote AI 的 StreamTool 实现了一套完整的 AI 编辑体验闭环:

核心机制

LLM 流式输出 partial JSON
  → StreamToolExecutor 逐 chunk 解析
  → 匹配 operation type → validate → execute
  → ProseMirror 事务逐条 apply(带延迟,模拟"AI 正在打字"
  → suggestChanges 标记 AI 编辑为 suggestion(红绿对比)
  → 用户逐条 accept / reject
  → 确认后才写入持久层

关键组件

组件 功能 mnote 远期对标
StreamTool 单操作的定义:name + inputSchema + validate + execute mnote 已有(mnote.block.* / mnote.doc.*),不需要重构
StreamToolExecutor 流式解析 partial JSON → 逐条 enqueue → 按顺序 execute Phase C 新增:StreamApplyController
suggestChanges ProseMirror suggestion marksAI 编辑不直接落盘,先标记为待审阅 Phase C 新增:在线文档的 ReviewSession
RebaseTool 协作场景:用户同时在编辑 → AI 操作 rebase 到最新文档状态 Phase C 考虑(协作场景依赖 Convex 的 revision 乐观锁)
delayAgentStep 逐条 apply 之间加 50-200ms 延迟,给用户"AI 正在操作"的可见性 Phase C 可选改善

BlockNote AI 不适合直接搬的原因:它的所有操作都通过 idblockId)寻址。add 需要 referenceIdupdate 需要 iddelete 需要 id。这意味着:

  • 本地 .md 文件无法使用(没有稳定 blockId)
  • 跨块内容修改("把所有 TODO 改成 DONE")需要模型逐一产出 N 个带 blockId 的操作
  • 这和 mnote 的"markdown 优先"方向背道而驰

但我们可以在 markdown_edit 之上叠加流式/reviewPhase C 时,mnote.doc.markdown_edit 的 apply 过程可以流式化——逐条 search/replace 执行后推送 BlockDelta,编辑器逐条渲染,用户可逐条撤回。这和 BlockNote AI 的体验效果一致,但底层是文本级操作而非块级操作。

2.3 Tiptap AI Autocomplete — 内联补全(独立功能)

纯文本补全器:发送光标前文本 → 返回下一句。不是文档编辑方案。mnote 将来可以考虑作为独立的"内联 AI 补全"功能,和本文讨论的文档编辑工具分开设计。


3. 当前问题

3.1 块级编辑是 AI 的过度抽象

参考对照:CLI Main 证明 str_replace 足以覆盖大多数 AI 编辑场景,不需要强迫模型理解 blockId。

当前 AI 写入链路的问题不在于技术实现,而在于模型必须理解 blockId 这个 UI 层的概念

用户: "把第一段改简洁一些"
  → 当前路径:模型 → { op: "replace", blockId: "block_1", content: "..." }
  → 期望路径:模型 → { search: "第一段原文", replace: "改写后文本" }

「把文章中所有 TODO 改成 DONE」这类跨块请求,当前需要逐一产出 N 个 { op: "replace", blockId: "..." }。文本级 search/replace 只需一条:{ search: "TODO", replace: "DONE" }

3.2 本地文件没有 AI 写入路径

参考对照:CLI Main 的 skill 可以操作任何 doc-token(包括本地文件和云端文档),因为它用的是文本级 + XML 级工具,不依赖特定存储。

mnote 当前:Hermes 只知道 workspace/document 模型,不知道本地文件夹/文件模型。本地 .md 文件在 AI 视角完全不可见。

3.3 两套体系互不相认

在线 Convex 文档 本地 .md 文件
存储 Convex documents.content 文件系统 *.md
AI 读取 mnote.doc.fetchblock projection
AI 写入 mnote.block.* / mnote.doc.apply_block_ops
写入粒度 块级(需要 blockId —(无 AI 路径)
设计覆盖 07-ai 全部文档 仅在 03-rust-web 讨论解析

两份体系在设计中互不相认。3-13 写明"不影响 Convex workspace 链路"——这是主动划清界限。收敛是必须的,markdown 是共同分母。


4. 设计原则

4.1 Markdown 是 AI 编辑的第一公民

参考对照:CLI Main 用 Markdown 做整篇导入/导出和对话引用,用 XML 做精确块编辑。mnote 直接用 Markdown 做所有 AI 操作,因为 mnote 没有 XML 存储层。

AI 最自然的编辑方式是对文本进行操作。块是 UI 概念,不是 AI 概念。在线文档的持久化格式和本地文件的持久化格式都可以投影为 markdown。

4.2 两层操作模型(兼容层)

工具 寻址方式 适用场景 占比
文件级(主) agent 原生 diff/apply_patch/文件编辑 授权文件引用 / selection local-first 普通 Markdown 改写 80%+
文本级(兼容) mnote.doc.markdown_edit search/replace 文本对 cloud / remote agent / 兼容旧页面 AI 次要
块级(辅助) mnote.doc.apply_block_ops blockId / matchText "把第三块拖到第一块后面"、"精确删除引用块" <20%

4.3 在线和本地的主写入路径

页面 AI / ACP
  → resolve_source(documentId) → Convex | LocalFS
  → local-first: 传授权文件引用给 agent runtime
      → agent 原生 patch/diff 写入文件
      → MNote 做权限 / 审计 / refresh
  → cloud / remote fallback: mnote.doc.markdown_edit

差异主要在执行层:local-first 直接让 agent 修改授权文件;cloud 或受限 remote runtime 无法直接访问本地文件时,再走 mnote.doc.markdown_edit 代理。这和 CLI Main 的 docs +update 可以操作任何 doc-token 的设计一致,只是 mnote 进一步把“编辑算法”让渡给 agent runtime。

4.4 Diff 是内部实现细节

对兼容 mnote.doc.markdown_edit 来说,AI 产出 unified diff(行号/上下文极易出错),而是产出两种形式之一:

形式 适用场景 AI 负担
operations: [{ search, replace }] 局部修改 低:只需找原文片段
full_content: "..." 小文档全文改写 低:直接写完整 markdown

而在 local-first 主路径中,agent runtime 自己可以安全使用成熟的 diff / apply_patch / 直接文件编辑能力;MNote 只要求这些写入被限制在授权路径内,并把 changed files / diff 摘要收回审计。


5. 工具合同

5.1 mnote.doc.fetch(已有,增强)

{
  "toolName": "mnote.doc.fetch",
  "args": {
    "documentId": "tree_xxx 或 /path/to/file.md",
    "format": "markdown",
    "scope": "full",
    "query": "TODO",
    "maxChars": 8000
  }
}

增强点:

  • format: "markdown" — 新增值。在线文档由 Page Aggregate 产出 PageMarkdown;本地文件直接返回 .md 原文。
  • documentId — 自动检测 sourceConvex workspace 文档 vs 本地文件系统路径。

返回:

{
  "ok": true,
  "source": "convex",
  "documentId": "tree_xxx",
  "revision": 3,
  "format": "markdown",
  "content": "# 标题\n\n段落内容...\n\n## 子标题\n\n...",
  "truncated": false,
  "charCount": 1234
}

5.2 mnote.doc.markdown_edit(新增,兼容 / fallback

{
  "toolName": "mnote.doc.markdown_edit",
  "args": {
    "documentId": "tree_xxx 或 /path/to/file.md",

    "operations": [
      { "search": "原文片段", "replace": "新文本" },
      { "search": "另一段", "replace": "改写后的内容" }
    ]
  }
}

服务端处理流程:

1. resolve_source(documentId) → ConvexAdapter | LocalFSAdapter
2. 读取当前 markdown 全文
3. 逐条 search_replace(精确匹配 → fuzzy fallback
4. 计算内部 diff(用于 BlockDelta 推送)
5. 写入目标
6. 返回结果

返回:

{
  "ok": true,
  "source": "convex",
  "documentId": "tree_xxx",
  "revision": { "before": 3, "after": 4 },
  "operationsApplied": 2,
  "operationsFailed": 0,
  "failedOperations": [],
  "changedText": "已将「原文片段」替换为「新文本」\n已将「另一段」替换为「改写后的内容」",
  "blockDelta": {
    "documentId": "tree_xxx",
    "revision": 4,
    "operations": [...]
  }
}

5.3 mnote.doc.apply_block_ops(已有,辅助路径)

保留不变。用于精确块结构调整(拖拽排序、指定 blockId 的精确删除)。在 Hermes tool manifest 中 markdown_edit 排在 apply_block_ops 前面。


6. 搜索替换语义

参考对照:CLI Main 的 str_replace 使用精确子串匹配。mnote 增加 fuzzy fallback 以提高模型输出容错率。

优先级 策略 说明
1 精确匹配 原文字串精确匹配,区分大小写
2 宽松匹配 忽略首尾空白、全角/半角差异后匹配
3 段落 fuzzy 按换行分段,每段独立 fuzzy match(允许 30% 字符差异)
4 失败 返回 operationsFailed,列出无法匹配的 search 和原因

多条 operation 按数组顺序串行执行。前一条的替换结果对后一条可见(和 sed 语义一致)。

乐观锁:执行前用 revision 检测。如果写入时 revision 已过期,返回冲突错误,让 AI 重新 fetch + edit。


7. 远期规划:流式 apply + suggest/reviewPhase C,当前不实施)

参考对照:BlockNote AI 的 StreamToolExecutor + suggestChanges + delayAgentStep。本节仅做设计规划,不列入当前实施阶段。

7.1 目标

当用户通过页面 AI 面板发起编辑后,不是等所有操作完成后一次性刷新,而是:

  1. AI 逐条产出 search/replace 对(流式)
  2. 服务端逐条 apply 并推送 BlockDelta
  3. 编辑器逐条渲染修改(带延迟,模拟"AI 正在编辑"
  4. 用户可逐条 accept / rejectsuggestion 模式)
  5. 确认后才最终写入 Convex(review 模式)

7.2 与 BlockNote AI 的差异

BlockNote AI mnote Phase C
操作粒度 块级(add/update/delete block 文本级(search/replace 文本对)
blockId 依赖 必须 不需要(文本匹配)
本地文件支持 不支持(无稳定 blockId 支持(文本匹配不依赖 blockId
Suggestion 层 ProseMirror suggestChanges marks Tiptap 的 suggestion 扩展或自建
写入时机 用户 accept 后统一写入

7.3 组件设计(草图)

StreamApplyController(新增)
  输入:LLM 流式输出的 partial operations JSON
  处理:
    1. 解析 partial JSON → 提取已完成的 operation
    2. 执行 search_replace → 产生 BlockDelta
    3. 通过 SSE 推送 delta 到编辑器
    4. 可选:注入 delay50-200ms)模拟人类编辑节奏
  输出:逐条 BlockDelta

ReviewSession(新增,在线文档)
  状态:pending → accepted | rejected
  存储:Convex review_session 表或 documents 的 review 字段
  生命周期:
    - AI 编辑 → 创建 ReviewSession → 所有修改标记为 pending
    - 用户逐条操作 → accept/reject → 更新 session 状态
    - 全部处理或用户确认 → 最终写入 → 关闭 session

GhostTextOverlay(新增,编辑器)
  参考:Tiptap AI Autocomplete 的 ghost text 覆盖层
  用于:展示 AI 修改前后的 diff(红删绿增),用户 hover 查看详情

7.4 分阶段实施顺序

阶段 内容 依赖
C-1 StreamApplyController:流式 apply + SSE 逐条推送 delta 7-13 EditorRuntimeActor delta 通道
C-2 GhostTextOverlay:编辑器内 diff 展示(红删绿增) leptos-tiptap 的 decoration 能力
C-3 ReviewSession:在线文档的 accept/reject session Convex review 表设计
C-4 delayAgentStep:可选的编辑节奏模拟 C-1 完成

当前不做实施决策。Phase C 的启动时机以 Phase A/B 完成后,编辑器能力和 Convex review 表设计就绪为前置条件。


8. 实施阶段(当前)

Phase A:文件引用主路径 + mnote.doc.* 兼容层

  • mnote.doc.fetch 增加 format: "markdown"(在线文档 Page Aggregate → PageMarkdown
  • mnote.doc.fetch 增加本地文件 source 路由(自动检测 Convex vs 文件系统路径)
  • 实现 resolve_source(documentId) — 本地文件路径 local_fs vs 其余走 Convex
  • 页面 AI / ACP 普通正文编辑默认只传当前文件引用、可选 selection 和用户指令,不再默认构造完整 page context
  • 本地 agent runtime 在 allowed_roots / allowed_file_paths 内执行 patch/diff,并把 changed files / diff 摘要回传 MNote
  • 实现 search_replace(text, operations) — 四级匹配策略(精确→宽松→段落 fuzzy→失败)
  • 实现 Convex 写入 adapter(复用 doc_apply_block_ops 链路)
  • 实现本地文件写入 adaptermnote.doc.markdown_edit 检测到本地文件路径时直接 fs::write 写回,不经过 Convex
  • 内部 diff 生成 + BlockDelta 推送(复用 7-13 delta 基础设施,待 Phase C 实现)
  • Hermes tool manifest 注册 mnote.doc.markdown_edit
  • Hermes mnote plugin 更新:mnote_doc_fetch schema + mnote_doc_markdown_edit 新增
  • 乐观锁:revision 冲突检测(doc_apply_block_ops 已有)
  • 浏览器 smoke(在线文档 format: "markdown" + markdown_edit 搜索替换)
  • 单元测试(search_replace 精确/失败/全文、blocks_to_markdown with_ids/heading 共 5 测试通过)
  • 浏览器 smoke(本地 .md 文件读取 mnote_doc_fetch + 写入 mnote_doc_markdown_editfull_content 创建 + operations 搜索替换 + 回读验证全部通过)

Phase Bpage_ai_workflow.rs 收口

  • 退役 direct_block_edit_operations(正则抠「」的快路径,代码保留但路由跳过)
  • /api/page-ai/block-edit-workflow 不再被当作 local-first 主路径
  • 模型 system prompt 重构:从产块操作 JSON 改为产 search/replace 文本对
  • 补全 operation schemaextract_markdown_operations_from_model_text 处理新旧格式
  • 浏览器 smokemarkdown_edit 搜索替换通过,自然语言编辑路径可用

Phase C:流式/review(规划中,不实施)

  • 流式 applyStreamApplyController
  • suggest/reviewReviewSession + GhostTextOverlay
  • delayAgentStep 编辑节奏模拟

见 §7。当前仅做设计规划,不实施。


9. 与参考实现和其他设计稿的关系

9.1 与 CLI Main 的关系

CLI Main 概念 mnote 对应
str_replace(文本级) mnote.doc.markdown_edit(兼容 / fallback
block_replace/insert_after/delete/move_after(块级) mnote.doc.apply_block_ops(辅助路径)
XML 用于精确编辑 mnote 不用 XML(没有 XML 存储层),direct path 退役后全部走 markdown
Markdown 用于导入/导出/对话引用 mnote 全部 AI 交互走 markdown
Skill 格式(YAML + Markdown + tool shortcuts Hermes mnote pluginplugin.yaml + SKILL.md + tool manifest

9.2 与 BlockNote AI 的关系

BlockNote AI 概念 mnote 远期对应 状态
StreamTool + StreamToolExecutor StreamApplyController Phase C 规划
suggestChanges + AIExtension state machine ReviewSession + GhostTextOverlay Phase C 规划
delayAgentStep 可选改善 Phase C 规划
RebaseTool revision 乐观锁(已有) 当前已覆盖
纯 blockId 寻址 不采用。mnote 用文本匹配

9.3 与其他设计稿的关系

设计稿 关系 修正状态
7-9 路线图 块级编辑降级为辅助,local-first 普通编辑改为授权文件 + agent patch/diffmarkdown_edit 退到兼容层 已修正
7-10 checklist 新增 Phase 9 markdown_edit 已修正
7-12 工具路由 PageAICommandRouter 主输出改为 markdown_edit 已修正
7-13 EditorRuntimeActor 补充 markdown_edit 的 delta 适配 已修正
3-13 本地 markdown 新增 AI 工具接入章节 已修正

10. 禁止项

  • 不删除 mnote.block.* 工具(保留为辅助路径)。
  • 不强迫 local-first agent 产出 MNote 自定义 diffHermes / Reasonix 可使用自身成熟 patch / diff / apply_patch 能力,MNote 负责白名单权限、文件版本冲突和审计。
  • 不要求本地文件有稳定的 blockId(本地文件没有 block identity)。
  • 不在 markdown_edit 内部引入新的 AI 模型调用(diff 是确定性算法)。
  • 不把 Convex documents:updateContent 重新提升为 local-first 正文主存储;markdown_edit 在 cloud / compat 场景可复用受控保存路径。
  • 不把 mnote.page.save 重新描述为精确编辑主入口(它仍是兜底工具)。
  • 不照搬 BlockNote AI 的纯 blockId 寻址模式(与 mnote 的 markdown 优先策略冲突)。

11. 成功标准

  • mnote.doc.fetch(documentId, format: "markdown") 对在线文档返回正确 markdown
  • mnote.doc.fetch(documentId, format: "markdown") 对本地 .md 文件返回正确 markdown(兼容路径)
  • mnote.doc.markdown_edit 的简单搜索替换(1 条 operation)浏览器 smoke 通过
  • mnote.doc.markdown_edit 的复杂改写(3+ 条 operations)成功率 > 80%
  • 本地 .md 文件通过页面 AI 面板可被读取和写入,且默认通过 agent 原生 patch/diff 完成
  • 在线文档的 markdown_edit 不增加 Convex RTT(和当前块操作持平)
  • direct_block_edit_operations 已退役(路由跳过,代码保留)
  • page_ai_workflow.rs 的 system prompt 已补全 search/replace schema
  • Phase C(流式/review)的设计已冻结,不阻塞 A/B 实施

12. CLI Main 成熟度采纳清单

以下清单按优先级排列,标注当前状态和对应 CLI Main 参考位置。

12.1 当前 Phase A/B 必须完成的

# CLI Main 模式 mnote 实施项 状态 参考文件
1 两层操作模型 local-first 默认 agent 原生 patch/diffmarkdown_editcompat fallback+ apply_block_ops(结构辅助)已在 7-10 Phase 9 登记 📋 设计完成,已按 2-2 改口径 lark-doc-update.md
2 Scope 四级控制 mnote.doc.fetch 增加 scope: full/section/outline/keyword 🔜 Phase A lark-doc-fetch.md
3 Detail 三级控制 mnote.doc.fetch 增加 detail: simple/with_ids/full 🔜 Phase A lark-doc-fetch.md
4 片段包装 fetch 返回中标记 <!-- fragment --> / <!-- excerpt --> 告知 AI 部分视图 🔜 Phase A lark-doc-fetch.mdfragment/excerpt 模式)
5 Code-Act Loop Hermes mnote plugin SKILL.md 中内联 Plan→Execute→Observe→Iterate 指导 已写入 ~/.hermes/skills/note-taking/mnote-block-ai/SKILL.md lark-doc-update-workflow.md
6 更新命令决策树 模型 system prompt 追加"优先搜索替换,不全文覆盖"规则 🔜 Phase B lark-doc-update.mdstr_replace vs block_* 决策)
7 写后回读 Hermes 每次写操作结束后自动 mnote.doc.fetch 回读 🔜 Phase B lark-doc-update-workflow.md
8 Markdown 优先格式策略 mnote 默认和唯一 AI 格式为 markdown(不需要 XML 已确认 lark-doc-md.md(对比 XML 路线)

12.2 Phase A/B 完成后应补充的

# CLI Main 模式 mnote 实施项 状态 参考文件
9 Skill YAML 规范化 Hermes mnote plugin 的 plugin.yaml 增加 descriptionmetadata.domain 已更新 v0.2.0 master-skill-template.md
10 参考文件分离 ~/.hermes/skills/note-taking/mnote-block-ai/references/ 目录已创建,mnote-doc-fetch.md 已写入 已创建(fetch),其余按需补充 skills/lark-doc/references/
11 Skill CI 校验 CI 步骤:检查所有 plugin.yaml 和 SKILL.md 的 YAML frontmatter 有效性 📋 待实施 skill-format-check.yml
12 格式规则文档 references/mnote-doc-md.md:定义 markdown 中 AI 应理解的特殊语法(任务列表标记 - [ ]、附件链接 [file:...] 等) 📋 待实施 lark-doc-md.md
13 上下文冻结规范 references/mnote-doc-context.md:冻结时保留哪些字段、截断规则、revision 注入方式 📋 待实施 lark-doc-fetch.mdfetch body 构建)

12.3 远期规划(Phase C 及以后)

# CLI Main 模式 mnote 实施项 状态 参考文件
14 跨域资源路由 文档内嵌思维导图/附件时,路由到对应 skill(见 §2.1.5 Phase C 后 lark-doc/SKILL.md(路由表)
15 流式增量 apply BlockNote AI 的 StreamToolExecutor 模式,见 §7 Phase C 规划 blocknote-ai/packages/xl-ai/
16 suggest/review BlockNote AI 的 suggestChanges 模式,见 §7 Phase C 规划 blocknote-ai/packages/xl-ai/

12.4 与 BlockNote AI / Tiptap AI Autocomplete 的采纳清单

# 参考模式 mnote 采纳 时机 参考文件
B1 StreamTool 流式 apply StreamApplyController(§7.3 Phase C blocknote-ai/packages/xl-ai/src/streamTool/
B2 suggestChanges review 层 ReviewSession + GhostTextOverlay(§7.3 Phase C blocknote-ai/packages/xl-ai/src/AIExtension.ts
B3 delayAgentStep 编辑节奏 可选改善 Phase C blocknote-ai/packages/xl-ai/src/streamTool/
T1 ghost text 内联补全 独立功能(非本文档范围) 独立立项 tiptap-ai-autocomplete/src/

12.5 实施优先级总结

Phase A(当前立即)
  ├── #2 Scope 四级控制  ← mnote.doc.fetch 增强
  ├── #3 Detail 三级控制 ← mnote.doc.fetch 增强
  ├── #4 片段包装        ← fetch 返回值增强
  ├── #8 Markdown 优先   ← 已确认
  └── 授权文件引用 + agent 原生 patch/diff 主路径;mnote.doc.markdown_edit 作为 compat fallback

Phase BPhase A 完成后)
  ├── #5 Code-Act Loop   ← Hermes plugin SKILL.md
  ├── #6 更新决策树      ← 模型 system prompt
  └── #7 写后回读        ← Hermes tool loop 逻辑

Phase A/B 完成后补充
  ├── #9-#13 Skill 规范化、参考文件分离、CI 校验、格式文档、上下文规范

Phase C(远期)
  ├── #14 跨域路由(Phase C 后)
  ├── #15 流式 applyPhase C
  └── #16 suggest/reviewPhase C