跳到主要内容

嵌入漂移问题:语义搜索的静默退化

· 阅读需 10 分钟
Tian Pan
Software Engineer

你的语义搜索很可能正在悄然恶化,而你的监控面板对此毫无显示。

没有错误日志,没有 p99 毛刺,没有健康检查失败。查询依然返回结果,余弦相似度评分依然看起来正常。但相关性正在一点一点地悄然下滑——每一个被遗漏的新词,都在拉大用户语言与嵌入模型训练语言之间的距离。

这就是嵌入漂移问题。它之所以难以察觉,正是因为它不产生任何可见的失败信号——只有检索质量的缓慢侵蚀。用户会说产品"越来越没用了",然后悄悄离开。

嵌入漂移的成因

嵌入模型是语言的冻结快照。当你使用 text-embedding-3-largebge-large-en-v1.5 对语料库进行嵌入时,你实际上是在将某一时刻的语义关系编码为向量。模型没有任何机制来更新它对世界语言演变的理解。

在生产环境中,三种不同的衰退机制共同加剧了这一问题:

语义漂移是最隐蔽的一种。词义随时间变化,新概念不断涌现。在"氛围编程(vibe coding)"或"MCP 服务器"成为常用词汇之前训练的模型,对这些术语没有有意义的表征。包含新行话的查询会被映射到近似发音相似或上下文相邻的概念向量——这些概念来自训练分布,可能与用户真正想要的相去甚远。研究表明,在快速演进的技术领域,基于一月份语料训练的嵌入模型在应用于六月份文本流时,检索准确率可能下降 15–20%。

数据漂移更为机械。你的源文档在变化——产品被重命名,策略被更新,新内容持续加入。向量索引中的文档是在某个时刻完成嵌入的,如果文档在索引后发生变更,存储的向量反映的仍是旧版本。检索系统根据过时内容找到文档,用户看到的是陈旧信息被当作相关结果呈现出来。

模型漂移由你自己的基础设施决策触发。当你将嵌入模型从一个版本升级到另一个版本时,两个版本的向量空间在几何上是不兼容的。用新模型编码的查询,在用旧模型构建的索引上会返回毫无意义的结果——即便两个模型的向量维度完全相同。你可能在部署后才意识到这一点,那时用户已经在反映搜索"坏了"。

失败的沉默

嵌入漂移在生产环境中之所以危险,正在于它的非灾难性失败方式——这与大多数基础设施故障截然相反。

数据库宕机时,请求会报错;模型端点超时时,告警会触发;而嵌入漂移蔓延时,查询依然完成,向量数据库依然返回结果,相似度评分依然看起来合理。没有异常可以捕获,没有阈值可以突破。

失败只体现在检索相关性上——而这个指标(如果有被监控的话)是通过用户行为信号来衡量的,这些信号噪声大、延迟高,且极易被归因于其他原因。搜索结果点击率下降?可能是 UX 问题。零结果率上升?可能是查询分布偏移。会话时长下降?可能是季节性因素。嵌入漂移很少得到应有的关注,也很少被追责。

一个实际后果是:构建语义搜索的团队往往直到某位领域专家进行针对性测试,或者某个支持升级暴露出一个显而易见的检索失败,才会发现系统已经退化。而此时,退化往往已经积累了数月之久。

在用户发现之前检测漂移

金丝雀查询集是最可靠的早期预警机制。设置方法很直接:在索引构建时,挑选 50–200 个有代表性的查询——覆盖核心使用场景、领域特定术语,以及任何你明确知道对检索质量至关重要的词汇。记录每个查询的预期 top-k 结果。然后定期自动运行这组查询(每日通常已足够),并测量预期结果与实际结果之间的重叠度。

重叠度的下降是你的主要信号。四周内从 90% 降至 75%,说明索引与查询之间的关系已经发生了变化。它不能告诉你为什么,但它给了你一个明确的调查触发点。

对于超出金丝雀集范围的更广泛覆盖,可以监控生产查询中相似度分数的分布。绘制 top-1 相似度分数随时间变化的直方图,若出现左移——即分数整体下降——则表明查询正在找到置信度较低的匹配。这可能反映词汇差距(新词映射不准确)或数据漂移(原本匹配良好的文档已被更新版本取代)。

另外两个被严重低估的信号:

  • 查询改写率:用不同措辞重新发起相似查询的用户,往往在暗示第一次结果没用。在会话层面追踪这一指标,可以给你一个独立于任何标注标签的检索质量行为度量。
  • 概念质心漂移:定期计算索引中与某一特定概念或类别相关联的所有向量的质心,并追踪该质心随时间的移动。在稳定语料库中质心突然移动,通常意味着新文档以不同的语义表征被摄入——这往往是因为你所在领域的术语已经发生了变化。

渐进式重建索引:避免全量重建

应对嵌入漂移的显而易见的方法是对所有内容重新索引,显而易见的问题是这代价高昂。曾有团队报告每周对 1TB 语料库重新嵌入,仅嵌入 API 费用每月就高达 12,000 美元,还未计入重建索引的计算开销。

替代方案是选择性重建索引——只重建那些实际发生漂移的索引部分。有几种可行方案:

差值评分计算文档现有嵌入与使用当前模型新鲜计算的嵌入之间的余弦距离。若距离超过某个阈值(0.05–0.10 是常被引用的实践值),该文档即被标记为需要重建索引。有效语义表征未发生变化的文档则保持不变。这种方法通常以全量刷新成本的 30–60% 维持检索准确率。

元数据驱动的失效更简单,通常也更实用。在索引时为每个文档嵌入打上嵌入模型版本和文档最后修改时间戳的标签。当模型版本或文档发生变化时,使存储的向量失效,并将其加入重新嵌入队列。这不能解决未变更文档中的词汇差距,但能自动处理最常见的衰退来源——模型升级和文档更新。

优先级分层刷新将重建索引预算分配给对检索质量最重要的文档。如果你有金丝雀查询集,你就知道哪些文档在关键查询的 top-k 结果中频繁出现。从这些文档开始刷新。很少被检索的长尾文档可以容忍更高的陈旧度,而不会对产品质量产生实质影响。

管理模型升级过渡

升级嵌入模型是语义搜索系统中风险最高的操作之一,因为它会同时使整个索引失效。以下几种模式可以让这一过程更加平稳:

蓝绿索引在迁移任何流量之前,先在现有索引旁边构建新索引。新模型将所有文档嵌入到一个并行索引中。新索引完成后,用金丝雀查询集对两个索引进行检索质量对比。只有在新索引通过测试后,才迁移流量。这会在迁移窗口期间使存储成本翻倍,但提供了清晰的回滚路径——这是原地重建索引无法提供的。

版本标记命名空间是一种轻量级变体。使用在索引名称中编码嵌入模型版本的命名约定(例如 articles_bge_v1_5 vs articles_bge_v2)。将新嵌入写入新命名空间,同时保持旧命名空间活跃。在验证期间将一小部分生产流量路由到新索引。当新索引通过质量门控时,将其提升为主索引。

金丝雀晋级门控将嵌入模型升级视同软件部署。在金丝雀集上定义最低可接受检索质量——比如与预期结果 85% 的重叠率。如果新模型未能达到这一阈值,则阻止索引晋级。这能捕捉到一类情况:某个模型升级在基准测试中提升了平均性能,但在你特定领域词汇上出现了退化。

设定合理的刷新频率

多久重建一次索引没有放之四海而皆准的答案。合适的频率取决于你的领域词汇演进速度,以及你能容忍多大程度的检索质量损失才会影响用户体验。

一个实用的框架:

  • 高速演进领域(新闻、产品目录、支持工单):预期在数周内出现有意义的语义漂移。这类系统适合在文档更新时进行增量重建索引,加上每月一次的全语料库定期验证。
  • 中速演进领域(内部文档、代码仓库、研究资料):漂移需要数月积累。每季度评估一次金丝雀集,结合差值评分触发的部分重建索引,通常已足够。
  • 低速演进领域(法律档案、学术文献、历史记录):语料库本身变化缓慢。主要风险来自新查询带来的词汇差距,而非数据漂移。如果持续监控查询侧信号,每年重建一次索引是合理的。

模型升级的决策逻辑有所不同:在决定升级前,先用金丝雀集同时测试新旧模型。如果新模型在你的领域上没有改善检索质量,就不要升级——通用数据集上的基准提升不能保证在你的特定工作负载上同样奏效。

结语

嵌入漂移是一个伪装成构建问题的维护问题。大多数团队在选择嵌入模型和索引构建时的分块方式上想得很仔细,却很少考虑接下来 18 个月里语言围绕这个索引不断演变会发生什么。

务实的做法是将嵌入视为易腐的工件,而非稳定的基础设施。首先建立可观测性——金丝雀查询集、相似度分数分布、查询改写率——这样在用户行为告诉你出了问题之前,你就已经有了信号。然后将重建索引流程设计得具有选择性和成本效益,而不是依赖定期全量重建——这在规模化后往往难以为继。

那些能长期维持检索质量的系统,不是因为一开始选择了最好的嵌入模型,而是因为构建了在漂移变得用户可见之前就能检测并纠正它的工具链。

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