Skip to content

fix: 复用未变化文档的重解析产物#2000

Open
zlh123123 wants to merge 4 commits into
Tencent:mainfrom
zlh123123:fix/issue-1679-reparse-cache
Open

fix: 复用未变化文档的重解析产物#2000
zlh123123 wants to merge 4 commits into
Tencent:mainfrom
zlh123123:fix/issue-1679-reparse-cache

Conversation

@zlh123123

@zlh123123 zlh123123 commented Jul 10, 2026

Copy link
Copy Markdown

Description

本 PR 修复 #1679 中同一文档重解析时重复执行解析、向量化和 LLM 生成任务的问题。

此前,重解析会清理已有 Chunk 并重新创建派生数据。即使文档内容没有变化,系统仍会重复计算向量、图片 OCR/描述、问题和摘要,导致处理时间长、模型调用次数增加,Chunk ID 也无法稳定复用。

主要修改:

  • 为文本、父级文本、摘要、图片 OCR/描述 Chunk 以及自动生成的问题创建确定性的内容寻址 ID。
  • 将全量删除重建改为 Chunk 差异更新。系统复用内容未变化的 Chunk,只写入发生变化的数据,并清理失效的 Chunk 和索引。
  • 新增按租户、模型、配置和提示词隔离的缓存,覆盖向量嵌入、图片 OCR/描述、图谱抽取、Wiki Map、文档解析、摘要和问题生成。
  • 仅缓存能够独立复用的 ReadResult,避免复用依赖本地临时图片文件的解析结果。
  • Wiki 删除操作继续读取实时状态,缓存只保存可安全复用的 Map 新增结果。
  • 规范化 Chunk ID 和 LLM 缓存输入中会变化的图片存储 URL,同时保留图片字节哈希作为 OCR/描述缓存的失效依据。
  • 手动重解析任务直接使用 API 分配的 attempt,避免一次操作生成两个 attempt 或 Trace 长时间停留在运行中。
  • 限制问题 Chunk 的 source_id 长度,确保不超过 PostgreSQL VARCHAR(64)
  • 新增 PostgreSQL 67 号迁移,并同步补充 SQLite 迁移和初始化 Schema,共加入 5 张缓存表。

本次修改没有引入破坏性 API 变更。

Type of Change

  • 🐛 Bug fix
  • ✨ New feature
  • 💥 Breaking change
  • 📚 Documentation update
  • 🎨 Refactor
  • ⚡ Performance improvement
  • 🧪 Test
  • 🔧 Configuration / Build / CI

Related Issue

Fixes #1679

Testing

自动化测试

执行命令:

go test ./internal/application/service ./internal/application/service/retriever ./internal/application/repository ./internal/container ./internal/types ./internal/router -count=1 -timeout=20m

执行结果:

ok github.com/Tencent/WeKnora/internal/application/service
ok github.com/Tencent/WeKnora/internal/application/service/retriever
ok github.com/Tencent/WeKnora/internal/application/repository
ok github.com/Tencent/WeKnora/internal/container
ok github.com/Tencent/WeKnora/internal/types
ok github.com/Tencent/WeKnora/internal/router

完整 go test ./... -count=1 -timeout=20m 也已执行。除依赖本地 DocReader(localhost:50051)以及被 SSRF 策略拦截的 localhost HTTP 测试外,其余 Go 包通过;上述 6 个与本 PR 及合并冲突直接相关的包全部通过。

新增的 reparse_reuse_test.go 覆盖以下场景:

  • 向量缓存首次未命中、再次命中。
  • 缓存键按租户和模型指纹隔离。
  • Chunk ID 和问题 ID 在相同输入下保持稳定。
  • 图片存储 URL 变化时,缓存身份保持稳定。
  • Chunk 差异计算保留已有 ID,并清理失效数据。
  • 基础 Chunk 被复用时保留其派生 Chunk;基础 Chunk 失效时级联清理派生产物。
  • 向量服务返回数量不足时立即报错,避免保存 nil/空向量。
  • Wiki 更新使用实时状态处理删除操作。
  • 缓存产物压缩和解压可以完整往返。

手动验证步骤

  1. 创建测试文档 issue-1679-cache-validation-final.md,包含 2 个文本 Chunk 和 1 张内嵌截图。
  2. 启用父子分段、图片 OCR、图片描述、每个 Chunk 生成 3 个问题以及文档摘要。
  3. 清空对应缓存后执行一次冷缓存解析,记录 attempt 1。
  4. 不修改文档和配置,连续执行两次重解析,记录 attempt 2 和 attempt 3。
  5. 对比 Trace、缓存表记录数以及有效 Chunk 的 ID 汇总哈希。

性能与缓存命中结果

指标 冷缓存(attempt 1) 首次不变重解析(attempt 2) 再次不变重解析(attempt 3)
根流水线耗时 18.344 秒 2.659 秒 2.600 秒
基础 Chunk 复用数 / 新建数 3 / 0 3 / 0 3 / 0
向量嵌入阶段 781 毫秒,复用 0/2 7 毫秒,复用 2/2 10 毫秒,复用 2/2
图片 OCR/描述生成 13.677 秒,均未命中 32 毫秒,均命中 38 毫秒,均命中
问题批量生成 11.413 秒 38 毫秒 31 毫秒
摘要生成 14.780 秒 54 毫秒 33 毫秒
最终状态 已完成 已完成 已完成

两次不变重解析分别耗时 2.659 秒和 2.600 秒。向量、图片 OCR/描述、问题和摘要均复用了已有结果。

冷缓存首次写入后,后续两次重解析没有增加缓存记录:

缓存表 冷缓存完成后 attempt 2 完成后 attempt 3 完成后
embedding_caches 104 104 104
image_multimodal_caches 2 2 2
reparse_artifact_caches 14 14 14

attempt 2 与 attempt 3 的有效 Chunk 数量和 ID 汇总哈希一致:

Chunk 类型 数量 ID 汇总哈希
text 2 7d03b5a81e9e80ccdc44429903b71b04
parent_text 1 9150bad92950b7fc3a37068c19e09468
image_ocr 1 8470948c90c571fd5b3901478bb1d011
image_caption 1 2519e97fd74ed1914e931ef139a0a095
summary 1 874daf7be1e9385a2543f994b3282292

大文档补充验证

  • 对一个未修改的 34,769 字节 Markdown 文件执行重解析,文件包含 65 个文本 Chunk。
  • 差异计算复用 70 个 Chunk、新建 0 个 Chunk,并清理 1 个失效的派生 Chunk。
  • 首次填充缓存写入 65 条文本向量缓存,并保存 1 条 document_parse 解析产物。
  • 第一次大文档问题生成受到模型服务每分钟 10 次请求的限流影响;最终的小文档验收连续完成,llm_failed=0

环境检查

手动验收环境(迁移重编号前):schema_migrations version=65, dirty=false
cache tables present: 5/5
WeKnora-app: healthy
当前分支迁移顺序:65 tenant_api_keys -> 66 expand_knowledge_span_name -> 67 reparse_caches
git diff --check: clean

Checklist

  • make fmt && make lint && make test pass locally(未宣称全量通过,见下方说明)
  • Self-reviewed the code
  • Added/updated tests covering the change
  • Updated related documentation (README, docs/, Swagger annotations, etc.)(本次无仓库文档变更)
  • Breaking changes are clearly called out in the description above(本次无破坏性变更)

Screenshots / Recordings

本次没有修改用户界面。以下截图用于展示重解析配置和缓存命中后的 Trace 结果:

  1. issue1679_final_trace.png:attempt 3 的 Trace 页面,5/5 个阶段全部完成,问题生成耗时 31 毫秒,摘要生成耗时 33 毫秒。
Trace 验证截图
  1. issue1679_final_warm_config.png:不变重解析使用的配置,包括 qwen 多模态处理和每个 Chunk 生成 3 个问题。
重解析配置截图

Copilot AI review requested due to automatic review settings July 10, 2026 14:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses #1679 by making reparse of unchanged documents reuse previously computed artifacts (chunks, embeddings, VLM OCR/caption, wiki map, graph extraction, DocReader parse, summary, and question generation) via deterministic chunk IDs, diff-based chunk updates, and tenant/model/config/prompt-scoped caches.

Changes:

  • Add new cache tables (PostgreSQL migration v65 + SQLite migrations/init schema) and repositories/types for embedding, image multimodal, graph extraction, wiki map, and generic reparse artifacts.
  • Introduce stable, content-addressed IDs and hash normalization utilities to enable chunk reuse and cache key stability across reparses (including canonicalizing volatile image URLs).
  • Update ingestion/postprocess stages to use cache lookups + upserts, and add tests for reparse reuse behavior and artifact compression round-trips.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
migrations/versioned/000065_reparse_caches.up.sql Adds PostgreSQL cache tables + indexes for reparse reuse.
migrations/versioned/000065_reparse_caches.down.sql Drops the PostgreSQL cache tables.
migrations/sqlite/000001_reparse_caches.up.sql Adds SQLite cache tables + indexes for reparse reuse.
migrations/sqlite/000001_reparse_caches.down.sql Drops the SQLite cache tables.
migrations/sqlite/000000_init.up.sql Adds cache tables to the SQLite initial schema.
internal/types/task.go Extends manual processing payload to carry an API-allocated attempt.
internal/types/reparse_cache.go Defines GORM models for the new cache tables.
internal/types/interfaces/reparse_cache.go Adds repository interfaces for cache access.
internal/types/interfaces/chunk.go Adds “list all chunks” + permanent delete methods to chunk interfaces.
internal/types/embedding.go Adds support for passing precomputed embeddings through indexing flow.
internal/container/container.go Registers new cache repositories in the DI container.
internal/application/service/wiki_ingest.go Wires wiki ingest service to use wiki map cache repository.
internal/application/service/wiki_ingest_batch.go Implements wiki map cache hit path and live-state reconciliation for deletes.
internal/application/service/retriever/keywords_vector_hybrid_indexer.go Consumes precomputed embeddings and exports embedding helpers.
internal/application/service/reparse_reuse.go Adds hashing/ID utilities, cache key builders, and diff planning helpers.
internal/application/service/reparse_reuse_test.go Adds tests covering cache keys, stable IDs, diff planning, and artifact gzip round-trip.
internal/application/service/knowledge.go Injects embedding/artifact cache repos into knowledge service.
internal/application/service/knowledge_process.go Switches to diff-based chunk writes, stable IDs, and adds caching for parse/summary/questions/embeddings.
internal/application/service/knowledge_create.go Passes attempt through manual processing enqueue path.
internal/application/service/image_multimodal.go Adds VLM OCR/caption caching, stable IDs for image-derived chunks, and cached embeddings for indexing.
internal/application/service/extract.go Adds graph extraction caching keyed by chunk/config/model fingerprints.
internal/application/service/chunk.go Implements “list all” and permanent delete chunk service methods.
internal/application/repository/reparse_cache.go Implements GORM repositories for the new cache tables.
internal/application/repository/chunk.go Implements DB-side list-all and hard-delete chunk repository methods.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread internal/application/service/knowledge_process.go
@zlh123123 zlh123123 changed the title 复用未变化文档的重解析产物 fix: 复用未变化文档的重解析产物 Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: 重建/重解析知识时应复用 OCR、Embedding、Wiki Map 等缓存,避免全量重算

2 participants