Files
mnote/design/07-ai/done/7-51-lightrag-post-commit-hardening-v1.md
T

189 lines
13 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-51 LightRAG post-commit hardening v1
> 创建时间:2026-06-07
>
> 状态:`done`
>
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认 LightRAG provider 的 hardening 基线;历史非 LightRAG 默认 provider 口径已标记 stale。
>
> Owner07-ai / knowledge-rag / 03-rust-web / plugin-ui
>
> 来源:`design/10-review/done/20-post-lightrag-runtime-hardening-checklist-v1.md` P0 审计
>
> 上位依据:
> - `design/07-ai/done/7-50-lightrag-knowledge-rag-provider-v1.md`
> - `design/10-review/done/20-post-lightrag-runtime-hardening-checklist-v1.md`
## 1. 第一结论
7-50 作为 LightRAG 资料库问答主线可以保持 `done`,但 post-commit audit 发现若干需要单独硬化的边界:
- watcher stale sync 在 best-effort 删除失败时会吞掉错误,并且已经清空 registry 中的 `lightRagDocId`,后续难以重试删除 provider orphan doc。
- `sourcePaths` 当前是 provider 检索后过滤 MNote mapped references,不是 provider 预过滤;HTTP API 仍返回未过滤 `raw`,存在调用方误用 raw 的风险。
- sidecar locator 在 quote 未命中时会 fallback 到 `first_positioned_block`,可能生成错误 page/bbox,却表现为非 degraded locator。
- `mnote-knowledge-rag` skill 文案对 degraded citation 过强,容易让 agent 误以为不能使用返回的降级 resourceTab 链接。
- Reasonix wrapper 仍手写工具表,能力注册漂移问题还没有真正解决。
本稿不恢复旧 evidence / LiteParse / local OCR 路径,不开发第二套 RAG,只硬化 7-50 已选定的 LightRAG provider 边界。
## 2. 非目标
- 不重建 `mnote.evidence.*` / `mnote.index.*` agent 工具。
- 不把 source scope 伪装成 LightRAG provider 原生过滤,除非实际实现了 provider 级约束。
- 不清空用户资料库、不删除用户原始 source、不自动大规模 reindex。
- 不把 LightRAG sidecar / chunk / graph 当作 MNote 正文真相。
- 不在本稿内实施完整 capability registry;只补必要漂移测试或文案约束,完整 registry 仍按 `7-47` / `20 P4` 推进。
## 3. Phase ADelete retry / orphan doc guard
目标:source 删除或 hash 变化后,即使 LightRAG 删除失败,也不能丢失重试 provider doc idprune 不能隐藏仍可能参与回答的 orphan doc。
Checklist
- [x] 修改 `sync_registry_source_state(...)`source missing / hash changed 时不要立即清空 `lightRagDocId`;应进入 `stale=true` + `lightRagStatus=delete_submitted``delete_retry_required`
- [x] `sync_registry_with_documents(...)` 调用 LightRAG delete 失败时,把失败写入 registry entry 的可见状态,而不是吞掉后只保留 stale。
- [x] 只有确认 LightRAG `/documents` 不再包含该 doc 后,才清空 `lightRagDocId` 并标记 `delete_completed`
- [x] `prune_registry` 不清理仍带 `lightRagDocId` 且 delete 未确认完成的 stale entry。
- [x] `mapped_references(...)` 对 missing registry 的 provider reference 需要区分:
- provider orphan / unknown source:默认不作为有效 MNote citation 返回,或明确返回 degraded + unmapped 分类,但不得进入 scoped answer citation。
- registry 命中但 stale / deleted:继续过滤。
- [x]`delete-source` route 保持 `delete_file=false`,并保留“不删除用户原始 source”的断言。
验证:
- [x] 单测:LightRAG delete 失败时 registry 保留 doc id 和 retry 状态。
- [x] 单测:delete completed 后才清 doc id。
- [x] 单测:prune 不删除 `delete_submitted` / `delete_retry_required` entry。
- [x] 单测:missing registry provider reference 不会作为正常 citation 返回。
- [x] `cargo test -p mnote-web knowledge_rag -- --nocapture`
实施记录:
- 2026-06-07`sync_registry_source_state(...)` 在 source missing / hash changed 时保留 `lightRagDocId`,并把状态置为 `delete_submitted`
- 2026-06-07`sync_registry_with_documents(...)` 会重试 `delete_submitted` / `delete_retry_required` doc idLightRAG delete 失败时写入 `delete_retry_required`,成功提交后保持 `delete_submitted`,等 `/documents` 消失后才标记 `delete_completed` 并清空 doc id。
- 2026-06-07:当前 registry schema 先用 `lightRagStatus=delete_retry_required` 表达失败状态,不额外扩写错误 message 字段,避免扩大持久化迁移面。
## 4. Phase BSource scope 语义收紧
目标:避免调用方误以为 `sourcePaths` 已限制 LightRAG provider 检索范围。
Checklist
- [x]`/api/knowledge-rag/query` 返回中新增 `sourceScopeMode`,当前值明确为 `post_filter_mapped_references`
- [x]`sourcePaths` 非空,默认不向普通 agent / Page AI 暴露未过滤 `raw` chunks;至少 compact tool output 继续不暴露 raw chunks。
- [x] HTTP API 如仍保留 `raw`,必须标注 `rawScopeFiltered=false` 或等价字段,提醒 raw 未受 source scope 过滤。
- [x] `mnote.knowledge_rag.query` manifest / Reasonix wrapper / skill 文案从“限制检索来源”改为“按 MNote source 过滤返回 referencesprovider raw 可能仍为全局检索结果”。
- [x] Page AI final answer 只允许引用 filtered `references/citations`,不得引用 raw LightRAG chunks。
验证:
- [x] 单测:`sourcePaths=[alpha]``references` 只含 alpha。
- [x] 运行态 smoke`sourceScopeMode=post_filter_mapped_references``rawScopeFiltered=false`
- [x] 单测或 wrapper selftestagent compact result 不含 raw chunks。
- [x] `task538-knowledge-rag-source-scope-api-smoke.js` 更新并通过。
- [x] `task534-knowledge-rag-source-management-scope-smoke.js` UI source management 部分已修复并通过。
实施记录:
- 2026-06-07`/api/knowledge-rag/query` 返回 `sourceScopeMode=post_filter_mapped_references``rawScopeFiltered=false`
- 2026-06-07`mnote.knowledge_rag.query` compact agent result 继续只暴露 filtered `references/citations`,不暴露 `raw.chunks`
- 2026-06-07manifest、Reasonix wrapper、`skills/mnote-knowledge-rag/SKILL.md` 已统一 sourcePaths post-filter 口径;新增 `task538-knowledge-rag-source-scope-api-smoke.js` 覆盖 API scope metadata、filtered references、`delete-source` 不删原始 source。
- 2026-06-07`task534` 修正为打开资料库设置后切到“全部”来源,避免默认“需处理”过滤隐藏已索引 source;在 3300 最新构建通过。
## 5. Phase CLocator strict match / degradation
目标:没有真实匹配到 quote / chunk 对应 sidecar block 时,不生成看似精确的 page/bbox。
Checklist
- [x] 修改 `find_lightrag_sidecar_block(...)`:移除或限制 `first_positioned_block` fallback;只有 quote 与 block content 达到明确匹配阈值时才返回 page/bbox locator。
- [x] 若只找到 source resource 但没有精确 block match,返回 clickable degraded resourceTab citation,但 `locatorDegraded=true`
- [x] `citationMarkdown` 对 degraded resourceTab 使用稳定文案,例如 `来源定位降级:<file>`,不包含伪造页码。
- [x] 对 PDF / image / DOCX fallback citation 分别保留打开能力,但不伪造 bbox。
- [x] 记录 LightRAG chunk 缺 `refs` 的当前边界;未来若 provider 暴露 refs,再改为 refs 优先、文本匹配 fallback。
验证:
- [x] 单测:quote 不匹配 sidecar block 时不返回 page/bbox locator。
- [x] 单测:quote 匹配 sidecar block 时仍返回 page/bbox locator。
- [x] 单测:known resource + no locator 返回 clickable degraded citation。
- [x] `task529-knowledge-rag-citation-resource-tab-smoke.js` 更新为当前 fixture 可重复的正例,并通过。
实施记录:
- 2026-06-07`find_lightrag_sidecar_block(...)` 已移除 `first_positioned_block` fallback;未匹配 quote 时返回降级 resourceTab citation,不生成 page / bbox。
- 2026-06-07:当前 LightRAG `/query/data` chunks 未提供可稳定回跳 MNote sidecar block 的 provider `refs`;MNote 暂以文本匹配做严格定位,未匹配时只给 degraded resourceTab。
- 2026-06-07`task529` 改为当前 image source 降级 citation 正例,验证 `open_reference` 与 citation URL 能打开 image resource tab,且不携带伪造 page locator。
## 6. Phase DAgent / UI degraded citation 口径统一
目标:agent 可以使用 MNote 返回的 degraded citation,但不能编造页码、bbox 或 provider 内部路径。
Checklist
- [x] 更新 `skills/mnote-knowledge-rag/SKILL.md``locatorDegraded=true` 时,可以引用返回的 `citationMarkdown`,但必须说明来源定位降级;禁止编造 page / bbox。
- [x] 更新 Reasonix system prompt 中 knowledge-rag 能力说明,保持同一口径。
- [x] 更新 Page AI final answer smoke,增加 degraded citation 场景:最终回答可以含降级 citation,但不能说成 p.N / bbox。
- [x] 更新 dashboard smoke,不再依赖固定 `Completed (5)` / `Fail (4)` / 固定 doc id;改为验证 Documents / Graph / Retrieval 入口与当前 documents summary 可见。
验证:
- [x] `MNOTE_REASONIX_ACP_SELFTEST=1 node scripts/reasonix-acp-wrapper.mjs`
- [x] `node scripts/task530-knowledge-rag-page-ai-final-answer-smoke.js`
- [x] `node scripts/task531-lightrag-dashboard-ui-smoke.js`
## 7. Phase EReasonix / manifest drift guard
目标:在完整 7-47 capability registry 实施前,先降低 Reasonix wrapper 与 Rust manifest 的漂移风险。
Checklist
- [x] 增加一个轻量 selftestRust manifest 中 `mnote.knowledge_rag.*` 三个工具必须都存在于 Reasonix wrapper 映射。
- [x] 增加一个反向 selftestReasonix wrapper 暴露的 `mnote_knowledge_rag_*` 必须能映射到 Rust manifest tool name。
- [x] 保留手写工具表作为短期现实,但在 7-47 中继续推进 manifest 动态注册。
- [x] 若发现旧 `mnote.evidence.*` / `mnote.index.*` 函数被 grep 误判,给 manifest 源码附近加注释:dead code 仅保留历史对照,不在 manifest vector 中注册。
验证:
- [x] `MNOTE_REASONIX_ACP_SELFTEST=1 node scripts/reasonix-acp-wrapper.mjs`
- [x] `cargo test -p mnote-web hermes_client_tools_uses_manifest_and_profile_disabled_state -- --test-threads=1`
实施记录:
- 2026-06-07Reasonix wrapper selftest 会读取 Rust `hermes_tools/manifest.rs`,校验 `mnote.knowledge_rag.*` manifest tools 与 wrapper `MNOTE_TOOL_NAMES` / `REASONIX_TOOL_TO_MNOTE_TOOL` 双向一致。
## 8. Phase Flegacy OCR / Convex media visible runtime 退役跟进
目标:LightRAG 成为资料库主线后,继续清掉仍可能被浏览器 runtime 或 worker 误读为 active fallback 的旧 OCR / Convex Files 链路。
Checklist
- [x] `/api/local-folder/events` 不再转发 `local_ocr.job.updated`resource tab 不再依赖 local OCR SSE 或 `synthetic_resource_write` 刷新 filetree。
- [x] UI preference 不再接受 `localOcr.*` 写入;历史 effective preference 强制为 `false` / retired。
- [x] FileTree 右键入口不再保留 `local-ocr` action alias;资料索引只走 `knowledge-rag-index``/api/knowledge-rag/ingest`
- [x] 浏览器 runtime 不再调用 `/api/media/sign` / `/api/media/upload`;旧 Convex Files 前端链路只标记 retired guard。
- [x] `AppState` 移除 `local_ocr_job_tx` / `local_ocr_active_jobs`,旧 local OCR helper 不再广播 runtime event。
- [x] local search incremental refresh 遇到 OCR sidecar 时只清理旧 evidence sqlite 投影,不再重建 OCR evidence。
- [x] `routes/local_ocr.rs` 从历史 HTTP job / MinerU runtime 实现收缩为历史 OCR sidecar 识别、frontmatter 解析和索引读取 helper;旧 ignored HTTP route tests 已移除。
- [x] resource tab 后台任务抽屉、toolbar 与 CSS 从 `mnote-local-ocr-*` 重命名为 `mnote-knowledge-rag-*`Knowledge RAG ingest/delete 不再挂旧 OCR DOM / function 名。
- [x] Page AI 目标包删除空实现的 OCR sidecar context enrichmentlocal-first agent 只携带当前文件/selection/allowed roots,不再保留 `ocrContext` / `ocrRootRelativePath` 注入点。
- [x] Sidebar 旧 `open-ocr-settings` / `toggle-ocr-tasks` / `data-local-ocr-*` 委托移除;历史 `.ocr/*.ocr.md` sidecar 仅以 `retired-ocr-sidecar:*` 资源身份打开,不再伪装为 active local OCR。
验证:
- [x] `cargo test -p mnote-web local_ocr -- --test-threads=1`
- [x] `cargo test -p mnote-web local_search_ocr_sidecar_is_hidden_after_lightrag_retirement -- --test-threads=1`
- [x] `cargo test -p mnote-web -- --test-threads=1`
- [x] `cargo test --workspace -- --test-threads=1`
- [x] `git diff --check`
- [x] `codegraph sync .` / `codegraph_status`
## 9. 归档条件
- delete retry / orphan doc guard 有单测覆盖,prune 不会隐藏未确认删除的 provider doc。
- `sourcePaths` 的 post-filter 语义在 API、skill、wrapper 和 tests 中一致。
- locator 不再用不匹配的 first positioned block 生成非 degraded page/bbox。
- degraded citation 的 agent / UI 文案统一。
- `task529` / `task530` / `task531` 不依赖已漂移的固定历史 LightRAG documents 数量或 doc id。
- 7-50 保持 `done`,本稿完成后移动到 `design/07-ai/done/`