Embedding 模型轮换是数据库迁移,而非代码部署
在某个预发布(staging)频道里,一位工程师写道:“将嵌入模型(embedder)升级到 v3,新模型在 MTEB 上的得分提高了 4 分,冒烟测试通过后合并。”两天后,客服工单开始陆陆续续出现,反馈搜索结果感觉“莫名其妙地不对劲”。一周后,检索精度下降了 14 个百分点,余弦相似度分数从 0.85 暴跌至 0.65 左右,而且没人能解释原因——因为这次部署看起来与过去五次模型升级完全一样。这根本不是一次普通的部署。而是一次披着部署外衣的数据库迁移。
嵌入模型轮转是 AI 基础设施中最容易被归类错误的变更类型。它通过与提示词(prompt)微调或生成模型版本更新相同的渠道进入你的系统——配置文件、PR、CI 检查——因此它遵循配置变更的治理流程。但从底层来看,新的嵌入模型并不会产生旧向量的更好版本。它产生的向量完全存在于不同的坐标系中,跨两个流形计算余弦相似度是一个范畴错误(category error)。正确的心理模型不是“升级依赖版本”,而是“在提供读取服务的同时,为一个拥有 5000 万行的表更换主键编码”。
那些将其视为普通部 署的团队会在切换过程中发现这一点,通常首先是从用户端发现。而那些将其视为迁移的团队会构建影子索引(shadow index)、运行双重查询、在切换别名前测量一致性,并让旧索引保持一周的热切换状态,以备需要回滚。这两类团队的区别不在于技术复杂程度,而在于是否在第一次提到该问题的冲刺规划(sprint planning)中正确命名了变更类别。
为什么流形无法对齐
每个嵌入模型都定义了一个高维空间,其几何结构反映了模型的训练方式——训练目标、数据组合、分词器(tokenizer)、投影层。两个同样声称将“英语文本嵌入到 1024 维”的模型,其产生的向量不仅仅是同一物理量的不同数值。它们是不同单位、不同拓扑结构空间中的测量值,其中的轴代表不同的含义,而“接近”的概念也由不同的邻居定义。
这就是为什么更换模型并直接用新的查询嵌入去对比旧的存储向量会发生静默失败。请求路径中没有任何报错。向量运算照常运行。数据库返回 k 个结果。但结果在结构上发生了细微的错误:语义上相邻的文档不再排名第一,而与查询共享表面词项(surface tokens)的文档开始超过共享含义的文档。你的余弦得分不会归零——它们会从 0.85 跌落到 0.6 左右的模糊地带,那里所有的东西看起来都差不多,但没有一个是正确的。如果你的可观测性仪表板没有衡量检索质量(大多数仪表板都没有),这种失败是隐形的。
实际后果是:你无法进行原地升级。你无法在保持索引可查询的同时逐步逐向量地迁移,因为任何命中新旧向量混合结果的查询,其返回的结果都是由两个不同相似度函数混合而成的。迁移必然需要完整的重新嵌入(re-embed)加一次性切换。唯一的问题在于你在切换时有多严谨。
迁移方案
直接借鉴数据库模式(schema)迁移的严谨性。这种模式分为四个阶段,跳过任何一个阶段都有其失败模式。
阶段 1:影子索引(Shadow index)。 创建一个新的向量列、集合或命名空间(取决于你的向量数据库),并在后台对整个语料库运行新的嵌入模型,将其写入影子索引。Weaviate 通过集合别名和共存的命名向量支持这一点。Pinecone 和 Qdrant 支持通过你可以起别名的多个索引或集合来实现。Postgres 配合 pgvector 支持通过 CONCURRENTLY 添加第二个 embedding_v2 列。影子索引必须从与实时索引相同的事实源(source of truth)填充,而不是从实时索引本身填充——嵌入是不可逆的,所以你无法将旧向量“翻译”到新空间。你必须在源文本上重新运行嵌入模型。
阶段 2:带有指标一致性的双重读取(Dual-read)。 在切换任何用户流量之前,建立一个离线或影子流量路径,将每个查询发送到新旧两个索引,并记录 Top-k 的重合度。标准做法是使用带有相关性标签的“黄金查询集”(通常几百个查询就足够了),并设定 Top-5 在新旧索引间的重合度目标在 60%–80% 之间。一位发布了完整迁移报告的从业者测量到了 82% 的重合度,并以此作为启动信号。如果重合度低于你的阈值,就是一个危险信号:这意味着要么新模型与旧模型存在显著分歧(在这种 情况下,你需要检索评估而非仅仅是基准测试分数来决定这种分歧是否是改进),要么是在重新嵌入过程中,你的分块(chunking)和预处理发生了漂移。无论哪种情况,都不要进行切换。
阶段 3:分阶段切换。 在几天(而非几分钟)内将流量从 5% 提升到 25% 再到 100%。观察点击率、下游回答质量以及任何你信任的产品级检索指标。慢速进行的原因不是因为新索引可能缺少数据,而是因为检索质量的回退不会触发错误警报。它们表现为用户满意度的缓慢下降,只有在积累了足够的会话后你才会注意到。渐进式放量为你提供了统计效能,让你在回退影响到 100% 的流量之前捕获它。
阶段 4:回滚计划,保持热备。 这是最容易为了节省时间而被砍掉的一步,但绝对不应该。旧索引在切换后必须保持在线并可查询至少一周,理想情况下更长。回滚应该是将特性开关从 embedding_v2 切回 embedding 的操作,而不是从备份中恢复。如果你为了节省存储空间已经删除了旧列,那么你就把一个 90 秒的回滚变成了一个耗时数天的全语料库重新嵌入过程——而此时用户正在投诉。影子索引的全部意义在于回滚是无代价的;丢弃旧索引会破坏这一特性。
你未曾预估的运营成本
那些正确地将其识别为“迁移”的团队,仍然会低估其代价,因为这些成本不会体现在 PR 中,而是会出现在账单和延迟仪表盘上。
重新嵌入的爆发性成本。 一次性对整个语料库进行嵌入(Embedding),是大多数团队见过的最大一笔嵌入 API 账单。对于 5 万个文档,成本微不足道,耗时也仅需几小时。对于 5000 万个文档,请做好准备:流水线需要运行一整个周末,支付数千美元的 API 费用,并预留足够的速率限制(Rate-limit)余量,以免在回填(Backfill)过程中耗尽嵌入吞吐量,导致你的实时流量因资源枯竭而瘫痪。如果你是私有化部署嵌入模型,重新嵌入所需的 GPU 小时数将远超稳态运行时的服务成本——有时甚至高出一个数量级。
索引预热。 HNSW 和其他基于图的索引在向量写入的那一刻,并不能以生产环境级别的延迟进行查询。图结构需要构建,可导航小世界(Navigable Small-world)链接需要形成,且缓存需要预热。在冷硬件上刚填充好的索引,其 p99 延迟可能比运行一小时真实流量后的相同索引高出 3 到 5 倍。如果你在重新嵌入完成后立即切换到冷的影子索引(Shadow index),即便检索质量完美,你也难逃延迟性能的回退。在切流之前,请先用回放流量(Replayed traffic)对影子索引进行预热。
迁移窗口期的双读成本。 当影子索引已上线但尚未成为主索引(Authoritative)时,你是在支付两套基础设施的费用。存储空间大致会翻倍——请为这段时间预留 2 到 3 倍的基础预算。在双读阶段,查询成本也会翻倍,因为每个黄金查询(Golden query)都会分发到两个索引中。在此期间,新文档的嵌入成本会增加到三倍,因为你需要同时写入旧索引、写入新影子索引,并运行验证嵌入。单独看每一项都不算贵得惊人,但它们都会出现在同一个计费周期里。如果有人提前打了招呼,财务方面的沟通会顺畅得多。
回填窗口期与新鲜度。 当你在重新嵌入语料库时,新文档仍在源源不断地到来。你有两种选择:在回填期间暂停对旧索引的写入(对大多数生产系统来说是不可行的),或者在整个迁移过程中对新文档进行双写。双写是正确的做法,但它要求嵌入流水线为每个新文档调用两个模型,这意味着每条摄取路径都必须意识到迁移的存在。忘记这一点的团队最后会发现,当他们切换别名时,影子索引缺失了最近一周的内容,而用户搜的第一样东西往往就是最近的内容。
表明你在做“部署”而非“迁移”的信号
最快的诊断方法是询问团队:如果新模型表现不佳,回滚需要多长时间?如果答案是一个具体的数字——几分钟、一小时——那么你正在进行的是迁移。如果答案是“我们需要重新运行旧的嵌入程序”,那么你正在进行的是部署,而系统最终会因为这种类别错误(Category error)而惩罚你。
一些其他的信号包括:
- 那份“升级嵌入模型”的 PR 只是一个单行的配置更改,没有任何配套的基础设施变动。没有新列,没有新索引,没有新别名,也没有新的功能开关(Feature flag)。
- 发布计划是“周一合并,观察仪表盘”。没有流量梯度切换(Traffic ramp),没有黄金查询基准,也没有双读窗口。
- 追踪的指标是 MTEB 分数或模型卡片上的基准测试。没有针对你特定领域的内部检索评估,因此你无法判断你的语料库是否真的能从更换模型中获益。
- 模型版本没有作为元数据记录在每个向量上。 当出现问题时,团队无法区分哪些文档使用的是旧嵌入器,哪些使用的是新嵌入器,因为向量本身不携带来源信息。
- “如果出问题,我们就进行分阶段发布”——一个仅作为应急预案存在的发布计划根本算不上发布计划,因为当你意识到出问题时,已有足够多的用户访问了错误的索引,性能回退已经造成了影响。
这些信号单独来看都不是致命的。但如果凑在一起,它们预测检索事故的准确率,甚至会超过你对嵌入模型准确率的期望。
文化上的修正源于命名上的修正
技术方案在现阶段已经很成熟了;经历过嵌入模型更迭事故的人,大多会对“影子索引-双读-分阶段切换”模式达成共识。较少被人意识到的是,只有当变更从进入计划文档的第一刻起就被正确命名为“迁移”时,这些方案才会被启用。一个名为“升级到 embedding-model-v3”的变更,受到的管理强度就像是升级一个库版本。而一个名为“将向量索引从 embedding-v2 迁移到 embedding-v3”的变更,受到的管理强度则等同于模式(Schema)变更。命名决定流程。
更有前瞻性的做法是将这种区分从文化层面提升到架构层面。经验丰富的团队会开始在他们的平台中内置嵌入更迭运行手册(Runbook):这是一个迁移作业,它能自动配置影子索引,针对固定的黄金集运行双读评估,生成一致性报告,并且除非满足一致性阈值且有人工签字确认,否则拒绝将影子索引提升为主索引。到那时,将变更称为“部署” 就不再是一个选项,因为部署系统根本不会允许你直接部署它。
在你的公司建立起这种基础设施之前,最廉价的改进方案就是语言上的:下次有人建议更迭嵌入模型时,请将该任务工单称为“迁移”。在编写重新嵌入计划的同时,在同一份文档中写好回滚计划。在提交 PR 之前就预估好存储空间翻倍的预算,而不是等账单来了才发现。模型可能只是一行配置,但底层的流形(Manifold)是整个数据库,而流形并不在乎你如何命名那个 PR。
- https://dev.to/humzakt/zero-downtime-embedding-migration-switching-from-text-embedding-004-to-text-embedding-3-large-in-1292
- https://medium.com/google-cloud/migrating-vector-embeddings-in-production-without-downtime-8a0464af6f55
- https://weaviate.io/blog/when-good-models-go-bad
- https://decompressed.io/learn/rag-observability-postmortem
- https://aclanthology.org/2025.emnlp-main.805.pdf
- https://mixpeek.com/guides/embedding-portability-versioning
- https://medium.com/@bhagyarana80/7-disaster-proof-backup-plans-for-vector-indexes-because-well-just-re-embed-everything-is-not-a-0bacf37ea07d
- https://www.salishseaconsulting.com/blog/vector-database-migration/
