# 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 ` - `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 内核,不再允许页面私有接口、编辑器私有接口、临时胶水接口长期并存。**