feat(rag): align LightRAG native citations and MCP bridge
This commit is contained in:
+164
-90
@@ -1,8 +1,8 @@
|
||||
# 7-55 LightRAG DOCX 引用定位与 rerank 对齐设计 v1
|
||||
# 7-55 LightRAG DOCX 引用清洗与定位合同对齐设计 v2
|
||||
|
||||
> 创建时间:2026-06-08
|
||||
>
|
||||
> 当前状态:`process`
|
||||
> 当前状态:`done`
|
||||
>
|
||||
> Owner:07-ai / knowledge-rag / 03-rust-web / office-preview
|
||||
>
|
||||
@@ -15,6 +15,11 @@
|
||||
> - LightRAG:`/mnt/Data1T/Mnote_data/lightrag/LightRAG`
|
||||
> - NexusRAG:`/tmp/mnote-rag-eval-nexusrag`
|
||||
> - MNote:`rust/crates/mnote-web/src/routes/knowledge_rag.rs`
|
||||
>
|
||||
> 决策更新:
|
||||
> - 当前优先级从 rerank 下调为“统一引用清洗 + DOCX 段落定位合同”。
|
||||
> - rerank 只保留 provider capability/status 观察,不进入当前实现主路径。
|
||||
> - 后续实现必须先复用 LightRAG sidecar / parsed block 与 NexusRAG citation/source-card 合同思想,不在 UI 和 preview 各自手搓第二套清洗/定位逻辑。
|
||||
|
||||
## 1. 背景
|
||||
|
||||
@@ -27,9 +32,10 @@
|
||||
- MNote 当前 `map_reference_plan(...)` 会用 chunk quote / query 去 sidecar `.blocks.jsonl` 里反查 block,再生成 `EvidenceLocator`。
|
||||
- PDF / image 经 MinerU 或 Docling 解析后通常有 `positions.type=bbox`,可以转 page/bbox。
|
||||
- DOCX native parser 生成的是 `positions.type=paraid`,真实样本里大量 `range=[null,null]`,不能可靠转 page/bbox。
|
||||
- 当前 LightRAG 已有 reranker,不应在 MNote 重复实现一套通用 reranker。
|
||||
- 当前 LightRAG 已有 reranker 入口,但本机运行态未启用,且 endpoint 未真实验证;rerank 不应挡住引用清洗和定位合同收口。
|
||||
|
||||
本设计目标是把“LightRAG 已有能力”和“MNote 必须补的引用定位层”明确切开。
|
||||
今天围绕 `吡咯烷` / `吗啉` 的连续修复说明:问题根因不是单一正则缺失,而是 raw chunk、展示文本、定位文本、用户 query、sidecar block 没有统一合同,导致搜索结果清洗、去重和 office-preview 段落定位各自补丁化。
|
||||
|
||||
## 2. CodeGraph 核对结论
|
||||
|
||||
@@ -62,6 +68,7 @@
|
||||
```
|
||||
|
||||
结论:MNote 不新增通用 reranker。MNote 只负责配置、健康状态展示、请求参数透传和结果引用消费。
|
||||
当前阶段不把 rerank 作为 Phase A;只记录 provider 状态,等真实 rerank endpoint 跑通后再单独开设计/实现。
|
||||
|
||||
### 2.2 LightRAG 已有 DOCX native parser 与 sidecar
|
||||
|
||||
@@ -108,17 +115,30 @@ NexusRAG 不是只针对 PDF。
|
||||
- MNote 已选 LightRAG 作为默认 provider,不能为了 citation UI 整套替换 RAG provider。
|
||||
- MNote 的 open-reference 必须回到 local-folder resource tab / Page Aggregate / Resource Tree,不应引入第二套文档 viewer 真相。
|
||||
|
||||
### 2.4 本轮实测暴露的合同缺口
|
||||
|
||||
已修复但仍需收口为统一合同的问题:
|
||||
|
||||
- LightRAG chunk 中混有 `<equation format="latex">`、`<drawing>`、残缺 XML/HTML 标签;搜索 UI 临时清洗后可读,但 Page AI / source card / citation markdown 仍可能各自遇到 raw 文本。
|
||||
- DOCX chunk 常把目录行、正文段和参考文献拼在一个上下文里;只用完整 chunk 做定位,容易落到后半段长文本或参考文献。
|
||||
- 只用用户 query 短词定位会错跳;只用长 chunk 定位会被无关长段覆盖。需要 query-centered、block-aware、display/locator 分离的 anchor contract。
|
||||
- 同一 resource tab 已支持 postMessage 更新 locator,不应因点击同一 DOCX 的另一个结果重新加载。
|
||||
|
||||
因此下一步不继续扩大前端正则,而是把清洗和定位输入前移到 `knowledge_rag` mapper:所有消费方拿同一份 `displayQuote` / `locatorEvidenceText` / `searchQuery` / `locatorPrecision`。
|
||||
|
||||
## 3. 设计原则
|
||||
|
||||
1. LightRAG 负责检索质量:parse、chunk、vector、graph、rerank、query。
|
||||
2. MNote 负责来源真相:source registry、权限、root-relative path、resource tab 打开、citation URL、locator 降级语义。
|
||||
3. 引用定位按精度分层,不伪造:
|
||||
3. 参考优先:优先复用 LightRAG sidecar block、heading、positions;借鉴 NexusRAG 的 citation/source-card contract;只有 viewer anchoring adapter 允许保留 MNote 自有实现。
|
||||
4. 清洗只做一次:后端 mapper 产出 raw/display/locator 三类文本,前端只展示或消费,不再各自猜 LightRAG 原始格式。
|
||||
5. 引用定位按精度分层,不伪造:
|
||||
- `bbox`:有 page + bbox,可精确高亮。
|
||||
- `paragraph`:有 block / heading / context,可滚动到段落并高亮上下文。
|
||||
- `file`:只能打开文件。
|
||||
4. DOCX 默认先做 paragraph locator,不把 DOCX 强制转换成 PDF viewer 管线。
|
||||
5. rerank 只走 LightRAG provider。MNote 可以做 source scope post-filter 和 citation ranking,但不能把它包装成 provider rerank。
|
||||
6. Agent final answer 只能引用 MNote 过滤和映射后的 `citations/references`,不能引用 raw LightRAG chunks。
|
||||
6. DOCX 默认先做 paragraph locator,不把 DOCX 强制转换成 PDF viewer 管线。
|
||||
7. rerank 暂缓。MNote 可以展示 provider rerank 状态,但当前不新增 request path、UI 开关或本地 rerank。
|
||||
8. Agent final answer 只能引用 MNote 过滤和映射后的 `citations/references`,不能引用 raw LightRAG chunks。
|
||||
|
||||
## 4. 目标架构
|
||||
|
||||
@@ -129,6 +149,7 @@ LightRAG /query/data
|
||||
-> Evidence Mapping Layer
|
||||
- match provider file_path/doc_id -> source entry
|
||||
- match chunk_id/content/query -> sidecar block
|
||||
- build rawQuote/displayQuote/locatorEvidenceText
|
||||
- classify locatorPrecision
|
||||
- build citationUrl/citationMarkdown
|
||||
-> knowledge-rag result
|
||||
@@ -163,8 +184,11 @@ pub struct KnowledgeRagCitation {
|
||||
pub light_rag_chunk_id: Option<String>,
|
||||
pub block_id: Option<String>,
|
||||
pub heading_path: Vec<String>,
|
||||
pub quote: Option<String>,
|
||||
pub raw_quote: Option<String>,
|
||||
pub display_quote: Option<String>,
|
||||
pub locator_evidence_text: Option<String>,
|
||||
pub quote_source: String,
|
||||
pub search_query: String,
|
||||
pub locator_precision: LocatorPrecision,
|
||||
pub locator_degraded: bool,
|
||||
pub citation_url: String,
|
||||
@@ -177,9 +201,10 @@ pub struct KnowledgeRagCitation {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CitationDiagnostics {
|
||||
pub quote_source: String,
|
||||
pub provider_rerank_requested: bool,
|
||||
pub provider_rerank_available: bool,
|
||||
pub raw_reference_mapped: bool,
|
||||
pub sidecar_block_mapped: bool,
|
||||
pub display_cleaned: bool,
|
||||
pub locator_text_source: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -191,7 +216,46 @@ pub enum LocatorPrecision {
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 citation ID
|
||||
### 5.2 引用文本清洗合同
|
||||
|
||||
后端 mapper 统一产出三份文本:
|
||||
|
||||
```rust
|
||||
pub struct CitationTextBundle {
|
||||
pub raw_quote: String,
|
||||
pub display_quote: String,
|
||||
pub locator_evidence_text: String,
|
||||
pub search_query: String,
|
||||
pub normalized_fingerprint: String,
|
||||
}
|
||||
```
|
||||
|
||||
字段语义:
|
||||
|
||||
- `rawQuote`:LightRAG / sidecar 原始内容,仅诊断可见,不直接给 UI 和 LLM final answer 引用。
|
||||
- `displayQuote`:搜索结果、source card、citation preview 使用。去除 XML/HTML 外壳、`drawing`、残缺 tag;公式先转为可读 plain text,不在本阶段做 LaTeX 渲染。
|
||||
- `locatorEvidenceText`:定位使用。优先 sidecar block 的 query-centered window,保留足够上下文和 heading,但去除会干扰 anchor 的 XML/LaTeX 包装。
|
||||
- `normalizedFingerprint`:用于同段落去重和 smoke 对比,只作为当前 query 内去重辅助,不当长期主键。
|
||||
|
||||
清洗规则必须集中在 Rust 侧一个 helper 中,例如:
|
||||
|
||||
```text
|
||||
raw LightRAG chunk / sidecar block
|
||||
-> stripXmlShellPreserveText(equation)
|
||||
-> dropDrawingTags()
|
||||
-> stripBrokenTags()
|
||||
-> collapseWhitespace()
|
||||
-> normalizeLatexCommandsForDisplay()
|
||||
-> build query-centered locator window
|
||||
```
|
||||
|
||||
禁止:
|
||||
|
||||
- 搜索 UI、Page AI、office-preview 分别维护不同的 equation/drawing 正则。
|
||||
- 为了显示好看删除用户 query 或化学关键词。
|
||||
- 把 `displayQuote` 反过来当唯一定位依据;定位必须优先用 `locatorEvidenceText + searchQuery + blockId/sourceMapPath`。
|
||||
|
||||
### 5.3 citation ID
|
||||
|
||||
采用 NexusRAG 式短 ID,但 ID 只作为 UI/display contract,不作为事实主键。
|
||||
|
||||
@@ -209,7 +273,7 @@ pub enum LocatorPrecision {
|
||||
- 同一 query 内冲突则加 salt 重算。
|
||||
- citation card 使用 `citation_id` 关联 answer badge。
|
||||
|
||||
### 5.3 locator precision 计算
|
||||
### 5.4 locator precision 计算
|
||||
|
||||
```rust
|
||||
fn locator_precision_from_block(block: &serde_json::Value) -> LocatorPrecision {
|
||||
@@ -267,7 +331,8 @@ fn locator_precision_from_block(block: &serde_json::Value) -> LocatorPrecision {
|
||||
"provider": "lightrag",
|
||||
"chunkId": "doc-xxx-chunk-001",
|
||||
"searchQuery": "三乙基硅",
|
||||
"evidenceText": "query-centered chunk/block context"
|
||||
"evidenceText": "locatorEvidenceText",
|
||||
"displayQuote": "cleaned display quote"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,7 +343,8 @@ fn locator_precision_from_block(block: &serde_json::Value) -> LocatorPrecision {
|
||||
|
||||
- 有 `blockid` 必须传 `blockId`。
|
||||
- 有 sidecar path 必须传 `sourceMapPath`。
|
||||
- `evidenceText` 优先取 sidecar block content 的 query-centered window,其次 chunk content。
|
||||
- `evidenceText` 使用统一 `locatorEvidenceText`;优先取 sidecar block content 的 query-centered window,其次 chunk content。
|
||||
- 搜索结果展示使用 `displayQuote`,不再由 UI 从 raw `quote/snippet` 临时清洗。
|
||||
- `page/bbox` 缺失时不得写假值。
|
||||
- `citationMarkdown` 文案可带“定位降级”,但链接仍应可点击打开 resource tab。
|
||||
|
||||
@@ -288,12 +354,12 @@ office-preview / docx-preview 收到 paragraph locator 后:
|
||||
|
||||
```text
|
||||
locator.evidenceText
|
||||
-> normalize text
|
||||
-> consume backend locatorEvidenceText
|
||||
-> build anchors:
|
||||
1. query-centered phrase
|
||||
2. long rare terms
|
||||
3. heading + quote window
|
||||
4. fallback query
|
||||
1. sidecar block/query-centered leading anchor
|
||||
2. heading + quote window
|
||||
3. long rare terms
|
||||
4. fallback query only when scoped by block/window
|
||||
-> scan rendered paragraphs / table cells
|
||||
-> score by token overlap + rare term bonus + heading match
|
||||
-> scroll into view + transient highlight
|
||||
@@ -305,17 +371,42 @@ locator.evidenceText
|
||||
- DOCX 无 page/bbox 时展示成精确页码。
|
||||
- 同一 resource tab 重新加载整个 document 来响应每次 citation click;应优先 postMessage 更新 locator。
|
||||
|
||||
## 7. Rerank 对齐
|
||||
## 7. Rerank 暂缓策略
|
||||
|
||||
### 7.1 配置策略
|
||||
### 7.1 当前决策
|
||||
|
||||
MNote 不实现 rerank model 调用,只管理 LightRAG 配置状态。
|
||||
MNote 当前不推进 rerank 实现,只做状态观察和诊断记录。
|
||||
|
||||
推荐配置优先级:
|
||||
原因:
|
||||
|
||||
1. 用户显式关闭:`enable_rerank=false`。
|
||||
2. LightRAG health 显示 `configuration.enable_rerank=true` 且 `rerank_queue_status.available=true`:MNote query 默认传 `enable_rerank=true`。
|
||||
3. LightRAG health 显示 rerank 不可用:MNote 仍可传 `enable_rerank=true`,但 diagnostics 标注 provider 未配置;或在 UI 选择“禁用 rerank”后传 false。
|
||||
- 本机 LightRAG `configuration.enable_rerank=false`、`rerank_queue_status.available=false`。
|
||||
- 之前测试过常见 NVIDIA rerank endpoint 返回 404,不能把模型名当成可用能力。
|
||||
- 当前用户痛点集中在“命中能否完整、结果是否干净、点击是否定位到同一段落”,rerank 主要解决排序,不解决 DOCX 段落坐标缺失。
|
||||
- 提前接 rerank 会扩大变量,影响定位合同验收。
|
||||
|
||||
当前只要求 dashboard/status 能展示:
|
||||
|
||||
```text
|
||||
LightRAG: healthy
|
||||
Embedding: nvidia/baai/bge-m3
|
||||
Rerank: disabled
|
||||
Rerank binding: null
|
||||
Rerank queue: unavailable
|
||||
```
|
||||
|
||||
### 7.2 后置启用条件
|
||||
|
||||
只有满足以下条件,才新增 rerank 实现任务:
|
||||
|
||||
1. 有真实 endpoint,并通过 provider smoke 证明 `/health.configuration.enable_rerank=true`、`rerank_queue_status.available=true`。
|
||||
2. LightRAG query result 能返回可消费的 `rerank_score`,或确认只能显示 provider order。
|
||||
3. 7-55 的 citation/locator contract 已稳定,`三甲基硅`、`三乙基硅`、`吡咯烷`、`吗啉` smoke 通过。
|
||||
|
||||
后置实现仍遵循:
|
||||
|
||||
- 不在 MNote 调 rerank model。
|
||||
- 不复制 LightRAG vector / graph / rerank。
|
||||
- 只透传 provider 参数、展示状态、消费 provider score。
|
||||
|
||||
`.env` 示例:
|
||||
|
||||
@@ -336,42 +427,6 @@ RERANK_TIMEOUT=30
|
||||
- 之前本机测过常见 NVIDIA rerank endpoint 返回 404;不能仅凭模型名就标“rerank 已启用”。
|
||||
- rerank 是 query-time 能力,启用后不要求重建 LightRAG 索引。
|
||||
|
||||
### 7.2 MNote query request
|
||||
|
||||
`/api/knowledge-rag/query` 和 `/api/knowledge-rag/search` 请求 LightRAG 时应显式传:
|
||||
|
||||
```json
|
||||
{
|
||||
"query": "...",
|
||||
"mode": "mix",
|
||||
"top_k": 20,
|
||||
"chunk_top_k": 20,
|
||||
"include_references": true,
|
||||
"include_chunk_content": true,
|
||||
"enable_rerank": true
|
||||
}
|
||||
```
|
||||
|
||||
`enable_rerank` 来源:
|
||||
|
||||
- API body 可传。
|
||||
- UI 设置可覆盖。
|
||||
- 未设置时使用 MNote 从 LightRAG health 缓存到的 provider capability。
|
||||
|
||||
### 7.3 UI diagnostics
|
||||
|
||||
资料库设置 / dashboard 显示:
|
||||
|
||||
```text
|
||||
LightRAG: healthy
|
||||
Embedding: nvidia/baai/bge-m3
|
||||
Rerank: disabled
|
||||
Rerank binding: null
|
||||
Rerank queue: unavailable
|
||||
```
|
||||
|
||||
当用户看到检索排序异常时,第一屏就能判断当前没有 rerank。
|
||||
|
||||
## 8. Source card 与 answer citation
|
||||
|
||||
借鉴 NexusRAG 的 UI contract:
|
||||
@@ -409,31 +464,32 @@ Rerank queue: unavailable
|
||||
- 页码:只有 locator 有 page 时显示。
|
||||
- 标题路径:sidecar `parent_headings + heading`。
|
||||
- 相关性:
|
||||
- 优先 LightRAG `rerank_score`,如果 provider 返回。
|
||||
- 否则使用 MNote 现有 reference ranking score,标注为 `mnoteScore`。
|
||||
- 当前显示 provider order / MNote mapping score。
|
||||
- 只有后置 rerank 验证完成后才显示 LightRAG `rerank_score`。
|
||||
- 图片引用:sidecar drawings / image OCR 命中使用 `[IMG-xxxx]`。
|
||||
|
||||
## 9. 实施阶段
|
||||
|
||||
### Phase A:rerank capability 透出
|
||||
### Phase A:统一引用文本清洗合同
|
||||
|
||||
- [ ] `KnowledgeRagQueryRequest` / `KnowledgeRagSearchRequest` 增加 `enable_rerank`。
|
||||
- [ ] 调 LightRAG `/query/data` / `/query/search` 时显式传 `enable_rerank`。
|
||||
- [ ] status/dashboard 暴露 LightRAG health 中的 rerank 字段。
|
||||
- [ ] diagnostics 标注 `providerRerankEnabled`、`providerRerankAvailable`、`rerankModel`。
|
||||
- [ ] 不新增 MNote reranker。
|
||||
- [x] 在 `knowledge_rag.rs` 增加统一 citation text helper,产出 `rawQuote/displayQuote/locatorEvidenceText/searchQuery/normalizedFingerprint`。
|
||||
- [x] search result、Page AI、Hermes tool result 全部消费 `displayQuote`,不再直接展示 raw LightRAG chunk。
|
||||
- [x] locator/openAction 全部消费 `locatorEvidenceText` 和真实 `searchQuery`。
|
||||
- [x] 去重优先使用 `sourcePath + blockId`,无 block 时再用 `sourcePath + chunkId + normalizedFingerprint`,避免同段落重复但保留同文件不同段落。
|
||||
- [x] 移除或瘦身 `sidebar-tree-runtime.js` 里的临时 equation/drawing 清洗,前端只做最后一道防御。
|
||||
|
||||
验收:
|
||||
|
||||
- LightRAG `.env` 为 `RERANK_BINDING=null` 时,UI 明确显示 rerank disabled。
|
||||
- API result metadata 能看出本次 query 是否请求 rerank、provider 是否实际可用。
|
||||
- 搜索 `吡咯烷`、`吗啉` 的 UI 结果不显示 `<equation>`、`latex`、`drawing`、残缺 XML 标签。
|
||||
- `displayQuote` 不丢失用户 query。
|
||||
- `locatorEvidenceText` 能在 diagnostics 中看到来源是 `sidecar`、`chunk` 还是 fallback。
|
||||
|
||||
### Phase B:citation contract 收口
|
||||
|
||||
- [ ] 在 knowledge-rag mapped reference 上补 `citationId`。
|
||||
- [ ] 输出统一 `citations[]`,字段包含 `citationMarkdown/citationUrl/locatorPrecision/quote/headingPath`。
|
||||
- [ ] Page AI / Hermes tool result 只暴露 filtered citations,不暴露 raw chunks 给 final answer 引用。
|
||||
- [ ] source card 消费 `citations[]`,不重新解析 raw LightRAG response。
|
||||
- [x] 在 knowledge-rag mapped reference 上补 `citationId`。
|
||||
- [x] 输出统一 `citations[]`,字段包含 `citationMarkdown/citationUrl/locatorPrecision/displayQuote/locatorEvidenceText/headingPath`。
|
||||
- [x] Page AI / Hermes tool result 只暴露 filtered citations,不暴露 raw chunks 给 final answer 引用。
|
||||
- [x] source card 消费 `citations[]`,不重新解析 raw LightRAG response。
|
||||
|
||||
验收:
|
||||
|
||||
@@ -442,11 +498,11 @@ Rerank queue: unavailable
|
||||
|
||||
### Phase C:DOCX paragraph locator
|
||||
|
||||
- [ ] `lightrag_locator_for_reference(...)` 对 DOCX sidecar block 无 bbox 时返回 paragraph locator。
|
||||
- [ ] `locatorPrecision=paragraph` 时保留 `blockId/sourceMapPath/evidenceText/headingPath`。
|
||||
- [ ] `citationMarkdown` 对 paragraph/file 降级文案明确,但链接可点击。
|
||||
- [ ] office-preview 支持 `blockId/evidenceText` 定位,按 chunk context 打分滚动。
|
||||
- [ ] 同一 DOCX 多次点击 citation 使用 postMessage 更新定位,不重复 reload。
|
||||
- [x] `lightrag_locator_for_reference(...)` 对 DOCX sidecar block 无 bbox 时返回 paragraph locator。
|
||||
- [x] `locatorPrecision=paragraph` 时保留 `blockId/sourceMapPath/locatorEvidenceText/headingPath/searchQuery`。
|
||||
- [x] `citationMarkdown` 对 paragraph/file 降级文案明确,但链接可点击。
|
||||
- [x] office-preview 支持 `blockId/locatorEvidenceText/searchQuery` 定位,按 sidecar block / chunk context 打分滚动。
|
||||
- [x] 同一 DOCX 多次点击 citation 使用 postMessage 更新定位,不重复 reload。
|
||||
|
||||
验收:
|
||||
|
||||
@@ -456,8 +512,8 @@ Rerank queue: unavailable
|
||||
|
||||
### Phase D:NexusRAG 式来源卡片
|
||||
|
||||
- [ ] source card 展示 citation ID、文件名、标题路径、定位精度、相关性、quote。
|
||||
- [ ] 有 page/bbox 时显示页码;无 page/bbox 不显示页码。
|
||||
- [x] source card 展示 citation ID、文件名、标题路径、定位精度、quote。
|
||||
- [x] 有 page/bbox 时显示页码;无 page/bbox 不显示页码。
|
||||
- [ ] 图片 citation 使用 `[IMG-xxxx]` 并打开原图 / PDF 页。
|
||||
- [ ] answer renderer 对 citation badge 做点击联动。
|
||||
|
||||
@@ -466,6 +522,18 @@ Rerank queue: unavailable
|
||||
- 答案和搜索结果都能从同一 citation model 打开来源。
|
||||
- source card 不依赖 LLM 生成的自由文本解析。
|
||||
|
||||
### Phase E:rerank status / provider score(后置)
|
||||
|
||||
- [x] status/dashboard 暴露 LightRAG health 中的 rerank 字段。
|
||||
- [x] diagnostics 标注 `providerRerankEnabled`、`providerRerankAvailable`、`rerankModel`。
|
||||
- [x] 只有真实 endpoint smoke 通过后,才考虑 `KnowledgeRagQueryRequest` / `KnowledgeRagSearchRequest` 增加 `enable_rerank`。
|
||||
- [x] 不新增 MNote reranker。
|
||||
|
||||
验收:
|
||||
|
||||
- LightRAG `.env` 为 `RERANK_BINDING=null` 时,UI 明确显示 rerank disabled。
|
||||
- 不会因为 rerank unavailable 改变当前 query/search 行为。
|
||||
|
||||
## 10. 测试计划
|
||||
|
||||
### Rust 单测
|
||||
@@ -473,11 +541,14 @@ Rerank queue: unavailable
|
||||
目标文件:`rust/crates/mnote-web/src/routes/knowledge_rag.rs`
|
||||
|
||||
- `mapped_reference_generates_short_citation_id`
|
||||
- `citation_text_bundle_strips_equation_and_drawing_for_display`
|
||||
- `citation_text_bundle_preserves_query_for_locator`
|
||||
- `docx_paraid_block_returns_paragraph_locator`
|
||||
- `docx_missing_block_degrades_to_file_locator`
|
||||
- `bbox_block_keeps_bbox_locator`
|
||||
- `query_request_passes_enable_rerank`
|
||||
- `provider_rerank_disabled_is_reported_in_diagnostics`
|
||||
- `same_block_same_fingerprint_deduplicates`
|
||||
- `same_file_different_block_is_not_deduplicated`
|
||||
- `provider_rerank_disabled_is_reported_in_status`
|
||||
|
||||
### Browser smoke
|
||||
|
||||
@@ -491,7 +562,8 @@ Rerank queue: unavailable
|
||||
- DOCX citation click 后 resource tab 不重载。
|
||||
- paragraph locator 触发可见高亮。
|
||||
- `locatorPrecision` 与 UI 文案一致。
|
||||
- rerank disabled 状态在资料库设置中可见。
|
||||
- 搜索结果不显示 raw equation / drawing / broken tag。
|
||||
- rerank disabled 状态在资料库设置中可见,但不影响 query/search。
|
||||
|
||||
### Provider smoke
|
||||
|
||||
@@ -512,14 +584,16 @@ curl /query/data -d '{"query":"...", "mode":"mix", "enable_rerank":true}'
|
||||
|
||||
- 不替换 LightRAG 为 NexusRAG。
|
||||
- 不在 MNote 复制 LightRAG vector / graph / rerank。
|
||||
- 当前阶段不启用、不调试、不 UI 化 rerank 参数;只展示 provider 状态。
|
||||
- 不把 DOCX 无 page/bbox 的命中伪造成 PDF 精确定位。
|
||||
- 不把 raw chunk 清洗逻辑继续分散在搜索 UI、Page AI、office-preview。
|
||||
- 不让普通本地搜索强依赖 LightRAG。
|
||||
- 不让 LLM 自己生成 citation ID。
|
||||
- 不把 raw LightRAG chunk 暴露为 agent final answer 可直接引用材料。
|
||||
|
||||
## 12. 开放问题
|
||||
|
||||
1. LightRAG `/query/data` 当前 `convert_to_user_format(...)` 没有把 `rerank_score` 放入 `formatted_chunks`。如果上游 rerank 已经写回 chunk,是否要向 LightRAG 提 patch 暴露 `rerank_score`,还是 MNote 只显示本地 ranking score?
|
||||
2. DOCX `paraid range=[null,null]` 的样本较多。是否需要在 LightRAG native parser 侧增强 paraId 恢复或写入 block ordinal,给 MNote 一个更稳定的 `blockOrder`?
|
||||
3. `/query/search` 与 `/query/data` 的 reference schema 不完全一致时,MNote 是否应该先在 connector 层 normalize 为同一个 `ProviderReference`?
|
||||
4. Source scope 当前仍是 MNote post-filter。长期是否需要 LightRAG provider 支持按 `file_path/doc_id` 过滤候选,以避免 raw retrieval 污染?
|
||||
1. DOCX `paraid range=[null,null]` 的样本较多。是否需要在 LightRAG native parser 侧增强 paraId 恢复或写入 block ordinal,给 MNote 一个更稳定的 `blockOrder`?
|
||||
2. `/query/search` 与 `/query/data` 的 reference schema 不完全一致时,MNote 是否应该先在 connector 层 normalize 为同一个 `ProviderReference`?
|
||||
3. Source scope 当前仍是 MNote post-filter。长期是否需要 LightRAG provider 支持按 `file_path/doc_id` 过滤候选,以避免 raw retrieval 污染?
|
||||
4. LightRAG `/query/data` 当前 `convert_to_user_format(...)` 没有把 `rerank_score` 放入 `formatted_chunks`。该问题放到 Phase E;在真实 rerank endpoint 跑通前不处理。
|
||||
@@ -0,0 +1,228 @@
|
||||
# 7-56 LightRAG Native DOCX Sidecar 可定位块粒度优化 v1
|
||||
|
||||
> 创建时间:2026-06-09
|
||||
>
|
||||
> 当前状态:`process`
|
||||
>
|
||||
> Owner:07-ai / knowledge-rag / LightRAG native parser / office-preview
|
||||
>
|
||||
> 前置完成:
|
||||
> - `design/07-ai/done/7-55-lightrag-docx-citation-rerank-alignment-v1.md`
|
||||
>
|
||||
> 参考:
|
||||
> - LightRAG native DOCX parser:`/mnt/Data1T/Mnote_data/lightrag/LightRAG/lightrag/parser/docx/`
|
||||
> - LightRAG sidecar writer:`/mnt/Data1T/Mnote_data/lightrag/LightRAG/lightrag/sidecar/writer.py`
|
||||
> - Docling adapter 对照:`/mnt/Data1T/Mnote_data/lightrag/LightRAG/lightrag/parser/external/docling/`
|
||||
> - NexusRAG:仅作为 parse-before-index 架构佐证,不照搬 pipeline
|
||||
|
||||
## 1. 结论
|
||||
|
||||
当前 DOCX 定位问题的根因不是 MNote 缺少查询后 parser,而是 LightRAG native DOCX sidecar 把已解析到的段落信息压成了过粗的 content block。
|
||||
|
||||
实测样本:
|
||||
|
||||
```text
|
||||
/mnt/Data1T/Mnote_data/users/mnote-e2e/workspaces/my-space/新页面233155/中国化妆品新原料产业发展调研报告_03(20260410).docx
|
||||
```
|
||||
|
||||
对比结果:
|
||||
|
||||
```text
|
||||
native parser 旧行为:
|
||||
- python-docx 可读到 68 个 paragraph,且有 w14:paraId
|
||||
- sidecar 只写 1 个 content block
|
||||
- positions 只有 paraid range [第一段, 最后一段]
|
||||
|
||||
docling parser 当前行为:
|
||||
- docling raw JSON 有 70 个 texts
|
||||
- LightRAG docling IR builder 也合并为 1 个 block
|
||||
- DOCX prov/bbox 为空,不提供 Word 原生 paraId
|
||||
|
||||
native parser 优化后:
|
||||
- sidecar 写 67 个 content block
|
||||
- 每个 block 带单段 paraid range
|
||||
- positions.paraid.anchor 写 paragraph ordinal
|
||||
- 额外写 text_fingerprint position
|
||||
```
|
||||
|
||||
因此当前主线选择:**优先优化 LightRAG native DOCX sidecar,不切换到 Docling 作为 DOCX 定位主路径。**
|
||||
|
||||
Docling 保留为外部 parser 能力,适合 PDF/图片/跨格式解析对照;但对 DOCX 回跳原 Word 段落,native 能拿到 `paraId`,更适合作为定位底座。
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
把 DOCX native sidecar 从 document-sized block 改为 paragraph/section-addressable blocks:
|
||||
|
||||
```text
|
||||
DOCX native parse
|
||||
-> paragraphs[]
|
||||
text
|
||||
paraId
|
||||
paragraphOrdinal
|
||||
inferred heading
|
||||
textFingerprint
|
||||
-> blocks.jsonl
|
||||
content block per addressable paragraph/table slice
|
||||
positions: paraid + paragraph ordinal
|
||||
positions: text fingerprint
|
||||
heading / parent_headings
|
||||
-> LightRAG indexing/query
|
||||
-> MNote citation/locator
|
||||
chunk/reference -> blockid/positions -> office-preview 定位
|
||||
```
|
||||
|
||||
## 3. 当前实施
|
||||
|
||||
已在 LightRAG 本地源码完成第一步:
|
||||
|
||||
- `lightrag/parser/docx/parse_document.py`
|
||||
- 新增 `addressable_blocks` 参数。
|
||||
- 新增 Normal 样式报告标题启发式识别。
|
||||
- 新增 paragraph-level block 输出。
|
||||
- 每个 addressable block 写 `paragraph_ordinal` 与 `text_fingerprint`。
|
||||
- `lightrag/parser/docx/ir_builder.py`
|
||||
- `IRPosition(type="paraid")` 的 `anchor` 写 paragraph ordinal。
|
||||
- 追加 `IRPosition(type="text_fingerprint")`。
|
||||
- `lightrag/pipeline.py`
|
||||
- native DOCX production parse path 调用 `extract_docx_blocks(..., addressable_blocks=True)`。
|
||||
|
||||
## 4. 验证证据
|
||||
|
||||
命令:
|
||||
|
||||
```bash
|
||||
cd /mnt/Data1T/Mnote_data/lightrag/LightRAG
|
||||
.venv/bin/python -m lightrag.parser.cli \
|
||||
'/mnt/Data1T/Mnote_data/users/mnote-e2e/workspaces/my-space/新页面233155/中国化妆品新原料产业发展调研报告_03(20260410).docx' \
|
||||
--engine native \
|
||||
-o /tmp/mnote-lightrag-parser-cosmetics-native-v3 \
|
||||
--preview 8
|
||||
```
|
||||
|
||||
结果:
|
||||
|
||||
```text
|
||||
[sidecar] wrote 67 blocks
|
||||
positions example:
|
||||
[
|
||||
{"type":"paraid","anchor":4,"range":["6692C49B","6692C49B"]},
|
||||
{"type":"text_fingerprint","anchor":"1454ec0b0ffd9fc0"}
|
||||
]
|
||||
```
|
||||
|
||||
PDF 对照样本:
|
||||
|
||||
```text
|
||||
/mnt/Data1T/Mnote_data/users/mnote-e2e/workspaces/my-space/新页面233155/parsertest.pdf
|
||||
```
|
||||
|
||||
MinerU parse 已能输出 `positions.type=bbox` 与 page anchor,PDF 当前先消费现有 bbox,不作为 7-56 首要补丁对象。
|
||||
|
||||
## 5. 后续收口
|
||||
|
||||
- MNote `knowledge_rag` mapper 应优先消费 provider sidecar 中的 `blockid/positions`,逐步退役 query-time quote 反查 block。
|
||||
- office-preview 应优先使用 `paraid.anchor` 或 paraid range 定位;没有可用 paraId 时再用 `text_fingerprint/displayQuote` 做降级匹配。
|
||||
- 重新索引 DOCX 后才能让旧资料库命中使用新 sidecar 粒度。
|
||||
- LightRAG 服务需要重启后才会使用本地源码改动。
|
||||
- 本轮已执行 `systemctl --user restart mnote-lightrag.service`,`/health` 返回 healthy;当前 parser routing 仍是 `*:native-iteP,*:mineru-iteP,*:legacy-R`,DOCX 默认走 native。
|
||||
|
||||
## 6. MNote 与 LightRAG 的新分工
|
||||
|
||||
LightRAG 已提供 keyword / vector / graph / hybrid 检索、rerank、query 与 chunk/reference 返回。MNote 不应继续维护第二套召回、排序或 sidecar exact search 作为主路径。
|
||||
|
||||
新的主合同:
|
||||
|
||||
```text
|
||||
MNote
|
||||
- source registry:本地路径、allowed roots、索引状态、LightRAG doc/file 映射
|
||||
- scope:搜索目录 / 后续排除目录 / 权限过滤
|
||||
- facade:把 UI / Page AI 请求转为 LightRAG query/search/data 参数
|
||||
- citation:把 LightRAG reference/chunk 归一化为 MNote result/citation
|
||||
- locator:消费 LightRAG sidecar positions 并打开原文件
|
||||
|
||||
LightRAG
|
||||
- parse / OCR / chunk / vector / graph / rerank / hybrid retrieval
|
||||
- DOCX native 输出 paraid / paragraph ordinal / text fingerprint
|
||||
- PDF/MinerU 输出 page/bbox
|
||||
```
|
||||
|
||||
因此,MNote 允许保留的“手搓”只限定位适配;定位适配只能消费 LightRAG 已返回的 reference/chunk/source sidecar 信息,不能重新执行一套 MNote 检索或 parser:
|
||||
|
||||
- `bbox -> pdf-preview`
|
||||
- `paraid / paragraphOrdinal / textFingerprint -> office-preview`
|
||||
- `blockId / sourceMapPath / evidenceText -> provider 引用后的定位线索`
|
||||
|
||||
不再作为主路径继续扩写:
|
||||
|
||||
- MNote 独立 sidecar exact search provider
|
||||
- MNote query-time DOCX parser
|
||||
- MNote 自己 rerank / 多 provider 融合排序
|
||||
- MNote query-time sidecar quote 反查
|
||||
- 旧 `docs_search` / `mnote.evidence.search` / LiteParse local evidence search
|
||||
|
||||
## 7. 退役计划
|
||||
|
||||
阶段 A:完成 provider positions 消费。
|
||||
|
||||
- `knowledge_rag` locator 解析 `positions.type=bbox / paraid / text_fingerprint`。
|
||||
- citation URL 与 resource tab 透传 `paragraphOrdinal / paraIdStart / paraIdEnd / textFingerprint`。
|
||||
- office-preview 优先按段落定位,再回退到当前文本定位。
|
||||
|
||||
阶段 B:退役 query-time sidecar 反查与旧 evidence/docs 搜索。
|
||||
|
||||
- 删除 `find_lightrag_sidecar_quote_for_query` / `find_lightrag_sidecar_block_for_query` 这类 query-time sidecar quote 反查 helper;MNote 不再用 sidecar 作为检索补召回。
|
||||
- `docs_search`、`docs_read`、`mnote.evidence.*` Hermes tool 统一返回退役错误,引导使用 `mnote.knowledge_rag.query/open_reference`。
|
||||
- `/api/evidence/search` 只保留退役 guard,不再保留可被内部调用的 `search_payload`。
|
||||
- 搜索结果去重可以保留在 MNote locator/result 层,但召回与排序不再依赖 MNote sidecar exact search。
|
||||
- `/api/knowledge-rag/search` 的 `mode=exact` 走 LightRAG `/query/search`;`mode=mix/local/global/hybrid/naive` 走 LightRAG `/query/data`。
|
||||
- 搜索面板“全盘资料库”接入 LightRAG 检索模式:综合 `mix`、图谱混合 `hybrid`、向量 `naive`、实体 `local`、关系 `global`、关键词 `exact`。`精确匹配`开关会强制走 `exact`。
|
||||
|
||||
阶段 C:退役 MNote parser 主路径。
|
||||
|
||||
- 删除或冻结 MNote 后端 DOCX/PDF parser 参与 RAG 搜索的入口。
|
||||
- 保留 source-map / locator 兼容读取,用于历史索引和旧 OCR 结果打开。
|
||||
- 新索引统一由 LightRAG parser sidecar 产出定位信息。
|
||||
|
||||
## 8. 不做
|
||||
|
||||
- 不在 MNote 新增一套 DOCX parser。
|
||||
- 不把 DOCX 默认切到 Docling。
|
||||
- 不在 MNote 实现 rerank;rerank 仍走 LightRAG 原生能力。
|
||||
- 不把 DOCX 无 bbox 伪造成 PDF 页内精确定位。
|
||||
|
||||
## 9. Agent / MCP 边界
|
||||
|
||||
LightRAG 官方内核不提供 Skill/MCP/plugin 系统;社区已有 `lightragmcp`、`mcp-lightrag` 等 MCP server,可把 LightRAG query、文档管理、图谱查询包装成 Agent 工具。这一层适合接给 Reasonix / Hermes / Claude Desktop 这类 Agent runtime,但它不能替代 MNote 的 source registry 与引用打开能力。
|
||||
|
||||
推荐边界:
|
||||
|
||||
```text
|
||||
Reasonix / Hermes
|
||||
-> 可直接挂 LightRAG MCP:查询、问答、图谱、管理
|
||||
-> 返回 LightRAG reference/chunk/citation 数据
|
||||
|
||||
MNote
|
||||
-> 提供 mnote.knowledge_rag.open_reference 或等价 locator bridge
|
||||
-> 把 Agent 引文里的 file_path/chunk_id/blockid/positions 映射到原文件 tab、PDF bbox、DOCX paragraph
|
||||
-> 做 allowed roots / workspace source registry / citationUrl
|
||||
```
|
||||
|
||||
因此更好的长期方案不是在 MNote 内继续手搓搜索工具,而是:
|
||||
|
||||
- Agent 侧:可以挂 `lightrag_native=/mnt/Data1T/mnote/scripts/lightrag-native-mcp.sh`,让 Agent 原生调用 LightRAG 检索、`query_data`、图谱、文档和 pipeline 能力;MNote 不把第三方 MCP server 嵌入 Web runtime。
|
||||
- MNote 侧:保留极薄的 `knowledge_rag.query/search/open_reference` facade,服务 UI、权限、source registry 与可点击引用;若 Agent 已直接通过 LightRAG MCP 检索,MNote 只需要 citation bridge。
|
||||
- Citation bridge:`mnote_lightrag_bridge=/mnt/Data1T/mnote/scripts/mnote-lightrag-mcp.sh` 已提供 `open_mnote_reference`,输入 LightRAG `file_path` / `chunk_id`,输出 MNote `citationUrl` / `openAction`,不参与检索和排序。
|
||||
|
||||
当前本机配置审计:
|
||||
|
||||
- `/home/lix/.reasonix/config.json` 已配置 `lightrag_native=/mnt/Data1T/mnote/scripts/lightrag-native-mcp.sh` 与 `mnote_lightrag_bridge=/mnt/Data1T/mnote/scripts/mnote-lightrag-mcp.sh`。
|
||||
- `/home/lix/.hermes/profiles/mnote-u-mnote-e2e-default/config.yaml` 已配置 `lightrag_native` 与 `mnote_lightrag_bridge` MCP server。
|
||||
- MNote 内已有 `skills/mnote-knowledge-rag/SKILL.md`、`skills/mnote-lightrag-bridge/SKILL.md` 和 Hermes manifest 的 `mnote.knowledge_rag.status/query/open_reference`,这是当前 agent 调用 LightRAG 后回到 MNote 引用定位的 facade。
|
||||
- 包名核验:PyPI 可见 `mcp-lightrag 0.2.2` 与 `lightrag-mcp 0.1.1`,但 `mcp-lightrag 0.2.2` 对当前本机 LightRAG 版本会触发 `QueryRequest.__init__() got an unexpected keyword argument 'max_token_for_text_unit'`。当前 Reasonix/Hermes 原生 LightRAG MCP 改用 `l-pw2c-lightrag-server-mcp@1.2.2`,并保留 MNote bridge 做 citation/open-reference 映射。
|
||||
|
||||
MCP / MNote tool 变更后的强制验收:
|
||||
|
||||
- 使用 Reasonix ACP / Page AI 发起真实资料库问题,确认 AI 可见回答包含 MNote citation 链接,而不是 raw JSON、provider 内部路径或手写 `/documents`。
|
||||
- 从 AI 回答中点击 citation 链接,必须在 MNote 中打开对应资源 tab;PDF 要落到 page/bbox,DOCX 要落到 paragraph/block,降级资源至少要打开到正确 resource tab 并明确 `locatorDegraded`。
|
||||
- 验收证据必须包含 Reasonix run payload 摘要、回答截图、点击后打开定位截图、console/network 摘要和 `result.json`。
|
||||
- 当前可复用 smoke:`scripts/task530-knowledge-rag-page-ai-final-answer-smoke.js`,它会使用 Reasonix Page AI 得到 citation,并点击 AI 返回的链接验证 MNote 资源定位打开。
|
||||
Reference in New Issue
Block a user