Skip to content

fix(knowledge): 修复 #9 —— 知识库文档全链路七处失败(上传/解析/向量化/删除/图谱) - #13

Open
hxxxi-malog wants to merge 4 commits into
larry-zy:mainfrom
hxxxi-malog:fix/knowledge-9-pipeline
Open

fix(knowledge): 修复 #9 —— 知识库文档全链路七处失败(上传/解析/向量化/删除/图谱)#13
hxxxi-malog wants to merge 4 commits into
larry-zy:mainfrom
hxxxi-malog:fix/knowledge-9-pipeline

Conversation

@hxxxi-malog

Copy link
Copy Markdown

概要

Fixes #9。一份 9.81 MB / 26 页 PDF 走完 上传 → 处理 → 删除 → 图谱生成 全链路暴露的七处问题逐一修复,另带两处前端可靠性修复(两个提交:f70fbcc 链路可靠性主体、989182f 上传限制统一与失败原因):

  • 上传被 Nginx 413 拒绝:Nginx、Go、前端三处上限统一为 10 MiB——client_max_body_size 10m、Go 侧 maxKnowledgeFileSize、前端 MAX_KNOWLEDGE_FILE_SIZE(原 50MB),超限文件在浏览器侧即被过滤并提示,不再打到后端。
  • Go 连不上 Python gRPC:gRPC server 从 127.0.0.1:50051 改监听 0.0.0.0,容器网络可达(原 Python 容器健康但不可访问)。
  • 解析依赖缺失 + PDF 延迟读取失败:生产镜像 uv sync 补装 --extra document 解析依赖(ARM64 无预编译 wheel 时临时引入 build-essential、装完即清);PDFParser 将文件路径直接交给 pypdf.PdfReader,由 pypdf 管理延迟读取的文件生命周期,不再因 with 块提前 close 报错。
  • Embedding 超单次 10 条限制返回 400:按 10 条分批调用并保序合并,批内返回数量不匹配即报错;首次写入前按真实向量维度自动建 Milvus collection + HNSW 索引并 load(互斥锁防并发上传重复建表)。
  • Milvus collection 不存在导致删除 500:删除向量前先 HasCollection 预检,缺失按幂等语义直接成功,真实查询/删除错误仍返回给调用方重试。
  • 图谱生成 JSON 截断且接口假成功:抽取按 4 块小批次调用 LLM,finish_reason=length 或 JSON 解析失败时对半缩批重试(直至单块),各批按归一化名称合并去重节点/关系并合并来源 chunk,记录 token 用量与截断日志;整库重建改用新增的 ReplaceGraph——Neo4j 单事务内"删旧图 + 写新图",失败保留原图不再清空;前端重建请求超时单独设为 5 分钟(全局默认不变)。
  • 页面只显示"失败"无原因:新增 knowledges.error_message 列(迁移 000004,含 up/down);异步解析、gRPC、分块乃至 panic 失败均持久化真实错误摘要(Python 侧 parse_document 也不再硬编码 success: true);失败状态改用独立 10s context 更新,处理超时后文档不再永远停留在 processing;列表/详情/状态接口透出,前端在状态标签下展示。
  • 另修复 GraphView 崩溃:vis-network 会接管容器 DOM,loading 遮罩移到容器外层兄弟节点,避免 Vue patch 以 insertBefore(null) 崩溃。

测试

  • Go 新增 12 个测试函数:图谱批次切分 / 截断缩批重试 / 合并去重(document_processor_graph_test.go 5 个)、collection 自动初始化与幂等删除(vector_projection_test.go 5 个)、10 MiB 边界校验与失败原因保留透传(knowledge_base_handler_test.go 2 个)。
  • 前端 vitest:rebuildGraph 5 分钟超时不影响全局配置、默认上传上限 10 MiB,改动相关 13 例通过。
  • Python:test_document.py 覆盖解析结果 success/error 如实透传与 PDF 路径解析生命周期,6 passed / 1 skipped。
  • 验证命令:go test -short ./internal/handler/ ./internal/service/knowledge/npx vitest run(相关 spec)、uv run pytest tests/test_document.py 均通过。

变更注意

  • 迁移:新增 000004_add_knowledge_error_message(up/down),部署时需执行。
  • 配置:Nginx client_max_body_size 与 Python 生产镜像依赖安装变化(--extra document),需重新构建镜像。

- Python gRPC 改为监听容器网络地址,生产镜像安装文档解析依赖并修复 PDF 延迟读取生命周期
- Embedding 按 10 条分批保序调用,Milvus 缺失 collection 删除幂等且首次写入自动初始化
- 图谱抽取按小批次处理,JSON 截断时缩批重试并合并去重节点、关系与来源 chunk
- 图谱重建在 Neo4j 单事务中替换旧图,失败时保留原图;前端请求超时调整为 5 分钟
- 持久化并展示文档处理错误摘要,修复 vis-network 画布容器与 Vue loading 遮罩冲突
- 将 Nginx、Go 与前端知识库上传上限统一为 10 MiB,并同步提示文案
- 增加前后端上传大小边界回归测试,超限文件在浏览器侧直接拦截
- 异步解析、gRPC 与分块失败时持久化真实错误摘要
- 失败状态更新改用独立短时 context,避免处理超时后文档停留在 processing
@larry-zy

Copy link
Copy Markdown
Owner
  1. document_processor_service.go (RebuildKnowledgeBaseGraph ~:1579 / replaceRebuiltGraph) — the rebuild response reports naccurate node/relation counts. resp.TotalNodes += len(graph.Node) accumulates per-document, pre-dedup counts, whereas what actually lands in Neo4j is the cross-document merged/deduplicated graph from mergeGraphExtractionResults. Whenever entities/relations recur across documents,he returned TotalNodes/TotalRelations overstate what was written. Worse: when failedDocuments > 0, replaceRebuiltGraph intentionally skips the replacement (old graph preserved), yet resp still reports ProcessedDocuments/TotalNodes/TotalRelations as usual — so the API returns a "success, 120 nodes" response while the graph was not modified at all. A caller (or the frontend will read this as a successful rebuild. Suggest computing counts from the actual write result, or surfacing a replacement skipped due to document failures" flag in the response.

  2. document_processor_service.go:1238-1240 — the global vectorInitMu serializes every vectorization's first step across a Milvus RPC. ensureVectorCollection's fast path is a single HasCollection RPC (returns early if the collection exists), but the lock wraps the entire call. That means the first step of every vectorization, across all KBs and all concurrent uploads, queues on one global mutex plus a Milvus metadata round-trip. Once the collection exists the lock guards nothing yet becomes a throughput bottleneck for concurrent uploads. Suggest a double-checked "already initialized" in-memory flag (skip both lock and RPC when
    known-present), or a per-kbID lock.

Verdict: approve / mergeable — worth fixing item 1 (observability / misleading response); item 2 is a good follow-up concurrency optimization.

- 仅对既有结构体做字段与 tag 对齐,无语义变化(git diff -w 为空)
- 补建响应的节点/关系计数改为跨文档合并去重后实际写入 Neo4j 的数量,不再逐文档累加虚报
- 新增 graph_replaced 字段:存在失败文档或合并结果为空时跳过替换并保留旧图、计数归零,不再把未写入伪装成成功
- 前端 RebuildGraphResult 同步新增字段,成功文案按是否实际替换分支展示,「新增」改为「写入」
- collection 初始化改为 sync.Map 双检缓存:已初始化时免全局锁与 HasCollection RPC,首次建表仍全局串行防重复建表
- 新增回归测试:跨文档去重计数、失败/空图跳过替换、缓存命中零 RPC、并发仅一次建表
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.

知识库文档全链路存在多处失败(上传 / 解析 / 向量化 / 删除 / 图谱生成)

2 participants