Skip to content

14.06-Embedding向量化

要点

  • Embedding 模型决定了向量空间的语义质量——选错模型等于在地基歪了的楼上盖房子
  • 不同模型生成不同语义空间,向量不可跨模型比较,索引和查询必须锁定同一模型
  • 统一维度比较:Gemini Embedding 2 精度领先、Qwen3-Embedding 多语言开源最强、BGE-M3 是最实用的全能工作马、OpenAI text-embedding-3 性价比最高
  • 选型决策由四个条件决定:语言、成本、部署模式、数据规模——没有通用最优解
  • 文档和查询的编码模式必须一致,否则检索效果显著下降
  • 批处理 + 缓存 + 重试是把 Embedding 从 demo 推到生产的三个关键工程动作

1. Embedding 模型选错,整条 RAG 链路白搭

上一章(14.05-文本切分)把文档切成了 chunks。下一步是把每个 chunk 变成向量——这一步叫 Embedding。

一个真实的工程场景:RAG 搭完了,chunk 切好了,向量数据库跑起来了,但检索准确率就是上不去。排查切分策略没问题,排查 Rerank 没问题,最后发现是 Embedding 模型选错了——用英文优化的模型处理中文内容,语义空间从一开始就是歪的。

Embedding 模型的选择是整个 RAG 系统里杠杆最大的决策。 它决定了向量空间的语义质量。后续所有检索、排序、上下文拼接都建立在这个空间之上。模型选错了,后面的优化都是在补救一个本可以避免的问题。

这一节回答三个问题:Embedding 的底层机制是什么,主流模型各有什么优劣,以及你的场景应该选哪个。

2. 语义空间:Embedding 到底在做什么

Embedding 模型把一段文本映射到一个高维向量空间。你可以把它想象成一张语义地图:意思相近的文本在这张地图上的坐标离得近,意思远的坐标离得远。

"今天天气真好"   → [0.12, -0.34, 0.88, ..., 0.05]   (768 维)
"阳光很棒"       → [0.11, -0.32, 0.85, ..., 0.07]   (距离很近——语义相似)
"今天股票跌了"   → [-0.45, 0.67, -0.12, ..., 0.33]  (距离很远——语义无关)

向量之间的距离用余弦相似度衡量(RAG 检索场景几乎都用这个指标)。距离越接近 1,语义越相似。

关键认知:不同模型生成的是不同的语义空间。 同一段文本用 OpenAI 和 BGE 编码,得到的向量完全不同。两个模型的向量不能直接比较——就像两张不同投影方式的地图,经纬度数值不一样,不能直接拿尺子量。

这个认知直接影响工程决策:一旦选定了 Embedding 模型,所有向量(文档和查询)必须用同一个模型编码。 中途换模型意味着全部重新向量化。

Embedding 模型的训练数据决定了它的语义空间怎么划分。通用模型(如 OpenAI text-embedding-3)在大规模通用语料上训练,擅长日常语义。领域专用模型在特定语料上训练,对专业术语的区分更精细。

三个关键参数决定了一个模型的能力边界:

  • 向量维度——768、1024、1536、3072 等。维度越高表达力越强,但存储和计算成本也越高。text-embedding-3 系列支持降维(通过 Matryoshka Representation Learning),可以在存储成本和语义精度之间灵活取舍
  • 最大输入长度——512 到 128K token 不等。超过限制的文本需要先切分(这就是上一章做的事)。长上下文模型(如 Cohere embed-v4 的 128K)对大块文本更友好,减少了因切分过碎导致语义丢失的风险
  • 训练语料——决定了模型擅长什么语言、什么领域。中文语料占比高的模型(如 BGE、Qwen3)在中文任务上明显优于纯英文训练的模型

3. 主流方案比较

把四类主流方案放在同一张表里,用统一维度比较。

方案模型维度最大输入MTEB 参考分价格(/1M token)中文能力核心特点
GoogleGemini Embedding 23072(可降维)32K$0.20跨语言 0.997精度最高,支持 5 种模态,2026 年跨语言基准冠军
OpenAItext-embedding-3-small1536(可降维)8K$0.02可用极致性价比,维度可调
OpenAItext-embedding-3-large3072(可降维)8K~64.6$0.13良好精度高,维度可调
Cohereembed-v41536(可降维)128K~65.2$0.12良好(0.955)多模态,超长上下文,区分文档/查询编码
AlibabaQwen3-Embedding-8B可变8K70.58(多语言)自托管优秀开源,MTEB 多语言榜首,中英双强
BAAIbge-m310248K~63.0自托管优秀(0.940)同时输出稠密 + 稀疏 + ColBERT 三种表示
CloudflareWorkers AI(bge 系列)768512按 Workers 计费可用与 Vectorize 原生集成,零额外运维

几个值得展开说的点:

Gemini Embedding 2 是 2026 年跨语言检索的精度标杆——在中文-英文跨语言基准上拿到 0.997,是唯一在 Hard 难度(含复杂中文成语)上拿到满分的模型。缺点是需要 Google Cloud,且 3072 维向量存储成本不低。

Qwen3-Embedding 是开源阵营的新王者。8B 模型在 MTEB 多语言排行榜排名第一(70.58),但 8B 对推理硬件要求高(至少需要 16GB 显存)。它的 0.6B 小模型在精度和部署成本之间取得了不错的平衡,适合资源有限的自托管场景。

BGE-M3 是最实用的开源全能选手。它的独特能力是一次推理同时输出稠密向量、稀疏向量和 ColBERT 多向量三种表示——这意味着你可以用它同时支持向量检索、关键词检索和多向量精排,不需要额外部署稀疏检索引擎(如 BM25)。MIT 协议,商用无顾虑。

OpenAI text-embedding-3-small 以 $0.02/1M token 的价格提供了够用的精度。如果你的场景对检索准确率没有极致要求,它是最省心的选择——不用管模型部署、不用管版本升级、不用管显存。

embed-v4 相对 embed-v3 的主要升级是多模态支持(文本 + 图片)和 128K 上下文窗口。如果你的知识库包含图片内容,embed-v4 是目前少数能直接处理图片 Embedding 的商业模型。

4. 选型决策框架

选型不是一个「哪个最好」的问题,而是一个「在什么条件下选什么」的决策。四个条件依次过滤。

4.1 内容是什么语言?

  • 纯英文 → text-embedding-3-small 够用,精度优先选 Gemini Embedding 2
  • 中文为主 → 自托管选 Qwen3-Embedding(精度最高)或 BGE-M3(最均衡);API 选 Gemini Embedding 2 或 embed-v4
  • 多语言混合 → Gemini Embedding 2(跨语言精度最高)或 BGE-M3(自托管多语言最佳)

4.2 精度和成本怎么取舍?

  • 成本优先 → text-embedding-3-small($0.02/1M token)或 BGE-M3(自托管免费)
  • 精度优先 → Gemini Embedding 2 或 Qwen3-Embedding-8B
  • 均衡 → text-embedding-3-large($0.13/1M token,精度可观)或 BGE-M3

4.3 能接受第三方依赖吗?

  • 接受 API → OpenAI、Cohere、Google 最省心
  • 需要私有部署 → Qwen3-Embedding 或 BGE-M3,配合 vLLM / TEI 部署推理服务
  • 已有 Cloudflare 技术栈 → Workers AI 省去跨服务集成的麻烦

4.4 数据规模多大?

  • < 100 万 chunk → 任何方案都行,优先选最省心的
  • 100 万 - 1 亿 → 关注 API 成本和向量存储成本,text-embedding-3-small 的降维功能在这里很有用
  • > 1 亿 → 自托管 + 量化压缩几乎是必选项,BGE-M3 的 1024 维比 3072 维省 3 倍存储

5. 边界条件

模型选型能解决 80% 的问题,剩下的 20% 来自边界条件:领域术语、多语言混合和数据规模。

5.1 领域术语

通用 Embedding 模型可能不认识你的领域术语。「RAG」「向量检索」在通用模型的语义空间里可能和「一个字母组合」「一种搜索方式」差不多——因为它在训练语料里很少见到这些概念的精确用法。

处理方式:用领域数据微调模型。成本不低(需要构造训练对和训练流程),但在垂直领域(法律、医疗、金融)效果提升明显。

更经济的做法是先确保切分策略合理——检索效果不好时,先检查 chunk 粒度是不是太粗或太细,Embedding 模型是最后才优化的环节。

5.2 中英文混合内容

中英文混合的技术文档很常见。用纯英文模型处理中文段落,语义空间会严重失真。

多语言模型(Gemini Embedding 2、BGE-M3、Qwen3-Embedding)在训练时对齐了不同语言的语义空间,能正确处理混合文本。实测数据:Gemini Embedding 2 跨语言检索准确率 0.997,BGE-M3 为 0.940。

如果你发现多语言模型效果仍不理想,可以考虑「先翻译后 Embedding」——把所有内容翻译成同一种语言再编码。代价是索引成本翻倍(翻译 + 向量化),且翻译可能引入错误。

5.3 规模带来的工程约束

百万 chunk 以下,任何模型都跑得动。超过一亿 chunk,向量存储本身就成了瓶颈——3072 维(text-embedding-3-large)比 1024 维(BGE-M3)多 3 倍存储。这时候降维或选低维模型就成了工程必需,而不只是精度取舍。

6. 编码一致性:一个容易忽略的硬约束

索引和查询必须用同一个模型编码,这一点在前面的「语义空间」里已经说过。但还有一个更细的坑:有些模型对文档和查询用不同的编码模式。

Cohere embed 系列要求设置 input_type

typescript
// 索引文档时
const docEmbedding = await cohere.embed({
  texts: ['这是一段文档内容...'],
  model: 'embed-v4',
  inputType: 'search_document',  // 文档编码模式
})

// 查询时
const queryEmbedding = await cohere.embed({
  texts: ['用户的问题'],
  model: 'embed-v4',
  inputType: 'search_query',  // 查询编码模式
})

为什么需要区分?文档通常更长、信息更完整;查询更短、更聚焦。模型对两者用不同的编码策略,让查询向量和文档向量在同一个空间里更好地对齐。如果不区分,检索效果会明显变差。

E5 系列用前缀区分:

typescript
const docText = `passage: ${documentText}`
const queryText = `query: ${queryText}`

如果你用的模型不需要区分(如 text-embedding-3、BGE-M3),那文档和查询用同一种编码方式就行。但如果模型需要区分而你没设置,检索效果会悄悄下降——不会报错,只是结果不好。 这是排查 RAG 检索质量时容易忽略的地方。

7. 统一 Embedding 服务与工程实践

把模型选择、批处理、缓存和重试封装成统一接口,业务代码不直接依赖具体模型。

typescript
// 统一接口
export type EmbeddingProvider = {
  name: string
  dimensions: number
  embed(texts: string[]): Promise<number[][]>
}

// 封装服务:批处理 + 重试 + 缓存
export class EmbeddingService {
  private provider: EmbeddingProvider

  constructor(provider: EmbeddingProvider) {
    this.provider = provider
  }

  get dimensions() {
    return this.provider.dimensions
  }

  async embed(texts: string[]): Promise<number[][]> {
    const BATCH_SIZE = 100
    const allEmbeddings: number[][] = []

    for (let i = 0; i < texts.length; i += BATCH_SIZE) {
      const batch = texts.slice(i, i + BATCH_SIZE)
      let attempts = 0

      while (attempts < 3) {
        try {
          const embeddings = await this.provider.embed(batch)
          allEmbeddings.push(...embeddings)
          break
        } catch (err) {
          attempts++
          if (attempts >= 3) throw err
          // 指数退避
          await new Promise((r) => setTimeout(r, 1000 * attempts))
        }
      }
    }

    return allEmbeddings
  }
}

批处理是成本控制的基本功。 逐条调用 API 和批量调用,成本差异可达 10-100 倍。每批不要超过 API 限制(OpenAI 最多 2048 条,Workers AI 最多 100 条)。

缓存避免重复计算。 用文本内容的哈希值作为缓存 key,相同文本不需要重复向量化。更新文档时只计算新增或修改的 chunk。查询缓存可以在短时间内复用同一问题的 Embedding。

成本估算

以 text-embedding-3-small 为例:

typescript
// 索引成本(一次性)
const totalChunks = 100_000
const avgChunkTokens = 200
const indexCost = (totalChunks * avgChunkTokens / 1_000_000) * 0.02  // = $0.04

// 查询成本(每月)
const dailyQueries = 1000
const queryTokensPerQuery = 50
const monthlyQueryCost = (dailyQueries * queryTokensPerQuery * 30 / 1_000_000) * 0.02
// = $0.03

// 月总成本 ≈ $0.07

中小规模应用下,Embedding 成本几乎可以忽略。真正的成本大头在 LLM 生成环节(第 13 章)。

但当规模上到亿级 chunk,情况完全不同:

typescript
// 亿级 chunk 的索引成本
const totalChunks = 100_000_000
const indexCost = (totalChunks * 200 / 1_000_000) * 0.02  // = $400
// 加上存储:3072 维 × 1 亿 × 4 字节 ≈ 1.2TB

$400 的索引成本 + 1.2TB 的存储,这时候自托管模型的成本优势就很明显了。 判断阈值:当月 Embedding API 费用超过一台 GPU 实例的月租(约 $200-500),就该认真评估自托管。

8. 下一步:选完模型,存在哪里

Embedding 模型的选型不是做一次就不变的事。随着数据规模增长、语言覆盖扩展、检索精度需求变化,你可能需要重新评估。以下是几个升级信号:

  • 检索准确率不达标 → 先查切分粒度(回到 14.05),再查 Embedding 模型是否匹配内容语言
  • 内容从单语变多语 → 切换到多语言模型,所有向量需要重新编码
  • 数据规模突破百万 → 评估 API 成本,考虑自托管或降维
  • 领域术语检索差 → 考虑领域微调,或先用 Rerank(第 12 章)补救

选定了 Embedding 模型,下一步是把向量存起来。第 7 节(14.07-向量数据库选型)讨论 pgvector、Qdrant、Milvus 的选择。

基于 MIT 协议开源