Files
mnote/design/07-ai/done/7-56-lightrag-native-docx-sidecar-addressable-blocks-v1.md
T

303 lines
15 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-56 LightRAG Native DOCX Sidecar 可定位块粒度优化 v1
> 创建时间:2026-06-09
>
> 当前状态:`done`
>
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认 LightRAG provider 的 native DOCX sidecar 定位基线;历史非 LightRAG 默认 provider 口径已标记 stale。
>
> Owner07-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/中国化妆品新原料产业发展调研报告_0320260410.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 anchorPDF 当前先消费现有 bbox,不作为 7-56 首要补丁对象。
## 5. 完成实现
2026-06-09 已完成 DOCX 查询与点击定位主链收口。
### 5.1 LightRAG provider 合同
- `/query/search` 保持关键词检索入口,`SearchRequest.query` 最小长度为 2。
- `/query/search` 新增 `include_sidecar=true`,结果 chunk 返回原始 `sidecar``heading`
- 返回 chunk 保留 `source_chunk_id``chunk_id` 可继续用 `#match-N` 表示同一 chunk 内第 N 个关键词命中。
- LightRAG `kv_store_text_chunks.json` 中的 paragraph semantic chunk 已携带 `sidecar.refs`,例如 `doc-ff0b60997a285a85e5704a114d7b3ffa-chunk-158` 返回 52 个 block refsMNote 不再需要凭完整 chunk 文本猜测来源 block。
### 5.2 MNote citation / locator 合同
- `/api/knowledge-rag/search` exact 模式请求 LightRAG `/query/search` 时显式传 `include_sidecar=true`
- `map_reference_plan(...)` 优先消费 LightRAG chunk `sidecar.refs`,按 `occurrenceIndex` 在 refs 指向的 blocks 中精确选择含 query 的 block。
- `find_lightrag_sidecar_block(...)` 已从“文本打分匹配”改为“sidecar.refs 精确 block 选择”;没有 refs 或 refs 中没有 query 命中时不伪造精确 locator。
- `displayQuote` / `locatorEvidenceText` 使用选中的 sidecar block 文本,搜索结果不再展示整段 chunk window 导致的重复、错位上下文。
- `sourceChunkId` 被写入 mapped reference,便于诊断 match chunk 与原始 LightRAG chunk 的对应关系。
### 5.3 Office preview 定位
- `mnote.open_resource_locator` 透传 `paragraphOrdinal / paraIdStart / paraIdEnd / textFingerprint / query / searchQuery`
- 同一 DOCX 点击不同搜索结果时,resource tab 的 base href 比较会剔除定位参数,避免把同一资源误判成新资源而重新加载。
- DOCX preview 优先用 `evidenceText` 精确段落锚定,再考虑 paragraph ordinal 附近搜索;避免短 query 命中相邻段落。
- `page=0` 不再写入 Office preview URL 或 panel state,避免污染定位状态。
### 5.4 验证证据
LightRAG API 证据:
```bash
curl -H "X-API-Key: $LIGHTRAG_API_KEY" \
-H 'Content-Type: application/json' \
http://127.0.0.1:9621/query/search \
-d '{"query":"吡咯烷","limit":2,"max_per_chunk":2,"include_chunk_content":true,"include_sidecar":true}'
```
结果摘要:
```text
chunk_id=doc-ff0b60997a285a85e5704a114d7b3ffa-chunk-158#match-0
source_chunk_id=doc-ff0b60997a285a85e5704a114d7b3ffa-chunk-158
sidecar.refs=52
first_ref=700ee42acf09aadc31f028f523f52fa9
```
MNote 验证:
```bash
cargo test -p mnote-web knowledge_rag -- --nocapture
MNOTE_KNOWLEDGE_RAG_PYRROLIDINE_QUERY='吡咯烷' MNOTE_KNOWLEDGE_RAG_TOP_N=5 \
node scripts/task543-knowledge-rag-pyrrolidine-top5-locator-smoke.js
MNOTE_KNOWLEDGE_RAG_REPOSITION_QUERY='吡咯烷' \
node scripts/task541-knowledge-rag-search-panel-office-reposition-smoke.js
```
结果:
```text
cargo test: 51 passed
task543: 前 5 个搜索结果 apiBlockId/uiBlockId/panelBlockId 一致
task543: 第 3 条已定位到 “吡咯烷,5 h, 90%”,不再跳到下一段
task541: 搜索面板关闭后状态保持;同一 DOCX 第二次定位 loadCount=0
```
用户验收:
```text
2026-06-09:用户确认本次 DOCX 的查询合格。
```
## 6. 后续收口
- MNote `knowledge_rag` mapper 已优先消费 provider sidecar refs;后续只需继续把 hybrid/mix 查询返回的 chunk provenance 也推向同一合同。
- office-preview 已优先用 sidecar block 的 evidenceText 精确段落锚定;后续可继续增强真实 `paraId` DOM 标注,减少 paragraph ordinal 对 docx-preview DOM 的依赖。
- 重新索引 DOCX 后才能让旧资料库命中使用新 sidecar 粒度。
- LightRAG 服务需要重启后才会使用本地源码改动。
- 本轮已执行 `systemctl --user restart mnote-lightrag.service``/health` 返回 healthy;当前 parser routing 仍是 `*:native-iteP,*:mineru-iteP,*:legacy-R`DOCX 默认走 native。
## 7. 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
## 8. 退役计划
阶段 A:完成 provider positions 消费。已完成 DOCX exact search 主链:
- [x] `knowledge_rag` locator 解析 `positions.type=bbox / paraid / text_fingerprint`
- [x] citation URL 与 resource tab 透传 `paragraphOrdinal / paraIdStart / paraIdEnd / textFingerprint`
- [x] office-preview 优先按 sidecar block 的 evidenceText 定位段落,再按 paragraph ordinal 附近校验定位。
- [x] LightRAG `/query/search` 返回 `sidecar.refs`MNote exact search 不再使用 query-time 文本打分反查。
阶段 B:退役 query-time sidecar 反查与旧 evidence/docs 搜索。
- 删除 `find_lightrag_sidecar_quote_for_query` / `find_lightrag_sidecar_block_for_query` 这类 query-time sidecar quote 反查 helperMNote 不再用 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 产出定位信息。
## 9. 不做
- 不在 MNote 新增一套 DOCX parser。
- 不把 DOCX 默认切到 Docling。
- 不在 MNote 实现 rerankrerank 仍走 LightRAG 原生能力。
- 不把 DOCX 无 bbox 伪造成 PDF 页内精确定位。
## 10. 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 中打开对应资源 tabPDF 要落到 page/bboxDOCX 要落到 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 资源定位打开。