仓库:sadofriod/graphrag-ts · npm 包:@ashes_born/graph-rag-ts
本文聚焦 graphrag-ts 中的增量构建实现,而不是泛指所有 GraphRAG 系统的增量索引方案。关键入口是 buildIncrementalRAG、startIncrementalBuild 与 resolveIncrementalFiles。
增量构建不会扫描输入目录推断“哪些文件消失了”。调用方传入的是本轮要检查的文件集合,系统只把这些文件与数据库中相同 namespace 的已有父记录比较:
MERMAID
因此,“本轮没传入的旧文件”不会被自动删除。需要删除时,应显式调用 deleteRAGDocument,按 parent ID 或 title 清理。
diffDocuments 只查询当前 namespace 的父记录,并按 title、createdAt 排序。比较逻辑分三步:
guide.md#Introduction。如果本轮文件集合中存在 guide.md,差异检测会将这些分节父记录归到原文件标题下,并按顺序用换行拼接内容后比较。content.trim() 计算 SHA-256。因而首尾空白差异不会触发重建,内部内容变化仍会触发更新。tsconst hash = new CryptoHasher('sha256') .update(content.trim(), 'utf8') .digest('hex');
该哈希由 computeContentHash 计算,用于判断内容是否变化,不是文档主键,也不代表数据库保存了唯一版本历史。
若没有待处理文件,构建返回空统计;若全部文件都被 skip,则直接返回跳过计数,不会重新检测社区。存在 insert 或 update 时,更新文件的旧 parent ID 会传给 excludeFromCommunityDetection:旧 claims 的 sourceParentId 与旧 edges 的 parentId 被设为 null。loadCommunityGraph 只读取 parentId 非空的边,所以这些旧边暂时不会参与本轮社区检测。
然后系统并行切分本轮所有待处理文件,将切分结果中的文本块、实体、边和 claims 持久化,重新运行 namespace 范围内的社区检测和摘要持久化,最后才删除 update 对应的旧 parent 记录。这不是仅重算受影响的几个社区:只要本轮存在待处理文件,社区检测就会重新运行。
顺序的目的,是避免更新前的旧来源和新来源同时作为有效社区输入。它同时意味着整个构建并非一个包住所有阶段的数据库事务;如果流程在旧记录清理前失败,部分旧关联可能已被置空,调用方应把失败纳入重试和修复策略。
清理旧 parent 前,pruneStaleDocuments 会再次将 claims 的 sourceParentId 和 edges 的 parentId 置空,然后删除 parent。数据库关系会级联删除该 parent 的 child chunks;claims 与 edges 则保留为没有父文档归属的记录。PruneDocumentResult 的 deletedClaims 当前返回 0,因为此操作不删除 claim 行。
需要特别注意边权语义:边按 namespace 和端点对 upsert,权重采用增量累加。更新文档时旧边会先退出社区图,但若新抽取产生相同端点对,upsert 会复用该边并增加权重,而不是精确扣除旧文档的贡献。因此当前实现提供的是文档级输入差异与重建流程,不是完整的来源级边权反向计数系统。若业务要求删除某个来源后严格重算权重,需要单独保存边的来源实例或采用可重建的聚合层。
增量 diff 的输入集合不是数据库快照:缺席的标题不会生成删除动作。显式删除走 deleteRAGDocument 的 { title, namespace } 或 parent ID 版本;按 title 删除会查出该标题的全部父记录,适用于删除一个普通文档。
对按标题切分的 Markdown,差异检测能把 file.md#heading 的既有父记录重新组合到 file.md 进行比较。删除时则应确认传入的 title 与实际持久化标题形态相符;若每节使用不同 title,按原始文件 title 查询未必能命中这些分节记录。
构建结果包含 insertedFiles、updatedFiles、skippedFiles,以及本轮生成的 parent、edge、claim 和 community 数。排查异常时可分别核对这些计数,而不要只看最终社区数。
仓库测试覆盖了首尾空白哈希一致、Markdown 分节父记录归组、skip/update/insert 分类、删除前解除来源关联,以及删除某标题下多个 parent 的行为:documentDiff.test.ts 和 documentPruner.test.ts。源码入口:documentDiff.ts、documentPruner.ts、buildRag/incremental.ts、buildRag/index.ts 和 startBuild.ts。