2026-06-07 10:35:21 +08:00
|
|
|
|
# 7-51 LightRAG post-commit hardening v1
|
|
|
|
|
|
|
|
|
|
|
|
> 创建时间:2026-06-07
|
|
|
|
|
|
>
|
|
|
|
|
|
> 状态:`done`
|
|
|
|
|
|
>
|
2026-07-28 17:04:27 +08:00
|
|
|
|
> 2026-07-03 口径回正:当前 runtime 已回到 OpenHub / native agent + LightRAG + Turso/libSQL。本文重新作为当前默认 LightRAG provider 的 hardening 基线;历史非 LightRAG 默认 provider 口径已标记 stale。
|
2026-06-26 20:01:02 +08:00
|
|
|
|
>
|
2026-06-07 10:35:21 +08:00
|
|
|
|
> Owner:07-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 A:Delete retry / orphan doc guard
|
|
|
|
|
|
|
|
|
|
|
|
目标:source 删除或 hash 变化后,即使 LightRAG 删除失败,也不能丢失重试 provider doc id;prune 不能隐藏仍可能参与回答的 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 id;LightRAG delete 失败时写入 `delete_retry_required`,成功提交后保持 `delete_submitted`,等 `/documents` 消失后才标记 `delete_completed` 并清空 doc id。
|
|
|
|
|
|
- 2026-06-07:当前 registry schema 先用 `lightRagStatus=delete_retry_required` 表达失败状态,不额外扩写错误 message 字段,避免扩大持久化迁移面。
|
|
|
|
|
|
|
|
|
|
|
|
## 4. Phase B:Source 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 过滤返回 references;provider 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 selftest:agent 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-07:manifest、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 C:Locator 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 D:Agent / 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 E:Reasonix / manifest drift guard
|
|
|
|
|
|
|
|
|
|
|
|
目标:在完整 7-47 capability registry 实施前,先降低 Reasonix wrapper 与 Rust manifest 的漂移风险。
|
|
|
|
|
|
|
|
|
|
|
|
Checklist:
|
|
|
|
|
|
|
|
|
|
|
|
- [x] 增加一个轻量 selftest:Rust manifest 中 `mnote.knowledge_rag.*` 三个工具必须都存在于 Reasonix wrapper 映射。
|
|
|
|
|
|
- [x] 增加一个反向 selftest:Reasonix 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-07:Reasonix wrapper selftest 会读取 Rust `hermes_tools/manifest.rs`,校验 `mnote.knowledge_rag.*` manifest tools 与 wrapper `MNOTE_TOOL_NAMES` / `REASONIX_TOOL_TO_MNOTE_TOOL` 双向一致。
|
|
|
|
|
|
|
|
|
|
|
|
## 8. Phase F:legacy 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 enrichment,local-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/`。
|