RAG 系统中的文档高频变更,如何维护向量索引?
RAG 系统中的文档频繁新增、修改和删除,会带来向量化成本、索引维护和数据一致性等问题。合理的设计需要先过滤无效变更,再按变更类型分别处理,并通过版本切换、异步清理和全链路对账保证稳定性。
一、文档变更带来的挑战
文档发生变化后,RAG 系统通常需要重新解析文本、切分分片、生成向量,再更新索引。如果每次变更都对整篇文档执行这套流程,文档越大、变更越频繁,计算和写入成本就越高。
更新过程还可能出现其他问题:
- 新旧版本同时出现在检索结果中;
- 更新中途失败,导致文档只写入了一部分;
- 删除后,旧内容仍然出现在搜索结果里;
- 大量写入和删除增加索引维护成本,影响查询表现。
因此,文档变更设计需要兼顾更新成本、检索时效和数据一致性。
二、先过滤无效变更
文档变更事件不一定意味着内容真的发生了变化。例如,文件可能只是改了名称、修改时间,或者调整了格式。对这类变化重新生成向量,通常没有必要。
1. 检查元数据
可以先检查文件大小、更新时间、ETag 或来源系统的版本号,快速识别未变化的文档。
元数据适合做初步判断,但不一定能证明内容相同。例如,文件可能在内容没有变化时被重新保存。对关键数据,可以再计算规范化文本的内容哈希,确认文本是否真正改变。
2. 检查内容指纹
对规范化后的文本计算哈希,可以识别内容完全相同的情况。也可以使用 SimHash 等方法检测近似内容,作为重复内容过滤的辅助信号。
近似指纹不等于语义等价判断,存在误判风险。如果跳过了实际有意义的修改,系统就会继续检索旧内容。因此,是否启用这层过滤以及如何设置阈值,需要结合文档类型和召回要求验证。
3. 使用消息队列缓冲变更
通过消息队列异步处理文档变更,可以把上游的突发写入和下游的解析、切分、向量化、索引写入解耦。
队列还可以配合去重和合并:同一文档短时间内连续变更时,只处理最新版本,避免对过期版本重复计算。
三、新增:按文档和版本写入
新增文档通常走标准的增量处理流程:
1 | 接收文档 |
每篇文档应有稳定的 doc_id,每次内容变化生成新的 doc_version。每个分片也要有自己的标识,例如 chunk_id,并记录所属文档、版本和向量模型版本。
这些字段可以帮助系统区分同一文档的不同版本,也便于后续定位、删除和对账。写入过程还应支持幂等,避免消息重复投递时产生重复数据。
四、修改:尽量只重算受影响的分片
修改文档时,最直接的方案是整篇重新切分和向量化。它实现简单,但如果大文档只改了一小段,就可能产生大量不必要的计算和写入。
可以通过增量差分缩小更新范围:
- 比较新旧文本,定位发生变化的段落或区域;
- 根据切分规则和分片重叠窗口,找出受影响的分片;
- 只为这些分片重新生成向量;
- 新分片完成并校验后,再切换到新版本。
分片边界可能随文本变化而移动。例如,在文档前部插入一段内容,后续分片也可能整体偏移。因此,计算差分时需要同时考虑切分规则、重叠窗口和边界变化。
当修改范围较大、文档结构变化明显,或差分计算本身已经不划算时,可以直接全量重建。具体阈值应根据文档大小、向量化成本和索引写入耗时,通过实际运行数据确定。
五、删除:先让内容不可见,再批量清理
删除通常分为逻辑删除和物理删除两个阶段。
1. 逻辑删除
收到删除事件后,先更新文档状态或可见版本,让查询不再返回该文档。这样可以尽快反映删除结果,不必等待向量索引完成物理清理。
查询路径必须实际使用删除状态或版本信息进行过滤。仅在元数据中标记“已删除”,但检索时没有检查这个标记,旧向量仍可能出现在结果中。
2. 物理清理
之后再异步删除相关分片和向量。可以合并多条删除任务,减少频繁操作索引带来的维护开销。
索引碎片和整理方式取决于具体向量数据库及其索引实现。系统可以根据数据库提供的能力,结合索引状态和查询表现,安排批量清理或索引重建。
对于有效期明确的数据,例如活动内容或每日资讯,也可以按时间或业务周期组织数据,过期后批量清理对应范围。
六、版本切换:保证查询只看到完整版本
如果直接覆盖旧数据,更新期间可能出现新旧内容混杂的情况。更稳妥的方式是先写入新版本,确认完整后再切换查询所使用的版本。
1 | 1. 写入新版本的全部分片和向量,标记为待发布 |
查询侧需要有明确的版本过滤规则,避免同一篇文档的新旧版本同时返回。实现方式可以是查询时过滤版本,也可以通过索引别名、集合切换或其他数据库能力实现,具体取决于存储系统。
并发修改可以使用乐观锁:更新时带上预期的旧版本号,只有版本匹配才允许提交。如果期间已经有新版本写入,就拒绝过期更新,并重新读取最新状态处理。
七、运维保障:对账与索引监控
在线写入链路难以覆盖所有异常。消息可能失败或重复,向量库也可能出现向量写入成功但元数据未更新等情况。因此,系统需要定期执行对账任务。
对账可以比较:
- 原始文档与文档元数据;
- 文档当前版本与向量库中的版本;
- 分片记录与实际向量;
- 向量索引与全文索引中的可见状态。
发现缺失、冗余或版本不一致时,可以按规则补写、重试或清理,并记录处理结果。
监控方面,除了写入成功率和队列积压,还可以关注:
- 文档从变更到可检索的耗时;
- 向量化和索引写入失败率;
- 查询延迟和检索结果中的旧版本比例;
- 孤立向量、缺失分片和待清理数据数量;
- 向量数据库提供的索引健康指标。
八、总结
处理 RAG 文档的高频变更,可以从四个方面入手:
- 过滤无效变更,减少不必要的解析和向量化;
- 按变更类型处理,新增走增量写入,修改尽量局部重算,删除先让内容不可见再异步清理;
- 通过版本切换保证一致性,新版本完整写入并校验后,再切换查询版本;
- 完善运维保障,通过幂等处理、失败重试、定期对账和索引监控发现并修复异常。
设计的关键,是让文档变化能够及时反映到检索结果中,同时控制向量化和索引维护成本。