graphify 增量更新与实体去重设计从全量重建到内容哈希驱动的增量知识图谱【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphifygraphify 将代码库连同文档、SQL 架构、配置文件与 PDF 解析为可查询的知识图谱。本文基于仓库中的设计文档 2026-05-04-incremental-updates-dedup-design.md系统讲解 graphify 的两大核心机制graphify extract的增量更新manifest 自动检测 内容哈希语义缓存与实体去重流水线熵门控 MinHash/LSH 候选生成 Jaro-Winkler 验证 并查集合并并结合 graphify/dedup.py、graphify/detect.py、graphify/cache.py、graphify/build.py 的实际源码说明设计如何落地、落地后又在哪些细节上偏离了原始方案。读完后你将掌握如何判断 graphify 当前运行处于全量还是增量模式、各缓存与阈值参数如何影响 token 成本与合并精度、以及去重流水线的每一步在源码中的对应位置。该设计文档的元信息为日期 2026-05-04关联 Issue #698增量更新与一项主动发起的实体去重需求无 Issue开发分支 v7。配套的 实现计划 将方案拆分为六个任务依赖引入、dedup.py新建、build.py接线、__main__.py增量改造、--dedup-llm平局裁决器、CHANGELOG 与版本号可作为逐任务对照实现顺序的参考。问题定义为什么需要增量与去重设计文档明确列出两个动机全量重建成本。graphify extract过去每次运行都从零重建整个图——无论文件是否变化都会把所有文件重新提交给 LLM。对于一个每日更新的 1000 文件 Markdown 语料库这是持续的高额 token 开销。缺乏语义级去重。LLM 提取是逐 chunk 进行的同一真实概念在不同 chunk 中可能得到不同标签如AuthManager、AuthenticationManager、auth_mgr。除了精确字符串归一化之外没有语义去重手段导致图中出现大量近重复实体。这两点分别对应文档中的 Feature 1Incremental Updates与 Feature 2Entity Deduplication二者独立实现、汇入同一条graphify extract流水线。每次graphify extract的完整流水线设计文档给出的每次运行的流水线如下原文骨架完整保留detect (full or incremental, auto-detected) ↓ AST extract (code files, AST cache-aware) ↓ Semantic LLM extract (doc/paper/image files, semantic cache-aware) ↓ build_merge (merge into existing graph, prune deleted nodes) ↓ deduplicate_entities (normalize → entropy gate → MinHash/LSH → Jaro-Winkler → community boost → optional LLM) ↓ cluster (full graph, always re-run) ↓ score_all god_nodes surprising_connections ↓ write graph.json .graphify_analysis.json manifest.json其中两个设计要点值得注意detect 阶段自动判断全量或增量调用方无需传 flag机制见下一节cluster 永远对全图重跑。即使只有 2 个文件变化社区划分也会基于合并后的完整图重新计算——因为单个文件的增删可能改变整个连通结构的社区边界。Feature 1增量更新自动检测机制设计规则极简当graphify-out/manifest.json与graphify-out/graph.json同时存在时进入增量模式不需要任何命令行 flag首次运行永远是全量。这一模式判定在实现中落在__main__.py的 extract 分支检查两个文件是否都存在是则调用detect_incremental否则调用普通detect。增量模式下的行为差异设计文档列出四点变更均可在源码中一一对应detect_incremental(target)替代detect(target)返回值多携带new_files、unchanged_files、deleted_files三个字段。只有new_files走 AST LLM 提取未变化文件完全跳过。build_merge(new_chunks, prune_sourcesdeleted_files)替代build_from_json——把新提取结果合并进已有图并修剪来自已删除文件的节点。manifest.json只在运行成功完成后写入。这一点是可靠性关键如果运行中途崩溃manifest 保持上一版本下一次运行的 diff 不会因此错位。detect_incremental的实际实现细节阅读 graphify/detect.py 可以看到实现比设计稿多了不少防御性细节快慢双路径mtime 未变且哈希匹配 → 直接判定 unchanged除了stat之外无磁盘 IOmtime 变化 → 再用 MD5 内容哈希与 manifest 中记录的哈希比对内容真变了才标记为 changed。双哈希字段manifest 条目区分ast_hash与semantic_hashkindsemanticextract 默认按语义提取记录判断kindast供graphify update纯 AST 路径使用。一个文件可能已被update提取过 AST 但尚未语义提取——此时semantic_hash缺失extract 会把它视为 changed。deleted 与 excluded 的区分manifest 中消失的条目会再按磁盘存在性分流——文件确实从磁盘消失才是deleted_files其缓存节点是幽灵需要修剪文件仍在磁盘上但被 ignore 规则或--exclude排除的只进excluded_files不会被当作删除处理。NFC 归一化与旧版 manifest 兼容manifest 键统一存 NFC 形式应对 macOS 上 NFD 路径同时兼容旧版仅存浮点 mtime 或{mtime, hash}的条目格式。--force/GRAPHIFY_FORCE1可以跳过增量 manifest 门控与语义缓存读取强制全量重扫见 graphify/main.py 的帮助文案。语义缓存全量与增量模式共用设计稿的语义缓存契约LLM 调用前check_semantic_cache(files)把文件列表切分为(cached_results, uncached_files)只有uncached_files送进extract_corpus_parallelLLM 调用后save_semantic_cache(fresh_results)以内容哈希为键文件重命名时缓存结果中的source_file更新为新路径与既有 AST 缓存同一模式。对照实现 graphify/cache.pycheck_semantic_cache确实按逐文件查缓存 → 命中则并入 nodes/edges/hyperedges、未命中进uncached列表的方式工作save_semantic_cachecache.py#L1379按source_file分组、每文件一条缓存条目存于cache/semantic/与 AST 条目的cache/ast/隔离以避免哈希键冲突。实现还引入了设计稿未展开的演进prompt 指纹命名空间缓存条目按提取 prompt 的指纹分代存储mode参数则提供semantic/semantic-{mode}命名空间提取 prompt 升级后会重新提取而不是重放旧版本结果partial 标记被截断的提取结果会打上partial: True标记读取时视为 miss保证不会用残缺缓存冒充完整结果。输出摘要格式设计稿定义的运行摘要保留原样[graphify extract] incremental: 20 changed, 980 cached, 2 deleted [graphify extract] graph: 4,821 nodes, 12,304 edges, 43 communities [graphify extract] tokens: 18,432 in / 6,201 out, est. cost: $0.08即每次运行都报告多少文件变化、多少走缓存、多少被删除以及本次真实消耗的 token 与估算成本——这让增量省了多少变成可直接验证的数字。Feature 2实体去重模块设计设计稿把去重收进单一职责模块graphify/dedup.py从图构建完成后调用返回去重后的(nodes, edges)。集成顺序明确要求去重运行在build_merge/build_from_json之后、cluster之前——顺序很重要图更干净社区检测质量更高# in build.py G build_merge(...) # or build_from_json G deduplicate_entities(G) # new step communities cluster(G) # unchanged这个顺序在实现中体现为 graphify/build.py 里build_merge的签名直接携带dedup: bool True与dedup_llm_backend: str | None None参数去重内嵌在构建流程中而不是外挂步骤。七步流水线与各步阈值步骤机制关键参数设计稿 → 实现常量1. 精确归一化大小写/标点归一后按归一化标签分组合并设计稿要求接线此前处于休眠状态的deduplicate_by_label归一化 小写 非字母数字折叠为空格2. 熵门控每字符 Shannon 熵 2.5 bits 的短歧义标签AI、DB、x跳过模糊匹配避免高风险自动合并_ENTROPY_THRESHOLD 2.53. MinHash LSH 分块3-gram 字符 shingle128 个置换阈值 0.7候选对生成 O(n) 而非 O(n²)设计稿称 1 万节点亚秒级_LSH_THRESHOLD 0.7_NUM_PERM 1284. Jaro-Winkler 验证每个候选对要求相似度 ≥ 0.92捕获拼写错误、复数、空格差异低于阈值丢弃_MERGE_THRESHOLD 92.0rapidfuzz 0–100 分制5. 同社区加分两节点共享同一 Leiden 社区 ID 时得分 0.05。设计稿特别指出这是 Graphify 相对 GraphRAG/LightRAG 的结构性优势——社区结构是强信号_COMMUNITY_BOOST 5.0百分制等值6. 并查集合并确认对喂入 union-find → 连通分量 → 每个分量合并为一个节点边重接到 survivor自环丢弃优先选更短的、无 chunk 后缀的 ID 作为 survivor_CHUNK_SUFFIX匹配_c\d$7. 可选 LLM 平局裁决--dedup-llmflag得分落在模糊区间设计稿为 0.75–0.85的对按 30 个一批、每批一次 LLM 调用裁决设计稿估算 1 万节点约 $0.01默认关闭实现签名dedup_llm_backend: str | None Nonededup.py 中 pass 3 仅在显式传入后端时执行实现相对设计稿的演化防误合并守卫群阅读 graphify/dedup.py 全文约 980 行可以发现最终实现比设计稿的七步骨架多了一整层宁可不合、不可错合的守卫源码注释中引用了大量后续 Issue 编号。这些守卫值得逐一点名代码节点完全豁免标签合并_is_code()检查file_type code。代码符号的身份是节点 ID编码了模块/类/符号的全限定路径标签只是显示名两个不同文件里的同名函数如平行实现的parse方法绝不能因字符串相似被合并。跨仓库硬性拦截deduplicate_entities开头检查节点是否来自多个 repo若是直接raise ValueError——不同项目的节点偶然共享标签名跨项目去重被明确禁用。短标签变体拦截_is_variant_pair、_short_label_blocked短标签 12 字符中的型号/版本兄弟M1vsM1 Pro、cranelvscranelr会因 Jaro-Winkler 前缀加分虚高得分除非是同长度、单字符替换这类真拼写错误否则一律拦截。数字 token 差异拦截_numeric_tokens_differ仅内嵌数字不同的长标签ADR 0011 §D5vsADR 0013 D4、block3vsblock13是带编号的兄弟实体而非重复直接放行禁止合并。前缀扩展拦截getActiveSessionvsgetActiveSessions这类一个标签严格是另一个的后缀扩展的对无论得分多高都不合并。内容词交换拦截_content_token_swap模板化兄弟标题Asset Contribution FlowvsAsset Consumption Flow共享大量样板词、仅一个内容词不同整串 JW 分数无法区分复述与换了实体名因此按 token 位置逐词裁决——换掉功能词/拼写变体可合并换掉内容词则拦截。文件锚定类型跨文件保护_crossfile_fileanchored_blockedrationale/document类型的节点身份锚定在源文件上平行模块的近相同样板文本跨文件不合并同文件内仍可合并。ID 冲突预去重正式流水线之前先按 ID 预去重survivor 由定义该 ID 的源文件碰撞排名决定而非到达顺序保证与 chunk 顺序无关同来源的落选记录缺失属性会被回填给 survivor。超边成员同步重接合并后不仅边端点重接hyperedge 的成员 ID 也原地重写到 survivor避免留下指向已合并节点的悬空成员。从源码结构看这些守卫的共同设计原则与 dedup.py 中一段注释的表述一致保留一个拼写变体优于制造一次虚构的合并never-merge-two-distinct-entities bar——即宁可少合并不可错合并。依赖选择从datasketch到自研_minhash设计稿与实现计划在New dependencies一节写明datasketch与rapidfuzz均加入[project.dependencies]基础依赖且明确排除sentence-transformers/ PyTorch。实际落地时有了一处可见偏离当前 pyproject.toml 中只有networkx3.4与rapidfuzz3.0没有datasketch。取而代之的是仓库内的 graphify/_minhash.py——一份 datasketch 兼容的 MinHash/MinHashLSH 直接替代实现。其模块 docstring 给出了原因datasketch.lsh在模块顶层导入 scipyscipy 的 import 链在某些企业 Windows 环境EDR 软件下会挂起数分钟因此改用自研实现覆盖 dedup 用到的 MinHash/MinHashLSH API 面哈希族梅森素数置换与 LSH 分带结构等价于 datasketch去重质量不变。rapidfuzz则按计划保留为正式依赖。build_merge侧的配套语义增量合并不是简单的追加新 chunk。graphify/build.py 的 build_merge 的 docstring 说明了几个关键行为它们共同保证增量多次运行结果一致重提取文件按层替换其旧贡献一个文件有 AST 与语义两个生产者层重提取某一层时只替换该层的旧节点/边另一层保持完整未出现在 new_chunks 中的文件原样保留删除文件通过prune_sources修剪且replace 优先于 delete——出现在 new_chunks 中的文件即使被调用方同时列进 prune_sources 也绝不修剪防止刚建的新节点被误删收缩守卫当既无 dedup 也无 prune 时合并结果节点数少于磁盘原图会直接抛错防止静默缩图超边跨运行携带未重提取也未删除的文件的 hyperedge 被携带进新图#1574否则每次增量更新会把超边集收缩为仅变化文件的那部分。测试策略设计稿在 Testing 一节列出的验证矩阵与仓库现有测试文件对应如下设计稿要求的测试仓库对应每个去重步骤的独立单元测试tests/test_dedup.py集成测试两个 chunk 重叠实体标签 → 输出图中单一合并节点tests/test_dedup.py实现计划中test_build_calls_dedup即此场景增量测试连续跑两次 extract断言第二次对未变化文件零 LLM 调用tests/test_incremental.py重命名测试重命名文件断言缓存命中且source_file更新缓存重命名语义见 graphify/cache.py 的source_file归一化机制删除测试删除文件断言其节点被修剪build_merge的prune_sources路径实现计划中的测试代码还展示了典型的断言风格例如精确重复应合并UserService/userservice/User Service三标签 → 一个节点、拼写变体应合并GraphExtractorvsGraph Extractor、无关标签不合并UserServicevsOrderService、低熵短标签不合并AIvsML、合并后边重接到 survivor 且自环丢弃。这些断言与 dedup.py 的流水线结构 一一对应。明确不做的事Non-goals设计稿划定的边界实现同样遵守不做--dedup embedMiniLM 余弦相似度——显式排除保持无 PyTorch 依赖不为graphify update纯 AST 路径增加增量支持——该路径已有 AST 缓存覆盖不做跨graph.json文件的去重合并两个图——列为独立特性。这一点也在 dedup.py 的跨仓库拦截中得到代码级体现需要去重全局图时必须先按仓库分别去重再合并。小结设计稿、计划与源码的对照这一对文档设计稿 实现计划展示了 graphify 典型的开发链路设计稿给出问题、流水线契约与参数取值实现计划按任务分解到文件与代码行Task 1 依赖 → Task 2 新建dedup.py 失败测试先行 → Task 3 接线build.py→ Task 4__main__.py增量改造 → Task 5 LLM 平局裁决器 → Task 6 版本与 CHANGELOG并附自检清单核对规格覆盖。落到源码可以提炼出三条对使用与二次开发都有价值的经验其一增量的正确性由 manifest 的仅在成功后写入与内容哈希兜底 mtime共同保证崩溃或时钟回拨都不会污染下一轮 diff其二去重的工程重点不在相似度算法本身而在防误合并守卫——熵门控、数字 token 差异、前缀扩展、内容词交换等守卫使宁可漏合成为默认姿态其三重依赖datasketch/scipy可以用小体积的 API 兼容自研实现替换在不改变算法质量的前提下消除环境陷阱。理解这三点就掌握了 graphify 增量更新与实体去重的全部设计意图与实现边界。【免费下载链接】graphifyTurn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store.项目地址: https://gitcode.com/GitHub_Trending/graph/graphify创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考