467 lines
24 KiB
Markdown
467 lines
24 KiB
Markdown
# 7-57 [done] Yuxi reference: AI run journal, config schema and knowledge facade v1
|
||||
|
|
|
|||
|
|
> 创建时间:2026-06-09
|
|||
|
|
>
|
|||
|
|
> 当前状态:`DONE`
|
|||
|
|
>
|
|||
|
|
> Owner:07-ai / Page AI runtime / Hermes client runs / Reasonix ACP / Knowledge RAG facade
|
|||
|
|
>
|
|||
|
|
> 参考代码:
|
|||
|
|
> - `reference-code/Yuxi/backend/package/yuxi/services/run_queue_service.py`
|
|||
|
|
> - `reference-code/Yuxi/backend/package/yuxi/services/agent_run_service.py`
|
|||
|
|
> - `reference-code/Yuxi/web/src/composables/useAgentRunStream.js`
|
|||
|
|
> - `reference-code/Yuxi/backend/package/yuxi/agents/context.py`
|
|||
|
|
> - `reference-code/Yuxi/backend/package/yuxi/agents/toolkits/kbs/tools.py`
|
|||
|
|
>
|
|||
|
|
> 上位依据:
|
|||
|
|
> - `ARCHITECTURE.md`
|
|||
|
|
> - `CURRENT_ARCHITECTURE.md`
|
|||
|
|
> - `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
|
|||
|
|
> - `design/07-ai/done/7-39-page-ai-agent-selector-context-authorization-settings-v1.md`
|
|||
|
|
> - `design/07-ai/done/7-40-page-ai-context-envelope-and-run-receipt-v1.md`
|
|||
|
|
> - `design/07-ai/done/7-50-lightrag-knowledge-rag-provider-v1.md`
|
|||
|
|
> - `design/07-ai/done/7-51-lightrag-post-commit-hardening-v1.md`
|
|||
|
|
|
|||
|
|
## 1. 第一结论
|
|||
|
|
|
|||
|
|
Yuxi 值得参考的是三类工程模型,不是整体架构:
|
|||
|
|
|
|||
|
|
1. **AI run event journal**:把一次 agent run 变成可查询、可恢复、可断线重连的事件流,而不是只依赖当前浏览器连接上的一次 SSE。
|
|||
|
|
2. **配置 schema 驱动 UI**:用后端声明 agent 可配置字段、权限、默认值和资源选项,前端按 schema 渲染,减少 Hermes / Reasonix / Chat-only 设置漂移。
|
|||
|
|
3. **知识库工具 facade**:agent 只通过稳定工具面访问知识库,query 返回引用,open_reference 负责把 provider reference 转成 MNote 可点击定位。
|
|||
|
|
|
|||
|
|
这些模型适合 MNote 当前 Page AI / ACP / LightRAG 主线,但必须受 MNote 边界约束:
|
|||
|
|
|
|||
|
|
- 不复制 Yuxi 的 LangGraph agent runtime。
|
|||
|
|
- 不引入 Yuxi 的 Milvus / Neo4j / Postgres 知识库主存储。
|
|||
|
|
- 不把 Vue/Ant Design 前端模式迁入 Rust SSR / browser runtime。
|
|||
|
|
- 不改变 local-first 普通 Markdown 编辑主路径:仍是授权文件引用 + agent 原生 patch/diff + watcher/BufferStore/Page Aggregate 同步。
|
|||
|
|
- 不恢复旧 LiteParse / evidence / local OCR 默认路径;知识库问答继续以 LightRAG 为唯一默认 provider。
|
|||
|
|
|
|||
|
|
## 2. Yuxi 对照结论
|
|||
|
|
|
|||
|
|
### 2.1 AI run event journal
|
|||
|
|
|
|||
|
|
Yuxi 的 run 链路是:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
POST /api/agent/runs
|
|||
|
|
-> 创建 AgentRun 行和 input message
|
|||
|
|
-> ARQ worker 执行
|
|||
|
|
-> Redis stream append event
|
|||
|
|
-> GET /api/agent/runs/{runId}/events
|
|||
|
|
-> 前端用 Last-Event-ID / after_seq 恢复
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
可借鉴点:
|
|||
|
|
|
|||
|
|
- 事件流有 `seq` cursor,浏览器刷新后可以从最后 seq 继续。
|
|||
|
|
- run 有 terminal reconciliation:SSE 断开时查 run 状态,必要时重连或补 terminal event。
|
|||
|
|
- active run snapshot 放在浏览器侧,服务端 `active_run` 是权威判定。
|
|||
|
|
- interrupted / resume run 用 `parent_run_id + resume_request_id` 做幂等。
|
|||
|
|
|
|||
|
|
不适合直接搬:
|
|||
|
|
|
|||
|
|
- MNote 不把 Hermes / Reasonix 包进 LangGraph worker。
|
|||
|
|
- MNote 不要求所有 agent run 进入一个 Python queue;第一阶段只给现有 `/api/hermes/client/runs`、Reasonix ACP bridge、Chat-only provider 增加宿主侧 run journal。
|
|||
|
|
- MNote 不用 Redis 作为默认 local-first 控制面;优先 SQLite control-plane,长连接仍由当前 Rust Web 承载。
|
|||
|
|
|
|||
|
|
### 2.2 配置 schema 驱动 UI
|
|||
|
|
|
|||
|
|
Yuxi 的 `BaseContext` 用 dataclass field metadata 声明 `model/tools/knowledges/mcps/skills` 等配置,并按角色过滤。
|
|||
|
|
|
|||
|
|
可借鉴点:
|
|||
|
|
|
|||
|
|
- agent 后端自己声明配置项,前端不硬编码每个 agent 的全部字段。
|
|||
|
|
- 字段带 `kind`,例如 `llm/tools/knowledges/mcps/skills`,可由后端补资源 options。
|
|||
|
|
- admin-only 字段由服务端过滤,不能只靠前端隐藏。
|
|||
|
|
|
|||
|
|
不适合直接搬:
|
|||
|
|
|
|||
|
|
- MNote 当前已有 `PAGE_AI_AGENT_REGISTRY`、SQLite `user_ui_preferences`、directory grants、Hermes tool manifest 和 Reasonix wrapper selftest。
|
|||
|
|
- 因此不新增一套 Python 式 context schema;应把现有 Rust manifest / control-plane preference / agent registry 收成一个统一的 `mnote.ai_agent_descriptor.v1`。
|
|||
|
|
|
|||
|
|
### 2.3 知识库工具 facade
|
|||
|
|
|
|||
|
|
Yuxi 的知识库工具强调:
|
|||
|
|
|
|||
|
|
- `list_kbs` 列出当前 runtime 可访问知识库。
|
|||
|
|
- `query_kb` 按 `kb_id + query_text` 检索。
|
|||
|
|
- `open_kb_document` 用 `kb_id + file_id + line/window` 打开更完整上下文。
|
|||
|
|
- 工具先做 visible scope 校验,再查 retriever。
|
|||
|
|
|
|||
|
|
可借鉴点:
|
|||
|
|
|
|||
|
|
- query 只返回检索片段和可追踪引用,不暴露 provider 内部目录作为最终引用。
|
|||
|
|
- open 是单独工具,避免 query 结果过大。
|
|||
|
|
- runtime visible scope 是工具入口的第一层校验。
|
|||
|
|
|
|||
|
|
MNote 当前已有更接近目标的实现:
|
|||
|
|
|
|||
|
|
- `mnote.knowledge_rag.status`
|
|||
|
|
- `mnote.knowledge_rag.query`
|
|||
|
|
- `mnote.knowledge_rag.open_reference`
|
|||
|
|
- `/api/knowledge-rag/search`
|
|||
|
|
- `sourceScopeMode=post_filter_mapped_references`
|
|||
|
|
- `citationMarkdown / citationUrl / locatorDegraded`
|
|||
|
|
|
|||
|
|
因此 7-57 不重做知识库工具,而是补 facade 合同和漂移保护。
|
|||
|
|
|
|||
|
|
## 3. 目标架构
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Page AI sidebar
|
|||
|
|
-> create host AI run
|
|||
|
|
-> freeze target/contextRefs/allowedRoots/capability snapshot
|
|||
|
|
-> dispatch to Hermes / Reasonix / Chat-only
|
|||
|
|
-> bridge provider events into AI run event journal
|
|||
|
|
-> browser consumes resumable run events
|
|||
|
|
-> tool calls use schema-driven capability manifest
|
|||
|
|
-> knowledge-rag query/open_reference returns MNote citations
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
三条线要共享同一个 run identity:
|
|||
|
|
|
|||
|
|
- `hostRunId`:MNote 宿主侧 run id,Page AI UI、audit、event journal 和 browser resume 使用。
|
|||
|
|
- `providerRunId`:Hermes / Reasonix / Chat-only 自己的 run/session id,作为外部关联字段。
|
|||
|
|
- `requestId`:用户一次点击发送生成的幂等键,浏览器重试时不重复创建 run。
|
|||
|
|
|
|||
|
|
## 4. Design A:AI run event journal
|
|||
|
|
|
|||
|
|
### 4.1 数据合同
|
|||
|
|
|
|||
|
|
第一阶段优先落 SQLite control-plane,避免引入 Redis。
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"schema": "mnote.ai_run.v1",
|
|||
|
|
"hostRunId": "run_...",
|
|||
|
|
"requestId": "client_req_...",
|
|||
|
|
"agentId": "reasonix",
|
|||
|
|
"provider": "acp_reasonix",
|
|||
|
|
"providerRunId": "external-run-id",
|
|||
|
|
"sessionId": "session-id",
|
|||
|
|
"actorId": "user-id",
|
|||
|
|
"workspaceId": "local:workspace",
|
|||
|
|
"status": "pending|running|interrupted|completed|failed|cancelled",
|
|||
|
|
"targetSnapshotHash": "sha256...",
|
|||
|
|
"capabilitySnapshotHash": "sha256...",
|
|||
|
|
"createdAt": "2026-06-09T00:00:00Z",
|
|||
|
|
"updatedAt": "2026-06-09T00:00:01Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"schema": "mnote.ai_run_event.v1",
|
|||
|
|
"hostRunId": "run_...",
|
|||
|
|
"seq": "000000000000000001",
|
|||
|
|
"kind": "message.delta|tool.started|tool.completed|plan.updated|session.info.updated|changed_files|end|error",
|
|||
|
|
"source": "mnote|hermes|reasonix|chat_only|tool",
|
|||
|
|
"payload": {},
|
|||
|
|
"createdAt": "2026-06-09T00:00:01Z"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`seq` 第一阶段可用单调递增整数文本,不需要复刻 Redis stream id。要求只满足:
|
|||
|
|
|
|||
|
|
- 同一 `hostRunId` 内严格递增。
|
|||
|
|
- 客户端传 `afterSeq` 时只返回更大的事件。
|
|||
|
|
- terminal event 必须可补发,防止浏览器漏掉结束状态。
|
|||
|
|
|
|||
|
|
### 4.2 API 合同
|
|||
|
|
|
|||
|
|
第一阶段不替换现有 `/api/hermes/client/runs`,而是在其外层增加宿主 run receipt:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
POST /api/page-ai/runs
|
|||
|
|
GET /api/page-ai/runs/{hostRunId}
|
|||
|
|
GET /api/page-ai/runs/{hostRunId}/events?afterSeq=...
|
|||
|
|
POST /api/page-ai/runs/{hostRunId}/cancel
|
|||
|
|
GET /api/page-ai/sessions/{sessionId}/active-run
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
兼容策略:
|
|||
|
|
|
|||
|
|
- 旧 `/api/hermes/client/runs` 可继续存在,但 Page AI runtime 默认走新 receipt route。
|
|||
|
|
- 新 route 内部仍可调用 Hermes client proxy / Reasonix ACP bridge。
|
|||
|
|
- 若 provider 已经有自己的 SSE,Rust Web 只做事件桥接和 journal append,不接管 provider agent loop。
|
|||
|
|
|
|||
|
|
### 4.3 前端恢复规则
|
|||
|
|
|
|||
|
|
浏览器保存轻量 snapshot:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"hostRunId": "run_...",
|
|||
|
|
"sessionId": "session-id",
|
|||
|
|
"lastSeq": "000000000000000031",
|
|||
|
|
"createdAt": 1780000000000
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
恢复流程:
|
|||
|
|
|
|||
|
|
1. 页面可见或 Page AI 打开时,查本地 snapshot。
|
|||
|
|
2. 调 `active-run` 做权威判定。
|
|||
|
|
3. 如果服务端 run 仍是 `pending/running/interrupted`,用 `afterSeq=lastSeq` 继续消费。
|
|||
|
|
4. 如果服务端 run 已 terminal,拉取缺失事件并清掉 snapshot。
|
|||
|
|
5. 如果本地 snapshot 对应的 run 不存在,清掉本地状态,不能伪装仍在运行。
|
|||
|
|
|
|||
|
|
### 4.4 验收
|
|||
|
|
|
|||
|
|
- 刷新浏览器后,正在运行的 Reasonix / Hermes run 不丢消息。
|
|||
|
|
- SSE 断开后,前端能基于 `afterSeq` 续接,不重复渲染旧 tool event。
|
|||
|
|
- terminal event 漏掉时,状态查询能补齐 `completed/failed/cancelled`。
|
|||
|
|
- 同一 `requestId` 重试不会创建两个 host run。
|
|||
|
|
- `changed_files`、tool citation、plan/session info 都进入 journal,但不进入页面正文真相。
|
|||
|
|
|
|||
|
|
## 5. Design B:AI agent descriptor schema
|
|||
|
|
|
|||
|
|
### 5.1 目标
|
|||
|
|
|
|||
|
|
把 7-39 / 7-40 已经落地的 registry、preference、tool manifest 收成一个后端可查询合同:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"schema": "mnote.ai_agent_descriptor.v1",
|
|||
|
|
"agentId": "reasonix",
|
|||
|
|
"displayName": "Reasonix",
|
|||
|
|
"provider": "acp_reasonix",
|
|||
|
|
"capabilities": ["file_read", "file_write", "knowledge_rag", "native_patch"],
|
|||
|
|
"defaultContextRefs": ["current_page", "active_editor"],
|
|||
|
|
"settingScopes": ["ai.common", "ai.agent.reasonix"],
|
|||
|
|
"fields": [
|
|||
|
|
{
|
|||
|
|
"key": "ai.agent.reasonix.profile_id",
|
|||
|
|
"label": "Profile",
|
|||
|
|
"kind": "select",
|
|||
|
|
"default": "default",
|
|||
|
|
"optionsSource": "reasonix_profiles",
|
|||
|
|
"auth": "user",
|
|||
|
|
"secret": false
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"tools": [
|
|||
|
|
{
|
|||
|
|
"name": "mnote.knowledge_rag.query",
|
|||
|
|
"capabilityIds": ["knowledge_rag"],
|
|||
|
|
"enabled": true
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.2 来源合并顺序
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Rust static agent registry
|
|||
|
|
+ Hermes tool manifest
|
|||
|
|
+ Reasonix wrapper mapping/selftest
|
|||
|
|
+ SQLite user_ui_preferences
|
|||
|
|
+ SQLite directory_grants derived allowedRoots
|
|||
|
|
-> ai_agent_descriptor response
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
规则:
|
|||
|
|
|
|||
|
|
- `directory_grants` 不进入普通 field;它是权限事实,只能作为 allowed roots 派生。
|
|||
|
|
- secret / token 类字段不得通过 descriptor 返回明文。
|
|||
|
|
- agent-specific 设置只写入 `ai.agent.<agentId>` scope。
|
|||
|
|
- common 设置只写入 `ai.common` scope,例如默认 agent、默认 contextRefs、citation 显示策略。
|
|||
|
|
- 前端可以缓存 descriptor,但发送 run 前服务端必须重新解析权限和 enabled tools。
|
|||
|
|
|
|||
|
|
### 5.3 UI 结果
|
|||
|
|
|
|||
|
|
前端不再维护多份工具表:
|
|||
|
|
|
|||
|
|
- Agent selector 从 descriptor 渲染。
|
|||
|
|
- 设置页 tabs 从 descriptor 渲染。
|
|||
|
|
- Knowledge RAG 能力是否可用从 descriptor 的 `tools/capabilities` 渲染。
|
|||
|
|
- Chat-only 默认不暴露文件写入和 MNote context tools。
|
|||
|
|
- Hermes / Reasonix wrapper 漂移由 descriptor selftest 捕获。
|
|||
|
|
|
|||
|
|
### 5.4 验收
|
|||
|
|
|
|||
|
|
- `GET /api/page-ai/agents/descriptors` 返回 Hermes / Reasonix / Chat-only 三类 descriptor。
|
|||
|
|
- Alice/Bob 的 common preference 隔离。
|
|||
|
|
- `mnote.knowledge_rag.*` manifest 与 Reasonix wrapper 双向一致。
|
|||
|
|
- 禁用 Knowledge RAG 后,descriptor 中 capability disabled,Page AI 不引导 agent 调用该工具。
|
|||
|
|
- 前端删除硬编码工具 enable 列表,只保留渲染逻辑和兼容 fallback。
|
|||
|
|
|
|||
|
|
## 6. Design C:Knowledge RAG facade hardening
|
|||
|
|
|
|||
|
|
### 6.1 工具面
|
|||
|
|
|
|||
|
|
维持当前主工具,不新增第二套 provider:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
mnote.knowledge_rag.status
|
|||
|
|
mnote.knowledge_rag.query
|
|||
|
|
mnote.knowledge_rag.open_reference
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
建议补一个 facade-level `search` 工具时再单独拆小设计;当前已有 `/api/knowledge-rag/search`,但未必需要默认暴露给 agent。
|
|||
|
|
|
|||
|
|
### 6.2 query 输出要求
|
|||
|
|
|
|||
|
|
`mnote.knowledge_rag.query` 必须稳定返回:
|
|||
|
|
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"schema": "mnote.knowledge_rag.query_result.v1",
|
|||
|
|
"answer": "...",
|
|||
|
|
"sourceScopeMode": "post_filter_mapped_references",
|
|||
|
|
"references": [
|
|||
|
|
{
|
|||
|
|
"schema": "mnote.knowledge_rag.reference.v1",
|
|||
|
|
"sourcePath": "docs/a.pdf",
|
|||
|
|
"filePath": "a.pdf",
|
|||
|
|
"citationMarkdown": "[a.pdf · p.3](...)",
|
|||
|
|
"citationUrl": "...",
|
|||
|
|
"locatorDegraded": false
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"citations": []
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
要求:
|
|||
|
|
|
|||
|
|
- agent 最终回答优先用 `citationMarkdown`。
|
|||
|
|
- `locatorDegraded=true` 时只能说“来源定位降级”,不能编页码 / bbox。
|
|||
|
|
- `sourcePaths` 的语义继续是 `post_filter_mapped_references`,不得描述成 provider 原生检索 scope。
|
|||
|
|
- compact tool output 不暴露 provider raw chunks,除非明确进入 debug。
|
|||
|
|
|
|||
|
|
### 6.3 open_reference 输出要求
|
|||
|
|
|
|||
|
|
`open_reference` 是唯一把 provider reference 转成 MNote UI locator 的工具:
|
|||
|
|
|
|||
|
|
- 输入可以是 query 返回的 reference,也可以是 `file_path/chunk_id` 这类 provider hint。
|
|||
|
|
- 输出必须包含 `citationUrl/citationMarkdown`。
|
|||
|
|
- 能定位 page/bbox/block 时返回精确 locator。
|
|||
|
|
- 不能定位时返回 degraded resourceTab citation。
|
|||
|
|
- 不返回 `file://`、LightRAG dashboard URL 或 `.mnote/index` 私有路径给 agent 作为最终引用。
|
|||
|
|
|
|||
|
|
### 6.4 与 Yuxi 的差异
|
|||
|
|
|
|||
|
|
Yuxi 的 `open_kb_document(kb_id,file_id,line/window)` 适合普通文本知识库;MNote 的资料库来源包含 PDF、DOCX、图片、resource tab 和 bbox,因此不能把 `file_id + line window` 作为唯一打开模型。
|
|||
|
|
|
|||
|
|
MNote 可以参考它的“两阶段工具”:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
query -> reference id / source hint
|
|||
|
|
open_reference -> MNote locator / clickable citation
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
但不要退回“按行打开一切”的模型。
|
|||
|
|
|
|||
|
|
### 6.5 验收
|
|||
|
|
|
|||
|
|
- `task529` 继续覆盖 open_reference 能打开 resource tab。
|
|||
|
|
- `task530` 继续覆盖 Page AI final answer 不泄漏 `citationMarkdown` 字段名、不编造 locator。
|
|||
|
|
- `task538` 继续覆盖 `sourceScopeMode=post_filter_mapped_references`。
|
|||
|
|
- Reasonix wrapper selftest 覆盖 `mnote.knowledge_rag.status/query/open_reference`。
|
|||
|
|
- 新增一个 descriptor smoke:禁用 Knowledge RAG 时 Page AI 不显示知识库能力,启用时工具表与 manifest 一致。
|
|||
|
|
|
|||
|
|
## 7. 实施拆分
|
|||
|
|
|
|||
|
|
### Batch A:只读对齐与 descriptor 草案
|
|||
|
|
|
|||
|
|
- [x] 读取当前 `PAGE_AI_AGENT_REGISTRY`、Hermes tool manifest、Reasonix wrapper 映射、SQLite AI preferences。
|
|||
|
|
- [x] 输出 `mnote.ai_agent_descriptor.v1` 的 Rust 结构和只读 route。
|
|||
|
|
- [x] 不改 run 创建链路。
|
|||
|
|
- [x] 验证:Rust 单测 + 一个 JS smoke 断言三类 agent descriptor。
|
|||
|
|
|
|||
|
|
执行记录(2026-06-09):
|
|||
|
|
|
|||
|
|
- 已新增 `GET /api/page-ai/agents/descriptors`,返回 `mnote.ai_agent_descriptors.v1`,包含 Hermes / Reasonix / Chat-only 三类 descriptor、当前 actor 的 `ai.common.*` / `ai.agent.*` preference values、manifest tools 和 capability packs。
|
|||
|
|
- Chat-only descriptor 不暴露 MNote tools,`canWriteFiles=false`;Hermes / Reasonix descriptor 继续从 Hermes manifest 和 MNote capability packs 暴露 `mnote.knowledge_rag.*` 等工具状态。
|
|||
|
|
- 已补 Rust route 单测 `page_ai_agent_descriptors_merge_manifest_capabilities_and_preferences`,覆盖三类 agent、Reasonix Knowledge RAG tool、Chat-only 无工具、Alice/Bob preference 隔离。
|
|||
|
|
- 已补 JS smoke `scripts/task557-page-ai-agent-descriptor-smoke.js`,覆盖真实 `3000` API 返回的三类 agent descriptor、Reasonix `mnote.knowledge_rag.status/query/open_reference`、Chat-only 无 MNote tools 与空默认 context refs。
|
|||
|
|
- 已验证:`cd rust && cargo test -p mnote-web page_ai_agent_descriptors --lib`、`cargo test -p mnote-web page_ai_capabilities_expose_knowledge_rag_and_toggle_tools --lib`、`cargo test -p mnote-web hermes_client_tools_uses_manifest_and_profile_disabled_state --lib`。
|
|||
|
|
- 已验证:`node scripts/task557-page-ai-agent-descriptor-smoke.js`。
|
|||
|
|
|
|||
|
|
前端继续执行记录(2026-06-09):
|
|||
|
|
|
|||
|
|
- Page AI runtime 已新增 descriptor 加载链路:打开 drawer 时请求 `/api/page-ai/agents/descriptors?profile=...`,成功后用后端返回的 Hermes / Reasonix / Chat-only descriptor 覆盖本地 fallback registry。
|
|||
|
|
- Agent picker、当前 agent chip、history agent label 和 mnote tool list 已优先消费 descriptor 的 `displayName`、`acpRuntime`、`canWriteFiles`、`capabilities`、`tools`。
|
|||
|
|
- Agent / Hermes / Reasonix / Chat-only 设置页已按 `descriptor.fields` 渲染最小 schema UI,当前支持 `select`、`string`、`boolean/toggle`,并通过 `ai.*` preference key 写回 `/api/ui/preferences`。
|
|||
|
|
- `PAGE_AI_AGENT_FALLBACK_REGISTRY` 仅作为 descriptor API 失败时的加载失败 fallback,不再作为正常路径的唯一 agent 字段来源。
|
|||
|
|
- 已补 SSR runtime 合同测试,确保前端 bundle 保留 descriptor endpoint、descriptor field 控件和 agent descriptor card。
|
|||
|
|
|
|||
|
|
### Batch B:host run journal 最小闭环
|
|||
|
|
|
|||
|
|
- [x] 新增 SQLite run / run_event 存储或复用现有 run 表,明确 migration。
|
|||
|
|
- [x] 为 Page AI run 创建 `hostRunId/requestId/status`。
|
|||
|
|
- [x] 将现有 Hermes / Reasonix SSE 事件 append 到 journal。
|
|||
|
|
- [x] 新增 `GET /events?afterSeq=`。
|
|||
|
|
- [x] 验证:单测覆盖 seq 去重、terminal 补发、requestId 幂等。
|
|||
|
|
|
|||
|
|
执行记录(2026-06-09):
|
|||
|
|
|
|||
|
|
- 已复用 `control-plane` 现有 `ai_runtime_runs` / `ai_runtime_events`(migration `002-ai-runtime-store.sql`),补 `find_ai_runtime_run` 与 `list_ai_runtime_journal_events(after_seq, limit)`;journal seq 由同一 run 内事件顺序生成并格式化为 18 位文本 cursor。
|
|||
|
|
- 已新增 `GET /api/page-ai/runs/{hostRunId}` 和 `GET /api/page-ai/runs/{hostRunId}/events?afterSeq=...`;当前第一步兼容现有 `run_id`,`hostRunId` 暂时 alias 现有 persisted run id,后续 `POST /api/page-ai/runs` 再拆真正 host/provider 双 id。
|
|||
|
|
- 已新增 `POST /api/page-ai/runs` 创建宿主侧 receipt:写入 `hostRunId/requestId/status=pending`,并追加 `run.created` 初始事件;同一 `requestId + sessionId + workspaceId + documentId` 重试返回既有 run,不重复追加事件。
|
|||
|
|
- `GET /api/page-ai/runs/{hostRunId}/events?afterSeq=...` 已在 terminal status 存在但 terminal event 缺失时补一个 synthetic terminal event,用于浏览器刷新或 SSE 断线后的终态 reconciliation。
|
|||
|
|
- 已补 Rust route 单测 `page_ai_run_events_expose_journal_after_seq`,覆盖 run receipt 查询、`afterSeq=000000000000000001` 只返回 seq 2、event schema/kind/source/payload。
|
|||
|
|
- 已补 Rust route 单测 `page_ai_run_create_is_request_id_idempotent`,覆盖 `requestId` 幂等创建、重复 POST 不重复追加 `run.created`。
|
|||
|
|
- 已补 Rust route 单测 `page_ai_run_events_reconcile_missing_terminal_event`,覆盖缺 terminal event 时按 run status 补发 synthetic terminal event。
|
|||
|
|
- 已扩展 `api_chat_events_stream_from_openai_compatible_upstream`,验证 provider SSE 消费后可通过 `/api/page-ai/runs/{runId}/events?afterSeq=0` 读到 journal 中的 `message.delta` 与 `run.completed`。
|
|||
|
|
- 已把 persisted journal `seq` 回填到 API Chat / ACP SSE payload,前端实时消费时可同步推进 active-run snapshot cursor;ACP/Reasonix 事件仍由既有 `persist_acp_runtime_event` 写入 SQLite journal。
|
|||
|
|
- 已验证:`cargo test -p control-plane ai_runtime_run_upsert_lists_updates_and_events --lib`、`cargo test -p mnote-web page_ai_run_ --lib`。
|
|||
|
|
|
|||
|
|
### Batch C:前端 resume
|
|||
|
|
|
|||
|
|
- [x] Page AI runtime 保存 `{hostRunId, sessionId, lastSeq}`。
|
|||
|
|
- [x] 页面可见 / 打开 sidebar 时执行 active-run 判定。
|
|||
|
|
- [x] 恢复 SSE 时用 `afterSeq`,避免重复 tool card。
|
|||
|
|
- [x] 验证:浏览器 smoke 模拟刷新后继续接收事件。
|
|||
|
|
|
|||
|
|
执行记录(2026-06-09):
|
|||
|
|
|
|||
|
|
- 已新增 `GET /api/page-ai/sessions/{sessionId}/active-run`,按 `workspaceId/documentId/sessionId` 返回当前非 terminal host run,响应 schema 为 `mnote.ai_active_run.v1`。
|
|||
|
|
- Page AI session runtime 已新增 `pageAiCheckActiveRun()`;打开 drawer 完成 session 加载后调用一次,页面重新 visible 且 drawer 打开时再次调用。命中 active run 后会更新当前 session `runId/status`、`data-mnote-page-ai-active-host-run-id` 和 run status UI。
|
|||
|
|
- Page AI runtime 已新增 `mnote.page_ai_active_run_snapshot.v1` localStorage snapshot;实时 SSE payload 含 `seq` 时同步写入 `lastSeq`,terminal status 清理 snapshot。
|
|||
|
|
- 打开 drawer / 页面重新 visible 命中 active run 后,会继续调用 `/api/page-ai/runs/{hostRunId}/events?afterSeq={lastSeq}` 恢复 journal,并把 `message.delta` / `tool.*` / `plan.updated` / terminal event 重新投递到当前 conversation。
|
|||
|
|
- 已补 Rust route 单测 `page_ai_session_active_run_returns_non_terminal_run`,覆盖同一 session 下 completed run 被忽略、running run 被选为 active。
|
|||
|
|
- 已补 SSR runtime 合同测试,确保前端 bundle 保留 active-run endpoint、`pageAiCheckActiveRun()`、`pageAiResumeActiveRunJournal()`、`afterSeq` cursor、snapshot schema 和 `visibilitychange` 判定。
|
|||
|
|
- 已补 `scripts/task557-page-ai-run-resume-smoke.js`:种入 running host run 与两条 journal event,浏览器 localStorage 保留 `lastSeq=000000000000000001` 后打开页面 AI,验证前端调用 `active-run` 与 `/events?afterSeq=...`,只恢复 `resumed-token`,不重复渲染 `old-token`。
|
|||
|
|
- Page AI 打开 drawer 后会优先按 active-run snapshot 的 `sessionId` 恢复;后端会话列表加载完成后再次判定,避免 profile/settings 等慢请求阻塞 journal resume。
|
|||
|
|
- 已验证:`cargo test -p mnote-web page_ai_session_active_run_returns_non_terminal_run --lib`、`cargo test -p mnote-web page_ai_uses_backend_acp_session_runtime_store --lib`、`cargo test -p mnote-web api_chat_events_stream_from_openai_compatible_upstream --lib`。
|
|||
|
|
- 已验证:`node scripts/task557-page-ai-run-resume-smoke.js`。
|
|||
|
|
|
|||
|
|
### Batch D:Knowledge facade 漂移保护
|
|||
|
|
|
|||
|
|
- [x] descriptor 暴露 Knowledge RAG capability enabled/disabled。
|
|||
|
|
- [x] selftest 同时校验 Rust manifest、Reasonix wrapper、skill 文案中的工具名。
|
|||
|
|
- [x] 若 query 输出缺 `citationMarkdown`,Page AI final answer smoke 必须失败。
|
|||
|
|
- [x] 验证:`task529`、`task530`、`task538` 加 descriptor 断言。
|
|||
|
|
|
|||
|
|
执行记录(2026-06-09):
|
|||
|
|
|
|||
|
|
- Descriptor 已新增 `capabilityStates` / `disabledCapabilities`;禁用 `mnote-knowledge-rag` 后,Reasonix descriptor 不再在 `capabilities` 中声明 `knowledge_rag`,对应 `mnote.knowledge_rag.*` tool 标记为 disabled。
|
|||
|
|
- 已扩展 Rust 单测 `page_ai_agent_descriptors_merge_manifest_capabilities_and_preferences`,覆盖 Knowledge RAG enabled/disabled descriptor 状态与 disabled tool policy。
|
|||
|
|
- 已扩展 `scripts/task557-page-ai-agent-descriptor-smoke.js`,真实 3000 API smoke 覆盖默认启用与禁用后的 descriptor/tool 状态。
|
|||
|
|
- 已扩展 `scripts/reasonix-acp-wrapper.mjs` selftest:同时校验 Rust manifest、Reasonix wrapper mapping、`skills/mnote-knowledge-rag/SKILL.md` 文案中的 `mnote.knowledge_rag.status/query/open_reference` 工具名一致。
|
|||
|
|
- 已给 `task529` / `task530` / `task538` 补 descriptor 断言;`task530` 现在会先直接调用 `knowledge-rag/query` 并要求 reference 含 `citationMarkdown`,否则 Page AI final answer smoke 直接失败。
|
|||
|
|
- 已把 `task529` / `task530` 的目标图片资料改为先 ingest + 等待 indexed,避免 smoke 依赖现场 registry 预置状态。
|
|||
|
|
- 已给 LightRAG provider HTTP 5xx `RetryError` / `InvalidResponseError` / empty content 增加一次短 retry,避免 Reasonix Page AI 路径因 provider 偶发空响应直接失败。
|
|||
|
|
- 已验证:`node scripts/task529-knowledge-rag-citation-resource-tab-smoke.js`、`node scripts/task530-knowledge-rag-page-ai-final-answer-smoke.js`、`node scripts/task538-knowledge-rag-source-scope-api-smoke.js`、`node scripts/task557-page-ai-agent-descriptor-smoke.js`、`MNOTE_REASONIX_ACP_SELFTEST=1 node scripts/reasonix-acp-wrapper.mjs`。
|
|||
|
|
|
|||
|
|
## 8. 非目标
|
|||
|
|
|
|||
|
|
- 不把 Yuxi 拉入运行依赖。
|
|||
|
|
- 不迁移到 FastAPI / Vue / Milvus / Neo4j。
|
|||
|
|
- 不把 AI run journal 当作聊天全文主存储;provider 自己的历史仍由 provider 管。
|
|||
|
|
- 不让 run journal 写页面正文或 Page Aggregate。
|
|||
|
|
- 不改变 LightRAG provider 的 source truth、delete/reindex 语义。
|
|||
|
|
- 不新增定时轮询刷新 Page AI;恢复靠显式打开、页面可见、SSE reconnect 和 terminal reconciliation。
|
|||
|
|
|
|||
|
|
## 9. 风险
|
|||
|
|
|
|||
|
|
- 如果 journal 记录过多 token delta,SQLite 可能膨胀。第一版应 compact tool/result payload,保留 UI 恢复必需事件,不保存大段 provider raw chunks。
|
|||
|
|
- 如果 descriptor 同时由前端和后端维护,会形成新漂移。必须以后端 route 为准,前端只渲染。
|
|||
|
|
- 如果 `hostRunId` 与 provider run id 混用,审计会断链。所有 UI、audit、changed files 都使用 hostRunId,provider id 只作为关联字段。
|
|||
|
|
- 如果把 Yuxi 的 `line/window open` 直接套到 PDF/DOCX/image,会破坏 MNote locator 模型。Knowledge facade 必须继续以 `open_reference -> citationUrl` 为中心。
|