- 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
34 KiB
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 也应该堆很多工具”,而在于它把文档工具做成了三层:
- Shortcut 层:面向人和 Agent 的高层命令,例如
docs +fetch、docs +update、docs +media-insert。 - 稳定文档操作协议层:用
doc-format、scope、detail、command、block_id、revision_id、dry-run表达文档读写。 - 底层 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 具体参考位置
参考根目录:
/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 |
<title>、<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 确认机制 |
快速定位命令:
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.operationsschema,且它面向 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/outputSchemareadonly/destructive/idempotent/requiresApproval- ACL role:
readonly、editor、admin selectionEffect- executor 前置检查、ACL、query/command 分发
mnote 应吸收这些元数据,但命名和执行面必须改成 Rust-owned:
{
"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 辅助,但不能替代 RustEditorBlock.block_id。server-ai-toolkit的_hash是 AI 编辑定位/变化检测辅助,不能替代业务blockId;它可以参与 mnoterevisionRef的 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.contentlegacy 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.getpage.body.savepage.body.apply_patcheditor.block.insert_aftereditor.block.replaceeditor.block.deleteeditor.block.move_aftertree.node.createtree.subtree.move
要求:
- 可以随内核演进调整。
- 必须有测试覆盖。
- 由 L1 canonical tools 翻译调用。
L1:AI 可调用 canonical tools
Hermes skill/plugin 对外暴露的稳定工具层。第一阶段只做少量、可解释、可验收工具。
建议命名:
mnote.doc.fetchmnote.doc.findmnote.doc.plan_updatemnote.doc.apply_updatemnote.block.fetchmnote.block.insert_aftermnote.block.replacemnote.block.deletemnote.block.move_aftermnote.page.appendmnote.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_reportmnote.workflow.rewrite_sectionmnote.workflow.extract_action_itemsmnote.workflow.create_meeting_notemnote.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、属性、资源引用和结构化块的场景。
最小形态示例:
<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 定位和编辑的稳定投影。
入参:
{
"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 | jsondetail:simple | with_ids | fullscope:full | outline | range | keyword | section | selection | block
返回:
{
"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 先定位再编辑。
入参:
{
"workspaceId": "ws_1",
"documentId": "doc_1",
"query": "待办",
"match": "text",
"limit": 20
}
返回:
{
"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
用途:只生成变更计划和诊断,不实际写入。
入参:
{
"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_replaceblock_insert_afterblock_replaceblock_deleteblock_move_afterappendoverwrite
返回:
{
"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 过的文档变更计划。
入参:
{
"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。
返回:
{
"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 的稳定服务端版本。
入参:
{
"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 | textincludeChildren默认true,但复杂块可返回unsupportedReason。contextBefore/contextAfter只返回同父级上下文。
返回:
{
"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
用途:最小精确块写工具,作为第一批块级写入候选。
入参:
{
"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
用途:在指定块后插入一个或多个块。
入参:
{
"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 块后。第一阶段仅作为受限结构性写工具开放。
入参:
{
"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。
- 跨页面移动。
返回:
{
"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 与 DOMdata-block-id/ Page Aggregate 一致。keyword读取不会返回整页超大正文。- 失败时返回权限/不存在/空页面的结构化错误。
Phase B:dry-run / plan
目标:
- 建立
mnote.doc.plan_update。 - 写入前先给出可解释 diff、风险、warnings。
可做:
- 支持
str_replacedry-run。 - 支持
block_replacedry-run。 - 支持
block_insert_afterdry-run。 - 支持
appenddry-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. 架构优先级判断
当前应并行推进,但优先级要明确:
- 先稳定 Page Aggregate / block identity / editor save-refresh 链路:这是块级写工具能否长期可靠的根。
- 同时设计并冻结 L1 canonical tools 合同:避免继续把临时页面保存扩展成长期能力。
- 先实现只读和 dry-run:这部分对底层写链依赖小,能立即改善 AI 可靠性。
- 等硬门槛满足后开放最小块写入:先
replace/insert_after,不要一口气做完整块操作。 - 最后做 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.getmnote.page.savemnote.page.update_titlemnote.page.update_optionsmnote.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 边界:
mnote.doc.fetch/mnote.doc.find已进入 Rust Hermes tool manifest 与 dispatch,并已有最小真实页面 smoke 证据。mnote.doc.plan_update已进入 dry-run 计划链,可返回 diff、warnings、risk、blocked。mnote.block.replace、mnote.block.insert_after、mnote.block.move_after已形成最小块写入闭环。mnote.page.save已在 UI/manifest 口径中降为页面级兜底工具,不再作为默认精确块编辑入口。- 后续执行项以
7-10跟踪,仍包括scope=selection、format=page_xml/text、多块插入、复杂块移动矩阵和持久审阅 UI。