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