跳到主要内容

嵌入刷新问题:像数据库工程师一样运营向量存储

· 阅读需 11 分钟
Tian Pan
Software Engineer

你的RAG流水线正在返回自信、格式良好的答案。大模型的响应看起来很好。然而用户却不断提交工单,说系统给出了错误信息。产品经理调出相关文档——信息六周前就已经更改,但向量索引仍然反映旧版本。没有任何错误抛出,没有任何告警触发。系统只是悄无声息、毫无察觉地给出了错误答案。

这就是嵌入刷新问题,它最终会咬到大多数生产RAG系统。对生产部署的分析显示,超过60%的RAG故障可追溯到知识库中陈旧或过时的信息——不是错误的提示词,不是检索算法失败,而是向量索引中的内容与源数据真实状态之间的简单错位。大多数AI工程师都是吃了亏才发现这个问题的,而大多数数据工程师早就知道如何预防它。

令人不安的真相是:向量存储就是一个数据库。它与任何其他数据存储有相同的维护要求:写入需要传播,删除需要被遵守,模式变更需要迁移策略。不同之处在于,大多数构建RAG系统的团队来自机器学习和AI背景,而非数据工程背景——他们将ML心智模型(训练一次、部署、完成)应用于一个需要完全不同框架的基础设施问题。

为什么嵌入会过期(以及为何难以察觉)

根本原因很简单:源数据在变化,但向量索引不会自动更新。

一个文档被修改,但索引中的嵌入仍然反映旧版本。一个产品下线了,但幽灵向量仍然出现在搜索结果中。一项政策改变了,关于该政策的查询开始检索法律上不正确的上下文。所有这些都不会产生错误。检索仍然"成功"——只是检索到了错误的内容。

多种力量加速了这个问题:

时间漂移是最常见的。财务报告、合规规则、产品规格和API文档都有保质期。从这些文档生成的嵌入捕获的是一个快照,随着时间的推移,其准确性会降低。时间嵌入研究表明,在不刷新的情况下,随着索引老化,对时间敏感查询的检索准确率会下降15-20%。

删除滞后更加微妙。当你从源系统中删除一个文档时,对应的向量通常会留在索引中。HNSW和其他基于图的ANN结构不支持干净的删除——常见的解决方法是使用过滤标志进行软删除,但这种方法会随时间膨胀索引,如果你需要保证删除的数据真正消失,还会产生合规风险。

模型版本漂移是最具破坏性的。当你的嵌入模型提供商发布新版本模型,你想要升级时,所有现有向量都会变得不兼容。它们是在不同的几何空间中计算的。用v3嵌入查询v1嵌入的索引不会抛出错误——它只会返回无意义的结果。

进程内架构失败是一个独立的类别。在库模式(进程内)下运行Chroma的开发者会发现,每个工作进程都加载自己的内存索引副本。当一个工作进程摄入新文档时,其他进程完全不知道。解决方法是以服务器模式运行向量存储,但团队通常只在生产环境中发生过时事件后才发现这个需求。

一次写入反模式

大多数团队在摄入时对语料库进行一次嵌入,并将索引视为静态基础设施。在原型阶段,当语料库较小且变化缓慢时,这种方法运行良好。但在生产环境中,它会崩溃。

一次写入反模式有三个症状:

  1. 不频繁的批量刷新 ——团队每晚或每周运行一次完整重新索引,接受在此窗口期间提供的每个答案都有数小时的过时时间
  2. 没有新鲜度监控 ——当源数据和向量索引之间的差距超过可接受范围时,没有告警
  3. 架构刚性 ——因为增量更新从未被设计进去,刷新索引的唯一路径就是完整重建

一个团队每周对1TB语料库进行完整重新索引,仅嵌入API调用就花费12000美元/月。这个成本迫使他们认真思考这种方法是否可持续。它不可持续,这指向了一种不同的架构。

完整重索引 vs. 增量更新

正确的策略取决于你的语料库如何变化以及你能容忍多少过时。

完整重索引给你带来一致性和干净的索引结构。它在操作上很简单——你按计划重建索引并替换进去。缺点是成本(与语料库大小成正比)、延迟(索引重建需要时间,在此期间你从旧版本提供服务),以及一个根本问题:你的新鲜度上限受到你能多频繁进行重建的限制。

增量更新只针对已更改的文档。挑战在于HNSW和类似的基于图的索引针对查询而非频繁变更进行了优化。动态插入需要添加节点和更新邻居连接,随着图结构变得不平衡,索引质量会随时间下降。在高插入量下,查询性能会以不明显的方式下降,除非进行仔细监控。

混合方法 ——在定期完整重索引之间进行增量更新——是大多数成熟团队最终采用的生产模式。当更改发生时进行增量写入,在低流量期间(通常是每周或每月)安排完整重建以恢复索引健康。这在新鲜度和结构完整性之间取得平衡。

选择标准大致对应于:

  • 语料库很少变化,规模小:按计划进行完整重索引效果很好
  • 语料库频繁变化,可以容忍最终一致性:增量更新加上定期完整重建
  • 语料库实时变化,新鲜度SLA以分钟计:变更数据捕获流水线提供持续增量更新

向量索引的变更数据捕获

解决实时新鲜度问题的数据工程模式是变更数据捕获(CDC)——与数据库工程师用于在系统间复制变更的相同模式。

在向量索引上下文中,CDC的工作方式如下:源系统中的每次插入、更新或删除都会发出一个变更事件。流水线消费这些事件,重新嵌入受影响的文档,并将新向量写入索引。源变更和索引更新之间的差距从几小时缩短到几秒。

这听起来很简单,但有操作上的细节。同步流水线需要处理:

  • 排序保证 ——紧接着删除的是更新文档的重新插入,必须按顺序处理,否则重新插入的文档会得到一个过时的向量
  • 幂等性 ——如果流水线重试一个失败的事件,重新嵌入相同的文档不应产生重复向量
  • 背压 ——如果源系统产生大量变更(批量导入、模式迁移),嵌入流水线需要吸收负载而不崩溃
  • 事务一致性 ——如果源数据库更新和向量索引更新需要是原子的(它们通常需要),你需要outbox模式或带有补偿逻辑的双写

对于以关系数据库作为数据源的团队,pgvector值得考虑:通过在与源数据相同的Postgres实例中存储嵌入,你可以将源更新和嵌入更新包装在单个事务中,完全消除一致性差距。

在用户之前检测漂移

嵌入过时的问题是它会静默降级。你需要在用户注意到之前检测索引何时偏离了源数据的监控工具。

新鲜度跟踪是最简单的信号:对于源系统中的每个文档,跟踪它上次嵌入的时间,并将该时间戳与上次更新时间进行比较。在新鲜度SLA之外的文档百分比是你应该在仪表板上监控的数字。

嵌入漂移检测衡量索引中向量的统计分布是否相对于基线发生了变化。最可靠的生产方法是当前向量样本质心与历史基线之间的欧氏距离。余弦距离也适用,但产生的告警阈值不够稳定。余弦空间中漂移分数超过0.05-0.10通常表明分布发生了有意义的变化,值得调查。

检索质量探针是你知道正确答案的黄金路径查询。按计划运行这些查询并监控已知良好文档的检索排名是否在下降,这给你提供了过时影响的直接衡量,而非代理指标。

针对ANN结构的索引健康指标可以补充全貌:软删除向量的比例(应保持在10-15%以下)、P95查询延迟(随着索引碎片化而下降)以及召回率@k(通过将结果与样本上的平面穷举搜索进行比较来近似)。

在模型版本迁移中存活

升级嵌入模型是一次写入反模式最危险的地方。旧向量和新查询存在于不兼容的几何空间中。用v3查询搜索v1索引不会失败——它只会返回垃圾结果,没有任何错误信号。

朴素的方法是在切换之前用新模型重建整个索引。在规模较大时,这既慢又昂贵,而且需要一次硬切换,不可避免地会产生服务中断窗口。

并列列模式避免了切换问题:

  1. 保留旧嵌入列并为新模型的向量添加新列
  2. 运行后台任务重新嵌入文档并填充新列,限速以避免耗尽API配额
  3. 在完全切换之前,将一定比例的影子流量路由到新列以验证质量
  4. 一旦验证通过,使用功能标志将实时查询路径切换到新列
  5. 在稳定窗口后清理旧列

这种方法将原本需要维护窗口的迁移扩展为零停机的48小时过程。

漂移适配器是一种研究方法,适用于对完整语料库重新嵌入确实不可行的情况。它训练一个轻量级转换层,将新模型的查询嵌入映射到旧模型的向量空间,允许未更改的索引服务来自新模型的查询。质量恢复相当可观(使用低秩仿射变换可恢复95-99%的召回率),计算成本大约比完整重索引少100倍。这不是永久解决方案,但可以争取时间。

数据工程师的思维方式

向量存储的操作纪律几乎直接映射到数据工程师已经了解的关系数据库知识:

  • 将向量索引视为派生数据存储,而非数据源。数据源存在于你的操作数据库中。索引是必须保持同步的物化视图。
  • 为写入而设计,而不仅仅是读取。如果你的索引流水线无法处理持续更新,你构建的基础设施只能进行批量刷新,你的新鲜度上限由计算预算决定,而不是你实际的新鲜度需求。
  • 对模式进行版本管理。当你升级嵌入模型时,把它当作数据库模式迁移——在过渡期间双写,在切换读取之前验证新模式,保持回滚能力。
  • 监控源数据和索引之间的差距,而不仅仅是查询延迟。一个返回结果很快但提供过时内容的向量存储并不满足其可靠性要求。
  • 明确设计删除操作。如果你需要保证删除的文档不出现在搜索结果中(GDPR删除、产品下线、文档撤回),你需要一个删除传播流水线,而不仅仅是在查询时过滤的软删除。

RAG系统失败不是因为检索算法错误。它们失败是因为算法搜索的数据是错误的。构建良好RAG系统的AI工程问题,其核心是在规模上维护同步派生数据存储的数据工程问题。早点弄清楚这一点的团队能够交付更可靠的系统。将向量索引视为一次写入产物的团队则会在生产环境中艰难发现这一点,那时证据早已过期。

Let's stay in touch and Follow me for more thoughts and updates