Files
mnote/design/07-ai/reference/7-46-document-evidence-retrieval-kernel-v1.md
T
lix-2026 9551d4c1dc 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.
2026-06-07 10:35:21 +08:00

940 lines
39 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 7-46 [reference] Document Evidence Retrieval Kernel v1
> 创建时间:2026-06-03
>
> 当前状态:`REFERENCE`
>
> 2026-06-07 状态治理:7-50 LightRAG 已覆盖默认资料库问答主线,`mnote.evidence.*` / LiteParse / evidence.sqlite 不再作为 active agent capability 或资料库 fallback。本文件只保留为历史迁移、locator 合同和旧实现审计参考,不再从 checklist 中派发新任务。
> 文内未勾选项均为历史状态,不作为当前 `process` 任务。
>
> Owner07-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 的“文本”升级为“可定位文本”。
- 对文本型 PDFOCR 是浪费;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
引用 1Page.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 只返回 artifactindexer 统一消费 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 textsection 构建仍待接入 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/blockOCR/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 idPDF / 图片 locator 会通过 `openLocalResourceInActiveTab` 打开 resource tabMarkdown locator 会把 `blockId` / `page` / `bbox` / `sourceMapPath` 带入文档 URL。
- resource tab 已消费 `EvidenceLocator`:面板记录 `data-mnote-evidence-*`PDF iframe 会把 `page` / `bbox` / `blockId` 传给 `/pdf-preview`,图片资源会绘制 bbox overlayMarkdown/text/code resource tab 会按 `blockId` 滚动并高亮。
- `/pdf-preview` 已支持 `page` / `bbox` / `blockId` query,渲染目标页时会标记页 canvas、绘制 bbox highlight 并滚到目标页。
- Markdown evidence index 已按 heading/非空行生成 evidence blockslocator 带 `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 runtimegraph 主链只读 `.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 主链加轮询刷新。
验收:
- 每个新增项目都能回答“它是否减少重复真相”。
- 不能回答时,默认不接入主链。