登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

Embedding 向量维度改了为何不能直接复用索引:模型切换、迁移双写与回滚

来源:17golang原创

时间:2026-08-30 04:37:15 222浏览 收藏

线上 RAG 换了 Embedding 模型,最容易踩的坑不是接口报错,而是新向量悄悄写不进旧索引,或者查询端用了新模型、库里仍是旧向量。只要向量维度或距离度量发生变化,就应把它当成一次索引迁移:保留旧路径,建立新路径,完成对比后再切流。

不要修改旧索引“凑合复用”。把模型、维度、度量和索引绑定成一个版本,双写验证通过后再切换查询流量,回滚时把读路径切回旧索引。

要点速览
  • vector(1536) 与 vector(3072) 是不同的数据契约,不能只改模型名。
  • 旧索引保留,新模型写入新列或新 collection,双写期间同时记录召回差异。
  • 切流前检查维度、距离度量、空值率、召回样本和延迟;异常时回退读路径。

向量维度变化,究竟改变了哪条约束

一条向量检索链至少包含模型输出、存储字段、距离度量和索引。以 pgvector 为例,vector(1536) 明确了列的维度;新模型输出 vector(3072) 后,数据类型约束、索引入口和查询参数都已经变了。Qdrant 的 collection 也会在创建时指定 vector size 与 distance,换维度通常意味着新 collection 或新的命名向量配置。

这里可以把模型切换看成一条实际的数据链:Embedding 先生成向量,再写入 vector(1536)vector(3072),最后交给 HNSW。同一条旧索引不能把两种长度当成同一种坐标空间。

Embedding 生成 vector(1536) 或 vector(3072),分别进入 HNSW 的维度契约示意图

先冻结模型契约,再决定迁移形态

迁移前先把以下值写入配置,而不是散落在调用代码里:模型标识、输出维度、距离度量、归一化方式、索引名和数据版本。模型返回成功不代表向量可用于旧链路,至少要校验数组长度和有限数值。

type EmbeddingSpec struct {
    Model      string
    Dimensions int
    Distance   string
    IndexName  string
}

func validateVector(values []float32, spec EmbeddingSpec) error {
    if len(values) != spec.Dimensions {
        return fmt.Errorf("dimension mismatch: got=%d want=%d", len(values), spec.Dimensions)
    }
    for _, value := range values {
        if math.IsNaN(float64(value)) || math.IsInf(float64(value), 0) {
            return errors.New("vector contains non-finite value")
        }
    }
    return nil
}

检查点很简单:旧模型仍能通过 validateVector,新模型必须落到独立的 IndexName。如果只是把 Dimensions 改成新值,却还指向旧表列或旧 collection,故障会推迟到写入、建索引或查询时才暴露。

双写迁移怎样把风险限制在可回滚范围

推荐保留 old_indexnew_index 两条清晰的读写路径。文档更新时,先写旧向量,再写新向量;新写入失败不能覆盖旧成功状态,应记录待补偿任务。查询灰度阶段由 Query 同时访问两条索引,线上响应仍以旧结果为准,只把新结果用于离线比较。

比较的不是“新结果看起来更聪明”,而是固定问题集上的命中率、重复率、空结果率和延迟。只有当 Recall 达到预设阈值且新索引没有持续超时,才把主读路径切到 new_index

Query 同时读取 old_index 与 new_index,通过 Recall 对比后切换读路径的双写迁移示意图
func searchWithShadow(ctx context.Context, q []float32) (Result, error) {
    oldResult, err := search(ctx, "old_index", q)
    if err != nil {
        return Result{}, err
    }
    go compareInBackground(ctx, "new_index", q, oldResult)
    return oldResult, nil
}

影子查询必须有超时和采样比例,不能让新索引拖慢旧链路。对比任务只写指标,不修改用户最终答案;这样新索引即使构建不完整,也不会扩大线上影响面。

切流前的验收与回滚按钮

验收至少分三层。第一层是结构:抽样检查向量维度、模型版本、距离度量和索引状态;第二层是数据:确认新旧文档覆盖率、空值率和失败补偿队列;第三层是效果:用固定问题集对比 Recall、延迟和错误率。pgvector 还支持按模型字段建立部分索引,适合在同表迁移时把模型边界写进索引条件。

  • 结构不一致:停止切流,修正模型配置或重建新索引。
  • 覆盖率不足:继续双写并补齐历史文档,不要用不完整库做全量流量。
  • 效果或延迟退化:把读开关切回 old_index,保留 new_index 供分析。

回滚应是一个配置变更,而不是临时改代码。至少保留旧模型密钥配置、旧索引名称和旧查询参数,直到新版本稳定运行一段时间并完成备份。

常见问题

只改变向量维度参数,旧数据能自动转换吗?

不能把自动补零或截断当成等价迁移。它改变了向量空间,除非业务明确验证过效果,否则应使用新模型重新生成并重建索引。

新旧模型可以放进同一个 collection 吗?

要看存储引擎是否支持命名向量或明确的多向量配置。没有清晰隔离时,使用新 collection 或新列更容易校验、灰度和回滚。

什么时候可以删除旧索引?

完成切流、回滚演练、历史补数核对和备份确认后再删。至少保留一次可验证的旧读路径,避免新模型出现隐藏分布问题时无路可退。

把模型切换变成一次可观察的工程变更

Embedding 迁移的核心不是追求更大的维度,而是让每个版本都有边界:模型输出对应一个维度契约,维度契约对应一个索引,索引通过双写和固定样本验收后才接收流量。这样即使新模型效果不及预期,也能把读取路径退回旧版本,而不必回滚整套应用。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>