RAG 进阶之路3:从 BERT 到 BGE揭秘 RAG 系统的“语义引擎”

上一篇我们搞定了“身体”(Milvus 向量数据库),这一篇我们要赋予系统“灵魂”(语义理解)。

很多开发者有一个误区:认为 RAG 的效果主要取决于大模型(LLM)的智商。其实不然,RAG 的“生死线”在于检索(Retrieval)。 如果你喂给 LLM 的是垃圾文档,在好的LLM 也救不了你。而决定检索质量的核心,就是 Embedding(向量化)Rerank(重排序) 模型。


在上一章中,我们搭建了 Milvus 向量数据库,它就像是一个巨大的、极其高效的图书馆书架。但是,把书放上去只是第一步,最难的是:当用户问“苹果怎么吃”时,你怎么知道要把关于“水果”的书给他,而不是关于“iPhone 手机”的说明书?

这就需要一个能够理解人类语言隐含意义的“翻译官”,将文字转化为计算机能理解的数字(向量)。这个过程叫 Embedding

目前在中文开源社区,BGE (BAAI General Embedding) 是当之无愧的王者。但它是如何工作的?它和 NLP 的祖师爷 BERT 有什么关系?为什么我们还需要一个叫 Reranker 的东西?

本文将为你揭开这些黑盒。

1. 始祖:BERT 的“双向”革命

要理解 BGE,必须先致敬 BERT。

在 BERT 出现之前,语言模型(如 Word2Vec)理解词汇通常是单向的,或者缺乏上下文。比如“银行”这个词,在“河岸边”和“存钱”这两个场景下意思是完全不同的。

BERT (Bidirectional Encoder Representations from Transformers) 的伟大之处在于它是**双向(Bidirectional)**的。它通过一种叫 Masked Language Model (完形填空) 的方式进行预训练:

“今天天气[MASK]错,适合出去玩。”

BERT 必须同时看左边的“今天天气”和右边的“适合出去玩”,才能猜出 [MASK] 是“不”。这种机制让 BERT 拥有了极强的上下文感知能力。

但在 RAG 中,原始 BERT 有一个致命缺陷:
它不擅长做“语义相似度检索”。如果你直接提取 BERT 的向量(通常取 [CLS] token)来算余弦相似度,你会发现所有句子的向量都挤在一起(各向异性问题),很难区分谁跟谁更像。

2. 进化:BGE 是如何炼成的?

BGE (BAAI General Embedding) 是智源研究院基于 BERT 架构(具体是 RetroMAE 变体)深度改造而成的。它之所以能霸榜 C-MTEB(中文嵌入模型榜单),主要做对了三件事:

A. 预训练的魔法:RetroMAE

BGE 使用了一种更激进的预训练任务。它不仅是简单的“完形填空”,而是故意把句子弄得支离破碎(Mask 掉很大比例),强迫编码器(Encoder)去重构原始语义。这让模型生成的向量具有更强的鲁棒性和语义浓缩能力。

B. 对比学习 (Contrastive Learning)

这是 BGE 变强的核心。原始 BERT 是自己学自己,BGE 则是**“成对”**地学。

  • 正例 (Positive): (用户问题, 正确答案) -> 拉近它们的向量距离。
  • 负例 (Negative): (用户问题, 错误文档) -> 推远它们的向量距离。

BGE 使用了海量的精选中文语料对进行这种训练,硬生生地把 BERT 这个“通才”训练成了专门判断“这就话和那句话是不是一回事”的“专才”。

C. 指令微调 (Instruction Tuning)

BGE 引入了指令机制。对于查询(Query),它要求你加上前缀:

为这个句子生成表示以用于检索相关文章:

这就像告诉模型:“嘿,我现在是在做搜索任务,别光顾着理解字面意思,要去想这句话背后的搜索意图。”

3. 架构之争:双塔 vs 单塔 (Bi-Encoder vs Cross-Encoder)

在 RAG 的实际落地中,我们经常听到 EmbeddingReranker。它们其实代表了两种不同的神经网络架构。

Embedding 模型 (Bi-Encoder / 双塔)

这也是 Milvus 配合的模型(如 bge-base-zh)。

  • 原理:
    1. 句子 A 进模型 -> 变向量 A。
    2. 句子 B 进模型 -> 变向量 B。
    3. 计算 A 和 B 的距离(点积或余弦)。
  • 特点: 句子 A 和 句子 B 在进入模型时是互不见面的,各自独立编码。
  • 优势: 极快。因为库里的文档可以提前算好向量存起来。搜索时只需要算一次 Query 的向量,然后做向量运算(Milvus 擅长这个)。
  • 劣势: 精度一般。因为丢失了句子间的细微交互信息。

Reranker 模型 (Cross-Encoder / 单塔)

这是 BGE-Reranker 的架构。

  • 原理:
    1. 把 句子 A 和 句子 B 拼起来[CLS] 句子A [SEP] 句子B
    2. 扔进 BERT 模型里,让 Attention 机制疯狂计算 A 中每个字和 B 中每个字的交互关系。
    3. 直接输出一个 0~1 之间的分数(相关性)。
  • 特点: 它们是深度交互的。模型能像人类一样仔细比对细节。
  • 优势: 极准。能识别出逻辑陷阱、否定词等微小差异。
  • 劣势: 极慢。你不能提前算好,必须实时算。如果库里有 100 万条数据,算一次要几小时,根本没法上线。

4. 黄金组合:漏斗式检索 (The Funnel Pipeline)

既然 Embedding 快但不准,Reranker 准但不快,RAG 的最佳实践就是把它们结合起来,形成一个**“漏斗”**:

graph TD
    A[海量文档库 (100万+)] -->|1. Embedding (Bi-Encoder)| B(Milvus 向量检索)
    B -->|粗排: 选出 Top 100| C[候选集 (100条)]
    C -->|2. Reranker (Cross-Encoder)| D(精细重排序)
    D -->|精排: 选出 Top 3| E[最终上下文]
    E -->|拼接 Prompt| F[大模型 LLM]
  1. 第一层(粗排): 使用 Milvus + BGE-base-zh。从海量数据中快速捞出最像的 100 条。虽然可能混进去几个不相关的,但保证了速度(毫秒级)。
  2. 第二层(精排): 使用 BGE-Reranker。对这 100 条进行深度阅读打分,把真正相关的排到最前面,截取 Top 3。
  3. 结果: 既享受了 Milvus 的速度,又拥有了 Cross-Encoder 的精度。

5. 避坑指南

在使用 BGE 系列模型时,有几个常见的坑需要注意:

  1. Query 前缀: 使用 bge-base-zh 时,仅对 Query(问题) 添加指令前缀 为这个句子生成表示以用于检索相关文章:,对于入库的文档(Passage)不要加
  2. 距离度量: BGE 官方推荐使用 Inner Product (IP / 点积) 距离,而不是 L2(欧氏距离)。并且建议开启 normalize_embeddings=True
  3. 最大长度: BGE 的最大上下文通常是 512 tokens。如果你的文档很长,必须先进行切片(Chunking),否则超出的部分会被截断丢弃。

总结

如果说 Milvus 是 RAG 的“海马体”(记忆),那么 BGE 就是 RAG 的“布罗卡区”(语言处理中枢)。

理解了 Bi-Encoder 和 Cross-Encoder 的区别,你就掌握了提升 RAG 准确率的一把钥匙。但是,光有理论是不够的。

在下一篇文章中,我们将拿出键盘,通过 Python 代码,使用 LangChain + Milvus + BGE + Reranker 亲手搭建这套流水线。