跳到主要内容

你的 Embedding 模型选择决定了 RAG 的上限,而 LLM 无法突破它

· 阅读需 12 分钟
Tian Pan
Software Engineer

我建议的一个团队花了两个月时间在其 RAG 流水中不断更换 LLM。从 Claude 到 GPT,再到 Gemini,最后又换了回来。每一次更换都能让幻觉率降低几个百分点,但从未在关键指标上有所进展:他们的支持代理找到正确知识库文章的概率仍然不到 60%。他们调优的层级错了。检索器返回的是无关的文本块,而无论 LLM 多聪明,都无法根据检索器从未呈现过的文档来回答问题。

嵌入模型是 RAG 系统中决定 LLM 甚至“被允许”看到什么的部分。它描绘了语料库的几何结构——即在向量空间中,哪些文档会落在哪些查询附近。一旦这种几何结构出错,LLM 就只是一个对错误上下文侃侃而谈的自信叙述者。换一个更聪明的 LLM 通常只会让回答更显“文采”,而不会让回答更准确。

这篇文章将探讨为什么嵌入层是检索流水线中最高杠杆的选择,以及我选择模型时使用的框架:领域匹配优先,其次是维度与召回率的权衡,然后是多语言行为,最后是指令微调。大多数团队在质量进入瓶颈期之前,对这一层的投入都严重不足,直到最后才发现,他们的生成预算一直被浪费在偿还“检索债”上。

检索天花板是真实存在的,而且比你想象的要低

在开展任何 RAG 项目之前,都值得进行这样一个思想实验:假设 LLM 是完美的。假设它从不产生幻觉,遵循每一条指令,逻辑推理无懈可击。那么你系统准确性的上限是多少?答案就是检索器返回正确文档的查询比例。这个数字就是你的“检索天花板”,它限制了下游的一切。

团队往往是在碰壁后才意识到这一点。他们运行流水线,看到平庸的输出,然后去调节最显眼的旋钮:LLM。但如果检索器仅在 70% 的时间内能找到正确的文本块,那么即便使用一个在宇宙热寂背景下训练出来的模型,系统准确率也无法超过 70%。天花板在嵌入步骤中就已经设定好了——就在查询被转换为向量并与语料库匹配的那一刻。

这种情况的隐蔽之处在于,其症状看起来很像 LLM 的问题。模型“忽略”了提供的上下文(因为提供了错误的上下文)。模型“虚构”了答案(因为正确的片段不在前 k 个结果中)。模型在不同查询之间“自相矛盾”(因为语义等价的查询返回了不同的邻域)。所有这些都被诊断为生成问题,并尝试通过提示工程(Prompt Engineering)来解决。实际上,修复工作应该在更上游的地方进行。

一个有效的诊断方法是:在你的流水线中增加记录功能,将检索到的文本块与模型输出一并记录,然后由人工分别评估“检索到的文本块是否正确”以及“答案是否正确”。大多数团队会发现,在失败的查询中,有 30–50% 是因为检索错误。这就是天花板。更换 LLM 无法改变这一点。

领域匹配几乎总是胜过排行榜名次

很多团队在选择嵌入模型时会参考 MTEB 排行榜。但这其实是一个错误的的首要信号。MTEB 衡量的是在网络文本、新闻、维基百科和学术摘要上的表现。如果你的语料库是生物医学文献、财务报告、法律合同、源代码或行业特定的支持工单,排行榜的排名几乎无法告诉你该模型在你的数据上的表现。

这在一定程度上是覆盖范围问题,也是数据污染问题。MTEB 数据集存在的时间已经足够长,以至于较新的模型已经将其纳入了训练分布,“零样本测试”与“在相似数据集上训练”之间的界限已经模糊。此外,该基准测试在拥有大量公共数据的任务(如语义文本相似度、通用检索)上权重过高,而对大多数企业真正关心的领域代表性不足。

实际的做法是在选择模型之前,建立一个小型领域内评估集。准备 200 个查询,并手工标注正确的文档,这些查询应源自真实的用户行为或生产日志。针对该集合运行前三或四个候选模型,并观察 recall@10 和 MRR。你会发现排名通常会与 MTEB 相反。一个在排行榜上排名第七的通用模型,在你的特定语料库上的表现可能会比第一名高出 10 到 15 个百分点。

对于专业领域,如果存在微调后的变体,请优先选择。例如:针对医学文本的 PubMedBERT 和 BioLORD;针对财务报告的 Voyage Finance 和 BGE Financial Matryoshka;针对代码库搜索的特定代码嵌入。在这些领域,它们相对于通用模型的提升通常在 10–30% 之间。如果你的领域还没有现成的微调变体,那么在几千个“查询-文档对”上微调基础模型,通常比升级 LLM 的投资回报率更高。

维度是成本杠杆,而非质量旋钮

存在一种普遍误解,认为维度越高,检索效果越好。有时在边缘情况下确实如此,但大多数情况下并非如此。马特廖什卡表示学习(Matryoshka Representation Learning, MRL)改变了这一计算方式,以至于在 MRL 成为标准之前做出的任何决定,可能都值得重新审视。

MRL 在训练嵌入模型时,会让靠前的维度携带最重要的语义信息,而靠后的维度则添加更精细的细节。结果是,你可以将 3,072 维的向量截断到 1,024 或 512 维,而仅损失几个百分点的检索质量。Gemini Embedding 2 在从 3,072 维降至 2,048 维时,其 recall@10 的损失不到 1%;而 Jina v3 即使在 64 维时也能保持全维度性能的 92%。

为什么这很重要?因为向量存储和搜索成本大致随维度线性增长。一个包含 1 亿个向量的 3,072 维索引在计算索引开销前约为 1.2TB。同样的语料库在 768 维下仅为 300GB。召回率的差异可能只有 2–3 个百分点,但成本差异却是 4 倍。对于大多数生产负载,这种权衡显然倾向于更小的表示形式,尤其是当配合重排序器(Reranker)来恢复前 k 个结果中损失的精度时。

决策框架:选择模型提供的最大维度作为上限,然后根据实际语料库的“成本 vs. recall@k”的帕累托前沿进行截断。测试 256、512、1024 维度以及模型的原生维度。绘制曲线。曲线的拐点通常远低于最大维度,你可以省下这笔钱——或者将其重新投入到索引更多文档中,这通常比提高维度能获得更高的召回率。

注意:这只有在模型使用 MRL 训练时才有效。对非 MRL 嵌入进行简单截断通常会破坏检索质量,因为信息并非前置。在假设截断安全之前,请先查阅模型卡。

多语言是一种模式,而非开关

构建面向全球用户产品的团队经常犯同一个错误:他们选择一个强大的英语嵌入模型,并假设它在其他语言中也“基本有效”。事实并非如此。以英语为中心的模型会根据表面特征——脚本、分词伪影、音译词——而不是根据含义对非英语文本进行聚类。一个西班牙语查询和一个关于相同主题的英语文档会落在向量空间的不同区域,这种检索失败看起来像是翻译问题,而实际上是嵌入问题。

真正的多语言嵌入——BGE-M3、Jina v3、Qwen3-Embedding-8B、multilingual-e5——是使用平行文本和对比目标进行训练的,这使得来自不同语言且语义相同的内容在同一个向量空间中彼此靠近。这才是最重要的特性:跨语言检索。你可以用韩语提问,系统就能找出一个相关的英语文档,无需翻译步骤。

这个决定不是二选一的。它取决于查询和文档中非英语所占的比例、查询和文档是否共享同一种语言,以及“相似”的含义在文化上的特定程度。一些有用的经验法则:

  • 如果你的语料库是单语言的,并且你不预期这种情况会改变,那么在英语 MTEB 上得分较高的纯英语模型通常会击败多语言模型。多语言在任何单一语言上都有质量成本,因为模型正在将容量分散到多种语言中。
  • 如果查询和文档可能使用不同的语言,你需要一个使用跨语言目标训练的多语言模型。在运行时翻译查询是一种脆弱的变通方案,会丢失细微差别并增加延迟。
  • 如果你的语料库包含长文档,请检查模型的最大序列长度。许多较旧的多语言模型上限为 512 token;较新的模型(BGE-M3、Jina v3、Qwen3)可以处理 8K–32K,这就是嵌入一个分块(chunk)与嵌入一个章节之间的区别。

指令微调让嵌入知道它在做什么工作

现代嵌入模型越来越多地接受指令和输入——一个告诉模型要生成哪种表示的简短提示。Voyage 的 instruct 模型、INSTRUCTOR、Qwen3 系列等都支持这种模式。指令可以指定任务(“为检索进行表示”)、输入类型(查询 vs. 文档),甚至是要优化的相似度类型(语义、事实、结构)。

这听起来微不足道,但事实并非如此。同一段文本在不同的检索背景下意味着不同的东西。字符串 "OAuth callback URL" 在安全背景下意味着一件事(寻找关于令牌拦截攻击的文档),而在集成背景下则意味着另一件事(寻找关于配置重定向 URI 的文档)。未经指令微调的嵌入为每个字符串生成一个向量,并强制每个下游任务共享它。经过指令微调的嵌入可以根据你要检索的内容,为同一个字符串生成不同的向量。

在实践中,正确使用指令带来的收益比在排行榜上升级到下一个嵌入模型带来的收益还要大。Voyage 的 instruct 变体在大多数检索基准测试中比其非指令版本高出好几个点,且无需更改基础设施。成本只是 API 调用中的一个短字符串。最常见的错误是对查询和文档使用相同的指令,这浪费了模型训练时利用的不对称性。文档应该被指令为“为检索表示该文档”,查询则为“为检索相关文档表示该查询”,或者使用模型卡(model card)指定的任何措辞。如果弄错了这一点,你就退回到了非指令模型。

为什么团队在为时已晚之前总是投入不足

嵌入层很少能得到它应得的预算,这是因为它在工程组织中所处的位置决定的。LLM 是显性的——有供应商关系、token 成本项、延迟仪表盘、评估新模型的季度周期。而嵌入模型是在项目早期做出的、通常由三个季度前在 Jupyter notebook 中做原型的某个人决定的一次性选择,然后它就僵化在向量索引中。

更换它的代价是昂贵的。重新嵌入数千万份文档的语料库需要数小时的计算和存储开销,任何缓存的嵌入或微调模型都会变成废弃项。迁移涉及并行运行两个索引、将查询路由到这两个索引并逐渐转移流量——这些工作看起来像是基础设施管道工作,而不是机器学习改进,因此很难获得资金支持。

结果是团队不断调整 LLM、分块策略、提示词和重排序器(reranker)——除了决定上限的那一层之外的每一层。当检索质量进入瓶颈期时,正确的做法几乎总是在触碰其他任何东西之前,在真实的评估集上测试新的嵌入模型。如果一个候选模型在你的域内评估中比现有模型高出 5 个点以上,那么单凭提升的上限就足以证明迁移成本是合理的,更不用说下游改进的复合效应了。

让这项工作变得可控的准则是从第一天起就将嵌入模型视为一个版本化的依赖项。为每个向量打上生成它的模型和版本的标签。在索引还小且重建成本较低时,尽早构建迁移工具。像运行类型检查(typecheck)一样运行检索评估作为 CI 门控。这样,当更好的模型出现时——大约每六个月就会出现一个——问题就是操作性的,而不是根本性的。

LLM 的切换是一个值得转动的旋钮。嵌入模型的切换则是值得仔细浇筑的地基。把地基打好,上层的旋钮才能像营销宣传的那样起作用。如果地基打错了,接下来的两个季度你都会在调整一个上限已经被封死的系统。

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