Files
mnote/rust/design/core/02-command-query-tool-protocol-v0.md
T

718 lines
12 KiB
Markdown
Raw Normal View History

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