跳到主要内容

你的 Embedding 流水线是关键基础设施——请像对待主数据库一样对待它

· 阅读需 11 分钟
Tian Pan
Software Engineer

大多数团队把 embedding 生成当作一次性的 ETL 任务:跑一个脚本、填充向量数据库、然后就不管了。这在演示中行得通,在生产环境中却是慢动作式的灾难。你的向量索引不是一个静态的产物——它是一条持续运行的流水线,有自己的故障模式、数据新鲜度保证和运维手册。与主数据库不同的是,它出问题时没有任何异常会被抛出。系统照样返回结果,只是这些结果悄悄地、自信地错了。

如果你在运行一个检索增强生成(RAG)系统、语义搜索功能,或任何依赖 embedding 的产品,你的向量索引值得获得与 PostgreSQL 集群同等的严谨对待。以下是大多数团队在这件事上犯错的原因,以及生产级 embedding 基础设施究竟应该是什么样子。

没有人监控的故障模式:静默语义漂移

传统基础设施的故障是显而易见的。数据库宕机,错误率飙升;磁盘写满,写入开始失败。这些问题你的监控系统早就能捕获。

Embedding 流水线的故障则是悄无声息的。最主要的故障模式不是"系统宕机",而是"系统正常运行,返回看似合理的结果,但这些结果是错的"。这背后有几个原因:

  • 源文档发生了变化,但 embedding 依然过时。 某个商品下架了、某项政策更新了、某位客户升级到了高级套餐——源数据变了,但索引中的向量仍然代表旧版本。你的检索系统自信满满地返回着过时信息。
  • 局部重新 embedding 导致向量空间几何不一致。 你在更新后对一部分文档重新做了 embedding,但剩余的向量是用不同的预处理逻辑或更旧的模型检查点生成的。现在你的向量空间里混杂着来自两个不同坐标系的点,最近邻搜索的排序结果因此变得毫无意义。
  • 预处理变更悄然传播。 有人更新了 HTML 过滤逻辑、调整了 Unicode 归一化方式,或修改了文本分块边界。没有测试失败,没有告警触发。但输入 embedding 模型的 token 序列已经不同了,生成的向量也悄悄地占据了向量空间中不同的区域。

最隐蔽的地方在于,每一次单独的检索依然会返回文档,而且看起来还挺合理。传统的请求级监控——延迟、错误率、吞吐量——显示系统一切正常。质量退化只有在有人手动核查结果质量时才会暴露,或者等到用户开始抱怨"AI 最近表现很奇怪"的时候。

Embedding 是物化视图,不是静态导出

最重要的思维转变:把你的 embedding 看作源数据的物化视图,而不是一次性的导出结果。关系数据库中的物化视图通过成熟的刷新机制与源表保持同步,你的 embedding 索引需要同样的契约。

这意味着索引中的每个向量都应该携带溯源元数据:

  • 来源标识符 —— 一个稳定的 ID,指向原始文档
  • 来源版本或校验和 —— 让你知道这个向量代表的是文档的哪个版本
  • 流水线配置哈希 —— 生成该向量所使用的 embedding 模型版本、分块策略和预处理步骤
  • 时间戳 —— 该向量最后一次计算的时间

没有这些元数据,你就无法回答最基本的运维问题:"这些向量是最新的吗?它们都是用同一个模型生成的吗?改了分块逻辑之后,哪些需要重新 embedding?"如果你无法回答这些问题,你拥有的不是基础设施,只是一个快照。

CDC 到 Embedding 的架构

按照计划对整个语料库进行批量重新 embedding,相当于每天晚上把数据库的表删掉再重建一遍。在小规模下可以运行,到了数千万文档的量级就根本行不通——成本爆炸,流水线赶不上 SLA。

生产环境下的模式是用变更数据捕获(CDC)驱动增量 embedding 更新:

  1. 在源头捕获变更。 使用 CDC 工具(Debezium、DynamoDB Streams、PostgreSQL 逻辑复制,或其他适合你技术栈的方案)从主数据存储中流式传输文档的增删改操作。
  2. 只对增量内容计算 embedding。 你的 embedding 服务接收变更事件,只为被修改的文档生成向量。未变更的文档保留其现有向量。
  3. 原子性地 upsert 到向量存储中。 用新向量替换过时向量,并显式处理删除操作——索引中的孤儿向量是导致不相关结果的隐性来源。
  4. 将新鲜度作为指标追踪。 测量从源文档发生变更到其 embedding 被更新之间的延迟,并为这个延迟设置 SLO,就像你为数据库集群的复制延迟设置 SLO 一样。

这种架构赋予你与关系数据库相同的可信性保证:当真相来源发生变化时,衍生的表示会在有界的时间窗口内完成更新。区别在于,没有人会质疑 PostgreSQL 副本是否需要保持同步,但团队却经常让向量索引在不知不觉中漂移数天乃至数周。

Embedding 模型漂移:重建索引的难题

即使解决了数据新鲜度问题,你仍然面临模型漂移的挑战。Embedding 模型会更新。你为了更好的领域性能对新版本进行了微调。提供商推出了新模型,导致你现有的向量不再兼容。不同模型版本产生的向量空间本质上是不同的坐标系——无法混用。

这带来了一个棘手的运维问题:当你更换 embedding 模型时,必须对整个语料库重新做 embedding。对于一个包含十亿条目的集合,这意味着数天的 GPU 计算。目前有三种应对策略:

蓝绿索引是最安全的方案。在旧索引(蓝)旁边构建一个全新的索引(绿)。等绿索引完全填充并验证通过后,原子性地切换流量。这在迁移期间会让存储成本翻倍,但可以实现即时回滚和零停机切换,本质上是将数据库迁移的最佳实践应用于向量索引。

Drift-Adapter 是来自近期研究的新技术,它训练一个轻量级的变换层,将新的查询 embedding 映射到旧版本向量空间中。该方法能以不到 10 微秒的额外查询延迟恢复 95-99% 的检索召回率,与全量重新索引相比,计算成本降低超过 100 倍。其缺点是这只是一种近似——适合桥接相邻模型版本之间的短暂过渡期,但不能永久替代重新 embedding。

双写是一种对冲策略:新进入的文档同时用旧模型和新模型做 embedding,同时在后台用历史文档填充新索引。这可以在可能漫长的迁移期间保持新索引的数据新鲜,代价是更高的写入延迟和更复杂的流水线。

无论选择哪种策略,前提条件都是一样的:你需要对 embedding 存储进行版本管理。如果你无法识别每个向量是由哪个模型版本生成的,就无法安全地规划和执行迁移。

真正能发现问题的监控

标准的应用监控会遗漏 embedding 特有的故障,你需要一套专门的可观测性体系:

哨兵文档的余弦距离。 维护一组 100 个以上、具有已知良好 embedding 的样本文档。定期对这些文档重新做 embedding,并将新向量与存储的向量进行比较。在稳定的系统中,余弦距离应该低于 0.005。如果超过 0.02,说明你的流水线某个环节发生了变化——立即调查。超过 0.05 则意味着严重漂移,很可能是某个模型或分块变更悄悄溜了进来。

基准查询的最近邻稳定性。 每周用 20 个以上的固定查询针对你的索引进行测试,追踪相邻两周 top-10 结果的重叠率。健康的系统应该有 85-95% 的重叠率。低于 70% 意味着质量正在主动下滑——你的用户一定已经注意到了,即使他们还没提工单。

向量数量核对。 比较索引中的向量数量与源文档数量。无法解释的差值要么意味着孤儿向量(源文档已删除,embedding 仍然存在),要么意味着缺失向量(源文档存在,embedding 从未生成)。两者都会损害检索质量。

新鲜度百分位。 追踪你的 embedding 的年龄分布。如果你的 p99 embedding 年龄是 30 天,但源数据每小时都在变化,你的索引根本无法服务于你的业务场景。根据领域需求定义新鲜度 SLO——新闻搜索引擎需要分钟级,知识库可能可以容忍小时级。

你的向量数据库需要的运维手册

你的 PostgreSQL 集群有针对故障切换、复制延迟、磁盘压力和索引损坏的运维手册。你的向量数据库也需要同等级别的文档:

  • 过时 embedding 的处理流程。 如何识别哪些文档过时,触发针对性的重新 embedding,并验证修复效果。如果 CDC 流水线出现积压,升级路径是什么?
  • 模型迁移流程。 蓝绿切换的分步操作,包括验证标准(黄金测试集上的召回率)、回滚触发条件,以及切换流量的确切命令。
  • 局部损坏恢复流程。 如果一部分向量损坏了(预处理出错、输入被截断、索引过程中的硬件故障),如何识别受影响的向量、重新 embedding 并验证完整性?
  • 全量重建索引流程。 当一切手段都失败时,完整的重新索引需要多长时间?计算成本是多少?能否在不停机的情况下完成?如果你对最后这个问题的回答是"我们还没测试过",那你的系统还没有达到生产就绪的标准。

判断标准很简单:如果全量回填让你感到恐惧,说明你还没有建立真正的基础设施。生产级系统会把重建索引当作常规操作——版本化的索引、离线验证、原子性流量切换。这件事应该枯燥无聊,而不是英雄主义式的救火行动。

生产级是什么样子

综合以上内容,一条生产级 embedding 流水线具备以下属性:

  • 每个向量都携带溯源信息。 来源 ID、来源版本、模型版本、流水线配置哈希和时间戳。索引中不存在匿名向量。
  • 变更自动传播。 CDC 或等效机制确保 embedding 在定义的新鲜度 SLO 内保持最新。
  • 模型迁移是原子性的。 蓝绿索引或等效方案提供零停机切换和即时回滚能力。
  • 监控能捕获语义漂移。 哨兵重新 embedding、邻居稳定性和数量核对持续运行,并在用户察觉之前触发告警。
  • 运维手册存在且经过演练。 团队知道如何处理数据过时、索引损坏、模型迁移和全量重建索引——并且已经演练过每种场景。

你使用的 LLM 可以替换,检索模型可以替换,不可替换的是底层的数据基础设施:血缘追踪、新鲜度保证、版本化的 embedding,以及让你的系统值得信赖的运维手册。这才是真正的竞争优势——而这恰恰是大多数团队会跳过的部分,因为它看起来像乏味的数据工程工作,而不是令人兴奋的 AI 开发。

你的向量索引是一个数据库,请开始把它当数据库来对待。

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