# [recycle] 7-9 [done] 页面/块 AI 工具体系规划 v1 > 更新时间:2026-05-16 > > 当前状态:`DONE`。 > > 本稿承接 `7-6` 的 mnote Hermes plugin tool 合同、`7-8` 的 Hermes Runtime BFF 方向,以及近期页面 AI 工具实测中暴露的问题:当前 `mnote.page.get/save/update_title/update_options` 已能完成页面级读写,但工具粒度仍偏粗,不能长期代表“AI 能精确编辑页面/块”。 > > 核心参考: > - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main` > - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18` > - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-vscode-main` > - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/ai-toolkit-demos` > - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-docs` > - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-main` > - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/tiptap-apcore` > - `/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` > - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md` > - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-6-page-aggregate-alignment-checklist-v1.md` > - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-13-page-block-identity-and-command-contract-v1.md` > - `/mnt/Data1T/mnote/design/02-convex-rust-long-term-architecture/done/2-1-page-block-storage-projection-alignment-v1.md` > - `/mnt/Data1T/mnote/design/07-ai/done/7-6-mnote-hermes-plugin-tool-contract-v1.md` > - `/mnt/Data1T/mnote/design/07-ai/done/7-8-page-ai-hermes-runtime-bff-next-v1.md` > - `/mnt/Data1T/mnote/design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md` --- ## 1. 结论 不要把问题理解成二选一: - 不是“等 Rust kernel / Page Aggregate / editor module 完全稳定后,才开始设计工具”。 - 也不是“现在立刻扩一批块级写工具,让 Hermes 直接调用当前 Convex / 前端私有 shape”。 正确路径是: > **现在继续写工具体系设计,冻结 AI 面向的稳定工具合同;实现上分阶段推进,先做只读、定位、dry-run、plan 和最小单块写入。复杂块移动、块嵌入、多块批量改写、媒体块编排,要等 Rust block model、Page Aggregate command、Convex 持久化和 editor session 刷新链路达到硬门槛后再开放。** 当前 `mnote.page.save` 可以继续作为页面级兜底工具,但不能继续被描述为长期块编辑方案。长期工具体系必须基于稳定的 `Page Aggregate / EditorBlockDocument / Rust runtime command` 中间合同,而不是让 AI 直接操作前端 Tiptap JSON、Convex `documents.content` 私有结构或历史 `blocks` 表。 --- ## 2. 为什么参考飞书 CLI `cli-main` 的价值不在于“飞书有很多命令,所以 mnote 也应该堆很多工具”,而在于它把文档工具做成了三层: 1. **Shortcut 层**:面向人和 Agent 的高层命令,例如 `docs +fetch`、`docs +update`、`docs +media-insert`。 2. **稳定文档操作协议层**:用 `doc-format`、`scope`、`detail`、`command`、`block_id`、`revision_id`、`dry-run` 表达文档读写。 3. **底层 API adapter 层**:把稳定协议翻译成真实平台 API,例如 `docs_ai/v1/documents` 或 MCP tool call。 这正好对应 mnote 当前问题: - Rust kernel、Page Aggregate、Convex、leptos-tiptap 还在收口。 - 如果现在让 Hermes 直接调用底层 shape,后续底层一变,skill/tool 就会失效。 - 如果先冻结一个 AI 面向的稳定文档操作协议,底层变动可以收口在 adapter。 因此 mnote 应参考的是飞书的“稳定投影 DSL + 少量高层命令 + dry-run 诊断层”,而不是照搬 Go CLI 或飞书 API。 --- ## 3. `cli-main` 具体参考位置 参考根目录: ```text /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main ``` 优先参考下表,不需要整仓搬运: | mnote 主题 | 参考文件 | 搜索点 | 可吸收内容 | 不采用内容 | | --- | --- | --- | --- | --- | | 文档工具注册 | `shortcuts/doc/shortcuts.go` | `Shortcuts()`、`docs +fetch`、`docs +update` | 工具分组、命令命名、文档域工具入口 | 不照搬 CLI 交互层 | | 文档读取 v2 | `shortcuts/doc/docs_fetch_v2.go` | `executeFetchV2`、`buildReadOption`、`--detail`、`--scope` | `simple/with-ids/full`、`outline/range/keyword/section`、局部读取 | 不使用飞书 token/API | | 文档更新 v2 | `shortcuts/doc/docs_update_v2.go` | `validCommandsV2`、`buildUpdateBody`、`revision-id`、`dry-run` | `str_replace/block_insert_after/block_replace/block_delete/block_move_after/append/overwrite` | 不直接把飞书 command 当 mnote 内部命令名 | | v1 兼容更新 | `shortcuts/doc/docs_update.go` | `CallMCPTool`、`update-doc` | 兼容层与主线层并存时的隔离方式 | 不保留多套长期真相 | | 更新前诊断 | `shortcuts/doc/docs_update_check.go` | `CheckDocsUpdateArgs`、`warning` | 工具调用前给 Agent 的静态语义警告 | 不只靠 LLM 自觉避免危险编辑 | | 媒体插入编排 | `shortcuts/doc/doc_media_insert.go` | `dry-run`、`steps`、`batch_update` | 多步工具先 dry-run 展示计划,再执行 | 第一阶段不做完整媒体工具 | | 文档 XML DSL | `skills/lark-doc/references/lark-doc-xml.md` | ``、`<callout>`、`<grid>`、`<img>`、`<cite>` | 用 PageXML/PageMarkdown 屏蔽底层块结构 | 不采用飞书专有块类型作为 mnote 类型 | | 文档更新说明 | `skills/lark-doc/references/lark-doc-update.md` | `str_replace`、`block_insert_after`、`revision`、`warnings` | 面向 Agent 的工具使用说明、返回结构 | 不把说明当实现 | | 文档读取说明 | `skills/lark-doc/references/lark-doc-fetch.md` | `detail`、`scope`、`with-ids` | 读取前先定位、再编辑的 workflow | 不让 AI 默认整页读取超大正文 | | 工具抽象 | `shortcuts/common/types.go` | `Shortcut`、`Validate`、`Execute`、`Risk` | 元信息、权限、风险、dry-run、validate 一体化 | 不复制 Go 框架 | | dry-run 通用能力 | `internal/cmdutil/dryrun.go` | `DryRun` | 所有写工具都能返回计划和风险 | 不做纯 CLI 文本输出 | | 风险提示 | `internal/cmdutil/risk.go` | `Risk` | 工具风险等级进入确认和审计 | 不用 CLI prompt 作为 Web 确认机制 | 快速定位命令: ```bash cd /mnt/Data1T/mnote/design/05-editor-mainline/reference-code/cli-main rg -n "validCommandsV2|buildReadOption|buildUpdateBody|revision|dry-run|CheckDocsUpdateArgs|Shortcut|Risk|CallMCPTool|scope|detail" shortcuts internal skills/lark-doc ``` --- ## 4. 官方 Tiptap AI Toolkit 与补充参考的吸收边界 Tiptap AI Toolkit 的核心价值是把编辑器能力拆成 AI 可调用工具,而不是让外部系统直接暴露编辑器内部实现。Context7 与本地 `tiptap-docs` 公开文档能确认的主线能力包括: - `toolDefinitions()` 向 AI SDK 暴露工具定义。 - `tiptapRead` 用高效格式读取文档,支持 range/chunk 类读取模型。 - `tiptapEdit` 用 operations 列表编辑文档。 - `tiptapReadSelection` 读取当前选区。 - `executeTool` / `streamTool` 把 AI 生成的 tool call 应用到 editor,并返回 `docChanged`、错误和工具结果。 - review options 支持 `disabled/review/preview/trackedChanges`,但 preview/suggestions 与持久 tracked changes 是两类语义。 需要修正之前口径: - 不能再说 “Tiptap 没有官方 AI 文档工具”。它有官方 AI Toolkit 工具层。 - 也不能说 “Tiptap AI Toolkit 可以直接解决 mnote 块工具”。公开文档没有给出完整 `tiptapEdit.operations` schema,且它面向 Tiptap/ProseMirror 文档层,不覆盖 mnote 的 Rust kernel、Page Aggregate、Convex revision/conflict key、Hermes audit。 - 当前 npm registry 无法直接获取 `@tiptap-pro/ai-toolkit*` 完整实现源码;本仓可参考的是公开 docs 与 `tiptap-main/packages/server-ai-toolkit`、`extension-unique-id`、`extension-drag-handle`、`extension-node-range` 等开源代码。 这些能力应映射为 mnote 自己的工具层: | Tiptap AI Toolkit | mnote 对应 | 吸收内容 | 不吸收内容 | | --- | --- | --- | --- | | `tiptapRead` | `mnote.doc.fetch` / `mnote.block.fetch` | 先读、带范围、返回适合 AI 的文档表示 | 不把 Tiptap JSON 作为长期工具格式 | | `tiptapEdit` | `mnote.doc.plan_update` + `mnote.block.*` | 操作列表、reviewable edit、meta justification | 不让浏览器 editor command 成为事实源 | | `tiptapReadSelection` | `mnote.doc.fetch scope=selection` | selection-aware workflow | 不直接持久化浏览器 selection range | | `toolDefinitions()` | Rust Hermes manifest | schema、description、capability、annotation | 不依赖私有 npm 包作为运行时硬依赖 | `tiptap-apcore` 的补充价值更偏工具基础设施。它已经把 Tiptap command 分成 query、format、content、destructive、selection、history,并为工具提供: - `inputSchema` / `outputSchema` - `readonly` / `destructive` / `idempotent` / `requiresApproval` - ACL role:`readonly`、`editor`、`admin` - `selectionEffect` - executor 前置检查、ACL、query/command 分发 mnote 应吸收这些元数据,但命名和执行面必须改成 Rust-owned: ```json { "name": "mnote.block.replace", "capabilityScope": ["page.write", "block.write"], "annotations": { "readonly": false, "destructive": false, "idempotent": false, "requiresApproval": true, "selectionEffect": "destroy" }, "runtimeOwner": "mnote-web", "writeOwner": "rust-runtime-kernel" } ``` `tiptap-ai-autocomplete` 的价值限于交互层: - ghost text 定位。 - 选区 bubble menu。 - streaming preview。 - accept/reject 后再写入。 它不应进入 Hermes tool contract 的核心,只能作为页面 AI 面板、选区 AI 菜单和 preview UI 的参考。 `tiptap-main` 开源代码给 mnote 的补充约束: - `extension-unique-id` 可作为 Tiptap runtime 节点 id 辅助,但不能替代 Rust `EditorBlock.block_id`。 - `server-ai-toolkit` 的 `_hash` 是 AI 编辑定位/变化检测辅助,不能替代业务 `blockId`;它可以参与 mnote `revisionRef` 的 hash 部分。 - `extension-drag-handle` 与 `extension-node-range` 说明块选择、拖拽、selection toolbar AI 应共享一套 node range 计算,而不是每个入口重新解析 DOM。 - schema awareness / editor context 应进入 `mnote.doc.fetch` 或工具 manifest 的上下文生成,避免 AI 猜测当前可用块类型。 --- ## 5. 当前 mnote 稳定性判断 ### 5.1 已经可以承载工具设计的边界 - Page Aggregate 读合同已经稳定为 `mnote.page_aggregate.v1`。 - 页面主读链已经可以通过 Rust `/api/page-aggregate/:id` 返回 meta/content/options/body snapshot。 - Hermes 页面 AI 已有 `mnote.*` 工具注册、dispatch、trace/audit、idempotency、dryRun 基础。 - 页面级工具 `mnote.page.get/save/update_title/update_options` 已能完成最小闭环。 - Rust `core-protocol` 已有工具规格方向,`bridge-runtime` 已出现 `doc_insert_blocks`、`doc_replace_range` 等工具/测试雏形。 这些足以支撑“工具合同设计”和“只读/定位/dry-run 工具实现”。 ### 5.2 还不适合大规模开放块级写工具的边界 - 当前保存主链仍是 `documents.content` legacy JSON snapshot,不是 `EditorBlockDocument` 原生落库,也不是 Tiptap JSON 原生落库。 - 历史 `blocks` 表仍存在,但没有维护正文主链所需的 order、父子顺序和 `documents.content` 同步闭环,不能描述为当前正文事实源。 - Page Aggregate 当前 `body.content` 仍主要是 `documents:getContent` 的 projection 包装,不是强类型 block protocol。 - Page Aggregate 当前仍有 compat join 痕迹,不是所有页面正文语义都已经 kernel-native。 - 部分 Rust block/editor command 已有 runtime 映射,但 Convex 执行面、editor session 刷新和真实页面可见性还需要逐条验收。 - 块移动、块复制、块嵌入、块引用维护需要稳定块 id、revision、父子关系、排序、权限和冲突处理;当前不应直接给 Hermes 开生产写入口。 - `mnote.page.save` 当前是整页/追加/前置级写入,适合作为过渡和兜底,不适合承诺“精确块编辑”。 - `mnote.page.save` 当前要求 `dryRun/idempotencyKey`,但页面级 revision/conflict key 还不是强制写入门槛;块级写工具开放前必须补上强制 CAS 或等价冲突阻断。 ### 5.3 设计上的硬边界 - AI 工具不得绕过 Rust runtime 直接写 Convex。 - AI 工具不得把前端 Tiptap JSON 当长期外部合同。 - AI 工具不得依赖浏览器临时 DOM id 或 runtime-only selection。 - 写工具必须有 `dryRun`、`idempotencyKey`、`revision` 或等价冲突键。 - 写工具必须返回 `warnings`,并能阻止明显错误的编辑假设。 - 写工具成功后必须能通过真实页面、`/api/page-aggregate` 和再次 `mnote.*.fetch/get` 三处验收。 --- ## 6. 工具体系分层 ### L0:内部 kernel / adapter 命令 只给 Rust runtime、Page Aggregate adapter、Convex bridge 使用,不直接暴露给 Hermes。 示例: - `page.aggregate.get` - `page.body.save` - `page.body.apply_patch` - `editor.block.insert_after` - `editor.block.replace` - `editor.block.delete` - `editor.block.move_after` - `tree.node.create` - `tree.subtree.move` 要求: - 可以随内核演进调整。 - 必须有测试覆盖。 - 由 L1 canonical tools 翻译调用。 ### L1:AI 可调用 canonical tools Hermes skill/plugin 对外暴露的稳定工具层。第一阶段只做少量、可解释、可验收工具。 建议命名: - `mnote.doc.fetch` - `mnote.doc.find` - `mnote.doc.plan_update` - `mnote.doc.apply_update` - `mnote.block.fetch` - `mnote.block.insert_after` - `mnote.block.replace` - `mnote.block.delete` - `mnote.block.move_after` - `mnote.page.append` - `mnote.page.overwrite` 其中: - `mnote.doc.fetch/find/plan_update` 可先做。 - `mnote.block.insert_after/replace` 是最小块写入切片。 - `mnote.block.delete/move_after` 等架构稳定后再做。 - `mnote.page.overwrite` 是高风险兜底,必须强确认和 dry-run。 ### L2:workflow tools 面向用户任务的编排工具,不应第一阶段优先做。 示例: - `mnote.workflow.write_weekly_report` - `mnote.workflow.rewrite_section` - `mnote.workflow.extract_action_items` - `mnote.workflow.create_meeting_note` - `mnote.workflow.generate_project_plan` 要求: - L2 必须调用 L1,不直接写 L0。 - L2 的产物先走 `plan_update` / `dryRun`,用户确认后再执行。 - 不用 L2 掩盖 L1 工具合同不稳定的问题。 --- ## 7. 稳定文档表示 ### 7.1 PageMarkdown 适合纯文本、标题、列表、引用、代码、简单表格等常见 AI 输出。 用途: - AI 生成大纲、摘要、会议纪要。 - `append/prepend/overwrite`。 - `str_replace` 简单替换。 限制: - 不表达复杂属性、块引用、嵌入、资源块、页面块关系。 - 不作为唯一长期格式。 ### 7.2 PageXML 参考飞书 DocxXML,但定义 mnote 自己的 PageXML。用于需要稳定 block id、属性、资源引用和结构化块的场景。 最小形态示例: ```xml <page title="项目计划"> <heading level="2" block-id="heading_1">目标</heading> <paragraph block-id="p_1">第一段文字</paragraph> <todo block-id="todo_1" checked="false">确认方案</todo> <callout tone="info">重要说明</callout> </page> ``` 原则: - `block-id` 只引用 mnote 已存在或本次 dry-run 分配的新 id。 - PageXML 是 AI 外部合同,内部可翻译为 `EditorBlockDocument`。 - 不暴露 Tiptap 节点私有字段。 - 不复制飞书专有块;只取 DSL 思想。 ### 7.3 EditorBlockDocument Rust 内部结构化文档模型,是 PageXML/PageMarkdown 到 Page Aggregate / editor session 的中间形态。 要求: - 持有稳定 `block_id`。 - 能表达 block type、text、attrs、children、parent、order、path、revisionRef。 - 能生成 diff plan。 - 能映射到当前 Convex-backed 保存链路。 - 能在 Tiptap JSON、legacy `documents.content` 与 Page Aggregate block projection 之间做受控转换。 --- ## 8. Canonical tool schema 草案 ### 8.1 `mnote.doc.fetch` 用途:读取当前文档或局部文档,返回可供 AI 定位和编辑的稳定投影。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "format": "markdown", "detail": "with_ids", "scope": "full", "startBlockId": null, "endBlockId": null, "keyword": null, "sectionTitle": null, "contextBefore": 2, "contextAfter": 2, "maxDepth": 6 } ``` 字段约束: - `format`: `markdown | page_xml | text | json` - `detail`: `simple | with_ids | full` - `scope`: `full | outline | range | keyword | section | selection | block` 返回: ```json { "ok": true, "revision": "rev_12", "conflictDetectionKey": "doc_1:12", "format": "markdown", "detail": "with_ids", "scope": "section", "content": "## 目标 <!-- block:heading_1 -->\n正文 <!-- block:p_1 -->", "blocks": [ { "blockId": "heading_1", "type": "heading", "text": "目标", "depth": 0, "revisionRef": "pageRev:rev_12:block:heading_1:hash:aaa" }, { "blockId": "p_1", "type": "paragraph", "text": "正文", "depth": 1, "revisionRef": "pageRev:rev_12:block:p_1:hash:bbb" } ], "warnings": [] } ``` 验收标准: - [ ] `scope=full` 能返回当前页面正文,且 `with_ids` 包含稳定 `blockId`。 - [ ] `scope=outline` 只返回标题/层级和必要 block id。 - [ ] `scope=keyword` 返回命中块和前后上下文。 - [ ] 返回内容与 `/api/page-aggregate/:id` 的 block snapshot 一致。 - [ ] 不依赖浏览器 DOM。 ### 8.2 `mnote.doc.find` 用途:在当前文档中查找关键词、块类型、标题或引用目标,帮助 AI 先定位再编辑。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "query": "待办", "match": "text", "limit": 20 } ``` 返回: ```json { "ok": true, "matches": [ { "blockId": "todo_1", "type": "todo", "text": "确认待办", "path": ["项目计划", "本周"], "score": 0.92 } ] } ``` 验收标准: - [ ] 可以按文本查找。 - [ ] 可以按 block type 查找。 - [ ] 返回结果能直接作为 `block.replace/insert_after` 的 anchor。 ### 8.3 `mnote.doc.plan_update` 用途:只生成变更计划和诊断,不实际写入。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "revision": "rev_12", "conflictDetectionKey": "doc_1:12", "command": "block_replace", "format": "markdown", "blockId": "p_1", "content": "替换后的段落", "dryRun": true } ``` 支持命令: - `str_replace` - `block_insert_after` - `block_replace` - `block_delete` - `block_move_after` - `append` - `overwrite` 返回: ```json { "ok": true, "dryRun": true, "revision": "rev_12", "plan": [ { "op": "replace", "targetBlockId": "p_1", "before": "旧段落", "after": "替换后的段落" } ], "warnings": [], "requiresConfirmation": true, "risk": "medium" } ``` 诊断规则参考 `docs_update_check.go`: - 替换目标匹配多个位置时,提示先用 `blockId` 精确定位。 - `str_replace` 找不到唯一匹配时,不执行。 - 跨多个块的自然语言替换必须转成 `block_delete + block_insert_after` 或 `block_replace`。 - `overwrite` 必须标高风险。 - 缺少 `revision` 时只允许 dry-run,不允许真实写入。 验收标准: - [ ] 所有写命令都能先 dry-run。 - [ ] dry-run 不改变 Convex 内容、不触发 editor 内容变化。 - [ ] 返回的 `plan` 可被 UI 折叠展示。 - [ ] 明显不安全的编辑返回 `warnings` 或 `blocked=true`。 ### 8.4 `mnote.doc.apply_update` 用途:执行已经 dry-run 过的文档变更计划。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "revision": "rev_12", "conflictDetectionKey": "doc_1:12", "idempotencyKey": "idem_1", "planId": "plan_1", "command": "block_replace", "format": "markdown", "blockId": "p_1", "content": "替换后的段落", "dryRun": false } ``` 执行要求: - 必须校验 `revision`。 - 必须校验 `idempotencyKey`。 - 必须落 Rust runtime / Page Aggregate command。 - 成功后必须返回新 revision 和受影响 block id。 返回: ```json { "ok": true, "revision": "rev_13", "changedBlocks": [ { "blockId": "p_1", "op": "replace" } ], "audit": { "effect": "write", "commandName": "page.body.apply_update", "commandId": "cmd_1" } } ``` 验收标准: - [ ] 执行后 `/api/page-aggregate/:id` 能读到更新。 - [ ] 当前打开页面能自动刷新或通过 editor session reload 看到更新。 - [ ] 再次 `mnote.doc.fetch` 能读到更新。 - [ ] 重复同一 `idempotencyKey` 不造成重复写入。 - [ ] revision 冲突返回 `mnote_tool_conflict`。 ### 8.5 `mnote.block.fetch` 用途:读取单个块及可选上下文,作为 `tiptapReadSelection` / selection-aware editing 的稳定服务端版本。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "blockId": "p_1", "revision": "rev_12", "includeChildren": true, "contextBefore": 1, "contextAfter": 1, "format": "json" } ``` 字段约束: - `blockId` 必须来自 `mnote.doc.fetch/find` 或 Page Aggregate block projection。 - `format`: `json | markdown | page_xml | text` - `includeChildren` 默认 `true`,但复杂块可返回 `unsupportedReason`。 - `contextBefore/contextAfter` 只返回同父级上下文。 返回: ```json { "ok": true, "revision": "rev_12", "block": { "blockId": "p_1", "type": "paragraph", "text": "正文", "attrs": {}, "path": [2], "parentBlockId": null, "order": "00020000", "revisionRef": "pageRev:rev_12:block:p_1:hash:abc", "editable": true }, "context": { "before": [], "after": [ { "blockId": "p_2", "type": "paragraph", "text": "下一段" } ] }, "warnings": [] } ``` 验收标准: - [ ] `block.fetch` 能读取 `doc.find` 返回的 block id。 - [ ] 返回 `revisionRef`,可被后续写工具用于冲突检测。 - [ ] 不存在 block 返回 `mnote_block_not_found`。 - [ ] 不可编辑块返回 `editable=false` 与 `unsupportedReason`。 ### 8.6 `mnote.block.replace` 用途:最小精确块写工具,作为第一批块级写入候选。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "blockId": "p_1", "revision": "rev_12", "conflictDetectionKey": "doc_1:12", "blockRevisionRef": "pageRev:rev_12:block:p_1:hash:abc", "format": "markdown", "content": "替换后的块内容", "idempotencyKey": "idem_1", "dryRun": true } ``` 验收标准: - [ ] 只替换目标 block,不影响相邻 block。 - [ ] block id 稳定,刷新后仍可定位。 - [ ] 支持 `dryRun=true`。 - [ ] 支持真实页面 smoke。 ### 8.7 `mnote.block.insert_after` 用途:在指定块后插入一个或多个块。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "anchorBlockId": "p_1", "revision": "rev_12", "conflictDetectionKey": "doc_1:12", "anchorRevisionRef": "pageRev:rev_12:block:p_1:hash:abc", "format": "markdown", "content": "- 新待办", "idempotencyKey": "idem_2", "dryRun": true } ``` 验收标准: - [ ] 插入位置准确。 - [ ] 新 block id 由 Rust/runtime 分配或确认,不由 AI 自造。 - [ ] 当前页面能看到新块。 - [ ] 再次 fetch 能拿到新 block id。 ### 8.8 `mnote.block.move_after` 用途:移动一个块到同父级 anchor 块后。第一阶段仅作为受限结构性写工具开放。 入参: ```json { "workspaceId": "ws_1", "documentId": "doc_1", "blockId": "p_3", "anchorBlockId": "p_1", "revision": "rev_12", "conflictDetectionKey": "doc_1:12", "blockRevisionRef": "pageRev:rev_12:block:p_3:hash:aaa", "anchorRevisionRef": "pageRev:rev_12:block:p_1:hash:bbb", "idempotencyKey": "idem_move_1", "dryRun": true } ``` 第一阶段允许: - `blockId` 与 `anchorBlockId` 同父级。 - 普通 paragraph。 - 普通 heading 叶子块。 - 普通 todo 叶子块。 第一阶段阻断: - 跨父级移动。 - 标题带子块整体移动。 - 列表项跨层级移动。 - 表格、resource、mindmap、page reference。 - 跨页面移动。 返回: ```json { "ok": true, "dryRun": true, "risk": "medium", "diff": [ { "op": "move_after", "blockId": "p_3", "anchorBlockId": "p_1", "from": { "parentBlockId": null, "order": "00030000" }, "to": { "parentBlockId": null, "afterOrder": "00010000" } } ], "warnings": [] } ``` 验收标准: - [ ] dry-run 不改变页面。 - [ ] 正式写入后 moving block id 保持不变。 - [ ] 同父级顺序正确。 - [ ] 不支持场景返回 `blocked=true` 和明确 warning。 - [ ] 再次 `mnote.doc.fetch` 能读回新顺序。 --- ## 9. 实施阶段 ### Phase A:只读和定位 目标: - 建立 `mnote.doc.fetch` 和 `mnote.doc.find`。 - 支持 `format`、`detail`、`scope`。 - 让 AI 能稳定读到 block id、标题路径、上下文。 可做: - [ ] 从 Page Aggregate snapshot 生成 PageMarkdown。 - [ ] 从 Page Aggregate snapshot 生成 PageXML 最小子集。 - [ ] 支持 `scope=full/outline/keyword/block`。 - [ ] 支持 `detail=simple/with_ids/full`。 - [ ] Hermes tool event UI 展示 fetch/find 摘要。 验收: - [ ] 真实登录页面中,Hermes 调用 `mnote.doc.fetch` 能读出当前页面。 - [ ] `with_ids` 返回的 block id 与 DOM `data-block-id` / Page Aggregate 一致。 - [ ] `keyword` 读取不会返回整页超大正文。 - [ ] 失败时返回权限/不存在/空页面的结构化错误。 ### Phase B:dry-run / plan 目标: - 建立 `mnote.doc.plan_update`。 - 写入前先给出可解释 diff、风险、warnings。 可做: - [ ] 支持 `str_replace` dry-run。 - [ ] 支持 `block_replace` dry-run。 - [ ] 支持 `block_insert_after` dry-run。 - [ ] 支持 `append` dry-run。 - [ ] 实现静态诊断规则。 验收: - [ ] dry-run 不改变页面内容。 - [ ] UI 可折叠展示 plan。 - [ ] 多重匹配、不存在 block、缺 revision 都返回明确 warning。 ### Phase C:最小块级写入 目标: - 开放 `mnote.block.replace` 和 `mnote.block.insert_after` 的真实写入。 前置硬门槛: - [ ] Page Aggregate 中每个可编辑块都有稳定 `blockId`。 - [ ] Rust runtime 能把 block replace/insert 变更映射到持久化结构。 - [ ] Convex-backed 保存链能保存变更并刷新。 - [ ] 当前 editor session 能 reload 或 live refresh。 - [ ] smoke 覆盖 AI tool -> Rust -> Convex -> 页面可见 -> fetch 回读。 验收: - [ ] AI 替换单个段落后,页面只变这一段。 - [ ] AI 在指定块后插入待办后,页面位置正确。 - [ ] 刷新页面后内容仍在。 - [ ] 版本冲突不会覆盖用户刚刚输入的内容。 ### Phase D:多块和结构性写入 目标: - 开放 `block_delete`、`block_move_after`、`block_copy_insert_after`。 前置硬门槛: - [ ] 块父子关系、排序、缩进、折叠状态稳定。 - [ ] 删除/移动能处理子树。 - [ ] 冲突检测覆盖移动前后的邻居和父节点。 - [ ] 有撤销或可审计回滚策略。 验收: - [ ] 移动标题块时,其子块处理规则明确且测试覆盖。 - [ ] 删除块需要确认,并返回被删除范围。 - [ ] 复制插入生成新 id,不复用旧 id。 ### Phase E:workflow tools 目标: - 基于 L1 工具做高层 AI 写作工作流。 可做: - [ ] 生成会议纪要。 - [ ] 生成周报。 - [ ] 重写某一节。 - [ ] 提取待办并插入当前页。 验收: - [ ] workflow 只调用 L1 canonical tools。 - [ ] 每个 workflow 都能先 plan,再执行。 - [ ] 不出现直接整页覆盖用户内容的默认行为。 --- ## 10. 架构优先级判断 当前应并行推进,但优先级要明确: 1. **先稳定 Page Aggregate / block identity / editor save-refresh 链路**:这是块级写工具能否长期可靠的根。 2. **同时设计并冻结 L1 canonical tools 合同**:避免继续把临时页面保存扩展成长期能力。 3. **先实现只读和 dry-run**:这部分对底层写链依赖小,能立即改善 AI 可靠性。 4. **等硬门槛满足后开放最小块写入**:先 `replace` / `insert_after`,不要一口气做完整块操作。 5. **最后做 workflow**:工具层不稳定时,workflow 只会放大错误。 因此,回答“先优化稳定架构,还是继续写工具设计”: > **架构稳定是实现复杂写工具的前置;工具设计现在就应该继续,而且必须用于反向约束架构稳定的验收标准。** --- ## 11. 近期不做清单 - [ ] 不让 Hermes 直接调用 Convex mutation。 - [ ] 不把 Tiptap JSON 暴露成 AI 长期工具入参。 - [ ] 不把 `mnote.page.save` 包装成所有块编辑的长期方案。 - [ ] 不先做大量 `workflow.*` 工具。 - [ ] 不做无 revision / 无 dry-run / 无 idempotency 的写工具。 - [ ] 不在工具里读取浏览器 DOM 来决定写入位置。 - [ ] 不用页面当前可见文本做唯一定位依据;必须支持 block id 或唯一匹配诊断。 --- ## 12. 执行 checklist ### 12.1 设计冻结 - [ ] 确认 `mnote.doc.fetch/find/plan_update/apply_update` 命名。 - [ ] 确认 `PageMarkdown` 最小语法。 - [ ] 确认 `PageXML` 最小语法。 - [ ] 确认 `revision` 来源。 - [ ] 确认 `blockId` 来源只来自 Page Aggregate / Rust runtime。 - [ ] 确认 `warnings` / `risk` / `requiresConfirmation` 返回格式。 验收标准: - [ ] `design/07-ai/process` 中有稳定工具合同。 - [ ] `7-6` 不再把 `mnote.page.save` 描述为长期块编辑合同。 - [ ] `5-6` 的 Page Aggregate checklist 能引用本稿作为 AI 写入门槛。 ### 12.2 只读工具实现 - [ ] Rust tool manifest 增加 `mnote.doc.fetch`。 - [ ] Rust tool manifest 增加 `mnote.doc.find`。 - [ ] Rust tool manifest 增加 `mnote.block.fetch`。 - [ ] dispatch 调用 Page Aggregate snapshot,而不是 Convex 私有 shape。 - [ ] 支持 `detail=simple/with_ids/full`。 - [ ] 支持 `scope=full/outline/keyword/block`。 - [ ] 工具结果进入 Hermes tool event UI。 验收标准: - [ ] `cargo test -p mnote-web hermes` 相关测试通过。 - [ ] 真实网页登录后,AI 能通过 `mnote.doc.fetch` 读取当前页。 - [ ] AI 能通过 `mnote.doc.find` 找到指定文本所在 block id。 - [ ] `/api/page-aggregate/:id` 与 tool 返回 block id 一致。 ### 12.3 dry-run 实现 - [ ] 增加 `mnote.doc.plan_update`。 - [ ] 支持 `str_replace` 计划。 - [ ] 支持 `block_replace` 计划。 - [ ] 支持 `block_insert_after` 计划。 - [ ] 返回 `plan/warnings/risk/requiresConfirmation`。 - [ ] 静态诊断阻止多重匹配和缺 revision 的真实写入。 验收标准: - [ ] dry-run 不改变页面。 - [ ] UI 能显示工具计划。 - [ ] 多重匹配返回 warning。 - [ ] 不存在 block 返回结构化错误。 ### 12.4 最小块写入实现 - [ ] 打通 `mnote.block.replace`。 - [ ] 打通 `mnote.block.insert_after`。 - [ ] 写入统一走 Rust runtime / Page Aggregate command。 - [ ] 成功后触发当前 editor session reload 或 live refresh。 - [ ] 成功后返回新 revision 和 changedBlocks。 验收标准: - [ ] AI 替换单块,页面立即可见。 - [ ] AI 插入新块,位置准确。 - [ ] 刷新后内容仍在。 - [ ] 再次 fetch 能读回变更。 - [ ] revision 冲突被拦截。 ### 12.5 真实网页 smoke - [ ] 新建测试页面,写入唯一前缀 `TEST-AI-TOOL-<timestamp>`。 - [ ] `mnote.doc.fetch scope=full detail=with_ids` 读取页面。 - [ ] `mnote.doc.find` 定位测试段落。 - [ ] `mnote.doc.plan_update command=block_replace dryRun=true` 生成计划。 - [ ] `mnote.block.replace dryRun=false` 替换段落。 - [ ] 页面截图证明内容可见。 - [ ] `/api/page-aggregate/:id` 证明内容持久化。 - [ ] `mnote.doc.fetch` 再次证明 AI 可读回。 验收标准: - [ ] 证据目录写入 `tmp/hermes-tester/<run-id>/`。 - [ ] 失败时记录到 `bugs/07-ai/process/` 或真正 owner 分类。 - [ ] 通过后才能把对应 checklist 勾到 done。 --- ## 13. 与当前页面 AI 的关系 当前已有工具继续保留: - `mnote.page.get` - `mnote.page.save` - `mnote.page.update_title` - `mnote.page.update_options` - `mnote.artifact.*` 但口径调整为: - `mnote.page.get` 是页面级读取,不是长期精确块读取。 - `mnote.page.save` 是页面级兜底写入,只适合 append/prepend/replace 等粗粒度操作。 - 精确编辑应迁移到 `mnote.doc.*` / `mnote.block.*`。 - 页面 AI 面板展示工具时,应把 `page.save` 标为高风险或粗粒度。 --- ## 14. 迁移完成定义 本文作为页面/块 AI 工具体系路线图与合同已经归档为 `DONE`;执行验收不再由本文继续承接,而是转入: - `/mnt/Data1T/mnote/design/old/07-ai/process/7-10-page-block-ai-tooling-execution-checklist-v1.md` 当前已成立的 done 边界: - [x] `mnote.doc.fetch` / `mnote.doc.find` 已进入 Rust Hermes tool manifest 与 dispatch,并已有最小真实页面 smoke 证据。 - [x] `mnote.doc.plan_update` 已进入 dry-run 计划链,可返回 diff、warnings、risk、blocked。 - [x] `mnote.block.replace`、`mnote.block.insert_after`、`mnote.block.move_after` 已形成最小块写入闭环。 - [x] `mnote.page.save` 已在 UI/manifest 口径中降为页面级兜底工具,不再作为默认精确块编辑入口。 - [x] 后续执行项以 `7-10` 跟踪,仍包括 `scope=selection`、`format=page_xml/text`、多块插入、复杂块移动矩阵和持久审阅 UI。