feat(ai): switch page ai to hermes panel
This commit is contained in:
@@ -11,7 +11,7 @@
|
||||
> - 本稿对应 `Phase 7 v2` 的“文档页 AI 最小闭环”已完成,故迁入 `done/`
|
||||
> - 本稿完成不等于整个 `Phase 7 v2` 已完成;结构化知识写链仍以后续阶段继续推进
|
||||
> - 2026-05-05 追加说明:本稿记录的是 `openai-agents-python` sidecar 作为过渡主链的完成状态,不代表当前长期方向;当时长期口径曾由 `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 收口为 `mnote-cli` 是唯一长期 agent 执行面
|
||||
> - 2026-05-13 追加说明:`mnote-cli` 唯一长期 agent 执行面口径已被 `/mnt/Data1T/mnote/design/07-ai/process/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖;新的长期方向是页面 AI 面板仅作为 Hermes 页面内客户端,Hermes 持有 AI 会话真相,mnote 通过 Hermes skill/plugin 暴露业务工具
|
||||
> - 2026-05-13 追加说明:`mnote-cli` 唯一长期 agent 执行面口径已被 `/mnt/Data1T/mnote/design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖;新的长期方向是页面 AI 面板仅作为 Hermes 页面内客户端,Hermes 持有 AI 会话真相,mnote 通过 Hermes skill/plugin 暴露业务工具
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -0,0 +1,544 @@
|
||||
# 7-2 [done] Phase 7 结构化 Artifact 写链最小落地方案 v1
|
||||
|
||||
> 更新时间:2026-04-23
|
||||
>
|
||||
> 上位依据:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-tree-first-graph-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-1-phase7-document-ai-minimum-loop-checklist-v1.md`
|
||||
>
|
||||
> 2026-05-05 追加说明:
|
||||
> - 本稿的对象模型、artifact 写链与 kernel 边界仍然有效
|
||||
> - 当时触发与执行口径曾被 `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md` 覆盖
|
||||
> - 该 `mnote-cli` host 口径现已被 2026-05-13 的 Hermes 面板主线覆盖
|
||||
>
|
||||
> 2026-05-13 追加说明:
|
||||
> - 本稿的对象模型、artifact 写链、projection-only `AI Artifacts` 分组与 kernel 边界继续有效
|
||||
> - 触发与执行口径改由 `/mnt/Data1T/mnote/design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md` 覆盖
|
||||
> - 当前凡是提到“页面 AI 面板触发”或“页面 AI host 固定动作”的位置,都应理解为:
|
||||
> 页面 AI 面板作为 Hermes 页面内客户端发起意图,Hermes 通过 mnote skill/plugin 调用正式 artifact 工具,最终写入仍回到 Rust runtime / kernel
|
||||
|
||||
---
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份稿只回答一个问题:
|
||||
|
||||
> **在文档页 AI 最小闭环已经完成之后,`summary node / ai_note node / reference edge` 这三条结构化知识写链,第一版到底如何落地,才能既进入 kernel,又不重新污染页面树、文件树和页面主链。**
|
||||
|
||||
当前不写这份稿,后续最容易出现两类跑偏:
|
||||
|
||||
- 把 `summary / ai_note` 继续做成 AI 面板里的临时聊天产物,而不是 kernel node
|
||||
- 为了让文件树看见它们,额外发明一个 `AI Artifacts` 真容器节点,重新污染树真相
|
||||
|
||||
因此本稿的目标不是“做完整 AI 平台”,而是:
|
||||
|
||||
> **只把 `summary node / ai_note node / reference edge` 这三条结构化写链,收口成第一版可执行、可验证、可继续扩展的最小实现。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前结论
|
||||
|
||||
第一版固定采用下面这组边界,不再开放:
|
||||
|
||||
1. 只做三类结构对象:
|
||||
- `summary node`
|
||||
- `ai_note node`
|
||||
- `reference edge`
|
||||
2. 只允许围绕**当前文档页**创建,不做跨页、跨工作区写链
|
||||
3. 第一版允许页面 AI 面板提供固定快捷入口,但快捷入口本质是向 Hermes 发送意图;Hermes 通过 mnote skill/plugin 调用 artifact 工具,不再由页面 AI host 或 `mnote-cli` host 私有触发
|
||||
4. 点击按钮后直接创建,不走“先预览再确认”的两阶段流
|
||||
5. `summary node` 默认单例覆盖更新;`ai_note node` 每次新建
|
||||
6. 两者都落成**可编辑的页面型节点**
|
||||
7. 文件树可见,页面树默认不可见
|
||||
8. 文件树中的 `AI Artifacts` 只是 projection-only 的虚拟分组,不是新的真实 kernel node
|
||||
9. 每次创建 `summary node` 或 `ai_note node` 时,自动连一条指向当前页的 `reference edge`
|
||||
|
||||
一句话收口:
|
||||
|
||||
> **第一版不是让 AI 自由生长“知识图谱”,而是让当前页能够稳定产出两类正式 artifact node,并带上一条最小来源边。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 为什么第一版只做这三条
|
||||
|
||||
### 3.1 这是从“正文写链”走向“结构写链”的最小跨越
|
||||
|
||||
当前 `7-1` 已经完成的是:
|
||||
|
||||
- AI 读取当前页 page aggregate 上下文
|
||||
- AI 通过 `page.head.updateTitle / page.body.save` 回写标题和正文
|
||||
|
||||
但这仍然只是在“改当前页”。
|
||||
|
||||
如果要让 AI 真正进入 `tree-first graph kernel`,第一步不应该直接跳到:
|
||||
|
||||
- 跨页知识网
|
||||
- PDF / Book 结构化摄取
|
||||
- 完整 index node / retrieval 主线
|
||||
|
||||
而应先从三条最小结构写链开始:
|
||||
|
||||
- 为当前页生成一个 `summary node`
|
||||
- 为当前页生成一个 `ai_note node`
|
||||
- 把它们用 `reference edge` 连回当前页
|
||||
|
||||
### 3.2 这三条最适合当前主线边界
|
||||
|
||||
这三者同时满足:
|
||||
|
||||
- 与当前页 AI 主链直接相邻
|
||||
- 不要求跨页权限扩展
|
||||
- 不要求额外引入新的树真相层
|
||||
- 能直接复用当前的 page aggregate 上下文
|
||||
- 能验证 `typed node / typed edge` 进入产品主线,而不是继续只停在文档里
|
||||
|
||||
### 3.3 `PDF / Book -> kernel index` 当前必须继续后置
|
||||
|
||||
`kernel index` 第一版不并入本稿,原因很明确:
|
||||
|
||||
- 它依赖更稳定的摄取边界
|
||||
- 它会引入更重的异步任务与检索协议
|
||||
- 它不是当前页 AI host 面板的最小邻接能力
|
||||
|
||||
所以本稿故意不把阶段 5 全写成一个“大包”,而是只切出:
|
||||
|
||||
> **当前页 -> artifact node / reference edge**
|
||||
|
||||
---
|
||||
|
||||
## 4. 对象模型
|
||||
|
||||
第一版真正进入 kernel 的对象只有三类。
|
||||
|
||||
### 4.1 `summary node`
|
||||
|
||||
`summary node` 是一个真实的 `KernelNode`,`node_type = summary`。
|
||||
|
||||
它的产品语义固定为:
|
||||
|
||||
- 它代表“当前页的最新摘要页”
|
||||
- 它不是聊天消息
|
||||
- 它不是页面内一段临时 block
|
||||
- 它是可编辑页面型节点,但语义上属于 artifact
|
||||
|
||||
它的最小数据要求:
|
||||
|
||||
- `node_type = summary`
|
||||
- `workspace_id = 当前页 workspace`
|
||||
- `metadata.title = 当前页标题 + Summary`
|
||||
- `content = 可被页面主编辑区消费的正文载荷`
|
||||
- `refs.reference_node_ids` 至少包含当前页 `pageId`
|
||||
- `refs.source_node_ids` 至少包含当前页 `pageId`
|
||||
- `audit` 带最小 request / trace / actor 信息
|
||||
|
||||
### 4.2 `ai_note node`
|
||||
|
||||
`ai_note node` 也是一个真实的 `KernelNode`,`node_type = ai_note`。
|
||||
|
||||
它的产品语义固定为:
|
||||
|
||||
- 它代表“围绕当前页生成的一份 AI 工作笔记页”
|
||||
- 它不是摘要快照
|
||||
- 它允许后续继续人工编辑
|
||||
- 它是页面型节点,但语义上属于 artifact
|
||||
|
||||
它的最小数据要求:
|
||||
|
||||
- `node_type = ai_note`
|
||||
- `workspace_id = 当前页 workspace`
|
||||
- `metadata.title = 当前页标题 + AI Note + 时间戳/序号`
|
||||
- `content = 可被页面主编辑区消费的正文载荷`
|
||||
- `refs.reference_node_ids` 至少包含当前页 `pageId`
|
||||
- `refs.source_node_ids` 至少包含当前页 `pageId`
|
||||
- `audit` 带最小 request / trace / actor 信息
|
||||
|
||||
### 4.3 `reference edge`
|
||||
|
||||
`reference edge` 在第一版里不新增新的 edge type 名字,先收口到现有 `KernelEdgeType::References`。
|
||||
|
||||
它的产品语义固定为:
|
||||
|
||||
- 表示 artifact node 引用了当前页
|
||||
- 它不是父子边
|
||||
- 它不进入页面树 / 文件树
|
||||
- 它只进入关系层 / 引用层 /后续 graph traversal
|
||||
|
||||
第一版边方向固定为:
|
||||
|
||||
- `from_node_id = artifact node`
|
||||
- `to_node_id = 当前页 node`
|
||||
- `edge_type = references`
|
||||
|
||||
这样可以稳定表达:
|
||||
|
||||
> **这个 summary / ai_note 是围绕当前页生成的。**
|
||||
|
||||
### 4.4 `AI Artifacts` 不是对象,只是投影分组
|
||||
|
||||
第一版明确禁止把 `AI Artifacts` 做成真实 node。
|
||||
|
||||
它只允许存在于:
|
||||
|
||||
- 文件树 projection
|
||||
- 可能的 Inspector / artifact 列表 projection
|
||||
|
||||
它不允许存在于:
|
||||
|
||||
- kernel node truth
|
||||
- tree command truth
|
||||
- page aggregate truth
|
||||
|
||||
理由很简单:
|
||||
|
||||
- 它只是展示分组
|
||||
- 它不是用户真正的知识对象
|
||||
- 它不应反向塑造树真相
|
||||
|
||||
---
|
||||
|
||||
## 5. 写入语义
|
||||
|
||||
### 5.1 统一动作面
|
||||
|
||||
第一版只开放两个显式动作:
|
||||
|
||||
- `create_summary_artifact_for_current_page`
|
||||
- `create_ai_note_artifact_for_current_page`
|
||||
|
||||
这里先用产品动作名描述,不在本稿中强行冻结底层最终 route/path 名。
|
||||
|
||||
长期原则固定为:
|
||||
|
||||
- 它们必须进入 Rust runtime / bridge-runtime 主写链
|
||||
- 不允许绕过 kernel 直接只写前端本地状态
|
||||
- 不允许只停留在 AI 面板会话日志里
|
||||
|
||||
### 5.2 `summary node` 的写入策略
|
||||
|
||||
`summary node` 第一版采用**单例覆盖更新**。
|
||||
|
||||
固定规则:
|
||||
|
||||
1. 先按“当前页 -> artifact node_type = summary”查询是否已有 summary node
|
||||
2. 若已存在:
|
||||
- 复用该节点 `node_id`
|
||||
- 覆盖其 `content`
|
||||
- 更新其 `metadata.updated_at`
|
||||
- 保留其页面型身份
|
||||
3. 若不存在:
|
||||
- 新建一个 `summary node`
|
||||
- 自动附加到当前页的 artifact projection 分组下
|
||||
- 自动创建一条 `reference edge`
|
||||
|
||||
为什么 `summary` 采用单例:
|
||||
|
||||
- 它更像“当前页最新摘要”
|
||||
- 否则文件树下会快速积累多份摘要页
|
||||
- 第一版要先控制 artifact 数量,而不是先保留完整历史
|
||||
|
||||
### 5.3 `ai_note node` 的写入策略
|
||||
|
||||
`ai_note node` 第一版采用**每次新建**。
|
||||
|
||||
固定规则:
|
||||
|
||||
1. 每次点击 `创建 AI Note` 都新建一个 `ai_note node`
|
||||
2. 标题默认带时间戳或递增序号
|
||||
3. 创建后自动创建一条 `reference edge`
|
||||
4. 不尝试合并、覆盖旧 `ai_note`
|
||||
|
||||
为什么 `ai_note` 采用多实例:
|
||||
|
||||
- 它更像工作产物
|
||||
- 一次次 AI 分析、本轮整理、本轮方案,本来就可能并存
|
||||
- 如果也做成单例,会过早丢失工作过程
|
||||
|
||||
### 5.4 来源边写入策略
|
||||
|
||||
创建 `summary node` 或 `ai_note node` 时,自动写一条 `reference edge`。
|
||||
|
||||
第一版不要求用户再单独确认,也不要求额外按钮。
|
||||
|
||||
这条边的作用不是“展示炫酷图谱”,而是保证两个基本能力:
|
||||
|
||||
1. 后续能追溯这个 artifact 来源于哪一页
|
||||
2. 后续文件树/关系面板/graph traversal 不需要重新猜测来源
|
||||
|
||||
### 5.5 第一版不做的写入动作
|
||||
|
||||
第一版明确不做:
|
||||
|
||||
- artifact -> 多来源页面的复合引用
|
||||
- artifact -> 块级 evidence 的精细挂接
|
||||
- 跨页创建 summary / ai_note
|
||||
- 人工拖拽 artifact 重新挂到别的页面
|
||||
- artifact 自动进页面树
|
||||
|
||||
这些都属于第二版以后再做的能力。
|
||||
|
||||
---
|
||||
|
||||
## 6. 触发方式与产品入口
|
||||
|
||||
### 6.1 允许固定按钮触发,但必须经过 Hermes
|
||||
|
||||
第一版允许页面 AI 面板提供两个固定快捷按钮:
|
||||
|
||||
- `创建 Summary`
|
||||
- `创建 AI Note`
|
||||
|
||||
但按钮不得绕过 Hermes 或 mnote plugin 直接写入。正确链路是:
|
||||
|
||||
```text
|
||||
Leptos 页面 AI 面板
|
||||
-> Hermes session/run
|
||||
-> mnote Hermes skill/plugin
|
||||
-> Rust runtime / kernel
|
||||
-> artifact node / reference edge
|
||||
```
|
||||
|
||||
自然语言触发可以作为后续能力加入,但第一版验收仍以固定按钮或固定 tool intent 为准,避免 artifact 写链质量和权限边界同时失控。
|
||||
|
||||
### 6.2 点击后直接创建
|
||||
|
||||
第一版点击按钮后,直接创建,不走预览确认。
|
||||
|
||||
这是一个非常明确的产品取舍:
|
||||
|
||||
- 优点:动作和结果一一对应,最容易验证主写链
|
||||
- 缺点:产物质量控制不如“先预览再落库”
|
||||
|
||||
当前优先级里,第一版更重视:
|
||||
|
||||
- 正式写链成立
|
||||
- kernel node / edge 成立
|
||||
- 文件树 projection 成立
|
||||
- Hermes tool call -> mnote plugin -> Rust kernel 的边界成立
|
||||
|
||||
而不是先做复杂的人机审核流。
|
||||
|
||||
### 6.3 内容来源固定为当前页全文
|
||||
|
||||
第一版两个按钮的内容来源都固定为:
|
||||
|
||||
- 当前页全文
|
||||
|
||||
不做:
|
||||
|
||||
- 仅选区生成
|
||||
- 仅当前可见块生成
|
||||
- 混入会话消息做额外上下文真相
|
||||
|
||||
这能确保第一版边界足够清楚:
|
||||
|
||||
> **artifact 是围绕当前页全文生成,而不是围绕聊天面板局部状态生成。**
|
||||
|
||||
---
|
||||
|
||||
## 7. 文件树 projection 与页面树隐藏规则
|
||||
|
||||
### 7.1 页面树默认不可见
|
||||
|
||||
`summary node` 与 `ai_note node` 第一版默认不进入页面树 projection。
|
||||
|
||||
原因:
|
||||
|
||||
- 页面树是日常导航主链
|
||||
- artifact 进入页面树会明显污染日常使用
|
||||
- 它们虽然是页面型节点,但不是“用户正常页面层级”的一部分
|
||||
|
||||
### 7.2 文件树可见
|
||||
|
||||
`summary node` 与 `ai_note node` 第一版可以在文件树 projection 中可见。
|
||||
|
||||
这样做的目的不是把文件树变成第二个知识图谱,而是保留一个“必要时可管理”的管理入口。
|
||||
|
||||
### 7.3 `AI Artifacts` 虚拟分组规则
|
||||
|
||||
在文件树 projection 中,为每个当前页生成一个 projection-only 分组:
|
||||
|
||||
- `AI Artifacts`
|
||||
|
||||
固定规则如下:
|
||||
|
||||
1. 它不是真实 node
|
||||
2. 它不持久化到 kernel
|
||||
3. 它只存在于 `file_tree` projection 结果中
|
||||
4. 它的 children 由“当前页关联的 artifact nodes”动态投影得出
|
||||
|
||||
### 7.4 文件树中的相对位置
|
||||
|
||||
第一版推荐把 `AI Artifacts` 放在当前页下面,但与普通子页面分区显示。
|
||||
|
||||
不是:
|
||||
|
||||
- artifact 混在普通子页面列表里
|
||||
|
||||
而是:
|
||||
|
||||
- 当前页
|
||||
- 普通文件/子页
|
||||
- `AI Artifacts`
|
||||
- `Summary`
|
||||
- `AI Note 2026-04-23 10:31`
|
||||
- `AI Note 2026-04-23 10:46`
|
||||
|
||||
这样既满足“文件树可管理”,又最大限度避免污染普通导航。
|
||||
|
||||
### 7.5 `reference edge` 不进树
|
||||
|
||||
`reference edge` 永远不是树项。
|
||||
|
||||
它应只进入:
|
||||
|
||||
- 关系面板
|
||||
- 引用面板
|
||||
- graph traversal
|
||||
- 后续结构阅读态
|
||||
|
||||
它不允许伪装成页面树 / 文件树节点。
|
||||
|
||||
---
|
||||
|
||||
## 8. 与当前主链的关系
|
||||
|
||||
### 8.1 不改变当前页主编辑区真相
|
||||
|
||||
第一版 artifact 写链不会改变:
|
||||
|
||||
- 当前页正文依然走 `page.body.save`
|
||||
- 当前页标题依然走 `page.head.updateTitle`
|
||||
- 当前页页面设置依然走 `page.layout.updateOptions`
|
||||
|
||||
artifact 写链是新增的结构写链,不是对当前页主链的替代。
|
||||
|
||||
### 8.2 与 page aggregate 的关系
|
||||
|
||||
第一版 artifact 生成必须依赖当前 page aggregate 上下文,但不把 artifact 自身并入当前页的 page aggregate truth。
|
||||
|
||||
也就是说:
|
||||
|
||||
- page aggregate 提供生成材料
|
||||
- artifact 是新的 kernel node
|
||||
- artifact 不是当前页 `page_body` 的附属字段
|
||||
|
||||
这是必须守住的边界,否则又会回到“把结构对象塞进页面壳状态”的旧路。
|
||||
|
||||
### 8.3 与 tree command 的关系
|
||||
|
||||
artifact node 虽然默认不进页面树,但它们仍然是 tree-first graph kernel 的真实 node。
|
||||
|
||||
因此长期上仍应可复用:
|
||||
|
||||
- `kernel.node.get`
|
||||
- `kernel.subtree.get`
|
||||
- `kernel.edges.list`
|
||||
- `kernel.edge.attach`
|
||||
|
||||
但第一版不要求把 artifact 的全部树命令交互一次做完。
|
||||
|
||||
---
|
||||
|
||||
## 9. 最小实现分层
|
||||
|
||||
### 9.1 Kernel / Rust runtime 层
|
||||
|
||||
需要补的最小能力:
|
||||
|
||||
1. 创建 `summary node`
|
||||
2. 更新现有 `summary node`
|
||||
3. 创建 `ai_note node`
|
||||
4. 创建 `references` edge
|
||||
5. 按当前页查找其关联 artifact nodes
|
||||
|
||||
第一版不要求完整 artifact 生命周期,只要求:
|
||||
|
||||
- create
|
||||
- update summary
|
||||
- list by current page relation
|
||||
|
||||
### 9.2 Projection 层
|
||||
|
||||
需要补的最小能力:
|
||||
|
||||
1. `file_tree` projection 能识别“当前页关联的 artifact nodes”
|
||||
2. 在 projection 里注入一个虚拟 `AI Artifacts` 分组
|
||||
3. 页面树 projection 不注入该分组
|
||||
|
||||
### 9.3 前端文档页层
|
||||
|
||||
需要补的最小能力:
|
||||
|
||||
1. 页面 AI 面板显示两个固定按钮
|
||||
2. 点击按钮后触发相应写入动作
|
||||
3. 创建完成后,文件树对应 projection 刷新
|
||||
4. 若新建了 `ai_note node`,允许点击进入该页面
|
||||
5. 若更新了 `summary node`,允许点击进入摘要页
|
||||
|
||||
---
|
||||
|
||||
## 10. 验证口径
|
||||
|
||||
第一版完成,至少要满足以下验证。
|
||||
|
||||
### 10.1 Summary 链
|
||||
|
||||
- 当前页点击 `创建 Summary`
|
||||
- 若不存在 summary node,则新建
|
||||
- 若已存在,则覆盖更新
|
||||
- 文件树中当前页下出现或保留 `AI Artifacts / Summary`
|
||||
- 页面树中不出现该节点
|
||||
- 可打开该 `summary node`
|
||||
- kernel 中存在从 `summary node -> 当前页` 的 `references` edge
|
||||
|
||||
### 10.2 AI Note 链
|
||||
|
||||
- 当前页点击 `创建 AI Note`
|
||||
- 每次都新建一个新的 `ai_note node`
|
||||
- 文件树中当前页下 `AI Artifacts` 分组内累积出现新节点
|
||||
- 页面树中不出现这些节点
|
||||
- 可打开新建的 `ai_note node`
|
||||
- kernel 中存在从 `ai_note node -> 当前页` 的 `references` edge
|
||||
|
||||
### 10.3 真相边界
|
||||
|
||||
- `AI Artifacts` 不出现在真实 kernel node 列表中
|
||||
- `AI Artifacts` 只存在于文件树 projection 结果中
|
||||
- 当前页 page aggregate 不被额外塞入 artifact truth
|
||||
- artifact 不混入普通页面树导航主链
|
||||
|
||||
---
|
||||
|
||||
## 11. 明确不做什么
|
||||
|
||||
第一版明确不做:
|
||||
|
||||
- 自然语言隐式触发 artifact 创建
|
||||
- 跨页 summary / ai_note 写链
|
||||
- artifact 多来源引用
|
||||
- block 级 evidence 精细挂接
|
||||
- `AI Artifacts` 真实容器节点化
|
||||
- artifact 进入页面树默认主链
|
||||
- artifact 复杂权限模型
|
||||
- artifact 预览确认流
|
||||
- artifact 版本历史产品壳
|
||||
|
||||
---
|
||||
|
||||
## 12. 最终冻结口径
|
||||
|
||||
当前冻结如下:
|
||||
|
||||
> **阶段 5 的第一版,只把 `summary node / ai_note node / reference edge` 作为最小结构化知识写链落到 kernel。**
|
||||
|
||||
> **`summary node / ai_note node` 都是真实页面型 kernel node,`reference edge` 使用现有 `references` typed edge;`AI Artifacts` 只是文件树 projection 下的虚拟分组,不是真实对象。**
|
||||
|
||||
> **第一版只允许围绕当前页,通过页面 AI 面板固定按钮直接触发;`summary node` 单例覆盖更新,`ai_note node` 每次新建;创建节点时自动连一条指向当前页的 `reference edge`。**
|
||||
|
||||
一句话收口:
|
||||
|
||||
> **第一版的目标不是做“大而全 AI 知识层”,而是让当前页第一次稳定地产出正式 artifact node,并且能在不污染页面树真相的前提下进入文件树管理视图。**
|
||||
@@ -0,0 +1,428 @@
|
||||
# 7-3 [done] 页面 AI Hermes 面板与 mnote Plugin 主线方案 v1
|
||||
|
||||
> 更新时间:2026-05-13
|
||||
>
|
||||
> 上位依据:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-05-current-priority-overview.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/process/3-rust-web-long-term-architecture-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-5-page-aggregate-single-truth-alignment-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/process/5-6-page-aggregate-alignment-checklist-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/done/5-10-wolai-page-settings-and-ai-surface-alignment-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/05-editor-mainline/reference-code/hermes-web-ui-0.5.18`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-2-phase7-structured-artifact-write-chain-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
|
||||
>
|
||||
> 覆盖关系:
|
||||
> - 覆盖 `/mnt/Data1T/mnote/design/old/07-ai/process/7-phase7-ai-kernel-projection-plan-v4.md`
|
||||
> 中“`mnote-cli` 是唯一长期 agent 执行面”的口径。
|
||||
> - 保留 `v4` 中“Web 不应拥有第二套工具注册表、结构化写入必须回到 Rust runtime / kernel”的判断。
|
||||
> - 保留 `7-2` 中 `summary node / ai_note node / reference edge` 的对象模型和写入边界,
|
||||
> 但触发方改为 Hermes tool call,而不是页面 AI host 私有按钮或 `mnote-cli` host。
|
||||
|
||||
---
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
这份稿只回答一个问题:
|
||||
|
||||
> **未来页面 AI 的长期主语到底是谁。**
|
||||
|
||||
当前冻结答案是:
|
||||
|
||||
> **页面 AI 面板只是 Hermes 的页面内客户端;Hermes session/message/tool event/usage/model 才是会话真相;mnote 通过 Hermes skill/plugin 暴露业务能力。**
|
||||
|
||||
这不是把 mnote 的业务真相交给 Hermes。长期边界必须分清:
|
||||
|
||||
- Hermes 负责 AI 编排、会话、模型、tool call 调度和聊天历史。
|
||||
- mnote 负责页面、树、正文、artifact、edge、projection 和审计事实。
|
||||
- 页面 AI 面板只负责在文档页里打开一个 Hermes 客户端。
|
||||
|
||||
一句话收口:
|
||||
|
||||
> **AI 会话归 Hermes,mnote 能力归 Rust kernel;页面 AI 面板只连接两者。**
|
||||
|
||||
---
|
||||
|
||||
## 2. 为什么要从 CLI-first 改为 Hermes-first
|
||||
|
||||
`CLI-first` 解决了一个真实问题:避免 Web 前端继续拥有私有 AI 编排、私有工具注册表和私有写链。
|
||||
|
||||
但它也带来了新的错位:
|
||||
|
||||
- 页面 AI 面板开始伪装成 `mnote-cli` 图形客户端。
|
||||
- Hermes、Codex、`openai-agents-python` 被统一压成“外置 agent”,但实际用户希望页面 AI 就是 Hermes 面板。
|
||||
- 真实会话能力、模型选择、tool event、thinking、usage、历史搜索这些已经是 Hermes 的强项,mnote 自己再做一套会重复。
|
||||
- 当前运行态已经出现冲突:页面壳默认发送 `provider=hermes`,而 `/api/ai-agent/run` 又按旧退场口径返回 `ai_provider_bridge_unavailable`。
|
||||
|
||||
因此新的主线不是恢复旧的 Web 私有 AI 编排,而是:
|
||||
|
||||
> **把页面 AI 从 `mnote-cli host` 改成 Hermes client,把 mnote 能力从 Web 私有 tool 改成 Hermes 可发现、可调用的 skill/plugin。**
|
||||
|
||||
---
|
||||
|
||||
## 3. 长期分层
|
||||
|
||||
### 3.1 Hermes
|
||||
|
||||
Hermes 负责:
|
||||
|
||||
- session 创建、恢复、重命名、删除、搜索
|
||||
- message 存储与 conversation history
|
||||
- model / provider / profile 选择
|
||||
- streaming 事件、thinking / reasoning、usage
|
||||
- tool call 调度、排队、取消、恢复
|
||||
- skill / plugin 的发现和启停
|
||||
|
||||
Hermes 不负责:
|
||||
|
||||
- 直接写 mnote 的 Convex 表
|
||||
- 直接构造第二套 page aggregate
|
||||
- 直接决定页面树、文件树、artifact、edge 的事实结构
|
||||
|
||||
### 3.2 mnote Rust kernel / runtime
|
||||
|
||||
mnote Rust 负责:
|
||||
|
||||
- `Page Aggregate`
|
||||
- `page.*` command
|
||||
- `tree.*` command
|
||||
- `kernel.*` query / edge
|
||||
- `summary node / ai_note node / reference edge`
|
||||
- projection、audit、idempotency、workspace / actor scope
|
||||
|
||||
Rust 不负责:
|
||||
|
||||
- 存储 Hermes 聊天历史
|
||||
- 维护 Hermes session 列表
|
||||
- 重做 Hermes 模型、profile、usage 管理
|
||||
|
||||
### 3.3 页面 AI Leptos 面板
|
||||
|
||||
页面 AI 面板负责:
|
||||
|
||||
- 右下角入口与右侧抽屉壳层继续服从 Wolai 对齐结果
|
||||
- 用 Leptos 实现 Hermes chat 的页面内子集
|
||||
- 调用 Hermes session / run / stream API
|
||||
- 把当前页面上下文作为 Hermes run 的输入或 session workspace context
|
||||
- 展示 Hermes 返回的 message、reasoning、tool event、error、usage
|
||||
|
||||
页面 AI 面板不负责:
|
||||
|
||||
- 自己保存聊天真相
|
||||
- 自己维护工具注册表
|
||||
- 自己执行页面写入
|
||||
- 自己 fallback 到 `mnote-cli` 或旧 sidecar
|
||||
|
||||
### 3.4 mnote Hermes skill/plugin
|
||||
|
||||
mnote 需要作为 Hermes skill/plugin 暴露能力。
|
||||
|
||||
第一版建议能力分组:
|
||||
|
||||
- `mnote.page.get`
|
||||
- `mnote.page.save`
|
||||
- `mnote.page.update_title`
|
||||
- `mnote.page.update_options`
|
||||
- `mnote.tree.create`
|
||||
- `mnote.tree.move`
|
||||
- `mnote.search.documents`
|
||||
- `mnote.artifact.create_summary`
|
||||
- `mnote.artifact.create_ai_note`
|
||||
- `mnote.kernel.attach_reference`
|
||||
|
||||
这些工具可以由 plugin 内部调用:
|
||||
|
||||
- Rust Web 同源 tool bridge
|
||||
- `mnote-cli` JSON adapter
|
||||
- 或后续更稳定的 Rust plugin bridge
|
||||
|
||||
但对 Hermes 来说,它们必须表现为一组稳定 Hermes tools,而不是页面前端私有函数。
|
||||
|
||||
---
|
||||
|
||||
## 4. 会话真相
|
||||
|
||||
页面 AI 的会话真相固定在 Hermes。
|
||||
|
||||
mnote 不保存:
|
||||
|
||||
- 聊天消息列表
|
||||
- assistant 文本历史
|
||||
- thinking / reasoning 历史
|
||||
- tool event 完整展开状态
|
||||
- session 标题、分组、usage
|
||||
|
||||
mnote 可以保存:
|
||||
|
||||
- 结构化写入产生的 audit
|
||||
- artifact node
|
||||
- reference edge
|
||||
- page/body/title/options 的正式变更
|
||||
- 与一次 Hermes tool call 对应的 request / trace / actor / reason
|
||||
|
||||
也就是说,mnote 只保存“对 mnote 事实源造成影响的结果”,不复制 Hermes 的聊天数据库。
|
||||
|
||||
---
|
||||
|
||||
## 5. 页面上下文进入 Hermes 的方式
|
||||
|
||||
页面 AI 面板打开时,mnote 应提供最小上下文包:
|
||||
|
||||
- `workspaceId`
|
||||
- `documentId`
|
||||
- 页面标题
|
||||
- `pageAggregate` 摘要
|
||||
- 当前选区 / blockId / selected text
|
||||
- 页面设置
|
||||
- 当前用户 actor / capability 摘要
|
||||
|
||||
上下文传入 Hermes 有两种可接受方式:
|
||||
|
||||
1. 作为 run input / instructions 的结构化上下文。
|
||||
2. 作为 Hermes session workspace context,由 mnote panel 在创建或恢复 session 时设置。
|
||||
|
||||
第一版优先采用简单方式:
|
||||
|
||||
> 页面 AI 面板每次发起 run 时附带当前页面上下文摘要;Hermes 如需读取最新正文,再通过 `mnote.page.get` 工具回读。
|
||||
|
||||
这样可以避免把 page aggregate 大对象长期塞进 Hermes session,也避免 stale context 变成事实源。
|
||||
|
||||
---
|
||||
|
||||
## 6. API 与路由边界
|
||||
|
||||
### 6.1 退役 `/api/ai-agent/run` 主路径
|
||||
|
||||
`/api/ai-agent/run` 不再作为页面 AI 的长期主入口。
|
||||
|
||||
允许状态:
|
||||
|
||||
- 暂时保留为 legacy compat,明确返回旧接口退场信息
|
||||
- 或只用于旧 smoke / 对照验证
|
||||
|
||||
禁止状态:
|
||||
|
||||
- 页面 AI 新实现继续向它发送 `provider=hermes`
|
||||
- `/api/ai-agent/run` 继续作为 Hermes 面板的主代理
|
||||
- 它继续持有 mnote 私有工具注册表或执行编排
|
||||
|
||||
### 6.2 新增 Hermes client proxy
|
||||
|
||||
浏览器不应直接暴露 Hermes API key。
|
||||
|
||||
建议在 `mnote-web` 中提供同源薄代理:
|
||||
|
||||
- `/api/hermes/client/sessions`
|
||||
- `/api/hermes/client/runs`
|
||||
- `/api/hermes/client/events`
|
||||
- `/api/hermes/client/models`
|
||||
- `/api/hermes/client/tools`
|
||||
|
||||
这层只做:
|
||||
|
||||
- auth / cookie / token 转发
|
||||
- 同源安全边界
|
||||
- 页面上下文最小注入
|
||||
- 错误码标准化
|
||||
|
||||
这层不做:
|
||||
|
||||
- session 真相存储
|
||||
- message 真相存储
|
||||
- tool 执行编排
|
||||
- 旧 provider fallback
|
||||
|
||||
### 6.3 mnote tool bridge
|
||||
|
||||
Hermes 调用 mnote 工具时,应进入窄桥:
|
||||
|
||||
```text
|
||||
Hermes tool call
|
||||
-> mnote Hermes plugin
|
||||
-> mnote-web /api/hermes/tools/mnote/*
|
||||
-> Rust runtime / kernel command/query
|
||||
-> Hermes tool result
|
||||
```
|
||||
|
||||
第一版不要求一次性冻结最终 URL,但要求协议字段稳定:
|
||||
|
||||
- `toolName`
|
||||
- `arguments`
|
||||
- `workspaceId`
|
||||
- `documentId`
|
||||
- `actor`
|
||||
- `sessionId`
|
||||
- `traceId`
|
||||
- `idempotencyKey`
|
||||
- `dryRun`
|
||||
- `capabilityScope`
|
||||
|
||||
---
|
||||
|
||||
## 7. Leptos 面板参考范围
|
||||
|
||||
参考 `hermes-web-ui-0.5.18`,但只采用页面内必要子集。
|
||||
|
||||
第一版采用:
|
||||
|
||||
- Chat session list
|
||||
- Message list
|
||||
- Chat input
|
||||
- streaming delta
|
||||
- thinking / reasoning 展开
|
||||
- tool started / tool completed 展开
|
||||
- model selector
|
||||
- error / retry / abort
|
||||
- session search 可后置
|
||||
|
||||
第一版不采用:
|
||||
|
||||
- 平台 Channels 管理
|
||||
- Jobs / Cron 管理
|
||||
- Profiles 管理全页面
|
||||
- Logs 全页面
|
||||
- Files 全浏览器
|
||||
- Terminal
|
||||
- Group Chat
|
||||
- Hermes 全局 Settings
|
||||
|
||||
这些能力属于 Hermes 管理台,不属于 mnote 页面 AI 抽屉。
|
||||
|
||||
实现要求:
|
||||
|
||||
- 使用 Leptos island 实现,不引入 Vue / Naive UI。
|
||||
- 保留 Wolai 对齐的右下角入口和右侧 drawer 容器。
|
||||
- 文案和视觉以 mnote 文档页密度为准,不照搬 Hermes Web UI 的整站导航。
|
||||
|
||||
---
|
||||
|
||||
## 8. 结构化 Artifact 写链
|
||||
|
||||
`7-2` 的对象模型继续成立:
|
||||
|
||||
- `summary node`
|
||||
- `ai_note node`
|
||||
- `reference edge`
|
||||
- `AI Artifacts` projection-only 分组
|
||||
|
||||
但触发方改为 Hermes tool call:
|
||||
|
||||
- 用户可以在 Hermes 面板中自然语言要求总结当前页。
|
||||
- Hermes 决定调用 `mnote.artifact.create_summary`。
|
||||
- 或页面面板提供快捷按钮,但按钮本质也是向 Hermes 发送意图,不是绕过 Hermes 直接写 mnote。
|
||||
|
||||
第一版允许两个快捷入口:
|
||||
|
||||
- `创建 Summary`
|
||||
- `创建 AI Note`
|
||||
|
||||
但它们必须走:
|
||||
|
||||
```text
|
||||
Leptos panel -> Hermes session/run -> mnote plugin tool call -> Rust kernel
|
||||
```
|
||||
|
||||
不得走:
|
||||
|
||||
```text
|
||||
Leptos panel -> /api/documents/* 直接写 artifact
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 权限与安全
|
||||
|
||||
Hermes 可以调度工具,但 mnote 必须做最终授权。
|
||||
|
||||
每个 mnote tool call 至少校验:
|
||||
|
||||
- 当前 actor 是否登录
|
||||
- actor 是否属于 workspace
|
||||
- 当前页面是否允许 AI 读取
|
||||
- 当前页面是否允许 AI 写入
|
||||
- 当前工具是否允许写入页面 / 树 / artifact
|
||||
- 是否需要 `dryRun` 或确认
|
||||
|
||||
默认第一版:
|
||||
|
||||
- 读当前页:允许
|
||||
- 写当前页:按页面 capability
|
||||
- 创建 summary / ai_note:按当前页 artifact capability
|
||||
- 跨页写入:默认禁止
|
||||
- 跨 workspace:禁止
|
||||
|
||||
---
|
||||
|
||||
## 10. 迁移步骤
|
||||
|
||||
### Phase A:设计口径统一
|
||||
|
||||
- 新增本稿作为 07-ai 当前主线。
|
||||
- 将 `7-v4 CLI-first` 移入 `design/old/07-ai/process/` 并标记 `[recycle]`。
|
||||
- 更新仍引用 `mnote-cli` 唯一长期 agent 执行面的活跃设计稿。
|
||||
|
||||
### Phase B:薄 Hermes client proxy
|
||||
|
||||
- 在 `mnote-web` 补 Hermes client proxy。
|
||||
- 页面 AI 面板不再调用 `/api/ai-agent/run`。
|
||||
- 浏览器不直接持有 Hermes API key。
|
||||
|
||||
### Phase C:Leptos Hermes 面板最小子集
|
||||
|
||||
- 实现 session 创建 / 恢复。
|
||||
- 实现 run / stream / abort。
|
||||
- 实现 message / reasoning / tool event 展示。
|
||||
- 保留 Wolai 右侧抽屉壳。
|
||||
|
||||
### Phase D:mnote Hermes plugin
|
||||
|
||||
- 暴露 mnote tool manifest。
|
||||
- 先接 `mnote.page.get`、`mnote.page.save`、`mnote.artifact.create_summary`、`mnote.artifact.create_ai_note`。
|
||||
- 工具结果回到 Hermes tool event。
|
||||
|
||||
### Phase E:退役旧面板执行链
|
||||
|
||||
- `/api/ai-agent/run` 从主路径移除。
|
||||
- 旧 React `DocumentAiAgentPanel.runtime` 降为历史参考或 compat。
|
||||
- `provider=hermes/codex/claudecode` 502 不再出现在新页面 AI 主链。
|
||||
|
||||
---
|
||||
|
||||
## 11. 验收标准
|
||||
|
||||
第一版完成时必须满足:
|
||||
|
||||
- 页面 AI 抽屉打开后可创建或恢复 Hermes session。
|
||||
- Hermes session 存储中能看到页面 AI 的消息历史。
|
||||
- mnote 本地不复制聊天消息真相。
|
||||
- 发送消息后,事件流来自 Hermes run。
|
||||
- tool call 展示使用 Hermes tool event。
|
||||
- Hermes 可调用至少一个只读 mnote 工具读取当前页面。
|
||||
- Hermes 可调用至少一个写入工具,经 Rust runtime 写回当前页或创建 artifact。
|
||||
- 页面刷新后,AI 会话从 Hermes 恢复,而不是从 mnote 本地 state 恢复。
|
||||
- 结构化写入产生的 artifact / edge 能在 mnote projection 中验证。
|
||||
|
||||
---
|
||||
|
||||
## 12. 禁止项
|
||||
|
||||
- 不再新增 Web 私有 AI tool registry。
|
||||
- 不再把 `mnote-cli` 写成页面 AI 唯一长期执行面。
|
||||
- 不把 Hermes 聊天历史复制进 mnote page aggregate。
|
||||
- 不让 Hermes plugin 直接写 Convex。
|
||||
- 不在 Leptos 面板里重做 Hermes 后台管理台。
|
||||
- 不把 `AI Artifacts` 做成真实 kernel node。
|
||||
- 不绕过 Rust runtime 创建 artifact / edge。
|
||||
|
||||
---
|
||||
|
||||
## 13. 最终冻结口径
|
||||
|
||||
> **页面 AI 是 Hermes 面板,不是 mnote-cli 面板。**
|
||||
|
||||
> **Hermes 持有 AI 会话真相,mnote 持有业务对象真相。**
|
||||
|
||||
> **mnote 通过 Hermes skill/plugin 暴露工具,工具最终回到 Rust runtime / kernel。**
|
||||
|
||||
> **Leptos 负责页面内 Hermes 客户端体验,不负责 AI 编排。**
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,243 @@
|
||||
# 7-5 [done] Hermes client proxy 合同 v1
|
||||
|
||||
> 更新时间:2026-05-14
|
||||
>
|
||||
> 上位依据:`design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`、`design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
|
||||
>
|
||||
> Hermes Web UI 参考:`packages/client/src/api/hermes/chat.ts`、`packages/client/src/api/hermes/sessions.ts`、`packages/server/src/routes/hermes/proxy-handler.ts`、`packages/server/src/services/hermes/chat-run-socket.ts`
|
||||
|
||||
## 1. 边界
|
||||
|
||||
- [x] 浏览器只访问 mnote-web 同源 `/api/hermes/client/*`。
|
||||
- [x] Hermes API key / gateway token 只存在 mnote-web 服务端环境变量。
|
||||
- [x] mnote-web proxy 只做 auth、同源安全、页面上下文注入、trace 注入和错误码标准化。
|
||||
- [x] mnote-web proxy 不保存 Hermes session、message、tool event、usage、model 真相。
|
||||
- [x] `pageContext` 是 run 输入上下文,不是 Hermes session 的长期事实源。
|
||||
- [x] 旧 `/api/ai-agent/run` 在并存期只作为 legacy endpoint,不是新 Hermes 面板主代理。
|
||||
|
||||
## 2. 路由
|
||||
|
||||
### `GET /api/hermes/client/sessions`
|
||||
|
||||
请求:
|
||||
|
||||
```http
|
||||
GET /api/hermes/client/sessions?workspaceId=ws_1&documentId=doc_1&limit=20
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"traceId": "trace_1",
|
||||
"sessions": [
|
||||
{
|
||||
"sessionId": "mnote_doc_1_20260514",
|
||||
"title": "当前页问答",
|
||||
"preview": "请总结当前页面",
|
||||
"messageCount": 2,
|
||||
"toolCallCount": 1,
|
||||
"updatedAt": 1778712000,
|
||||
"model": "hermes-agent"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/hermes/client/sessions`
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1",
|
||||
"traceId": "trace_1",
|
||||
"title": "当前页问答"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1",
|
||||
"persistence": "hermes_on_first_run"
|
||||
}
|
||||
```
|
||||
|
||||
说明:Hermes Web UI 参考实现没有独立 session create HTTP route,客户端生成 session id,首次 run 时由 Hermes 持久化。mnote 保留本 route 是为了同源客户端合同稳定,但不得在 mnote 保存聊天历史。
|
||||
|
||||
### `GET /api/hermes/client/sessions/{session_id}`
|
||||
|
||||
请求:
|
||||
|
||||
```http
|
||||
GET /api/hermes/client/sessions/mnote_doc_1_trace_1?workspaceId=ws_1&documentId=doc_1
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1",
|
||||
"messages": [
|
||||
{
|
||||
"messageId": "42",
|
||||
"role": "assistant",
|
||||
"content": "当前页标题是...",
|
||||
"toolCallId": null,
|
||||
"toolName": null,
|
||||
"timestamp": 1778712000,
|
||||
"reasoning": null
|
||||
}
|
||||
],
|
||||
"usage": {
|
||||
"inputTokens": 120,
|
||||
"outputTokens": 30,
|
||||
"totalTokens": 150
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `POST /api/hermes/client/runs`
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1",
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"message": "概括当前页面标题和第一段",
|
||||
"model": "hermes-agent",
|
||||
"pageContext": {
|
||||
"title": "项目计划",
|
||||
"outline": [],
|
||||
"pageOptions": { "wideLayout": true }
|
||||
},
|
||||
"selectedBlockId": null,
|
||||
"selectedText": null,
|
||||
"traceId": "trace_1"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"runId": "run_123",
|
||||
"messageId": null,
|
||||
"events": [],
|
||||
"traceId": "trace_1"
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/hermes/client/events/{run_id}`
|
||||
|
||||
请求:
|
||||
|
||||
```http
|
||||
GET /api/hermes/client/events/run_123?sessionId=mnote_doc_1_trace_1
|
||||
```
|
||||
|
||||
响应:`text/event-stream`
|
||||
|
||||
```text
|
||||
data: {"event":"message.delta","run_id":"run_123","session_id":"mnote_doc_1_trace_1","delta":"当前页"}
|
||||
|
||||
data: {"event":"run.completed","run_id":"run_123","session_id":"mnote_doc_1_trace_1","output":"当前页...","usage":{"input_tokens":120,"output_tokens":30,"total_tokens":150}}
|
||||
```
|
||||
|
||||
### `POST /api/hermes/client/runs/{run_id}/abort`
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1",
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"runId": "run_123",
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"traceId": "trace_1",
|
||||
"events": [
|
||||
{ "event": "abort.started", "run_id": "run_123" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/hermes/client/models`
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"traceId": "trace_1",
|
||||
"defaultModel": "hermes-agent",
|
||||
"models": [
|
||||
{ "id": "hermes-agent", "label": "Hermes Agent", "provider": "hermes" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `GET /api/hermes/client/tools`
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"traceId": "trace_1",
|
||||
"tools": [
|
||||
{ "name": "mnote.page.get", "scope": "page.read", "schemaVersion": "mnote.hermes_tool.v1" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 错误响应
|
||||
|
||||
统一错误体:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"code": "hermes_client_unconfigured",
|
||||
"message": "Hermes client proxy 未配置 upstream",
|
||||
"traceId": "trace_1",
|
||||
"requestId": "req_1"
|
||||
}
|
||||
```
|
||||
|
||||
稳定错误码:
|
||||
|
||||
- [x] `hermes_client_unauthorized`:未登录或缺少有效 mnote 会话。
|
||||
- [x] `hermes_client_unconfigured`:未配置 `MNOTE_WEB_HERMES_UPSTREAM_URL`。
|
||||
- [x] `hermes_client_bad_request`:请求 JSON 或必要字段错误。
|
||||
- [x] `hermes_client_upstream_unauthorized`:Hermes upstream 拒绝服务端 token。
|
||||
- [x] `hermes_client_upstream_rate_limited`:Hermes upstream 429。
|
||||
- [x] `hermes_client_upstream_unavailable`:Hermes upstream 连接失败或 5xx。
|
||||
|
||||
## 4. 并存期规则
|
||||
|
||||
- [x] 新页面 AI 面板只允许请求 `/api/hermes/client/*`。
|
||||
- [x] `/api/ai-agent/run` 保留为 legacy guard,不再承载 Hermes 页面 AI 主链。
|
||||
- [x] `provider=hermes` 不再通过旧 `/api/ai-agent/run` 表达。
|
||||
- [x] 所有新 smoke 应断言页面 AI 主链没有 `/api/ai-agent/run` 请求。
|
||||
@@ -0,0 +1,304 @@
|
||||
# 7-6 [done] mnote Hermes plugin tool 合同 v1
|
||||
|
||||
> 更新时间:2026-05-14
|
||||
>
|
||||
> 上位依据:`design/07-ai/done/7-3-page-ai-hermes-panel-and-mnote-plugin-v1.md`、`design/07-ai/done/7-4-page-ai-hermes-panel-execution-checklist-v1.md`
|
||||
>
|
||||
> Hermes Web UI 参考:`packages/client/src/api/hermes/plugins.ts`、`packages/client/src/api/hermes/skills.ts`、`packages/server/src/services/hermes/plugins.ts`、`packages/server/src/services/hermes/chat-run-socket.ts`
|
||||
|
||||
## 1. 总边界
|
||||
|
||||
- [x] Hermes 看到的工具名必须是 `mnote.*`,不是 `mnote-cli`、Next route 或前端私有函数。
|
||||
- [x] plugin 内部可以临时调用 `mnote-cli` JSON adapter,但这只是内部 adapter,不是长期 tool 名称。
|
||||
- [x] Hermes plugin 不直接写 Convex;所有写入必须回到 Rust runtime / kernel。
|
||||
- [x] mnote 只保存业务事实、audit、artifact、edge、page/body/title/options 结果,不保存 Hermes 聊天历史。
|
||||
- [x] 所有 tool call 必须携带 `sessionId/runId/toolCallId/traceId` 便于串联 Hermes run 与 Rust command。
|
||||
|
||||
## 2. 统一入参
|
||||
|
||||
```json
|
||||
{
|
||||
"toolName": "mnote.page.get",
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1",
|
||||
"actorId": "user_1",
|
||||
"sessionId": "mnote_doc_1_trace_1",
|
||||
"runId": "run_123",
|
||||
"toolCallId": "call_123",
|
||||
"traceId": "trace_1",
|
||||
"idempotencyKey": "idem_123",
|
||||
"dryRun": false,
|
||||
"capabilityScope": ["page.read"],
|
||||
"args": {}
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 统一出参
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"toolName": "mnote.page.get",
|
||||
"toolCallId": "call_123",
|
||||
"traceId": "trace_1",
|
||||
"result": {},
|
||||
"audit": {
|
||||
"effect": "read",
|
||||
"commandId": null,
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1"
|
||||
},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
失败:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"toolName": "mnote.page.save",
|
||||
"toolCallId": "call_123",
|
||||
"traceId": "trace_1",
|
||||
"result": null,
|
||||
"audit": {
|
||||
"effect": "none",
|
||||
"workspaceId": "ws_1",
|
||||
"documentId": "doc_1"
|
||||
},
|
||||
"error": {
|
||||
"code": "mnote_tool_permission_denied",
|
||||
"message": "当前用户没有页面写权限"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 4. 第一批工具 schema
|
||||
|
||||
### `mnote.page.get`
|
||||
|
||||
入参:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId"],
|
||||
"properties": {
|
||||
"workspaceId": { "type": "string" },
|
||||
"documentId": { "type": "string" },
|
||||
"includeBody": { "type": "boolean", "default": true },
|
||||
"includeOptions": { "type": "boolean", "default": true },
|
||||
"includeBlocks": { "type": "boolean", "default": true }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
成功:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"toolName": "mnote.page.get",
|
||||
"toolCallId": "call_get_1",
|
||||
"traceId": "trace_1",
|
||||
"result": {
|
||||
"documentId": "doc_1",
|
||||
"workspaceId": "ws_1",
|
||||
"title": "项目计划",
|
||||
"bodySummary": "第一段...",
|
||||
"pageOptions": { "wideLayout": true },
|
||||
"blocks": [{ "id": "heading_1", "type": "heading", "text": "章节一" }]
|
||||
},
|
||||
"audit": { "effect": "read", "commandId": null }
|
||||
}
|
||||
```
|
||||
|
||||
失败:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"toolName": "mnote.page.get",
|
||||
"toolCallId": "call_get_1",
|
||||
"traceId": "trace_1",
|
||||
"error": { "code": "mnote_tool_permission_denied", "message": "无页面读取权限" }
|
||||
}
|
||||
```
|
||||
|
||||
### `mnote.page.save`
|
||||
|
||||
入参:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["workspaceId", "documentId", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun", "content"],
|
||||
"properties": {
|
||||
"workspaceId": { "type": "string" },
|
||||
"documentId": { "type": "string" },
|
||||
"content": { "type": "array" },
|
||||
"mode": { "type": "string", "enum": ["replace", "append"] },
|
||||
"idempotencyKey": { "type": "string" },
|
||||
"dryRun": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
成功:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"toolName": "mnote.page.save",
|
||||
"toolCallId": "call_save_1",
|
||||
"traceId": "trace_1",
|
||||
"result": { "commandName": "page.body.save", "revision": 8 },
|
||||
"audit": { "effect": "write", "commandId": "page_body_save_trace_1" }
|
||||
}
|
||||
```
|
||||
|
||||
dryRun:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"toolName": "mnote.page.save",
|
||||
"toolCallId": "call_save_1",
|
||||
"traceId": "trace_1",
|
||||
"result": { "dryRun": true, "diff": [{ "op": "append", "blocks": 1 }] },
|
||||
"audit": { "effect": "dry_run", "commandId": null }
|
||||
}
|
||||
```
|
||||
|
||||
权限失败:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"toolName": "mnote.page.save",
|
||||
"toolCallId": "call_save_1",
|
||||
"traceId": "trace_1",
|
||||
"error": { "code": "mnote_tool_permission_denied", "message": "无页面写权限" }
|
||||
}
|
||||
```
|
||||
|
||||
业务失败:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"toolName": "mnote.page.save",
|
||||
"toolCallId": "call_save_1",
|
||||
"traceId": "trace_1",
|
||||
"error": { "code": "mnote_tool_conflict", "message": "页面版本冲突,需要刷新后重试" }
|
||||
}
|
||||
```
|
||||
|
||||
### `mnote.page.update_title`
|
||||
|
||||
入参:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["workspaceId", "documentId", "title", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
||||
"properties": {
|
||||
"workspaceId": { "type": "string" },
|
||||
"documentId": { "type": "string" },
|
||||
"title": { "type": "string", "minLength": 1 },
|
||||
"idempotencyKey": { "type": "string" },
|
||||
"dryRun": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
成功、dryRun、权限失败、业务失败响应形状同 `mnote.page.save`,成功 command 为 `page.head.updateTitle`。
|
||||
|
||||
### `mnote.page.update_options`
|
||||
|
||||
入参:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["workspaceId", "documentId", "options", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
||||
"properties": {
|
||||
"workspaceId": { "type": "string" },
|
||||
"documentId": { "type": "string" },
|
||||
"options": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"wideLayout": { "type": "boolean" },
|
||||
"smallText": { "type": "boolean" },
|
||||
"showToc": { "type": "boolean" },
|
||||
"protectEditing": { "type": "boolean" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"idempotencyKey": { "type": "string" },
|
||||
"dryRun": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
成功、dryRun、权限失败、业务失败响应形状同 `mnote.page.save`,成功 command 为 `page.layout.updateOptions`。`runtimeSupport !== "wired"` 的字段必须返回 dryRun 警告或业务失败。
|
||||
|
||||
### `mnote.artifact.create_summary`
|
||||
|
||||
入参:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["workspaceId", "documentId", "summary", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
||||
"properties": {
|
||||
"workspaceId": { "type": "string" },
|
||||
"documentId": { "type": "string" },
|
||||
"summary": { "type": "string" },
|
||||
"idempotencyKey": { "type": "string" },
|
||||
"dryRun": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
成功:创建或更新当前页面唯一 summary node,并创建或确认 reference edge。
|
||||
|
||||
失败示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"toolName": "mnote.artifact.create_summary",
|
||||
"toolCallId": "call_summary_1",
|
||||
"traceId": "trace_1",
|
||||
"error": { "code": "mnote_tool_idempotency_conflict", "message": "同一幂等键已用于不同 summary 内容" }
|
||||
}
|
||||
```
|
||||
|
||||
### `mnote.artifact.create_ai_note`
|
||||
|
||||
入参:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "object",
|
||||
"required": ["workspaceId", "documentId", "content", "sessionId", "runId", "toolCallId", "traceId", "idempotencyKey", "dryRun"],
|
||||
"properties": {
|
||||
"workspaceId": { "type": "string" },
|
||||
"documentId": { "type": "string" },
|
||||
"content": { "type": "string" },
|
||||
"idempotencyKey": { "type": "string" },
|
||||
"dryRun": { "type": "boolean" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
成功:每次创建独立 ai_note node,并创建 reference edge。
|
||||
|
||||
## 5. 权限、幂等与 dryRun
|
||||
|
||||
- [x] 权限校验点:登录、workspace member、页面读、页面写、artifact 写。
|
||||
- [x] 同一 `idempotencyKey` 重试不得重复创建 artifact 或重复写正文。
|
||||
- [x] `dryRun=true` 只能返回计划和 diff,不得写入。
|
||||
- [x] 所有失败响应不得泄露无权限页面标题、正文或 artifact 内容。
|
||||
- [x] 写工具返回的 `audit.commandId` 必须能追踪到 Rust runtime / kernel command。
|
||||
Reference in New Issue
Block a user