fix(knowledge-graph): KG Build 管线七项级联缺陷端到端修复 — 从根因到下游全链路优化 - #523
Merged
ThreeFish-AI merged 3 commits intoMay 12, 2026
Conversation
修复 KG Build 日志中暴露的 ~740 条事务级联崩溃 + 全部下游阶段跳过问题。 日志现象:20 chunks 耗时 18 分钟,317 实体仅同步 22 个、444 关系同步 0 条, PageRank/Community/Summary 三阶段全部因 empty_graph 跳过。 七项正交修复(按依赖顺序): Issue 0(根因):sync_entity_from_knowledge 的幂等 SELECT 移除 entity_type 列 条件,与 UNIQUE 约束 uq_kg_entity_corpus_name(corpus_id, canonical_name) 对齐; 命中已有记录时按 type_precedence 更新 entity_type,消除 UniqueViolationError "第一块多米诺骨牌"。 Issue 1(防御层):batch_sync_from_graph_build 中每条 entity/relation 操作 包裹在 async with db.begin_nested() 中(SAVEPOINT),单条失败仅回滚该 savepoint,杜绝整批级联崩溃(Kleppmann DDIA §7.3)。 Issue 2(容错):_execute_build 在 PageRank/Community/Summary 三阶段开始前 各加 session 健康检查(in_transaction → rollback),防御前序阶段失败 污染 session 状态。 Issue 3(LLM 退避):抽出 _compute_retry_backoff() 统一函数,检测 524/ timeout 采用递增退避(30s/60s/90s + jitter),普通错误指数退避。 KG_LLM_TIMEOUT_SECONDS 默认 300→110(低于 Cloudflare 120s Proxy Read Timeout),让应用层先于代理斩断连接。 Issue 4(实体质量):新增 _GENERIC_ENTITY_STOPWORDS frozenset + is_noise_entity() — 过滤泛化术语(CSS/HTML/JSON/agent/spec/UI/...)、 URL、日期字符串、文件名、源码引用、过短/过长实体。RegexEntityExtractor 复用同一函数;LLM prompt 显式约束避免提取噪声。 Issue 5(可见性):chunk_processing 起止日志升至 INFO,含 chunk_index/ total_chunks/elapsed_ms/mode(5 种路径区分);小批次(≤50 chunks) 进度上报间隔 5s→2s。 Issue 6(降级):CommunitySummarizer 当 community_entities 为空但实体表 非空时,调用 _load_all_entities() 加载 Top-200 实体作为单一全局社区 生成 level=0 摘要,确保 GraphRAG Global Search 仍有召回基线。 测试:669 个 knowledge 单元测试通过(test_extraction_llm_plan 1 个失败为 pre-existing 与本次改动无关);新增 SAVEPOINT mock 支持(conftest + test_graph_entity_service)。 详细 RCA 记录于 docs/issue.md ISSUE-081。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
二轮 review Plan 实施完整度后发现的三处遗漏,本次一并补齐: 缺口 A(Issue 2 补漏):service.py syncing 阶段(emit_phase 调用前)缺少 session 健康检查,可能在 resolving 异常时让 shared_session.begin() 重抛 "Can't operate on closed transaction"。补加 `if shared_session.in_transaction(): await rollback()` 与下游三个阶段保持一致。 缺口 B(Issue 2 补漏):graph_algorithms.py 的 compute_pagerank 和 compute_communities 入口未做防御性 session 检查。Plan 原文明确要求 "在 export_graph_to_networkx 调用前添加 session 状态检查"——对外部调用方 (非 service.py 主链路)同样有保护作用。 缺口 C(Issue 5 补漏):emit_phase 未输出前一阶段 elapsed_ms。补加 phase_timing 闭包字典记录 prev_name / prev_started_at,在 graph_phase_started 日志中追加 prev_phase + prev_phase_elapsed_ms 字段,便于排查各阶段 性能瓶颈(如 extracting 80s vs syncing 0.5s)。 测试:knowledge 单元测试 669 通过(同 pre-existing 1 个 test_extraction_llm_plan 失败与本次改动无关)。 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
- extractors: 新增 JS/TS 框架白名单短路放行 _FILE_NAME_PATTERN 避免 Node.js / Vue.js / Three.js / Next.js / Express.js 等 高价值产品实体被误判为文件名过滤 - strategy: 移除宽泛 try/except Exception: pass,将 is_noise_entity import 上提到模块顶部,恢复异常反馈信号 - graph_algorithms: compute_pagerank / compute_communities 改用 try/except PendingRollbackError 仅在事务 invalid 时回滚, 避免对调用方健康事务产生破坏性副作用 🤖 Generated with [Claude Code](https://github.com/claude), [CodeX](https://openai.com), [Gemini](https://github.com/apps/gemini-code-assist) Co-Authored-By: Aurelius Huang<threefish.ai@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
empty_graph跳过。核心变更
按依赖顺序的 10 项正交修复(7 项主修复 + 3 项二轮 review 补齐):
sync_entity_from_knowledge幂等 SELECT 移除entity_type列条件,与 UNIQUE 约束uq_kg_entity_corpus_name(corpus_id, canonical_name)对齐;命中已有记录时按 type_precedence 更新entity_type,消除 `UniqueViolationError` "第一块多米诺骨牌"。batch_sync_from_graph_build每条 entity/relation 操作包裹在async with db.begin_nested()中(SAVEPOINT),单条失败仅回滚该 savepoint(Kleppmann DDIA §7.3)。_execute_build在 syncing/pagerank/communities/summaries 四阶段开始前各加 session 健康检查(in_transaction → rollback);graph_algorithms.py的compute_pagerank/compute_communities入口同步做防御。_compute_retry_backoff()统一函数,检测 524/timeout 采用递增退避(30s/60s/90s + jitter),普通错误指数退避;KG_LLM_TIMEOUT_SECONDS默认300 → 110(低于 Cloudflare 120s Proxy Read Timeout)。_GENERIC_ENTITY_STOPWORDSfrozenset +is_noise_entity()— 过滤泛化术语、URL、日期、文件名、源码引用、过短/过长实体;RegexEntityExtractor复用同一函数;LLM prompt 显式约束避免噪声。emit_phase输出prev_phase_elapsed_ms便于排查阶段瓶颈。CommunitySummarizer当community_entities为空但实体表非空时,调用_load_all_entities()加载 Top-200 实体作为单一全局社区生成 level=0 摘要,确保 GraphRAG Global Search 仍有召回基线。风险与回滚
sync_entity_from_knowledge的语义——已有记录若 entity_type 不同会按 precedence 升级;不会下降。KG_LLM_TIMEOUT_SECONDS默认值从 300 降至 110——极端复杂 chunk 可能触发更频繁的 fallback,但断路器 + co-occurrence fallback 保证图谱非空。git revert 393a90cc fbb947db即可;无 DB schema 变更,无数据迁移。验证证据
test_extraction_llm_plan失败为 pre-existing 与本次改动无关);新增 SAVEPOINT mock 支持。sync_result实体/关系计数对齐 +build_duration_ms显著降低。影响范围
apps/negentropy/src/negentropy/knowledge/graph/下 5 个核心源文件(community_summarizer / entity_service / extractors / graph_algorithms / service / strategy)+ 测试支持。docs/issue.md新增 ISSUE-081 详细 RCA 条目。Next Best Action
prev_phase_elapsed_ms找出真正瓶颈阶段。export_graph_to_networkx优先从 AGE 图读取(当前只读一等公民表,Issue 7 — 长期方案)。chunks_fallback/circuit_breaker_opened指标曲线。🤖 Generated with Claude Code