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

718 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 内核,不再允许页面私有接口、编辑器私有接口、临时胶水接口长期并存。**