跳到主要内容

嵌入模型更迭:当你的提供商悄然导致整个向量索引失效

· 阅读需 10 分钟
Tian Pan
Software Engineer

你花了数周时间构建检索流水线。分块策略已调整,相似度阈值已校准,用户反馈看起来很积极。然后,在某个周一的早晨,在你没有任何部署的情况下,检索质量开始下降。以前能搜出正确文档的查询,现在返回的却是关联度极低的噪音。没有错误日志。没有异常。流水线运行顺畅。

发生变化的是你的嵌入(Embedding)提供商更新了模型。你的整个向量索引——那些费尽心力嵌入的数百万个文档——现在填充的是来自一套坐标系统的向量,而这套系统与你的查询编码器生成的向量已不再匹配。结果不是系统崩溃,而是不可见的垃圾数据。

为什么不同模型的嵌入向量不能混用

嵌入模型将文本映射到高维向量空间中。语义被编码为几何图形:相似的概念聚集在一起,关系被捕捉为方向上的接近。但这种几何结构并不是通用的,它特定于创建它的模型。

当你切换嵌入模型时——即使是同一提供商的更新版本——你也在完全改变坐标系统。由不同模型版本生成的、代表相同文本的两个向量,可能会指向完全不同的方向。它们之间的余弦相似度(Cosine similarity)毫无意义。你是在比较不兼容空间中的距离。

当两个模型版本共享相同的输出维度时,这种失效模式尤为隐蔽。如果你从 text-embedding-ada-002(1536 维)切换到另一个同样输出 1536 维向量的版本,基础设施层面不会发生任何损坏。你的向量数据库会毫无怨言地接受新的查询向量。近似最近邻(Approximate nearest neighbor)结构——如 HNSW 图、IVF 集群——是围绕旧模型的几何结构构建的,但它们不会拒绝来自新模型的向量。它们只是返回了“错误邻里”中的邻居。

结果是:查询静默地返回看似合理但在语义上错误的搜索结果。用户得到的回答看起来像是检索出来的,但实际上并非基于他们的真实文档。你不会在错误率中看到这一点,但最终你会在用户信任度上看到它的影响。

你无法控制的提供商更新时间线

主要提供商的弃用周期往往比许多团队的产品路线图还要短。OpenAI 已于 2025 年 1 月起弃用 text-embedding-ada-002,停用窗口将于 2025 年 6 月结束。Cohere 在 2025 年 1 月弃用了其用于分类的默认 Embed 模型端点。HuggingFace 的 sentence-transformers 库在 v5.4 版本中更改了仅解码器(decoder-only)模型的默认池化策略,静默地改变了受影响架构的向量语义。

这些并非特例。它们是你无法控制的基础设施上的标准操作。

关键区别在于两种类型的模型变更:

  • 已公告的弃用(Announced deprecations):提供商会给你一个迁移窗口(通常为六个月)。你仍然需要重新索引,但你有准备时间。
  • 静默更新(Silent updates):提供商保留在不更改端点名称或不提前通知的情况下升级、微调或更换底层模型的权利。大多数 API 服务条款都明确允许这样做。

第二种类型是最危险的。你的索引是针对 text-embedding-large-preview-2024-03(或该端点目前指向的任何模型)构建的。明天它可能会指向别的东西。你的系统中没有任何变化,但语义空间中的一切都变了。

团队通常在替换发生数周后,当回归评估开始失败、产品经理注意到搜索质量下降,或者用户抱怨聊天机器人似乎忘记了如何查找相关文档时,才会发现这个问题。

在用户发现之前检测问题

如果你不持续衡量检索质量,在问题变得显而易见之前,你将无法检测到嵌入模型的漂移——这意味着问题已经发生一段时间了。

一个最小化的检测设置包含三个组件:

模型身份日志记录(Model identity logging)。每一次嵌入 API 调用都应记录实际使用的模型版本。许多提供商在响应元数据中返回此信息。将其与你的嵌入向量一起存储。如果今天查询响应中的模型标识符与索引中存储的不符,则说明发生了不匹配。这是成本最低的预警系统。

检索质量基准(Retrieval quality baselines)。在任何计划内或计划外的模型变更之前,你都需要一个基准。一套“黄金查询集”:50 到 200 个带有已知相关文档的查询。每周运行一次。跟踪平均倒数排名(Mean Reciprocal Rank)或 NDCG。在没有部署的情况下,如果指标突然下降,则直接指向嵌入层的问题。

统计漂移检测(Statistical drift detection)。如果你定期嵌入同一组哨兵文档(sentinel documents),并将生成的向量与存储的参考嵌入进行比较,你就可以检测到嵌入函数何时改变了行为。工具如 Evidently AI 将此作为核心指标。当分类器尝试区分参考嵌入和当前嵌入时,如果 AUC 接近 0.5,则意味着分布稳定;如果 AUC 向 0.7 攀升,则意味着发生了某些偏移。

这些实施起来并不复杂。难点在于团队通常将嵌入向量视为“一次性写入”的产物。一旦向量进入数据库,就会被认为是稳定的。事实并非如此。

发生重索引时的策略

当你检测到嵌入模型发生变化时——或者当计划中的迁移迫使你做出改变时——你有几种方法,每种方法都有不同的风险特征。

原位替换是最危险的。你停止写入,重新嵌入所有文档,覆盖现有向量,然后恢复。在这个窗口期,你的索引是旧向量和新向量的混合体:这是一个混合向量空间,其中邻近项是在不兼容的坐标系中计算的。即使你很快完成操作,也没有回滚路径。如果新模型降低了针对你特定数据分布的检索质量,你将无路可走。

蓝绿索引是以存储空间为代价的最安全方法。在旧索引(蓝色)保持运行并提供查询服务的同时,使用新嵌入模型构建一个全新的索引(绿色)。在迁移期间,将传入的数据双写到两个索引中,以保持绿色索引的实时性。一旦绿色索引完成回填并经过验证,就切换查询路径。如果质量下降,立即切回。为了回滚能力,翻倍的存储成本几乎总是值得的。在 800 万份文档的规模下,重新嵌入任务会运行数小时且可能中途失败——你需要能够干净地中止并恢复。

带有特性标志(Feature Flags)的影子索引是生产就绪的折中路径。在现有列旁边添加一个新列或索引(embedding_v2)。通过具有检查点功能的后台任务进行回填,以便在崩溃后恢复——存储最后一次成功嵌入的文档 ID,这样你就可以在不重新处理的情况下恢复。应用特性标志来控制查询路径使用哪个嵌入列。一旦回填完成且在具有代表性的查询集上验证了质量,就翻转标志。回滚是瞬时的。在任何用户受到影响之前,验证都是可能的。

任何迁移方法都适用三个工程要求:

  • 幂等性:在已处理的文档上重新运行任务不会损坏索引。
  • 检查点:迁移进度是持久化的,因此无需从头开始即可恢复失败。
  • 版本标记:数据库中存储的每个向量都带有其模型版本作为元数据,因此你始终知道它属于哪个空间。

版本固定与自托管的权衡

解决由供应商控制的嵌入模型变动的最彻底方案是自己控制嵌入模型。自托管的开源模型——部署在你自己的 GPU 基础设施上——为你提供精确的版本固定:特定提交的特定模型权重,未经你的明确操作绝不会改变。

这用一种运维负担换取了另一种。你获得了确定性并消除了供应商端静默变更的风险。你承担了基础设施维护、安全补丁以及查询时的扩缩容工作。

对于大多数产品团队来说,混合方法更有意义:使用具有严格日志记录和经过测试的迁移手册的托管 API,同时保留在供应商更新严重降低你的使用场景质量时进行自托管的能力。

如果你必须使用托管 API,最小可行保护措施包括:

  • 记录来自每个 API 响应的模型版本,而不仅仅是你在请求中指定的模型名称。即使你指定了特定的端点,供应商也可能路由到不同的底层模型。
  • 维护一套黄金评估集并定期运行。通常每周一次就足以在漂移影响到用户之前捕获它。
  • 保留前一个索引版本,直到你获得两到四周的迁移后信号。相对于检测不到的质量退化所带来的成本,存储成本是廉价的。

最近的一个研究方向——嵌入翻译(Vec2Vec)——已经证明向量可以在模型空间之间高保真地翻译,在不同架构之间实现与原生嵌入超过 0.9 的余弦相似度。当全面重索引不可行时,这是一个有希望的后备方案,但它不能替代主动的版本控制。翻译会引入自身的近似误差,且可靠执行该操作的模型和基础设施仍在成熟过程中。

能够抵御模型变动的运维态势

能够无事故处理嵌入模型更新的团队都有一个共同的姿态:他们将嵌入视为版本化的、可审计的产物,而不是一次性的中间结果。

实际上,这意味着数据库中的每个向量都携带元数据:生成它的模型版本、时间戳以及生成它的文档版本。你始终保持检索质量指标,而不仅仅是在发布时。你定期进行重索引演练,这样当供应商宣布弃用时,执行迁移是一个熟练的过程,而不是紧急情况。

核心见解是,你不仅仅是在构建软件。你是在一个你无法控制的资源之上进行构建——任何供应商都可以更改、弃用或静默更新模型。工程上的对策不是阻止这种情况发生,而是为你的系统配备监控,以便你立即检测到它,在用户受到影响之前验证质量,并在不中断服务的情况下执行迁移。

嵌入模型变动不是一种理论风险。它是一个预定的必然事件。唯一的变量是当它到来时,你是否已经做好了准备。

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