12 KiB
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 再写一套逻辑
正确关系:
Tool -> Command / Query / Job -> Domain + Storage + EventLog
2.3 每条写命令都要可审计
至少要记录:
- 谁发起
- 从哪里发起
- 改了什么
- 为什么改
- 影响了哪些对象
- 是否成功
2.4 AI 默认只能调用系统工具
AI 不应默认获得:
- 任意 SQL
- 任意文件写入
- 任意 UI 按钮点击
- 任意内部未定型方法
AI 应调用:
- 稳定的 Tool API
- 明确的参数模型
- 明确的权限模型
3. 三层协议面
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。
建议结构:
{
"actor": {
"type": "human",
"id": "user_123",
"session_id": "ui_session_xxx"
}
}
或:
{
"actor": {
"type": "agent",
"id": "agent_session_456",
"provider": "openai",
"model": "gpt-5.4-mini"
}
}
或:
{
"actor": {
"type": "system",
"id": "job_runner"
}
}
用途:
- 审计
- 权限判断
- 限流
- 错误追踪
5. Command API
5.1 通用结构
建议所有命令统一壳:
{
"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 通用返回
{
"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_pagerename_pagemove_pagearchive_pagerestore_pageset_page_property
块类
insert_blockupdate_blockdelete_blockmove_blockbatch_apply_block_opsreplace_block_content
引用类
create_referenceremove_referencerebind_reference_anchor
资产类
attach_assetreplace_asset_versionset_asset_metadataextract_asset_outline
工作区 / 系统类
create_taskcancel_taskrebuild_search_indexrun_import_jobrun_export_job
6. Command 设计约束
6.1 命令应小而明确
优先:
insert_blockmove_blockset_page_property
避免:
save_everythingupdate_editor_statemutate_page_with_ui_payload
6.2 支持幂等键
AI 可能重试、网络可能重放,因此:
- 所有外部写命令建议支持
idempotency_key - 同一作用域内重复提交应可去重
6.3 支持 dry-run / validate-only
这样 AI 可以先问:
- 这个操作是否合法?
- 会影响哪些对象?
- 是否需要确认?
这对 Agent 很重要。
6.4 支持批量 ops,但要有边界
对于 Mindmap、结构调整、导入转换,可以提供:
batch_apply_block_opsapply_mindmap_opsapply_document_transform_ops
但要求:
- ops 必须结构化
- 单条失败策略明确
- 返回逐条结果
7. Query API
7.1 通用结构
{
"query": "page.get",
"actor": {
"type": "human",
"id": "user_1"
},
"scope": {
"workspace_id": "ws_1"
},
"params": {
"page_id": "page_1"
}
}
7.2 查询分类
页面与块
page.getpage.treepage.list_childrenblock.getblock.subtree
检索
search.textsearch.referencesearch.assetsearch.page_by_title
资产
asset.getasset.list_versionsasset.resolve_anchor
日志与会话
command_log.listcommand_log.getevent.listagent_session.gettask.list
7.3 输出要求
- 输出稳定、字段可预期
- 支持分页、游标、过滤
- 避免直接返回前端组件所需临时形态
- 对大型对象支持摘要与展开模式
8. Tool API
8.1 Tool API 的职责
Tool API 是给:
- AI Agent
- CLI
- 自动化脚本
- 外部集成方
用的能力层。
它的原则是:
- 工具名清晰
- 参数结构明确
- 返回格式稳定
- 明确确认策略与权限策略
8.2 工具命名风格
建议按领域分组:
note.read_pagenote.list_childrennote.insert_blocknote.update_blocknote.move_blocknote.searchasset.attach_fileasset.extract_outlineoffice.get_selectionoffice.replace_selectionmindmap.apply_opssystem.get_recent_commands
也可以对外映射为 MCP 风格扁平命名,但内核层建议保留层级语义。
8.3 Tool 与 Command/Query 的映射
例如:
note.read_page->page.getnote.insert_block->insert_blocknote.search->search.textasset.attach_file->attach_assetmindmap.apply_ops->apply_mindmap_ops
8.4 Tool 返回
建议统一包含:
okdatawarningsrequires_confirmationconfirmation_reasonnext_actions
例如:
{
"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 通用结构
{
"job": "import_document",
"job_id": "job_1",
"actor": { "type": "agent", "id": "agent_1" },
"input": {
"asset_id": "asset_1"
}
}
9.2 Job 状态
queuedrunningwaiting_confirmationsucceededfailedcancelled
9.3 Job 输出
- 结果对象
- 生成的 page / asset / reference
- 日志摘要
- 错误详情
10. Confirmation Policy
不是所有工具都应直接执行。
建议分级:
10.1 无需确认
- 只读查询
- 本地摘要生成
- 小范围插入草稿块
10.2 建议确认
- 批量删除
- 批量重排
- 覆盖资产版本
- 导出到外部位置
10.3 强制确认
- 大范围页面删除
- 全库重建/迁移
- 覆盖性导入
- 对外同步/发布
确认策略应由内核或 policy 层判断,不能完全由前端决定。
11. Error Model
错误必须结构化。
建议格式:
{
"ok": false,
"error": {
"code": "BLOCK_NOT_FOUND",
"message": "目标块不存在",
"details": {
"block_id": "block_xxx"
},
"retryable": false,
"suggested_action": "请先刷新页面树或重新读取块子树"
}
}
分类建议:
VALIDATION_ERRORPERMISSION_DENIEDNOT_FOUNDCONFLICTSTALE_REVISIONRATE_LIMITEDEXTERNAL_ADAPTER_ERRORINTERNAL_ERROR
这样 AI 才能根据错误做下一步,而不是只看到一段字符串。
12. Revision / Concurrency
12.1 为什么必须有 revision
AI 与人可能同时修改。
如果没有 revision:
- 很容易覆盖彼此内容
- 工具重试会写乱
- 无法做冲突判断
12.2 建议
Page级 revisionBlock级 revisionAssetVersion级 version_no
命令可支持:
expected_revisionon_conflict策略(fail / merge_if_possible / create_patch)
13. OnlyOffice 协议边界
OnlyOffice 不应直接暴露为“让 AI 操控 iframe”。
建议拆成两层:
13.1 Office Adapter Query
office.get_active_assetoffice.get_selectionoffice.get_current_pageoffice.list_bookmarks
13.2 Office Adapter Command
office.insert_textoffice.replace_selectionoffice.insert_commentoffice.save_as_new_versionoffice.extract_outline
原则:
- Tool 面暴露的是“Office 文档能力”
- 不是浏览器点击坐标或 iframe DOM
- 若插件不可用,应返回结构化不可用错误
14. Mindmap 协议边界
继续保留既有设计里最有价值的部分:ops。
建议:
mindmap.getmindmap.apply_opsmindmap.expand_nodemindmap.attach_refs
其中 mindmap.apply_ops 入参可以继续沿用:
addChildaddSiblingAfterupdateTextsetHyperlinksetRefsappendNotedeleteNode
但要求:
- 所有调用都写入统一命令日志
- mindmap 不再走单独一套野生落盘体系
15. CLI 协议面
CLI 不是开发附属品,而是系统正式壳层。
建议 CLI 优先覆盖:
mnote page get <page-id>mnote page treemnote block insertmnote block movemnote search textmnote asset attachmnote office selectionmnote job run import-documentmnote 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_pagerename_pageinsert_blockupdate_blockdelete_blockmove_blockattach_assetcreate_reference
Query
page.getpage.treeblock.subtreesearch.textasset.getcommand_log.list
Tool
note.read_pagenote.insert_blocknote.update_blocknote.move_blocknote.searchasset.attach_filesystem.get_recent_commands
这套足以先打通:
- 人类基础编辑
- CLI 基础操作
- AI 基础介入
- 审计与回放链路
18. 一句话结论
mnote-rust 的协议设计应坚持:
Command 负责改,Query 负责读,Tool 负责给 AI/CLI 用,Job 负责长流程;所有入口最终收敛到同一 Rust 内核,不再允许页面私有接口、编辑器私有接口、临时胶水接口长期并存。