feat(rag): harden post-LightRAG runtime
Retire legacy OCR/media/evidence fallbacks, add local-folder event bus and Page Aggregate guards, and archive completed design checklists. Validation: cargo test -p mnote-web -- --test-threads=1; cargo test --workspace -- --test-threads=1; git diff --check; codegraph sync .; codegraph_status.
This commit is contained in:
@@ -1,151 +0,0 @@
|
||||
# 7-18 Agent edit clean / dirty smoke closure checklist v1
|
||||
|
||||
> 创建时间:2026-06-06
|
||||
>
|
||||
> 状态:`process`
|
||||
>
|
||||
> Owner:07-ai / 05-editor-mainline / 03-rust-web
|
||||
>
|
||||
> 父设计:`design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
|
||||
|
||||
## 1. 边界
|
||||
|
||||
本 checklist 只推进 local-first 普通 Markdown 的真实 agent 文件编辑闭环,不碰 LightRAG、Knowledge RAG、OCR、evidence index 或 OnlyOffice recipe 扩展。当前另一个 agent 正在修改 LightRAG 相关文件,本轮不得编辑:
|
||||
|
||||
- `rust/crates/mnote-web/src/routes/knowledge_rag.rs`
|
||||
- `rust/crates/mnote-web/src/hermes_tools/knowledge_rag.rs`
|
||||
- `design/07-ai/done/7-50-lightrag-knowledge-rag-provider-v1.md`
|
||||
- `scripts/task529*` 到 `scripts/task534*`
|
||||
|
||||
## 2. 当前代码证据
|
||||
|
||||
已有基础能力:
|
||||
|
||||
- Page AI target 会生成 `mnote.agent_target_package.v1`,并给目标写入 `allowedFiles`、`policy.conflictPolicy=fail_on_dirty_or_stale`。
|
||||
- 发送 run 前会通过 `/api/documents/buffer-state` 检查 dirty / external modified 状态;阻塞时抛 `page_ai_target_buffer_not_writable`。
|
||||
- 后端会生成 `mnote.agent_run_envelope.v1`,其中 `resultPolicy.changedFiles=required`、`refresh=watcher_or_explicit_resync`。
|
||||
- `scripts/reasonix-acp-wrapper.mjs` 会把 `agentRunEnvelope` 写进模型可见 prompt,要求 agent 使用自身文件工具编辑真实文件。
|
||||
- `local_agent_audit_finalize_run` 会基于前后快照生成 `changedFiles` 和 `mnote.agent_run_receipt.v1`。
|
||||
- 前端 `pageAiDispatchReceiptRefresh(...)` 会把 receipt 转成 `tree:local-folder-watch-batch` 和 `mnote:page-ai-tool-write-completed`。
|
||||
|
||||
当前缺口:
|
||||
|
||||
- 没有一条真实浏览器 smoke 证明 agent 原生 patch 修改当前 `.md` 后,前台 tiptap 在 clean buffer 下可见更新。
|
||||
- dirty buffer 的阻塞虽然已有函数,但缺少端到端 smoke 证明不会启动可写 run 或不会静默覆盖。
|
||||
- readonly 写入目前仍偏后置审计:`read_only_write_rejected` 可以记录结果,但还需要前置拒绝或 tool/ACP 层明确失败。
|
||||
- 审计快照在 folder context 下会退回 full snapshot;产品化前需要限制为 `allowedFiles` / changedFiles 优先,并对超限降级有可见记录。
|
||||
|
||||
## 3. 非目标
|
||||
|
||||
- 不新增 MNote 普通 Markdown 写入工具。
|
||||
- 不把 `mnote.doc.markdown_edit` 恢复为 local-first fallback。
|
||||
- 不实现 streaming apply、review session、GhostTextOverlay。
|
||||
- 不要求真实外部大模型;smoke 可以用受控 ACP stub / Hermes test runtime 模拟 agent 原生文件 patch,但必须真实写磁盘文件。
|
||||
- 不把 LightRAG retrieval、evidence citation 或 OCR sidecar 混入本闭环。
|
||||
|
||||
## 4. Phase A:clean buffer 真实写入回收 smoke
|
||||
|
||||
目标:证明 agent 原生文件编辑 -> watcher / receipt -> 当前前台 tiptap 可见更新。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 新增 `scripts/task535-page-ai-local-agent-clean-edit-smoke.js`。
|
||||
- [x] smoke 创建临时 local-folder workspace 和目标 `AgentClean.md`。
|
||||
- [x] 浏览器打开该 Markdown,确认 `mnote-leptos-tiptap-island-editor-root` ready。
|
||||
- [x] 通过 Page AI 发起本地 agent run,run payload 必须包含 `targetPackage.currentFile.relativePath`、`allowedFiles`。
|
||||
- [x] agent stub / ACP test runtime 只能在 `allowedFiles` 指向的真实 `.md` 上做最小 patch。
|
||||
- [x] run completed 后必须返回或触发 `agentRunReceipt.changedFiles`,路径为目标 `.md`。
|
||||
- [x] 前端必须触发 `tree:local-folder-watch-batch`,并对当前文档触发 `mnote:page-ai-tool-write-completed`。
|
||||
- [x] tiptap 可见正文更新为 agent 写入后的内容,不需要手动 reload。
|
||||
- [x] 网络断言本路径不调用 `/api/documents/save`。
|
||||
- [x] 工具事件断言本路径不调用 `mnote.doc.markdown_edit`。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] smoke 输出 `tmp/task535-page-ai-local-agent-clean-edit-smoke/result.json`,包含 run payload、changedFiles、current refresh、最终 editor text。
|
||||
- [x] `result.json` 中 `ok=true`,`usedDocumentsSave=false`,`usedMarkdownEdit=false`。
|
||||
|
||||
## 5. Phase B:dirty buffer 不静默覆盖 smoke
|
||||
|
||||
目标:证明用户本地未保存时,agent 写入不会绕过 buffer 冲突模型。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 新增 `scripts/task536-page-ai-local-agent-dirty-guard-smoke.js`。
|
||||
- [x] 打开目标 Markdown 后在 tiptap 中输入未保存内容,制造 BufferStore dirty 状态。
|
||||
- [x] Page AI target chip / run 前检查能读到 dirty 状态。
|
||||
- [x] 点击发送可写 run 时,应出现明确阻塞或确认流程;当前最低验收是阻塞并返回 `page_ai_target_buffer_not_writable`。
|
||||
- [x] smoke 断言没有启动可写 ACP run。
|
||||
- [x] smoke 断言磁盘 `.md` 没有被 agent stub 修改。
|
||||
- [x] smoke 断言 editor 中未保存内容仍可见。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] smoke 输出 `tmp/task536-page-ai-local-agent-dirty-guard-smoke/result.json`。
|
||||
- [x] `result.json` 中 `ok=true`,`blockedBeforeRun=true`,`diskChanged=false`,`editorDirtyTextStillVisible=true`。
|
||||
|
||||
## 6. Phase C:readonly 前置拒绝 smoke
|
||||
|
||||
目标:证明 readonly target 不靠事后审计才发现写入失败。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 新增 `scripts/task537-page-ai-local-agent-readonly-write-guard-smoke.js`。
|
||||
- [x] 构造只读授权或只读 targetPackage:`permission=read`,`allowedFiles` 可读但不可写。
|
||||
- [x] 发起“请修改当前文件”的 Page AI run。
|
||||
- [x] 前端或后端应在启动可写 ACP run 前拒绝,错误码稳定,例如 `page_ai_target_readonly` 或 `local_agent_write_not_allowed`。
|
||||
- [x] 若当前实现只能后置审计,应先把 smoke 写成 RED,记录实际行为,不伪造通过。
|
||||
- [x] smoke 断言磁盘文件未变化。
|
||||
- [x] smoke 断言 audit 中没有把 readonly 写入说成成功;若出现 `read_only_write_rejected`,必须在 UI 可见错误里体现。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] smoke 输出 `tmp/task537-page-ai-local-agent-readonly-write-guard-smoke/result.json`。
|
||||
- [x] `result.json` 中 `ok=true` 仅在前置拒绝落地后允许;当前若为 RED,应输出 `ok=false` 和真实行为证据。
|
||||
|
||||
## 7. Phase D:审计快照范围收口
|
||||
|
||||
目标:避免 local agent audit 因 folder context 退回扫全 root,影响大 workspace 和 run 返回速度。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] 后端 `local_agent_audit_relative_paths_from_payload(...)` 优先消费 `agentRunEnvelope.allowedFiles` / `targetPackage.allowedFiles`。
|
||||
- [x] folder context 不再直接强制 full snapshot;除非用户明确选择 folder-wide edit 且有上限。
|
||||
- [x] 增加审计上限:文件数、总字节数、耗时;超限时进入 `auditScope=truncated`,并在 receipt 中可见。
|
||||
- [x] 单测覆盖 allowedFiles 优先、folder context 不扫全 root、超限截断。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] `cargo test -p mnote-web local_agent_audit -- --test-threads=1` 或等价 targeted tests 通过。
|
||||
- [ ] clean smoke 中 audit snapshot 只包含目标文件及必要 changed file。
|
||||
|
||||
## 8. Phase E:文档与 manifest 退役口径
|
||||
|
||||
目标:锁死 local-first 普通 Markdown 不走旧 MNote 写入工具。
|
||||
|
||||
Checklist:
|
||||
|
||||
- [x] Page AI / Hermes guidance 对 local-folder 普通 Markdown 明确优先 agent 原生 patch/diff。
|
||||
- [x] `mnote.doc.markdown_edit` 在 manifest 中只标注为 online/cloud/compat 历史工具或结构校验辅助。
|
||||
- [x] `reasonix-acp-wrapper.mjs` selftest 断言 prompt 包含“不使用 MNote doc/page write tools for ordinary local Markdown edits”。
|
||||
- [x] smoke 断言 local-first agent edit 不调用 `/api/documents/save`、`mnote.doc.markdown_edit`、`mnote.page.save`。
|
||||
|
||||
验收:
|
||||
|
||||
- [x] `7-18-local-first-agent-file-editing-control-plane-v1.md` Phase B/C 可勾选。
|
||||
- [ ] 本 checklist 可移动到 `design/07-ai/done/`,父设计 `7-18` 仅剩跨 workspace / 多 target 的产品确认项。
|
||||
|
||||
## 9. 推荐执行顺序
|
||||
|
||||
1. 先写 `task535` clean smoke,允许 RED,固定真实 run payload / receipt / 前台刷新证据。
|
||||
2. 再写 `task536` dirty guard smoke,优先验证当前已有 dirty blocker。
|
||||
3. 再写 `task537` readonly guard smoke,若当前只能后置审计则保持 RED。
|
||||
4. 最后做审计快照范围收口和 manifest/guidance 口径收紧。
|
||||
|
||||
## 10. 归档条件
|
||||
|
||||
- clean buffer smoke 真实通过。
|
||||
- dirty buffer smoke 证明不会静默覆盖。
|
||||
- readonly write smoke 证明前置拒绝或 UI 明确失败。
|
||||
- local-first 普通 Markdown agent edit 不调用旧 MNote 写入工具。
|
||||
- LightRAG 相关 diff 不在本 checklist 中被修改或作为验收前置。
|
||||
@@ -1,936 +0,0 @@
|
||||
# 7-46 [process] Document Evidence Retrieval Kernel v1
|
||||
|
||||
> 创建时间:2026-06-03
|
||||
>
|
||||
> 当前状态:`PROCESS`
|
||||
>
|
||||
> Owner:07-ai / 03-rust-web / 01-tree-first-graph-kernel
|
||||
>
|
||||
> 上位依据:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/01-tree-first-graph-kernel/process/1-8-mvp-post-process-execution-order-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/done/3-25-local-folder-mineru-ocr-sidecar-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/done/7-40-page-ai-context-envelope-and-run-receipt-v1.md`
|
||||
>
|
||||
> 参考项目:`reference-code/PageIndex`、`reference-code/BookRAG`、`reference-code/Kwipu`、`reference-code/ladybug`、`reference-code/seekdb` 均已单独建 CodeGraph;本文只吸收架构和合同,不把这些项目整体引入运行时。
|
||||
|
||||
## 1. 第一结论
|
||||
|
||||
MNote 应建设一个统一的 `Document Evidence Retrieval Kernel`,作为 Hermes / Reasonix / Page AI 搜索 PDF、Word、图片 OCR、Markdown 和附件内容的唯一证据检索入口。
|
||||
|
||||
核心路径:
|
||||
|
||||
```text
|
||||
本地 Markdown / PDF / Word / 图片 / 附件
|
||||
-> Parse Provider: LiteParse 或 MinerU
|
||||
-> Parsed Resource Artifact: parsed text + source-map
|
||||
-> Evidence Index: SQLite FTS + section tree + locator
|
||||
-> Agent Tool: mnote.evidence.search / read / open
|
||||
-> UI: 打开 owner 文档 / resource tab,并定位 page / bbox / section / line
|
||||
-> 可选 Graph Projection: LadybugDB
|
||||
```
|
||||
|
||||
这不是一个多项目拼装方案。PageIndex、BookRAG、Kwipu、LadybugDB、SeekDB 都只作为参考或可插拔后端,不成为默认数据真相。
|
||||
|
||||
第一优先级不是 graph DB,也不是替换现有 OCR,而是先把“搜索命中”升级为“可点击、可复核、可被 agent 引用的 Evidence Locator”。
|
||||
|
||||
## 2. 设计原则
|
||||
|
||||
### 2.1 尽量减少重复真相
|
||||
|
||||
每类数据只能有一个默认真相:
|
||||
|
||||
| 数据 | 默认真相 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 原始文档 | 用户文件本身 | PDF / Word / 图片 / Markdown 原文件不被索引替代 |
|
||||
| OCR / parse 结果 | resource sidecar artifact | 只作为原始文档的派生证据真相,可删除重建 |
|
||||
| 搜索索引 | `.mnote/index/evidence.sqlite` | 只作为缓存和加速层,不作为正文真相 |
|
||||
| graph | SQLite edge table 或 LadybugDB projection | 只作为 evidence 的投影,不反向成为文档真相 |
|
||||
| agent 引用 | `EvidenceLocator` | 引用必须落回原始文档或 owner Markdown |
|
||||
|
||||
禁止新增第二套“文档正文真相”。不允许让 OCR Markdown、LiteParse JSON、FTS rows、graph nodes 彼此独立竞争正文口径。
|
||||
|
||||
### 2.2 尽量少引入项目
|
||||
|
||||
默认运行时只接受两个 parser/provider:
|
||||
|
||||
- `MinerUProvider`:复用当前已有 MinerU HTTP OCR route 和 token 管理。
|
||||
- `LiteParseProvider`:作为轻量本地 PDF parser,优先用于文本型 PDF,扫描件再启 OCR。
|
||||
|
||||
其他项目定位:
|
||||
|
||||
- PageIndex:借鉴 tree search,不引入运行时。
|
||||
- BookRAG:借鉴 `tree + graph + evidence mapping` 架构,不引入运行时。
|
||||
- Kwipu:借鉴 Markdown note graph 和 MCP query 形态,不引入运行时。
|
||||
- LadybugDB:只作为后续 graph projection 后端。
|
||||
- SeekDB:只作为后续 hybrid search backend 候选。
|
||||
|
||||
### 2.3 优先和当前项目耦合
|
||||
|
||||
默认落点是 MNote 现有 Rust Web / local-first / Page AI 工具链:
|
||||
|
||||
- 解析任务沿 `mnote-web` 的 local resource route 和当前 OCR job 模型扩展。
|
||||
- 检索沿当前 `/api/search/documents`、`docs_search`、`docs_read` 兼容升级。
|
||||
- agent 权限沿 `AiAccessScope`、allowed roots、targetPackage 和 run receipt。
|
||||
- UI 打开沿现有 document / resource tab / local-folder route。
|
||||
- 文件变化沿 watcher 刷新索引,不新增前端轮询主链。
|
||||
|
||||
## 3. 当前问题
|
||||
|
||||
当前 local search 已能搜索 Markdown、资源标题和 OCR sidecar,`includeOcr=true` 时可以返回 owner page。但它仍然不够支撑 agent 文档问答:
|
||||
|
||||
- OCR 命中只有 owner page 和 OCR sidecar 路径,缺 page / bbox / section 级定位。
|
||||
- PDF 文本型文件即使不需要 OCR,也需要页面和 bbox source-map,否则只能纯文本命中。
|
||||
- agent 需要同时拿到 quote、上下文、locator 和 open action,而不是只拿文档 id。
|
||||
- graph 如果直接建在纯文本 chunk 上,会和 UI 定位脱节。
|
||||
- PageIndex / BookRAG 这类 tree/graph 思路有价值,但直接引入会形成多套索引和多套真相。
|
||||
|
||||
## 4. Canonical Artifact
|
||||
|
||||
每个被解析的资源只生成一份 canonical parsed artifact。它是原始文件的派生结果,可删除重建。
|
||||
|
||||
### 4.1 文件布局
|
||||
|
||||
继续尊重当前 `{pageStem}.ocr/` sidecar 约定,不强行迁移已有 OCR 文件。新增 source-map 文件与当前 OCR / parse Markdown 同目录,避免分散到多个项目缓存。
|
||||
|
||||
示例:
|
||||
|
||||
```text
|
||||
docs/Page.md
|
||||
docs/Page.assets/spec.pdf
|
||||
docs/Page.ocr/spec.pdf.ocr.md
|
||||
docs/Page.ocr/spec.pdf.source-map.json
|
||||
```
|
||||
|
||||
文本型 PDF 使用 LiteParse 时,也写到同一 owner sidecar 目录:
|
||||
|
||||
```text
|
||||
docs/Page.ocr/spec.pdf.parse.md
|
||||
docs/Page.ocr/spec.pdf.source-map.json
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `*.ocr.md`:OCR 结果,通常来自 MinerU。
|
||||
- `*.parse.md`:非 OCR parse 结果,通常来自 LiteParse。
|
||||
- `*.source-map.json`:定位真相,包含 page、bbox、text item、section、char range。
|
||||
- `.mnote/index/evidence.sqlite`:索引缓存,引用上述 artifact,不保存不可追溯的新正文真相。
|
||||
|
||||
长期可以把目录名从 `.ocr` 演进为 `.evidence`,但不作为当前必要前置;当前先减少迁移风险。
|
||||
|
||||
### 4.2 `source-map.json`
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "mnote.resource_source_map.v1",
|
||||
"provider": "liteparse",
|
||||
"modelVersion": "2.0.5",
|
||||
"ownerDocumentPath": "docs/Page.md",
|
||||
"sourceRootRelativePath": "docs/Page.assets/spec.pdf",
|
||||
"sourceHash": "sha256:...",
|
||||
"pageCount": 12,
|
||||
"pages": [
|
||||
{
|
||||
"page": 1,
|
||||
"width": 595,
|
||||
"height": 842,
|
||||
"textItems": [
|
||||
{
|
||||
"id": "p1_t1",
|
||||
"text": "Revenue recognition",
|
||||
"bbox": [72, 124, 260, 140],
|
||||
"charRange": [0, 19]
|
||||
}
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"id": "p1_b1",
|
||||
"type": "paragraph",
|
||||
"text": "Revenue recognition ...",
|
||||
"bbox": [72, 124, 520, 180],
|
||||
"charRange": [0, 220]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"sections": [
|
||||
{
|
||||
"id": "sec_1",
|
||||
"title": "Revenue",
|
||||
"path": ["Annual Report", "Revenue"],
|
||||
"pageStart": 1,
|
||||
"pageEnd": 3,
|
||||
"blockIds": ["p1_b1"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `source-map.json` 是定位真相,不把同一定位信息散落到 OCR frontmatter、SQLite row 和 graph node 中。
|
||||
- SQLite 和 graph 只复制必要 locator 字段作为查询加速,必须能由 source-map 重建。
|
||||
- Markdown / Word 也使用同一 `EvidenceLocator` 合同,只是定位字段从 bbox 换成 heading / line / char range。
|
||||
|
||||
## 5. Provider 选择
|
||||
|
||||
### 5.1 LiteParseProvider
|
||||
|
||||
默认用途:
|
||||
|
||||
- 文本型 PDF。
|
||||
- 需要快速本地解析并保留 bbox 的 PDF。
|
||||
- 不需要 OCR 的 PDF 必须走 `no-ocr` 模式。
|
||||
|
||||
价值:
|
||||
|
||||
- 不是纯搜索增强,而是把文本 PDF 的“文本”升级为“可定位文本”。
|
||||
- 对文本型 PDF,OCR 是浪费;LiteParse 可以保留页面坐标和阅读顺序。
|
||||
- 与 MNote Rust 主线耦合度较好,适合做二次开发或 provider adapter。
|
||||
|
||||
不做:
|
||||
|
||||
- 不把 LiteParse 当独立搜索引擎。
|
||||
- 不让 LiteParse 直接替代 MNote index / agent tool。
|
||||
- 不强行让 LiteParse 直接调用 MinerU;如需 MinerU OCR,走 MNote 自己的 `MinerUProvider`,或做明确 adapter。
|
||||
|
||||
### 5.2 MinerUProvider
|
||||
|
||||
默认用途:
|
||||
|
||||
- 扫描 PDF。
|
||||
- 图片 OCR。
|
||||
- 复杂版式 PDF。
|
||||
- Office / PPT / Word 转换后需要 OCR 或结构提取的资源。
|
||||
|
||||
当前 MNote 已有 MinerU HTTP OCR 链路,应继续复用:
|
||||
|
||||
- 后端读取 token,前端不接触 token。
|
||||
- 结果写入 owner sidecar。
|
||||
- `.mnote/ocr-index.json` 继续作为状态缓存。
|
||||
- 新增 source-map 提取时,优先消费 MinerU zip 中的 JSON,而不是只取 `full.md`。
|
||||
|
||||
### 5.3 Provider 选择规则
|
||||
|
||||
```text
|
||||
if file is markdown:
|
||||
use MarkdownParserProvider
|
||||
if file is text PDF and LiteParse succeeds:
|
||||
use LiteParseProvider(no_ocr)
|
||||
if file is image/scanned PDF or LiteParse text confidence is low:
|
||||
use MinerUProvider
|
||||
if file is image:
|
||||
use MinerUProvider
|
||||
if file is Word/PPT/Excel:
|
||||
prefer office parser/export path, then MinerUProvider when visual locator is needed
|
||||
```
|
||||
|
||||
失败策略:
|
||||
|
||||
- LiteParse 失败不自动调用外部 MinerU,除非用户或 policy 允许上传。
|
||||
- MinerU 缺 token 时返回明确 `mineru_token_missing`,不伪装为成功。
|
||||
- Provider output 必须写入统一 artifact 合同后才能进入 evidence index。
|
||||
|
||||
## 6. Evidence Index
|
||||
|
||||
### 6.1 存储
|
||||
|
||||
新增或扩展本地索引:
|
||||
|
||||
```text
|
||||
.mnote/index/evidence.sqlite
|
||||
```
|
||||
|
||||
SQLite 是当前最合适的默认底座:
|
||||
|
||||
- 和 local-first 主线耦合度高。
|
||||
- 无新增常驻服务。
|
||||
- 可用 FTS5 做快速全文搜索。
|
||||
- 可同时存 metadata、locator、section tree 和 graph edge cache。
|
||||
- 后续可把同一合同投影到 LadybugDB 或 SeekDB。
|
||||
|
||||
当前 `.mnote/index/search-index.json` 可以继续作为兼容缓存,但不应继续承载大文档 evidence 主索引。
|
||||
|
||||
### 6.2 表模型
|
||||
|
||||
```sql
|
||||
evidence_resource(
|
||||
resource_id,
|
||||
owner_document_id,
|
||||
owner_document_path,
|
||||
source_root_relative_path,
|
||||
provider,
|
||||
source_hash,
|
||||
artifact_root_relative_path,
|
||||
source_map_root_relative_path,
|
||||
updated_at_ms
|
||||
)
|
||||
|
||||
evidence_block(
|
||||
block_id,
|
||||
resource_id,
|
||||
text,
|
||||
section_path_json,
|
||||
page_start,
|
||||
page_end,
|
||||
bbox_json,
|
||||
char_range_json,
|
||||
line_range_json,
|
||||
locator_json
|
||||
)
|
||||
|
||||
evidence_fts(
|
||||
block_id UNINDEXED,
|
||||
text
|
||||
)
|
||||
|
||||
evidence_section(
|
||||
section_id,
|
||||
resource_id,
|
||||
title,
|
||||
path_json,
|
||||
summary,
|
||||
page_start,
|
||||
page_end,
|
||||
parent_section_id
|
||||
)
|
||||
|
||||
evidence_edge(
|
||||
edge_id,
|
||||
from_id,
|
||||
to_id,
|
||||
edge_type,
|
||||
source_block_id,
|
||||
confidence,
|
||||
created_by
|
||||
)
|
||||
```
|
||||
|
||||
### 6.3 Index 不是真相
|
||||
|
||||
FTS row 可以复制 `text`,但只作为查询缓存。验收规则:
|
||||
|
||||
- 删除 `evidence.sqlite` 后,可由原始文件和 sidecar artifact 重建。
|
||||
- 删除 `source-map.json` 后,对应定位能力失效,需要重新 parse。
|
||||
- 删除原始文件后,sidecar 和 index 必须标记 stale,不继续当成可打开证据。
|
||||
|
||||
## 7. Tree Retrieval
|
||||
|
||||
PageIndex 的价值在于 tree search,不在于它的代码本身。
|
||||
|
||||
MNote 应从 source-map 和 Markdown heading 生成 `evidence_section`:
|
||||
|
||||
```text
|
||||
Document
|
||||
-> Resource
|
||||
-> Section
|
||||
-> Page range
|
||||
-> EvidenceBlock
|
||||
```
|
||||
|
||||
Agent 检索时不是只做 BM25,也不是让 LLM 扫全文:
|
||||
|
||||
1. FTS 快速召回候选 evidence blocks。
|
||||
2. section tree 收拢上下文和页码范围。
|
||||
3. 对长文档问题,可让 LLM 在 section tree 上选择相关 section。
|
||||
4. 再读取 section 内 evidence blocks。
|
||||
|
||||
这保留 PageIndex 的“目录树推理”优点,但不引入 PageIndex 的独立文档系统。
|
||||
|
||||
## 8. Graph Retrieval
|
||||
|
||||
Graph 是 Evidence Kernel 的投影,不是起点。
|
||||
|
||||
### 8.1 Day-one graph
|
||||
|
||||
第一版 graph 先用 SQLite edge table 表达确定性关系:
|
||||
|
||||
- `document_contains_resource`
|
||||
- `resource_contains_page`
|
||||
- `page_contains_block`
|
||||
- `section_contains_block`
|
||||
- `markdown_links_to`
|
||||
- `resource_refers_to`
|
||||
- `block_mentions_entity`
|
||||
|
||||
这些边都必须能回到 `EvidenceLocator`。
|
||||
|
||||
### 8.2 LadybugDB projection
|
||||
|
||||
当出现以下情况,再引入 LadybugDB:
|
||||
|
||||
- 需要 Cypher 做多跳实体关系查询。
|
||||
- SQLite edge table 查询已经影响交互速度。
|
||||
- 多文档 entity graph 已有稳定抽取合同。
|
||||
- 需要 graph traversal 给 agent 提供明确收益。
|
||||
|
||||
LadybugDB 只读投影:
|
||||
|
||||
```text
|
||||
evidence.sqlite / source-map artifacts
|
||||
-> graph projection job
|
||||
-> .mnote/graph/ladybug/
|
||||
```
|
||||
|
||||
禁止让 LadybugDB 反向成为 source-map、正文或 locator 真相。
|
||||
|
||||
## 9. Agent Tool Contract
|
||||
|
||||
### 9.1 `mnote.evidence.search`
|
||||
|
||||
请求:
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "合同解除条件",
|
||||
"scope": {
|
||||
"workspaceId": "local:notes",
|
||||
"rootUri": "file:///mnt/Data1T/notes",
|
||||
"targetDocumentId": null,
|
||||
"includeResources": true,
|
||||
"includeOcr": true
|
||||
},
|
||||
"mode": "hybrid",
|
||||
"topK": 8
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"results": [
|
||||
{
|
||||
"evidenceId": "ev_spec_pdf_p3_b7",
|
||||
"quote": "合同任一方可在提前三十日通知后解除...",
|
||||
"score": 0.82,
|
||||
"source": {
|
||||
"schema": "mnote.evidence_locator.v1",
|
||||
"rootUri": "file:///mnt/Data1T/notes",
|
||||
"ownerDocumentId": "local-md:docs~2FPage.md",
|
||||
"ownerDocumentPath": "docs/Page.md",
|
||||
"resourcePath": "docs/Page.assets/spec.pdf",
|
||||
"resourceKind": "pdf",
|
||||
"page": 3,
|
||||
"bbox": [72, 220, 510, 268],
|
||||
"sectionPath": ["第二章", "解除条件"],
|
||||
"sourceMapPath": "docs/Page.ocr/spec.pdf.source-map.json",
|
||||
"openAction": {
|
||||
"type": "mnote.open_resource_locator",
|
||||
"url": "/documents/local-md:docs~2FPage.md?sourceKind=local_folder&rootUri=...&resource=docs%2FPage.assets%2Fspec.pdf&page=3&bbox=72,220,510,268"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 `mnote.evidence.read`
|
||||
|
||||
根据 locator 读取周边上下文:
|
||||
|
||||
- 同页前后 blocks。
|
||||
- 同 section 摘要。
|
||||
- owner Markdown 中引用该资源的位置。
|
||||
- OCR / parsed artifact 片段。
|
||||
|
||||
### 9.3 `mnote.evidence.open`
|
||||
|
||||
只做 open action 归一化,不让 agent 拼 URL。
|
||||
|
||||
UI 负责:
|
||||
|
||||
- Markdown:打开文档并跳 heading / line / block。
|
||||
- PDF:打开 resource tab,滚到 page,绘制 bbox highlight。
|
||||
- 图片:打开 resource tab,按 bbox highlight。
|
||||
- Office:打开 resource tab,尽量定位页/段落;定位能力不足时退化到资源级打开。
|
||||
|
||||
### 9.4 兼容 `docs_search` / `docs_read`
|
||||
|
||||
`docs_search`、`docs_read` 保留,但逐步转发到 evidence tool:
|
||||
|
||||
- 旧调用仍返回 `results`。
|
||||
- 新调用额外返回 `evidence` 和 `source.locator`。
|
||||
- Hermes / Reasonix prompt 中优先推荐 `mnote.evidence.search`。
|
||||
|
||||
## 10. UI Contract
|
||||
|
||||
搜索结果和 agent answer citation 必须使用统一 locator。
|
||||
|
||||
展示:
|
||||
|
||||
```text
|
||||
引用 1:Page.md / spec.pdf / 第 3 页 / 第二章 解除条件
|
||||
```
|
||||
|
||||
点击行为:
|
||||
|
||||
```text
|
||||
openAction -> document shell -> resource tab -> page/bbox highlight
|
||||
```
|
||||
|
||||
禁止:
|
||||
|
||||
- 只返回 OCR sidecar 文件给用户。
|
||||
- 只返回内部 `local:folder:...` id。
|
||||
- agent 自己拼接路径或 URL。
|
||||
- graph answer 没有 evidence locator。
|
||||
|
||||
## 11. 与参考项目的关系
|
||||
|
||||
### 11.1 PageIndex
|
||||
|
||||
采用:
|
||||
|
||||
- section tree。
|
||||
- tree search。
|
||||
- page / section traceability。
|
||||
|
||||
不采用:
|
||||
|
||||
- 独立 PageIndex runtime。
|
||||
- 让 PageIndex 拥有文档真相。
|
||||
- 让 LLM 每次从大文档原文重新推理检索。
|
||||
|
||||
### 11.2 BookRAG
|
||||
|
||||
采用:
|
||||
|
||||
- `hierarchy + entity graph + fine-grained evidence mapping` 的整体思想。
|
||||
- 不同 query 使用不同 retrieval workflow。
|
||||
|
||||
不采用:
|
||||
|
||||
- Python 3.12 / conda / 研究型 pipeline 作为 MNote runtime。
|
||||
- 独立 vector DB / graph DB / parser 多套真相。
|
||||
|
||||
### 11.3 Kwipu
|
||||
|
||||
采用:
|
||||
|
||||
- Markdown note graph 的 wikilink / frontmatter 抽取。
|
||||
- MCP 工具返回带 source 的 answer 形态。
|
||||
- 增量更新、模型不匹配检测和 anti-hallucination prompt 思路。
|
||||
|
||||
不采用:
|
||||
|
||||
- Ollama + LlamaIndex + Python graph runtime 作为 MNote 默认后端。
|
||||
- 让 Kwipu 扫描 MNote workspace 后生成另一套 graph truth。
|
||||
|
||||
### 11.4 LadybugDB
|
||||
|
||||
采用:
|
||||
|
||||
- 后续 property graph / Cypher / embedded graph projection。
|
||||
|
||||
不采用:
|
||||
|
||||
- 第一版即替换 SQLite FTS。
|
||||
- 把 graph DB 当正文、source-map 或 locator 真相。
|
||||
|
||||
### 11.5 SeekDB
|
||||
|
||||
采用:
|
||||
|
||||
- 观察其 hybrid vector + full-text + scalar 查询能力。
|
||||
- 后续可做 `SearchBackend` provider。
|
||||
|
||||
不采用:
|
||||
|
||||
- 当前默认引入。
|
||||
- 用它替代 graph。
|
||||
- 在 Rust local-first 核心尚未确认耦合成本前,把它放入主链。
|
||||
|
||||
## 12. 权限与安全
|
||||
|
||||
- evidence search 必须先经过 workspace / rootUri access check。
|
||||
- 外部 OCR / parse provider 上传文件前,必须遵守当前 UI 授权和 provider policy。
|
||||
- MinerU token 只在后端读取,不进入前端 payload、日志和 agent context。
|
||||
- Agent 只能拿到 evidence locator 和 quote,不拿 provider token、上传 URL、完整外部响应。
|
||||
- `EvidenceLocator` 中的 path 必须是授权 root 下的 root-relative path 或可展示绝对路径,不返回无意义内部投影 id。
|
||||
|
||||
## 13. Watcher 与刷新
|
||||
|
||||
索引刷新触发:
|
||||
|
||||
- Markdown 保存。
|
||||
- resource 上传 / 删除 / 移动 / 重命名。
|
||||
- OCR / parse job 完成。
|
||||
- source-map 写入。
|
||||
- local-folder watcher 观察到相关文件变化。
|
||||
|
||||
刷新方式:
|
||||
|
||||
- 单文件增量更新优先。
|
||||
- 大范围 rebuild 只在索引缺失、schema 变化或 source-map 无法匹配时触发。
|
||||
- 前端不新增 setInterval / polling 主链。
|
||||
- job 状态继续沿 local realtime event / WS / SSE。
|
||||
|
||||
## 14. 验收标准
|
||||
|
||||
### 14.1 文本型 PDF
|
||||
|
||||
- LiteParse no-OCR 解析成功。
|
||||
- 生成 `*.parse.md` 和 `*.source-map.json`。
|
||||
- evidence search 命中 quote。
|
||||
- result 带 page + bbox + openAction。
|
||||
- 点击能打开 owner document 的 resource tab,并定位到 PDF 页。
|
||||
|
||||
### 14.2 图片型 PDF / 图片
|
||||
|
||||
- MinerUProvider 解析成功。
|
||||
- 生成 `*.ocr.md` 和 `*.source-map.json`。
|
||||
- `includeOcr=true` 命中 evidence。
|
||||
- 缺 token 时返回 `mineru_token_missing`,不写成功 artifact。
|
||||
- 点击能打开资源并定位 page / bbox;无法 bbox 时至少定位到页。
|
||||
|
||||
### 14.3 Markdown / 笔记
|
||||
|
||||
- Markdown heading 生成 section tree。
|
||||
- wikilink / resource ref 进入 deterministic edge。
|
||||
- evidence search 返回 line / heading locator。
|
||||
- 点击回到 Markdown 文档对应位置。
|
||||
|
||||
### 14.4 Agent
|
||||
|
||||
- Hermes / Reasonix 回答引用至少包含 quote、source title、page/section 和 openAction。
|
||||
- agent 不直接读取 `.mnote/index/evidence.sqlite`。
|
||||
- agent 不自行拼接 open URL。
|
||||
- answer 中每个关键事实至少能回到一个 `EvidenceLocator`。
|
||||
|
||||
### 14.5 Graph
|
||||
|
||||
- SQLite edge table 能从 evidence/source-map 重建。
|
||||
- graph answer 必须返回 source evidence,不允许只返回 entity relation。
|
||||
- LadybugDB projection 删除后不影响 search / locator 主链。
|
||||
|
||||
## 15. 实施顺序
|
||||
|
||||
这是同一个整体架构的落地顺序,不是多个互相竞争的方案。
|
||||
|
||||
1. 定义 `EvidenceLocator`、`ParsedResourceArtifact`、`SourceMap` Rust 类型和 JSON schema。
|
||||
2. 扩展 MinerU sidecar:从结果 zip 中保留 JSON,写 `*.source-map.json`。
|
||||
3. 接入 LiteParseProvider:文本 PDF 走 no-OCR parse,输出同一 artifact。
|
||||
4. 建 `.mnote/index/evidence.sqlite` + FTS5 + section tree。
|
||||
5. 新增 `mnote.evidence.search/read/open` route / tool,并让 `docs_search/docs_read` 兼容转发。
|
||||
6. UI resource tab 支持 locator open/highlight。
|
||||
7. Markdown wikilink / resource ref 进入 deterministic edge table。
|
||||
8. 评估 LadybugDB projection,只有当 graph traversal 真正需要时再接。
|
||||
|
||||
## 16. 明确非目标
|
||||
|
||||
- 不一次性替换当前 OCR 系统。
|
||||
- 不引入 PageIndex / BookRAG / Kwipu 作为默认 runtime。
|
||||
- 不默认引入 SeekDB。
|
||||
- 不先上 LadybugDB 再倒推检索。
|
||||
- 不让 agent 直接扫描 sidecar、SQLite 或 graph 存储。
|
||||
- 不把 PDF 全量转图片作为默认方案;图片只作为 visual locator / OCR fallback。
|
||||
- 不把 OCR 文本直接污染 owner Markdown 正文。
|
||||
- 不让 graph 节点脱离 evidence locator。
|
||||
|
||||
## 17. 需要二次开发的接口
|
||||
|
||||
### 17.1 Rust trait
|
||||
|
||||
```rust
|
||||
pub trait ParseProvider {
|
||||
fn provider_id(&self) -> &'static str;
|
||||
fn can_parse(&self, input: &ParseInput) -> ParseCapability;
|
||||
async fn parse(&self, input: ParseInput) -> Result<ParsedResourceArtifact, ParseError>;
|
||||
}
|
||||
```
|
||||
|
||||
Provider 不直接写索引。Provider 只返回 artifact;indexer 统一消费 artifact。
|
||||
|
||||
### 17.2 Search backend trait
|
||||
|
||||
```rust
|
||||
pub trait EvidenceSearchBackend {
|
||||
fn refresh_resource(&self, artifact: &ParsedResourceArtifact) -> Result<()>;
|
||||
fn search(&self, request: EvidenceSearchRequest) -> Result<EvidenceSearchResponse>;
|
||||
fn read(&self, locator: EvidenceLocator, context: EvidenceReadContext) -> Result<EvidenceReadResponse>;
|
||||
}
|
||||
```
|
||||
|
||||
默认实现是 SQLite FTS。SeekDB 只能作为后续实现之一。
|
||||
|
||||
### 17.3 Graph projection trait
|
||||
|
||||
```rust
|
||||
pub trait EvidenceGraphProjection {
|
||||
fn refresh_edges(&self, edges: Vec<EvidenceEdge>) -> Result<()>;
|
||||
fn traverse(&self, request: EvidenceGraphRequest) -> Result<EvidenceGraphResponse>;
|
||||
}
|
||||
```
|
||||
|
||||
默认实现是 SQLite edge table。LadybugDB 只能作为后续 projection 实现。
|
||||
|
||||
## 18. 最终口径
|
||||
|
||||
MNote 的文档问答能力应以 Evidence 为核心,而不是以 OCR、vector、graph 或某个外部 RAG 项目为核心。
|
||||
|
||||
判断一个工具能否进入主链,只看三件事:
|
||||
|
||||
1. 是否能产出或消费 MNote 的 canonical `EvidenceLocator`。
|
||||
2. 是否减少重复真相,而不是制造另一套文档库。
|
||||
3. 是否和当前 Rust local-first / Page AI / watcher / resource tab 主链低耦合接入。
|
||||
|
||||
按这个标准,当前默认组合是:
|
||||
|
||||
```text
|
||||
LiteParseProvider + MinerUProvider
|
||||
-> MNote SourceMap / Evidence Artifact
|
||||
-> SQLite FTS + section tree
|
||||
-> mnote.evidence.* agent tool
|
||||
-> UI locator open/highlight
|
||||
-> optional LadybugDB graph projection
|
||||
```
|
||||
|
||||
这条线最贴近当前 MNote 项目,也最少引入互相冲突的外部项目真相。
|
||||
|
||||
## 19. 详细 Checklist
|
||||
|
||||
> 目标:把“能搜到”升级为“能定位、能引用、能回跳、能扩展图谱”,同时不引入重复真相。
|
||||
|
||||
### 19.1 统一证据合同
|
||||
|
||||
- [x] 定义 `EvidenceLocator` schema。
|
||||
- [x] 定义 `ParsedResourceArtifact` schema。
|
||||
- [x] 定义 `SourceMap` schema。
|
||||
- [x] 定义 `EvidenceSearchRequest` / `EvidenceSearchResponse`。
|
||||
- [x] 定义 `EvidenceReadRequest` / `EvidenceReadResponse`。
|
||||
- [x] 定义 `EvidenceEdge` schema。
|
||||
- [x] 统一 JSON 字段命名,保证 LiteParse / MinerU / Markdown 共用一套定位合同。
|
||||
- [x] 明确哪些字段是 canonical,哪些字段只是 cache copy。
|
||||
|
||||
验收:
|
||||
|
||||
- 任一证据都能从 locator 回到 owner 文档或 resource。
|
||||
- 同一个定位字段不在多个地方各自定义不同语义。
|
||||
|
||||
2026-06-03 首刀证据:
|
||||
|
||||
- 已新增 `rust/crates/core-protocol/src/evidence.rs`,导出 `EvidenceLocator`、`ResourceSourceMap`、`ParsedResourceArtifact`、`EvidenceSearch*`、`EvidenceRead*`、`EvidenceEdge`。
|
||||
- 已在 `core-protocol` tool registry 注册 `mnote.evidence.search`、`mnote.evidence.read`、`mnote.evidence.open` 只读工具。
|
||||
- 字段口径:原始文件、owner Markdown、`*.source-map.json` 与 `EvidenceLocator` 是 canonical;`search-index.json`、未来 `evidence.sqlite` row、graph node/edge 中复制的 page/bbox/text/section 字段都只是 cache copy,必须能从 canonical artifact 重建。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p core-protocol --lib`。
|
||||
|
||||
### 19.2 资源解析层
|
||||
|
||||
- [x] 实现 `ParseProvider` trait。
|
||||
- [x] 实现 `MarkdownParserProvider`。
|
||||
- [x] 实现 `LiteParseProvider`。
|
||||
- [x] 复用现有 `MinerUProvider` 路径并输出统一 source-map sidecar。
|
||||
- [x] 文本型 PDF 默认走 `no-ocr`。
|
||||
- [x] 扫描 PDF / 图片默认走 MinerU。
|
||||
- [x] 解析失败时保留可诊断错误,但不污染正文真相。
|
||||
- [x] 解析输出必须包含 source hash,便于 stale 检测。
|
||||
|
||||
验收:
|
||||
|
||||
- 同一输入文件重复解析能稳定生成一致 locator。
|
||||
- 不同 provider 输出的 artifact 能进入同一后续索引链。
|
||||
|
||||
2026-06-03 解析层证据:
|
||||
|
||||
- 已新增 `rust/crates/mnote-web/src/evidence_parse.rs`,定义 `ParseProvider`、`ParseInput`、`ParseProviderOutput`、`ParseCapability`、`ParseProviderMode`、`ParseError`。
|
||||
- `MarkdownParserProvider` 已能读取 Markdown,生成 `ParsedResourceArtifact`、Markdown source-map、section path、line block,并输出稳定 `fnv1a64` source hash。
|
||||
- `LiteParseProvider` 已接入真实 `lit parse --format json --no-ocr` CLI adapter,读取 LiteParse JSON 后生成 `ParsedResourceArtifact`、`ResourceSourceMap`、page / bbox / section / char range 与 Markdown 输出;测试用 fake `lit` 验证命令调用和 JSON 映射,不依赖 mock provider 返回。
|
||||
- 2026-06-04 复核补强:`LiteParseProvider` 已兼容 LiteParse 2.0 实际 JSON 结构 `pages[].textItems[]`,bbox 同时支持 `x/y/width/height` 和 `bbox[]` 两种口径;`liteparse_runtime_available()` 会检测 `MNOTE_LITEPARSE_BIN`、`lit`、`liteparse`,当前机器已安装 `/home/lix/.npm-global/bin/lit`。
|
||||
- provider 选择已补 `select_parse_provider_id`:文本 PDF / `NoOcr` 默认走 `liteparse`;OCR policy、低 native text confidence 的扫描 PDF,以及图片资源默认走 `mineru`。
|
||||
- 解析失败会返回结构化 `ParseError.code/message`,不会写入 owner Markdown 或 evidence index。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web evidence_parse -- --test-threads=1`。
|
||||
|
||||
### 19.3 Source-map 落盘
|
||||
|
||||
- [x] 确定 sidecar 目录仍沿 `{pageStem}.ocr/` 复用。
|
||||
- [x] 写入 `*.parse.md`。
|
||||
- [x] 写入 `*.ocr.md`。
|
||||
- [x] 写入 `*.source-map.json`。
|
||||
- [x] 为 source-map 记录 page / bbox / char range。
|
||||
- [x] 为 source-map 记录 section path。
|
||||
- [x] 为 source-map 记录 ownerDocumentPath 和 sourceRootRelativePath。
|
||||
- [x] 为 source-map 记录 provider、modelVersion、sourceHash。
|
||||
- [x] 保证 source-map 可独立重建定位,不依赖 `.mnote` 索引。
|
||||
|
||||
验收:
|
||||
|
||||
- 删除 `.mnote/index` 后,仍可从 sidecar 重建 evidence index。
|
||||
- 删除 sidecar 后,索引必须标记 stale,而不是继续假装有效。
|
||||
|
||||
2026-06-03 首刀证据:
|
||||
|
||||
- MinerU 真实 HTTP mock 路径已从结果 zip 读取 `content_list.json`,生成 `*.source-map.json`。
|
||||
- Source-map 目前已记录 provider、modelVersion、ownerDocumentPath、sourceRootRelativePath、sourceHash、page、bbox、block text;section 构建仍待接入 tree/outline 层。
|
||||
- Evidence search route 已能从 OCR sidecar 对应的 `*.source-map.json` 读回 page、bbox、blockId、charRange 和 sourceMapPath,证明定位字段不依赖 `.mnote` 索引缓存。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_ocr_jobs_route_runs_mineru_runtime_against_http_mock -- --test-threads=1`。
|
||||
|
||||
### 19.4 Evidence Index
|
||||
|
||||
- [x] 建 `.mnote/index/evidence.sqlite`。
|
||||
- [x] 建 `evidence_resource` 表。
|
||||
- [x] 建 `evidence_block` 表。
|
||||
- [x] 建 `evidence_section` 表。
|
||||
- [x] 建 `evidence_edge` 表。
|
||||
- [x] 建 FTS5 索引。
|
||||
- [x] 将 `search-index.json` 降级为兼容缓存,不再承载主 evidence 语义。
|
||||
- [x] 索引写入必须由 artifact 驱动,而不是由 UI 拼装驱动。
|
||||
- [x] 增量更新优先于全量 rebuild。
|
||||
|
||||
验收:
|
||||
|
||||
- 单文件修改只更新相关 resource 和 blocks。
|
||||
- 索引可由原始文件和 sidecar 全量重建。
|
||||
|
||||
2026-06-03 首刀证据:
|
||||
|
||||
- `local_search_index` 刷新时会同步写 `.mnote/index/evidence.sqlite`,包含 `evidence_meta`、`evidence_resource`、`evidence_block`、`evidence_fts`、`evidence_section`、`evidence_edge`。
|
||||
- 当前 evidence.sqlite 已覆盖 Markdown、resource 和 OCR sidecar 基础 blocks;`/api/evidence/search` 已切为 evidence.sqlite FTS/LIKE 优先,`search-index.json` 只作为缺少 evidence.sqlite 时的兼容 fallback。
|
||||
- Evidence index 写入已拆到 artifact 链:Markdown 会构造 `ParsedResourceArtifact` 后写 resource/block;OCR/source-map 资源会由 `ParsedResourceArtifact + ResourceSourceMap` 驱动写入 resource、section、block 与 locator,不再由 UI projection 拼 evidence 语义。
|
||||
- 2026-06-04 复核补强:local index 刷新已对 Markdown 引用到的 PDF / Office 资源执行 LiteParse 正文解析,写入 `{ownerStem}.ocr/{resource}.parse.md` 与 `{ownerStem}.ocr/{resource}.source-map.json`,再由 artifact 写入 `.mnote/index/evidence.sqlite`;sidecar 自身会被跳过,避免 parse 结果被当普通 Markdown 重复索引。
|
||||
- `refresh_local_search_index_for_path` 已改为写兼容 `search-index.json` 后调用 `refresh_evidence_sqlite_index_for_path`,只删除并重建受影响 resource / block / edge;全量 `write_evidence_sqlite_index` 仍只用于初始/强制 rebuild。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web search_documents_local_folder_uses_authorized_root_index -- --test-threads=1`。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web evidence_search_route_prefers_sqlite_index -- --test-threads=1`。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web evidence_sqlite_query_returns_locator_results -- --test-threads=1`。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web evidence_index_parses_resource_body_with_liteparse_sidecar -- --test-threads=1`。
|
||||
|
||||
### 19.5 搜索与读回
|
||||
|
||||
- [x] 新增 `mnote.evidence.search` 工具合同。
|
||||
- [x] 新增 `mnote.evidence.read` 工具合同。
|
||||
- [x] 新增 `mnote.evidence.open` 工具合同。
|
||||
- [x] 新增 `mnote.evidence.search` route / runtime 执行。
|
||||
- [x] 新增 `mnote.evidence.read` route / runtime 执行。
|
||||
- [x] 新增 `mnote.evidence.open` route / runtime 执行。
|
||||
- [x] 旧 `docs_search` / `docs_read` 兼容转发到 evidence tool。
|
||||
- [x] search 结果必须返回 quote + locator + openAction。
|
||||
- [x] read 必须返回上下文窗口和同 section 周边证据。
|
||||
- [x] open 只做定位归一化,不让 agent 自己拼 URL。
|
||||
|
||||
验收:
|
||||
|
||||
- search 命中后可以直接点击跳转。
|
||||
- read 结果可以作为 agent 回答引用,不需要二次猜测。
|
||||
|
||||
2026-06-03 首刀证据:
|
||||
|
||||
- 已新增 `/api/evidence/search`,复用 local-folder search/OCR 索引并输出 `EvidenceSearchResponse`。
|
||||
- OCR 命中会优先使用 `ocrEvidence.sourceRootRelativePath` 作为真实 resourcePath,并从 `ocrEvidence.ocrRootRelativePath` 推导 `*.source-map.json`,返回真实图片/PDF resourceKind、page、bbox、charRange 和 openAction params。
|
||||
- 已新增 `/api/evidence/read` 和 `/api/evidence/open` 最小 route。`read` 目前仍基于现有 local search 上下文回填,后续需要接 source-map / evidence block 后才能满足 section 周边证据验收。
|
||||
- Hermes tool runtime 已接入 `mnote.evidence.search/read/open`,不再只是 core-protocol 注册。
|
||||
- `/api/search/documents` 的 local_folder 路径已补回 `evidence` 与 `source.locator`,旧兼容面至少能看到可回跳证据。
|
||||
- Hermes runtime 已补旧 `docs_search` / `docs_read` 兼容:`docs_search` 转到 `mnote.evidence.search`,`docs_read` 带 locator 时转到 `mnote.evidence.read`,旧 `documentId` 读法保留并附加 locator。
|
||||
- `/api/evidence/search` 已优先查询 `.mnote/index/evidence.sqlite`,命中 SQLite OCR/resource block 后仍会回读 `*.source-map.json` 补 page、bbox、blockId、charRange 和 openAction params。
|
||||
- `/api/evidence/read` 已优先基于 locator 读 `*.source-map.json`,按 blockId 返回前后 blocks,并补入同 section 的周边 blocks;无 source-map 时回退到 evidence.sqlite 的同 owner 上下文窗口,再回退旧 local search。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web evidence -- --test-threads=1`。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web evidence_sqlite_read_context_returns_anchor_block -- --test-threads=1`。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_legacy_docs -- --test-threads=1`。
|
||||
|
||||
### 19.6 UI 侧回跳
|
||||
|
||||
- [x] resource tab 支持按 locator 打开 PDF 页。
|
||||
- [x] resource tab 支持 bbox 高亮。
|
||||
- [x] Markdown 支持 heading / line 定位。
|
||||
- [x] owner page 和资源页使用同一 locator。
|
||||
- [x] 结果列表里不要暴露内部 cache id 作为唯一入口。
|
||||
- [x] 侧栏、搜索结果和 agent citation 使用同一跳转合同。
|
||||
|
||||
验收:
|
||||
|
||||
- 用户点结果可以进入正确页码或正确位置。
|
||||
- 同一条证据在 UI、agent 和索引中的展示一致。
|
||||
|
||||
2026-06-03 首刀证据:
|
||||
|
||||
- 搜索结果行已携带 `data-evidence-locator`,点击时不再只依赖内部 cache id;PDF / 图片 locator 会通过 `openLocalResourceInActiveTab` 打开 resource tab,Markdown locator 会把 `blockId` / `page` / `bbox` / `sourceMapPath` 带入文档 URL。
|
||||
- resource tab 已消费 `EvidenceLocator`:面板记录 `data-mnote-evidence-*`,PDF iframe 会把 `page` / `bbox` / `blockId` 传给 `/pdf-preview`,图片资源会绘制 bbox overlay,Markdown/text/code resource tab 会按 `blockId` 滚动并高亮。
|
||||
- `/pdf-preview` 已支持 `page` / `bbox` / `blockId` query,渲染目标页时会标记页 canvas、绘制 bbox highlight 并滚到目标页。
|
||||
- Markdown evidence index 已按 heading/非空行生成 evidence blocks,locator 带 `sectionPath`、`lineRange`、`blockId`;文档页主编辑器已支持从 URL 读取 `blockId` / `lineRange` 并对对应 `data-block-id` 做滚动和高亮。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web pdf_preview_page_does_not_render_visible_toolbar -- --test-threads=1`。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web document_shell_returns_page_aggregate_snapshot -- --test-threads=1`。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web evidence_sqlite_query_returns_locator_results -- --test-threads=1`。
|
||||
- 验证:`node --check rust/crates/mnote-web/browser/document-resource-tab-runtime.js`、`node --check rust/crates/mnote-web/browser/sidebar-tree-runtime.js`、`node --check rust/crates/mnote-web/browser/sidebar-filetree-open-runtime.js`、`node --check rust/crates/mnote-web/browser/document-editor-adapter-runtime.js`。
|
||||
|
||||
### 19.7 图谱投影
|
||||
|
||||
- [x] 先用 SQLite edge table 表达 deterministic relation。
|
||||
- [x] 先支持 `contains` / `links_to` / `resource_refers_to` 这类稳定边。
|
||||
- [x] 接入 `mentions` entity edge 抽取。
|
||||
- [x] 只有 locator 能回溯的边才能进入 graph。
|
||||
- [x] LadybugDB 只做 projection,不做 source of truth。
|
||||
- [x] graph traversal 结果必须带证据引用。
|
||||
- [x] graph 删除后不影响搜索主链。
|
||||
|
||||
验收:
|
||||
|
||||
- 图谱可删可重建。
|
||||
- 图谱回答不会脱离 evidence locator。
|
||||
|
||||
2026-06-03 首刀证据:
|
||||
|
||||
- evidence.sqlite 写入时已从 Markdown `backlinks` / `resourceRefs` 生成确定性边:`markdown_links_to`、`resource_refers_to`、`document_contains_resource`。
|
||||
- deterministic edge 的 `source_block_id` 指向对应 Markdown body evidence block,`insert_evidence_edge` 会拒绝没有 locator block 的边;`mentions` 已以 `@Entity` 规则进入 `entity:*` 边。
|
||||
- `mnote.evidence.search` 的 `mode=graph` 已走 SQLite edge table traversal;返回仍是 `EvidenceSearchResult`,包含 `quote + EvidenceLocator`,因此 graph answer 不会脱离 evidence 引用。
|
||||
- 当前没有引入 LadybugDB runtime,graph 主链只读 `.mnote/index/evidence.sqlite` 的 edge projection;测试直接删除 `evidence_edge` 后,FTS 搜索仍可命中 evidence blocks,证明 graph projection 删除不影响 search / locator 主链。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web local_search_index -- --test-threads=1`。
|
||||
|
||||
### 19.8 Agent 接入
|
||||
|
||||
- [x] Hermes / Reasonix prompt 中优先使用 evidence tool。
|
||||
- [x] agent 回答模板强制带 source title、page / section 和 quote。
|
||||
- [x] agent 不直接读取 SQLite 或 sidecar 文件。
|
||||
- [x] agent 不自己拼接 open URL。
|
||||
- [x] agent 的 run receipt 记录 evidence ids。
|
||||
|
||||
验收:
|
||||
|
||||
- Agent 说出的每个关键结论都能被用户点击回到原文。
|
||||
- Agent 的检索路径不依赖隐藏的二级真相。
|
||||
|
||||
2026-06-03 首刀证据:
|
||||
|
||||
- 已新增 `skills/mnote-document-evidence/SKILL.md`,Hermes / Reasonix 可见,明确要求优先使用 `mnote.evidence.search/read/open`。
|
||||
- Hermes tool manifest 已暴露 `mnote.evidence.search/read/open`,运行时 dispatch 到 evidence route helper。
|
||||
- 2026-06-04 复核补强:`scripts/reasonix-acp-wrapper.mjs` 已注册 `mnote_evidence_search/read/open` 三个 Reasonix ACP 只读工具,并把它们转发到 Rust `mnote.evidence.search/read/open`;wrapper selftest 覆盖 evidence payload 继承 `rootUri` 与 evidence search 只读属性。
|
||||
- 2026-06-04 真实服务验证:登录测试账号后,`/api/hermes/client/tools?scope=mnote&profile=reasonix` 已返回 `mnote.evidence.search/read/open`;`/api/hermes/client/skills?runtime=mnote&agentId=reasonix` 已返回启用的 `mnote-document-evidence` skill。
|
||||
- 2026-06-04 索引 skill 合并:公开 skill 改为 `mnote-local-index`,合并 evidence 检索与索引管理说明;`mnote-document-evidence` 仅保留为 `mnote.skill.read` 兼容别名,不再作为公开 skill 摘要展示。新增 `mnote.index.status/refresh/update_settings`,Reasonix ACP 对应 `mnote_index_status/refresh/update_settings`。
|
||||
- 2026-06-04 真 Reasonix ACP 验证:带 `agentId=reasonix`、`contextRefs=[current_page, folder]`、local-folder `rootUri` 发起 `/api/hermes/client/runs`,SSE 中出现 `tool.started/tool.completed`,工具为 `mnote_evidence_search`,返回 `quote="Printer test page"`、`page=1`、`bbox`、`sourceMapPath` 和 `mnote.agent_run_receipt.evidence.v1`。
|
||||
- 2026-06-04 修正:Reasonix wrapper 原先用字符串包含 `"error"` 判断工具失败,导致 `error:null` 的成功结果在 UI/SSE 中被标记成 `tool.failed`;已改为解析 JSON,仅 `ok:false` 或非空 `error` 才标记失败。
|
||||
- Skill 约束明确要求回答保留 quote、source 和 openAction,禁止直接读取 `.mnote/index`、OCR sidecar 或自行拼接 URL。
|
||||
- 验证:`cargo test --manifest-path rust/Cargo.toml -p mnote-web skill_registry -- --test-threads=1`、`cargo test --manifest-path rust/Cargo.toml -p mnote-web hermes_tools_manifest_returns_first_batch_tools -- --test-threads=1`。
|
||||
- 验证:`node --check scripts/reasonix-acp-wrapper.mjs`。
|
||||
- 验证:`MNOTE_REASONIX_ACP_SELFTEST=1 node scripts/reasonix-acp-wrapper.mjs`。
|
||||
- 验证:`node scripts/task528-document-evidence-liteparse-agent-smoke.js`,确认真实 PDF 正文 `Printer test page` 命中 `mnote.evidence.search`,返回 `page=1`、`bbox`、`sourceMapPath`,并在 agent tool audit/run receipt 中记录 evidence id。
|
||||
- 验证产物:`tmp/task528-document-evidence-liteparse-agent-smoke/reasonix-tools-events.sse`。
|
||||
|
||||
### 19.9 兼容与迁移
|
||||
|
||||
- [x] 保留现有 OCR sidecar 约定,不强制迁移存量数据。
|
||||
- [x] 保留现有 `includeOcr=true` 兼容入口。
|
||||
- [x] 保留现有 `docs_search` / `docs_read` 兼容壳。
|
||||
- [x] 新 contract 先增后替,不做硬切。
|
||||
- [x] 迁移阶段允许 `search-index.json` 和 `evidence.sqlite` 并存,但只能有一个主语义。
|
||||
|
||||
验收:
|
||||
|
||||
- 存量数据不需要先整体重跑就能继续使用。
|
||||
- 新旧接口同时存在时,主链不会分叉出两套真相。
|
||||
|
||||
### 19.10 不做的事
|
||||
|
||||
- [x] 不新增独立 PageIndex runtime。
|
||||
- [x] 不新增独立 BookRAG runtime。
|
||||
- [x] 不新增独立 Kwipu runtime。
|
||||
- [x] 不默认引入 SeekDB。
|
||||
- [x] 不把 LadybugDB 变成正文或 locator 真相。
|
||||
- [x] 不把 OCR 文本直接写回 owner Markdown 正文。
|
||||
- [x] 不让 agent 直接扫 `.mnote` 索引文件。
|
||||
- [x] 不为 search 主链加轮询刷新。
|
||||
|
||||
验收:
|
||||
|
||||
- 每个新增项目都能回答“它是否减少重复真相”。
|
||||
- 不能回答时,默认不接入主链。
|
||||
@@ -3,13 +3,22 @@
|
||||
> 状态:process
|
||||
>
|
||||
> 目标:把当前分裂的 MNote builtin skill、mnote tool manifest、Hermes plugin、Reasonix wrapper 和 Page AI UI 开关收口为同一个“AI 能力”模型。对用户来说 skill / plugin / tool 都是“授予 AI 的能力”,UI 不应暴露实现层分类;实现层再把一个能力映射到说明书、工具、runtime adapter 和权限策略。本轮只整理 MNote 公共能力;Reasonix / Hermes 自带的 skills/plugins 维持现状,不纳入统一注册表迁移范围。
|
||||
>
|
||||
> 2026-06-07 口径更新:7-50 后资料库问答主线已切到 LightRAG,第一批能力包试点从旧 `mnote-local-index` 改为 `mnote-knowledge-rag`。`mnote-document-evidence` / `mnote-local-index` 只作为兼容 alias 映射到 `mnote-knowledge-rag`,不再恢复 `mnote.evidence.*` / `mnote.index.*` 作为 active tool。
|
||||
|
||||
## 当前实施状态
|
||||
|
||||
- Phase A 已落地:`rust/crates/mnote-web/src/hermes_tools/skill.rs` 已有 `MnoteCapabilityPack` / `CAPABILITY_PACKS`,`manifest.rs` 已输出 `capabilities[]` 并给 tools 标注 `capabilityId` / `capabilityIds`。
|
||||
- Phase B 已落地:`/api/hermes/client/capabilities` 已存在,Page AI `runtime=mnote` 技能目录优先请求 capabilities,能力 payload 内含 tools、readOnly、contextRefs、enabled/status。
|
||||
- Phase C 已落地:`/api/hermes/client/capabilities/toggle` 会同步 capability skill preference 和 profile tool disabled policy;直接调用被关闭 tool 会走 `mnote_tool_disabled` 硬拒绝。
|
||||
- Phase D 已完成 Reasonix 侧最小收口:`scripts/reasonix-acp-wrapper.mjs` 启动时优先读取 `/api/hermes/tools/mnote/manifest` 动态注册 MNote tool specs,mnote-web 不可用时才回落到静态 fallback;Hermes Python plugin 生成仍作为后续独立收口。
|
||||
|
||||
## 0. 用户口径
|
||||
|
||||
用户不需要理解 skill、plugin、tool 的区别。Page AI 设置中统一展示为“AI 能力”:
|
||||
|
||||
- `当前页读取`
|
||||
- `本地索引与证据检索`
|
||||
- `资料库问答`
|
||||
- `本地文件编辑`
|
||||
- `思维导图`
|
||||
- `ONLYOFFICE 实时编辑`
|
||||
@@ -35,7 +44,7 @@
|
||||
现有内置 skill:
|
||||
|
||||
- `mnote-current-page`
|
||||
- `mnote-local-index`
|
||||
- `mnote-knowledge-rag`
|
||||
- `mnote-local-file`
|
||||
- `mnote-onlyoffice-live`
|
||||
- `mnote-mindmap`
|
||||
@@ -46,7 +55,7 @@
|
||||
- skill 是 Rust 静态注册,正文来自 `skills/*/SKILL.md`。
|
||||
- `/api/hermes/client/skills?runtime=mnote&agentId=...` 通过 `mnote_builtin_skills_payload()` 输出到 UI。
|
||||
- 每个 skill 现在已经带 `toolNames` 和 `requiresContextRefs`,但这些只是弱引用,不是一个正式 capability/plugin 合同。
|
||||
- `mnote.skill.read` 能懒加载正文;旧 `mnote-document-evidence` 已作为兼容别名映射到 `mnote-local-index`。
|
||||
- `mnote.skill.read` 能懒加载正文;旧 `mnote-document-evidence` / `mnote-local-index` 只作为兼容别名映射到 `mnote-knowledge-rag`,不恢复旧 evidence / index 工具。
|
||||
|
||||
### 1.2 MNote tools
|
||||
|
||||
@@ -55,7 +64,8 @@
|
||||
现有工具大类:
|
||||
|
||||
- skill/context:`mnote.skill.read`、`mnote.context.*`
|
||||
- evidence/index:`mnote.evidence.*`、`mnote.index.*`
|
||||
- knowledge-rag:`mnote.knowledge_rag.status`、`mnote.knowledge_rag.query`、`mnote.knowledge_rag.open_reference`
|
||||
- retired evidence/index:`mnote.evidence.*`、`mnote.index.*` 只保留历史对照,不作为 active manifest / capability 示例
|
||||
- doc/block/page/artifact:`mnote.doc.*`、`mnote.block.*`、`mnote.page.*`、`mnote.artifact.*`
|
||||
- mindmap:`mnote.mindmap.*`
|
||||
- office/onlyoffice:`mnote.office.*`、`mnote.onlyoffice.*`
|
||||
@@ -178,25 +188,22 @@ pub struct MnoteCapabilityPack {
|
||||
"title": "MNote",
|
||||
"capabilities": [
|
||||
{
|
||||
"id": "mnote-local-index",
|
||||
"title": "MNote local index",
|
||||
"description": "Search local documents with evidence locators and manage local index scopes.",
|
||||
"id": "mnote-knowledge-rag",
|
||||
"title": "资料库问答",
|
||||
"description": "Ask the LightRAG-backed knowledge library and open returned MNote source references.",
|
||||
"enabled": true,
|
||||
"toggleable": true,
|
||||
"readOnly": false,
|
||||
"skillId": "mnote-local-index",
|
||||
"skillId": "mnote-knowledge-rag",
|
||||
"toolNames": [
|
||||
"mnote.evidence.search",
|
||||
"mnote.evidence.read",
|
||||
"mnote.evidence.open",
|
||||
"mnote.index.status",
|
||||
"mnote.index.refresh",
|
||||
"mnote.index.update_settings"
|
||||
"mnote.knowledge_rag.status",
|
||||
"mnote.knowledge_rag.query",
|
||||
"mnote.knowledge_rag.open_reference"
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "mnote.index.update_settings",
|
||||
"kind": "write",
|
||||
"name": "mnote.knowledge_rag.query",
|
||||
"kind": "read",
|
||||
"status": "available",
|
||||
"enabled": true,
|
||||
"requiresWritePermission": true
|
||||
@@ -221,7 +228,7 @@ pub struct MnoteCapabilityPack {
|
||||
{
|
||||
"runtime": "mnote",
|
||||
"profile": "reasonix",
|
||||
"id": "mnote-local-index",
|
||||
"id": "mnote-knowledge-rag",
|
||||
"enabled": true
|
||||
}
|
||||
```
|
||||
@@ -240,16 +247,16 @@ pub struct MnoteCapabilityPack {
|
||||
{
|
||||
"capabilities": [
|
||||
{
|
||||
"id": "mnote-local-index",
|
||||
"skillId": "mnote-local-index",
|
||||
"toolNames": ["mnote.evidence.search", "mnote.index.status"]
|
||||
"id": "mnote-knowledge-rag",
|
||||
"skillId": "mnote-knowledge-rag",
|
||||
"toolNames": ["mnote.knowledge_rag.status", "mnote.knowledge_rag.query", "mnote.knowledge_rag.open_reference"]
|
||||
}
|
||||
],
|
||||
"tools": [
|
||||
{
|
||||
"name": "mnote.index.status",
|
||||
"capabilityId": "mnote-local-index",
|
||||
"capabilityScope": ["index.read", "evidence.read"]
|
||||
"name": "mnote.knowledge_rag.query",
|
||||
"capabilityId": "mnote-knowledge-rag",
|
||||
"capabilityScope": ["knowledge_rag.read", "evidence.read"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -261,8 +268,8 @@ pub struct MnoteCapabilityPack {
|
||||
|
||||
在 Page AI 的 Skills 页中,用户看到的是统一“AI 能力”列表,不再分 skill / plugin / tool:
|
||||
|
||||
- 行标题:`MNote local index`
|
||||
- 副标题:`索引 / 证据检索 · 6 tools · 需要 folder`
|
||||
- 行标题:`资料库问答`
|
||||
- 副标题:`知识库 / LightRAG · 3 tools · 需要 folder`
|
||||
- 状态 chip:`只读` / `可写` / `部分工具关闭` / `只读上下文不可写`
|
||||
- 主开关:启用/关闭整个能力包
|
||||
- 展开项:列出 tools,显示 read/write、enabled、status
|
||||
@@ -281,12 +288,12 @@ Runtime 页继续保留 `mnote tools`,但作为高级调试面:
|
||||
|
||||
### 4.3 索引面板与能力包关系
|
||||
|
||||
Sidebar 的“索引设置”仍是用户直接管理索引范围的产品 UI;Page AI 的 `mnote-local-index` capability 是 agent 能力开关。
|
||||
Sidebar 的“资料库 / 知识库设置”仍是用户直接管理 LightRAG source、索引范围和服务状态的产品 UI;Page AI 的 `mnote-knowledge-rag` capability 是 agent 能力开关。
|
||||
|
||||
两者职责不同:
|
||||
|
||||
- 索引设置面板:用户手动新增/删除/刷新索引范围。
|
||||
- MNote local index capability:允许 agent 使用工具帮用户查看、新增、刷新、删除索引范围。
|
||||
- 知识库设置面板:用户手动新增/删除/刷新 source,查看 LightRAG 服务和 source registry 状态。
|
||||
- MNote knowledge-rag capability:允许 agent 使用工具查询资料库、查看状态和打开返回来源;sourcePaths 当前只过滤返回 references,不声称 provider 层预过滤 raw chunks。
|
||||
|
||||
## 5. Runtime 适配
|
||||
|
||||
@@ -302,8 +309,8 @@ Sidebar 的“索引设置”仍是用户直接管理索引范围的产品 UI;
|
||||
|
||||
- 删除 `REASONIX_TOOL_TO_MNOTE_TOOL` 手写表。
|
||||
- 工具名转换统一由函数生成:
|
||||
- `mnote.index.status` -> `mnote_index_status`
|
||||
- `mnote.evidence.search` -> `mnote_evidence_search`
|
||||
- `mnote.knowledge_rag.status` -> `mnote_knowledge_rag_status`
|
||||
- `mnote.knowledge_rag.open_reference` -> `mnote_knowledge_rag_open_reference`
|
||||
|
||||
### 5.2 Hermes ACP
|
||||
|
||||
@@ -332,7 +339,7 @@ Sidebar 的“索引设置”仍是用户直接管理索引范围的产品 UI;
|
||||
|
||||
验收:
|
||||
|
||||
- `mnote-local-index` 在 `/client/skills?runtime=mnote` 可见。
|
||||
- `mnote-knowledge-rag` 在 `/client/skills?runtime=mnote` 与 `/client/capabilities?runtime=mnote` 可见,`mnote-local-index` 不再作为 active capability 暴露。
|
||||
- `/api/hermes/tools/mnote/manifest` 可看到 `capabilities[]`。
|
||||
- 旧 `mnote.skill.read` 不破。
|
||||
|
||||
@@ -345,8 +352,8 @@ Sidebar 的“索引设置”仍是用户直接管理索引范围的产品 UI;
|
||||
|
||||
验收:
|
||||
|
||||
- Page AI Skills 页中 `MNote local index` 像公共 skill 一样可见。
|
||||
- 展开能看到 `mnote.evidence.*` 和 `mnote.index.*`。
|
||||
- Page AI Skills / 能力页中 `资料库问答` 像公共 skill 一样可见。
|
||||
- 展开能看到 `mnote.knowledge_rag.*`,看不到 `mnote.evidence.*` / `mnote.index.*` active tools。
|
||||
- 开关 capability 后,下一次 run 的 `skillPreferences.mnote` 同步变化。
|
||||
|
||||
### Phase C:能力包开关驱动工具开关
|
||||
@@ -357,8 +364,8 @@ Sidebar 的“索引设置”仍是用户直接管理索引范围的产品 UI;
|
||||
|
||||
验收:
|
||||
|
||||
- 关闭 `mnote-local-index` 后 agent 不再看到该 skill 摘要。
|
||||
- 关闭后直接调用 `mnote.index.status` 返回 `mnote_tool_disabled` 或 capability disabled。
|
||||
- 关闭 `mnote-knowledge-rag` 后 agent 不再看到该 skill 摘要。
|
||||
- 关闭后直接调用 `mnote.knowledge_rag.query` 返回 `mnote_tool_disabled` 或 capability disabled。
|
||||
- 再打开后恢复。
|
||||
|
||||
### Phase D:生成 Reasonix/Hermes adapters
|
||||
@@ -373,22 +380,19 @@ Sidebar 的“索引设置”仍是用户直接管理索引范围的产品 UI;
|
||||
- `node scripts/reasonix-acp-wrapper.mjs` selftest 覆盖 manifest 动态注册。
|
||||
- `hermes plugins list` / Hermes tool list 显示与 Rust manifest 一致。
|
||||
|
||||
## 7. 对 mnote-local-index 的落地形态
|
||||
## 7. 对 mnote-knowledge-rag 的落地形态
|
||||
|
||||
`mnote-local-index` 是第一批公共能力包试点:
|
||||
`mnote-knowledge-rag` 是第一批公共能力包试点:
|
||||
|
||||
- skill:`skills/mnote-local-index/SKILL.md`
|
||||
- skill:`skills/mnote-knowledge-rag/SKILL.md`
|
||||
- tools:
|
||||
- `mnote.evidence.search`
|
||||
- `mnote.evidence.read`
|
||||
- `mnote.evidence.open`
|
||||
- `mnote.index.status`
|
||||
- `mnote.index.refresh`
|
||||
- `mnote.index.update_settings`
|
||||
- `mnote.knowledge_rag.status`
|
||||
- `mnote.knowledge_rag.query`
|
||||
- `mnote.knowledge_rag.open_reference`
|
||||
- requiresContextRefs:`folder`
|
||||
- readOnly:`false`
|
||||
- write guard:只 `mnote.index.update_settings` 写设置;必须 `dryRun/idempotencyKey`;共享只读禁止写。
|
||||
- UI 文案:`索引 / 证据检索 · 可管理索引范围`
|
||||
- readOnly:`true`
|
||||
- write guard:本能力包不写用户 source;source 管理 UI / API 另归知识库设置面,不通过 agent capability 暴露写入。
|
||||
- UI 文案:`知识库 / LightRAG · 可回跳来源`
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
|
||||
@@ -1,540 +0,0 @@
|
||||
# 7-48 [process] Paperless-ngx Reference: Resource Ingestion / Job Ledger / Evidence Index v1
|
||||
|
||||
> 创建时间:2026-06-05
|
||||
>
|
||||
> 当前状态:`PROCESS`
|
||||
>
|
||||
> Owner:07-ai / 03-rust-web / control-plane / 01-tree-first-graph-kernel
|
||||
>
|
||||
> 参考项目:`/mnt/Data1T/mnote/reference-code/paperless-ngx`
|
||||
>
|
||||
> 参考版本:`f56f29111`
|
||||
>
|
||||
> CodeGraph:已单独建立,`712 files / 16,952 nodes / 35,407 edges`
|
||||
>
|
||||
> 上位依据:
|
||||
> - `/mnt/Data1T/mnote/ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/CURRENT_ARCHITECTURE.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-46-document-evidence-retrieval-kernel-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/03-rust-web/done/3-25-local-folder-mineru-ocr-sidecar-v1.md`
|
||||
> - `/mnt/Data1T/mnote/design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md`
|
||||
|
||||
## 1. 第一结论
|
||||
|
||||
Paperless-ngx 对 MNote 最有价值的不是 Django / Angular / Celery 技术栈,而是三套工程结构:
|
||||
|
||||
1. `PaperlessTask`:所有后台任务有统一账本,能记录来源、状态、耗时、输入、结果和用户是否已确认。
|
||||
2. `consume_file` plugin pipeline:资源导入、预检、解析、OCR、存储、索引、通知按阶段推进,失败可以定位到阶段。
|
||||
3. `Tantivy search backend + sanity checker`:索引有 schema 版本、权限字段、锁、延迟补偿和可重建性;文件与派生物有一致性检查。
|
||||
|
||||
MNote 应把这些模式收口成自己的 `Resource Work Kernel`:
|
||||
|
||||
```text
|
||||
- local file / attachment / OCR source / parsed artifact
|
||||
-> ResourceWorkJob control-plane ledger
|
||||
-> ResourceIngestionPipeline stage runner
|
||||
-> sidecar artifact + source-map
|
||||
-> evidence.sqlite / local search projection
|
||||
-> realtime job events
|
||||
-> sanity check / recovery job
|
||||
```
|
||||
|
||||
这不是要引入 paperless-ngx 的运行时。MNote 的数据真相仍然是 local Markdown、附件原文件、resource tree 和 control-plane;索引、OCR Markdown、parse Markdown、source-map 都是可删除重建的派生物。
|
||||
|
||||
## 2. Paperless-ngx 可借鉴点
|
||||
|
||||
### 2.1 统一后台任务账本
|
||||
|
||||
参考路径:
|
||||
|
||||
- `src/documents/models.py`:`PaperlessTask`
|
||||
- `src/documents/signals/handlers.py`:Celery task publish / prerun / postrun / failure handlers
|
||||
- `src-ui/src/app/components/admin/tasks/tasks.component.ts`
|
||||
|
||||
可借鉴点:
|
||||
|
||||
- 任务不是只存在于内存事件或前端状态,而是落库。
|
||||
- 每个任务有 `task_type`、`trigger_source`、`status`、`date_created`、`date_started`、`date_done`、`duration_seconds`、`wait_time_seconds`、`input_data`、`result_data`、`acknowledged`。
|
||||
- 系统任务和用户触发任务统一展示,但保留来源差异。
|
||||
|
||||
MNote 映射:
|
||||
|
||||
- 当前 `JobTicket`、OCR job、index refresh、agent run、OnlyOffice bridge 任务、recovery job 不应继续分散。
|
||||
- 新增 control-plane 表 `resource_work_jobs`,先覆盖 local OCR / evidence parse / index refresh,后续再纳入 agent run 和 recovery job。
|
||||
|
||||
### 2.2 阶段化资源导入管线
|
||||
|
||||
参考路径:
|
||||
|
||||
- `src/documents/tasks.py`:`consume_file`
|
||||
- `src/documents/plugins/base.py`:`ConsumeTaskPlugin`
|
||||
- `src/documents/consumer.py`:解析、OCR、存储、索引、progress
|
||||
- `src/documents/plugins/helpers.py`:`ProgressManager`
|
||||
|
||||
可借鉴点:
|
||||
|
||||
- 导入任务按插件链执行,每个 stage 有 `setup / run / cleanup`。
|
||||
- 阶段状态通过 websocket 通知。
|
||||
- 文件写入、数据库更新、索引更新分层处理,失败时能说明卡在预检、解析、写 sidecar 还是索引。
|
||||
|
||||
MNote 映射:
|
||||
|
||||
- 不引入插件框架泛化;先定义窄的 `ResourceIngestionPipeline`。
|
||||
- stage 只覆盖当前真实需要:`preflight`、`parse_text`、`ocr`、`write_artifact`、`write_source_map`、`refresh_evidence_index`、`broadcast_done`。
|
||||
- 当前 `local_ocr` 里的 stage 字符串和 sidecar 写入逻辑可以作为第一批迁移对象。
|
||||
|
||||
### 2.3 索引生命周期和自愈
|
||||
|
||||
参考路径:
|
||||
|
||||
- `src/documents/search/_backend.py`
|
||||
- `src/documents/search/_schema.py`
|
||||
- `src/documents/search/_query.py`
|
||||
- `src/documents/tasks.py`:`index_document`、`remove_document_from_index`
|
||||
|
||||
可借鉴点:
|
||||
|
||||
- schema version sentinel 决定是否重建。
|
||||
- 写索引用 file lock 和 retry,锁耗尽后排延迟任务,而不是让前台操作失败。
|
||||
- 查询层有权限过滤、autocomplete、highlight、CJK bigram、simple search。
|
||||
|
||||
MNote 映射:
|
||||
|
||||
- `.mnote/index/evidence.sqlite` 当前已存在,但需要更明确的 schema sentinel 和 rebuild reason。
|
||||
- `query_evidence_sqlite_results` 继续作为默认 evidence path;后续补充 autocomplete / CJK / highlight 时仍以 `EvidenceLocator` 为返回真相。
|
||||
- 索引写失败不能悄悄丢失,应写入 `resource_work_jobs` 的 retry-scheduled 状态。
|
||||
|
||||
### 2.4 权限过滤的实时事件
|
||||
|
||||
参考路径:
|
||||
|
||||
- `src/paperless/consumers.py`
|
||||
- `src/documents/plugins/helpers.py`
|
||||
|
||||
可借鉴点:
|
||||
|
||||
- websocket payload 带 owner / visible users / visible groups。
|
||||
- server-side websocket consumer 根据当前用户过滤。
|
||||
|
||||
MNote 映射:
|
||||
|
||||
- local-only 阶段可以先只带 `workspaceId`、`actorId`、`rootUri`、`targetDocumentId`、`grantId`。
|
||||
- 一旦进入 share / team workspace,OCR / index / agent job event 不能只按广播频道粗暴推送。
|
||||
- `AiAccessScope` 和 share grants 应能映射成 job event 可见性字段。
|
||||
|
||||
### 2.5 Sanity checker
|
||||
|
||||
参考路径:
|
||||
|
||||
- `src/documents/sanity_checker.py`
|
||||
- `src/documents/management/commands/document_sanity_checker.py`
|
||||
|
||||
可借鉴点:
|
||||
|
||||
- 独立检查原文件、派生文件、checksum、孤儿文件和空 OCR 内容。
|
||||
- 输出按 error / warning / info 分级,既能 CLI 显示,也能作为后台任务结果。
|
||||
|
||||
MNote 映射:
|
||||
|
||||
- 新增 `workspace_sanity_check`,先检查 local-folder resource evidence:
|
||||
- Markdown owner 是否存在。
|
||||
- 附件路径是否存在且在 allowed root 内。
|
||||
- `{pageStem}.ocr/` sidecar 是否能回到 owner Markdown。
|
||||
- `*.source-map.json` 是否能解析为 `mnote.resource_source_map.v1`。
|
||||
- `evidence.sqlite` 是否能由 sidecar 重建。
|
||||
- evidence locator 的 `openAction` 是否能落回 document / resource tab。
|
||||
|
||||
## 3. 目标架构
|
||||
|
||||
### 3.1 ResourceWorkJob
|
||||
|
||||
新增 control-plane job ledger,不替代 domain event,也不替代 agent run receipt。
|
||||
|
||||
```rust
|
||||
pub struct ResourceWorkJob {
|
||||
pub job_id: String,
|
||||
pub job_type: ResourceWorkJobType,
|
||||
pub trigger_source: ResourceWorkTriggerSource,
|
||||
pub status: ResourceWorkJobStatus,
|
||||
pub stage: Option<String>,
|
||||
pub progress_current: Option<u32>,
|
||||
pub progress_total: Option<u32>,
|
||||
pub stage_label: Option<String>,
|
||||
pub workspace_id: String,
|
||||
pub root_uri: String,
|
||||
pub actor_id: Option<String>,
|
||||
pub target_document_id: Option<String>,
|
||||
pub source_root_relative_path: Option<String>,
|
||||
pub artifact_root_relative_path: Option<String>,
|
||||
pub source_map_root_relative_path: Option<String>,
|
||||
pub input_json: serde_json::Value,
|
||||
pub result_json: Option<serde_json::Value>,
|
||||
pub error_code: Option<String>,
|
||||
pub error_message: Option<String>,
|
||||
pub created_at_ms: u128,
|
||||
pub started_at_ms: Option<u128>,
|
||||
pub finished_at_ms: Option<u128>,
|
||||
pub acknowledged_at_ms: Option<u128>,
|
||||
}
|
||||
```
|
||||
|
||||
首批枚举:
|
||||
|
||||
```text
|
||||
job_type:
|
||||
- local_ocr
|
||||
- resource_parse
|
||||
- evidence_index_refresh
|
||||
- evidence_index_rebuild
|
||||
- workspace_sanity_check
|
||||
|
||||
trigger_source:
|
||||
- web_ui
|
||||
- api
|
||||
- watcher
|
||||
- agent_tool
|
||||
- system
|
||||
- recovery
|
||||
|
||||
status:
|
||||
- pending
|
||||
- running
|
||||
- succeeded
|
||||
- failed
|
||||
- retry_scheduled
|
||||
- canceled
|
||||
```
|
||||
|
||||
约束:
|
||||
|
||||
- `job_id` 由 MNote 生成,不复用外部 provider task id。
|
||||
- MinerU task id、LiteParse request id、Reasonix run id 只进入 `input_json / result_json`。
|
||||
- `result_json` 必须能存 evidence id、artifact path、source-map path、locator count、index row count。
|
||||
- `stage / progress_current / progress_total / stage_label` 是前台任务中心的稳定合同;不能只塞在 provider 私有 payload 里。
|
||||
- job ledger 是 control-plane 事实,不是用户正文真相。
|
||||
|
||||
### 3.2 ResourceIngestionPipeline
|
||||
|
||||
先实现窄接口,不做任意插件市场:
|
||||
|
||||
```text
|
||||
preflight
|
||||
-> detect_resource_kind
|
||||
-> choose_provider
|
||||
-> parse_or_ocr
|
||||
-> write_artifact
|
||||
-> write_source_map
|
||||
-> refresh_evidence_index
|
||||
-> broadcast_job_event
|
||||
```
|
||||
|
||||
Provider 策略:
|
||||
|
||||
- 文本型 PDF / Office parse 优先走 `LiteParseProvider` 或当前轻量 parser。
|
||||
- 扫描件 / 图片走 `MinerUProvider`。
|
||||
- mock provider 仅用于 smoke,不得在真实功能报告中冒充成功链路。
|
||||
|
||||
阶段状态:
|
||||
|
||||
```text
|
||||
queued
|
||||
preflight
|
||||
parsing
|
||||
ocr_uploading
|
||||
ocr_processing
|
||||
writing_artifact
|
||||
writing_source_map
|
||||
indexing
|
||||
done
|
||||
failed
|
||||
retry_scheduled
|
||||
stale
|
||||
```
|
||||
|
||||
当前 `local_ocr.job.updated` 可兼容保留,但 payload 应逐步包含 `jobId / jobType / stage / workspaceId / rootUri / sourceRootRelativePath / targetDocumentId / artifactPath / sourceMapPath`。
|
||||
|
||||
### 3.3 Evidence Index Lifecycle
|
||||
|
||||
当前 `.mnote/index/evidence.sqlite` 继续作为默认 evidence index。升级点:
|
||||
|
||||
- 增加 `schema_version` 和 `build_settings` sentinel。
|
||||
- 每次 write / refresh 记录 `job_id`。
|
||||
- 索引 row 必须能通过 `source_map_root_relative_path` 回到 canonical artifact。
|
||||
- lock 失败进入 `retry_scheduled` job,不直接吞掉。
|
||||
- watcher 增量刷新失败时,不做前端轮询补偿;排 recovery job 并广播一次明确事件。
|
||||
|
||||
建议最小表:
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS evidence_index_meta (
|
||||
key TEXT PRIMARY KEY,
|
||||
value_json TEXT NOT NULL,
|
||||
updated_at_ms INTEGER NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS evidence_index_jobs (
|
||||
job_id TEXT PRIMARY KEY,
|
||||
last_error_code TEXT,
|
||||
last_error_message TEXT,
|
||||
updated_at_ms INTEGER NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
如果 control-plane 已存 job,这里只保存 index-local rebuild metadata,不重复完整 job 账本。
|
||||
|
||||
### 3.4 Workspace Sanity Check
|
||||
|
||||
新增只读检查,不自动删除、不自动修复。
|
||||
|
||||
检查项:
|
||||
|
||||
| 级别 | 检查 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| error | owner Markdown 不存在 | evidence locator 无法打开 |
|
||||
| error | resource file 不存在 | 附件或 OCR source 丢失 |
|
||||
| error | source-map JSON 无法解析 | 定位真相损坏 |
|
||||
| error | evidence.sqlite schema 不匹配 | 需要 rebuild |
|
||||
| warning | sidecar 孤儿文件 | 有 OCR/parse artifact 但找不到 owner link |
|
||||
| warning | source hash / mtime stale | 可重建,但不阻断 |
|
||||
| info | parse/OCR 内容为空 | 允许,但要可见 |
|
||||
|
||||
输出既可作为 API:
|
||||
|
||||
```text
|
||||
POST /api/workspaces/sanity/check
|
||||
GET /api/work/jobs/:jobId
|
||||
```
|
||||
|
||||
也可作为 smoke / CLI helper 的 JSON 结果。
|
||||
|
||||
### 3.5 Unified Task Foreground UI
|
||||
|
||||
统一后台任务必须有统一前台可见入口。否则用户仍然只能在 OCR 设置、索引设置、toast、局部状态灯之间猜系统是否还在运行。
|
||||
|
||||
当前已有的 `mnote-local-ocr-task-dock` 是迁移起点,不是长期终点。它应升级为全局 `Work Task Center`:
|
||||
|
||||
```text
|
||||
Topbar task button
|
||||
-> badge: active / failed / needs attention count
|
||||
-> drawer: current jobs + recent completed + failed
|
||||
-> row: icon + title + stage label + progress bar + actions
|
||||
-> detail: input/result/error + related document/resource + trace/run receipt
|
||||
```
|
||||
|
||||
入口位置:
|
||||
|
||||
- 顶栏保留一个任务中心图标,建议使用 `progress_activity` 或 `pending_actions`。
|
||||
- OCR 和索引设置按钮仍可保留,但它们不再各自承载任务列表。
|
||||
- 任务中心抽屉优先靠右打开,保持轻量;不做全屏管理台作为第一阶段。
|
||||
|
||||
任务行最小字段:
|
||||
|
||||
| 字段 | 来源 | UI 用途 |
|
||||
| --- | --- | --- |
|
||||
| `jobId` | job ledger | 稳定 row key 和详情查询 |
|
||||
| `jobType` | job ledger | 图标、分类、筛选 |
|
||||
| `status` | job ledger | 颜色、分组、是否需要确认 |
|
||||
| `stageLabel` | job event | 当前阶段文案 |
|
||||
| `progressCurrent / progressTotal` | job event | 确定性进度条 |
|
||||
| `createdAtMs / startedAtMs / finishedAtMs` | job ledger | 排队/耗时/最近完成 |
|
||||
| `targetDocumentId` | job ledger | 打开 owner 文档 |
|
||||
| `sourceRootRelativePath` | job ledger | 打开附件或定位资源 |
|
||||
| `artifactRootRelativePath / sourceMapRootRelativePath` | result | 打开 OCR/parse/source-map |
|
||||
| `errorCode / errorMessage` | job ledger | 失败摘要和复现证据 |
|
||||
|
||||
进度规则:
|
||||
|
||||
- 有 `progressCurrent / progressTotal` 时显示确定性进度条。
|
||||
- 没有总量但 status 为 running 时显示细条 indeterminate,不伪造百分比。
|
||||
- `queued / retry_scheduled` 显示排队态,不显示假进度。
|
||||
- `failed` 和 `retry_scheduled` 必须进入“需要处理”计数。
|
||||
- `succeeded` 默认保留在最近完成列表,可由用户清除/ack。
|
||||
|
||||
任务动作:
|
||||
|
||||
| 状态 | 动作 |
|
||||
| --- | --- |
|
||||
| running | 打开目标、查看详情 |
|
||||
| succeeded | 打开结果、打开目标、清除 |
|
||||
| failed | 查看错误、重试、打开目标、复制错误摘要 |
|
||||
| retry_scheduled | 查看重试原因、立即重试、取消重试 |
|
||||
| stale | 重新生成、打开旧结果 |
|
||||
|
||||
事件与数据流:
|
||||
|
||||
```text
|
||||
GET /api/work/jobs?scope=currentWorkspace&active=true
|
||||
GET /api/work/jobs?scope=currentWorkspace&recent=true
|
||||
GET /api/work/jobs/:jobId
|
||||
POST /api/work/jobs/:jobId/ack
|
||||
POST /api/work/jobs/:jobId/retry
|
||||
SSE/WS event: resource_work.job.updated
|
||||
```
|
||||
|
||||
前端运行时:
|
||||
|
||||
- 新增 `browser/work-task-center-runtime.js`,由 `layout.rs` 统一注入。
|
||||
- 现有 `document-resource-tab-runtime.js` 中 OCR task dock 的 state/render 逻辑迁入该 runtime。
|
||||
- `mnote:local-ocr-job-updated` 作为兼容事件继续转发为 `resource_work.job.updated`,直到后端统一 payload 完成。
|
||||
- 前端只在启动时拉一次 active/recent snapshot;后续靠 WS/SSE 事件更新,不新增周期轮询。
|
||||
|
||||
视觉约束:
|
||||
|
||||
- 顶栏只显示一个任务中心 badge,避免 OCR/索引/AI 各自占顶栏状态位。
|
||||
- 抽屉行要密集、可扫描,不能做大卡片堆叠。
|
||||
- 每行必须有可见进度或阶段文本,长路径截断但 title 保留完整路径 tooltip。
|
||||
- 移动端抽屉宽度占满可用宽度,任务行按钮折到第二行,避免文字溢出。
|
||||
|
||||
## 4. 不做什么
|
||||
|
||||
- 不把 paperless-ngx 的 Django model / Angular UI / Celery worker 引入 MNote。
|
||||
- 不把 OCR Markdown、parse Markdown 或 evidence.sqlite 变成正文真相。
|
||||
- 不新增前端轮询来弥补 job 状态;状态更新走现有 WS / SSE / watcher event。
|
||||
- 不把任务 UI 继续拆成 OCR 一套、索引一套、AI 一套;这些只能是任务中心里的分类或过滤。
|
||||
- 不让 workflow UI 先行。先做 event-triggered job 和少量内置动作,再考虑可视化配置。
|
||||
- 不在本轮实现向量 RAG。Paperless 的 FAISS append-only 方案只作为反例和参考,不作为 MNote 默认路径。
|
||||
|
||||
## 5. 实施分期
|
||||
|
||||
### Phase A:设计冻结和接口补齐
|
||||
|
||||
Owner:07-ai / 03-rust-web
|
||||
|
||||
任务:
|
||||
|
||||
- [ ] 在 `core-protocol` 增加 `ResourceWorkJob` / `ResourceWorkJobStatus` / `ResourceWorkTriggerSource` 合同。
|
||||
- [ ] 在 job 合同中加入 `stage / stageLabel / progressCurrent / progressTotal`,作为前台任务中心稳定字段。
|
||||
- [ ] 明确 `local_ocr.job.updated` 与新 `resource_work.job.updated` 的兼容关系。
|
||||
- [ ] 在 `7-46` 里引用本设计作为 job / index lifecycle 的执行补充。
|
||||
|
||||
验收:
|
||||
|
||||
- [ ] Rust unit 覆盖 job status 序列化。
|
||||
- [ ] Rust unit 覆盖 progress 字段缺省、确定性进度和 indeterminate 语义。
|
||||
- [ ] 不改变现有 OCR smoke 行为。
|
||||
|
||||
### Phase B:OCR job ledger 收口
|
||||
|
||||
Owner:03-rust-web / control-plane
|
||||
|
||||
任务:
|
||||
|
||||
- [ ] 给 `local_ocr::create_job` 创建 control-plane job 记录。
|
||||
- [ ] 每次 stage advance 同步 job ledger。
|
||||
- [ ] `done / failed` 写入 `finished_at_ms / result_json / error_code`。
|
||||
- [ ] 增加 `GET /api/work/jobs/:jobId`。
|
||||
- [ ] 增加 `GET /api/work/jobs?active=true&recent=true`,供任务中心首屏 snapshot 使用。
|
||||
|
||||
验收:
|
||||
|
||||
- [ ] 现有 OCR sidecar smoke 仍通过。
|
||||
- [ ] 新增 smoke 验证 OCR job 可查询、失败错误脱敏、done 后 result 包含 sidecar 和 source-map。
|
||||
- [ ] 新增 smoke 验证 active snapshot 包含运行中 OCR job,完成后转入 recent。
|
||||
|
||||
### Phase B2:统一任务中心 UI
|
||||
|
||||
Owner:03-rust-web / 05-editor-mainline
|
||||
|
||||
任务:
|
||||
|
||||
- [ ] 新增 `browser/work-task-center-runtime.js`。
|
||||
- [ ] 将 `mnote-local-ocr-task-dock` 的状态聚合和 drawer 渲染迁移到 work task center。
|
||||
- [ ] 顶栏新增统一任务中心按钮和 badge,OCR 按钮回归 OCR 设置入口或合并进设置面板。
|
||||
- [ ] 支持 active / needs attention / recent 三个分组。
|
||||
- [ ] 任务行支持确定性 progress bar、indeterminate running bar、失败重试、打开目标、打开结果、ack。
|
||||
- [ ] 兼容接收 `mnote:local-ocr-job-updated`,并在后端统一事件上线后接 `resource_work.job.updated`。
|
||||
|
||||
验收:
|
||||
|
||||
- [ ] 浏览器 smoke:触发 OCR 后顶栏 badge 从 0 变 1,抽屉显示阶段和进度。
|
||||
- [ ] 浏览器 smoke:OCR 完成后任务移入 recent,能打开 OCR sidecar。
|
||||
- [ ] 浏览器 smoke:mock 失败任务进入 needs attention,能查看错误和重试。
|
||||
- [ ] 截图验证桌面和移动端抽屉不溢出、不遮挡主编辑区关键内容。
|
||||
|
||||
### Phase C:Evidence index lifecycle
|
||||
|
||||
Owner:07-ai / 03-rust-web
|
||||
|
||||
任务:
|
||||
|
||||
- [ ] 给 evidence.sqlite 增加 schema/settings sentinel。
|
||||
- [ ] index refresh 写入 job id 或 index-local job metadata。
|
||||
- [ ] lock / write / parse source-map 失败时进入 retry-scheduled 或 failed job。
|
||||
- [ ] `mnote.index.status` 返回 schema、last build、last job、pending retry。
|
||||
- [ ] index refresh / rebuild 通过任务中心显示阶段和结果摘要。
|
||||
|
||||
验收:
|
||||
|
||||
- [ ] `task528-document-evidence-liteparse-agent-smoke.js` 继续通过。
|
||||
- [ ] 新增 index stale / rebuild smoke,验证删除 evidence.sqlite 后可通过 job 重建。
|
||||
- [ ] 浏览器 smoke 验证 index rebuild 任务出现在任务中心。
|
||||
|
||||
### Phase D:Workspace sanity check
|
||||
|
||||
Owner:03-rust-web / 04-tree-domain
|
||||
|
||||
任务:
|
||||
|
||||
- [ ] 实现只读 `workspace_sanity_check` job。
|
||||
- [ ] 检查 owner Markdown、resource file、sidecar、source-map、evidence.sqlite。
|
||||
- [ ] 结果按 error / warning / info 分级。
|
||||
- [ ] 从任务中心和 index/OCR 设置 surface 都能启动检查;结果统一进入任务中心详情。
|
||||
|
||||
验收:
|
||||
|
||||
- [ ] smoke 构造缺失 source-map、孤儿 sidecar、损坏 sqlite,能得到稳定 JSON。
|
||||
- [ ] 不删除、不移动任何用户文件。
|
||||
- [ ] 浏览器 smoke 验证 sanity check 运行时有任务行,完成后详情展示 error / warning / info 摘要。
|
||||
|
||||
### Phase E:内置 workflow actions
|
||||
|
||||
Owner:01-tree-first-graph-kernel / 07-ai
|
||||
|
||||
任务:
|
||||
|
||||
- [ ] 定义内置事件:`resource.created`、`resource.changed`、`ocr.done`、`evidence.index.failed`。
|
||||
- [ ] 定义内置动作:`parse_resource`、`run_ocr`、`refresh_evidence_index`、`schedule_sanity_check`。
|
||||
- [ ] 先用静态配置或 settings 控制,不做复杂 UI。
|
||||
|
||||
验收:
|
||||
|
||||
- [ ] 新增附件后能按 settings 自动排 parse/OCR/index job。
|
||||
- [ ] 失败不会循环重试;必须有 retry budget 和可见错误。
|
||||
|
||||
## 6. 设计验收基线
|
||||
|
||||
本设计完成后,MNote 应具备以下能力:
|
||||
|
||||
- 用户能看到后台 OCR / parse / index / sanity job 的真实状态,而不是只看到散落的 toast 或局部状态灯。
|
||||
- 顶栏任务中心能显示 active / failed / recent 任务,且所有任务进度来自 job ledger 或 job event,不伪造百分比。
|
||||
- agent 调用 evidence 工具时,MNote 能把本次证据索引状态、evidence ids、source-map path 写入 run receipt。
|
||||
- 删除或损坏 evidence.sqlite 不会让系统静默退化;会显示 rebuild required 或排 rebuild job。
|
||||
- OCR sidecar、parse artifact、source-map 和 evidence locator 可以被 sanity check 证明互相可追溯。
|
||||
- 多人 / share 场景下,job event 有明确可见性字段,不把本地单用户广播模型固化成长期事实。
|
||||
|
||||
## 7. 参考代码索引
|
||||
|
||||
Paperless-ngx:
|
||||
|
||||
- `src/documents/models.py`:`PaperlessTask`、workflow model、document version fields。
|
||||
- `src/documents/tasks.py`:`consume_file`、index deferred tasks、bulk update。
|
||||
- `src/documents/consumer.py`:resource consume main path。
|
||||
- `src/documents/plugins/base.py`:plugin lifecycle contract。
|
||||
- `src/documents/plugins/helpers.py`:progress websocket payload。
|
||||
- `src/documents/search/_backend.py`:Tantivy backend、lock retry、autocomplete、highlight。
|
||||
- `src/documents/search/_schema.py`:schema version sentinel。
|
||||
- `src/documents/search/_query.py`:permission filter、date rewrite、CJK/simple query。
|
||||
- `src/documents/sanity_checker.py`:archive consistency checker。
|
||||
- `src/paperless/consumers.py`:permission-aware websocket consumer。
|
||||
|
||||
MNote 当前落点:
|
||||
|
||||
- `rust/crates/core-protocol/src/governance.rs`
|
||||
- `rust/crates/core-protocol/src/evidence.rs`
|
||||
- `rust/crates/core-protocol/src/tool.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_ocr.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_search_index.rs`
|
||||
- `rust/crates/mnote-web/src/routes/evidence.rs`
|
||||
- `rust/crates/mnote-web/src/routes/local_folder_events.rs`
|
||||
- `rust/crates/mnote-web/src/routes/ws.rs`
|
||||
- `scripts/task528-document-evidence-liteparse-agent-smoke.js`
|
||||
Reference in New Issue
Block a user