RAG实战-Embedding模型(BGE)
- [为什么 RAG 需要 Embedding 模型?](#为什么 RAG 需要 Embedding 模型?)
- 1 RAG两阶段检索:Embedding召回 + Rerank精排 #embeddingrerank
-
-
- [1.1 Bi-Encoder(Embedding召回模型)](#1.1 Bi-Encoder(Embedding召回模型))
- [1.2 Cross-Encoder(Rerank精排模型)](#1.2 Cross-Encoder(Rerank精排模型))
- [2 对称任务 vs 非对称任务(BGE指令机制的根源)](#2 对称任务 vs 非对称任务(BGE指令机制的根源))
-
- [2.1 对称任务 Symmetric Tasks](#2.1 对称任务 Symmetric Tasks)
- [2.2 非对称任务 Asymmetric Tasks(检索任务属于此类)](#2.2 非对称任务 Asymmetric Tasks(检索任务属于此类))
- [3 Embedding基础概念与分类](#3 Embedding基础概念与分类)
-
- [3.1 什么是文本Embedding](#3.1 什么是文本Embedding)
- [3.2 Embedding三种类型](#3.2 Embedding三种类型)
- [4 Embedding评测体系 MTEB / C-MTEB](#4 Embedding评测体系 MTEB / C-MTEB)
-
- [4.1 MTEB(Massive Text Embedding Benchmark)](#4.1 MTEB(Massive Text Embedding Benchmark))
- [4.2 C-MTEB](#4.2 C-MTEB)
- [4.3 Scifact数据集(MTEB检索任务示例)](#4.3 Scifact数据集(MTEB检索任务示例))
- [5 BGE v1.5核心原理(重点面试)](#5 BGE v1.5核心原理(重点面试))
-
- [5.1 BGE v1.5 官方指令(只给Query!文档绝对不加)](#5.1 BGE v1.5 官方指令(只给Query!文档绝对不加))
- [5.2 BGE 核心本质](#5.2 BGE 核心本质)
- [5.3 为什么 BGE 要区分「带指令 / 不带指令」](#5.3 为什么 BGE 要区分「带指令 / 不带指令」)
- [5.4 向量空间三种情况对比](#5.4 向量空间三种情况对比)
- [5.5 输入输出向量规则](#5.5 输入输出向量规则)
- [5.6 工程标准函数封装(RAG 通用规范)](#5.6 工程标准函数封装(RAG 通用规范))
- [6 BGE-M3三合一模型(BAAI)](#6 BGE-M3三合一模型(BAAI))
- [7 完整工程链路:Embedding召回 → Top-K → Rerank精排](#7 完整工程链路:Embedding召回 → Top-K → Rerank精排)
- [8 BGE-M3与Milvus向量库集成](#8 BGE-M3与Milvus向量库集成)
- [9 总结](#9 总结)
- 可直接运行代码片段汇总
-
- [1. MTEB评测](#1. MTEB评测)
- [2. BGE v1.5 编码封装](#2. BGE v1.5 编码封装)
- [3. BGE-M3 编码](#3. BGE-M3 编码)
- [4. Milvus 集成](#4. Milvus 集成)
-
为什么 RAG 需要 Embedding 模型?
想象这样一个场景:你是一家企业的知识库管理员,库里躺着几十万份技术文档、产品手册和售后记录。用户抛来一句口语化的问题------"上次那个接口超时的问题最后怎么解决的?"------你需要在几秒内从海量文档里捞出最相关的那几段,交给大模型组织成答案。
如果让大模型逐篇读完所有文档再回答,成本和时间都不可接受;如果只靠关键词匹配,又会被"超时""接口"这类同义表达卡住,漏掉真正有用的内容。这正是 RAG(检索增强生成)要解决的问题,而它的第一步,就是用 Embedding 模型把文本变成向量,让"语义相近"变成"向量距离近",从而在百万级文档中快速完成召回。
本文围绕 BGE 系列模型展开,从两阶段检索流水线讲起,逐步拆解 BGE v1.5 的指令机制、BGE-M3 的三合一能力,以及它们如何与 Milvus 等向量库集成,最终串成一条可直接落地的 RAG 工程链路。无论你是准备面试,还是正在搭建生产级检索系统,这份实战笔记都值得从头读一遍。
1 RAG两阶段检索:Embedding召回 + Rerank精排 #embeddingrerank
RAG系统不会直接把全部文档送入大模型,采用先粗召回,后精细排序两阶段流水线,平衡速度与精度。
| 阶段 | 模型类型 | 核心目标 | 特点 |
|---|---|---|---|
| 第一阶段:召回Retrieval | Embedding(Bi-Encoder双塔) | 从十万/百万级文档库快速筛选Top-K候选文档 | 优先速度,牺牲部分精度;文档向量可以离线预先计算存入向量数据库 |
| 第二阶段:精排Rerank | Cross-Encoder重排器 | 对少量候选文档做细粒度相关性打分,重新调整顺序 | 优先精度 ,计算开销大;不能直接跑全量文档,仅用于召回输出的小候选集 |
1.1 Bi-Encoder(Embedding召回模型)
概念:双塔模型,逻辑上分为Query塔、Document塔。
⚠️重点:BGE v1.5底层并不是两套独立网络,是单套权重,靠输入指令区分两种输入;DPR才是真正双权重双塔。
- Query塔:把用户问题文本映射向量 V q V_q Vq
- Document塔:把知识库段落映射向量 V d V_d Vd
- 工程流程:
1)知识库全部文档提前离线编码,向量存入Milvus、FAISS等向量库;
2)用户提问到来,只编码Query得到向量;
3)向量库做ANN近似最近邻搜索,计算余弦/点积相似度,返回相似度最高的Top-K文档。 - 优点:文档可预计算,在线服务推理开销极小,支持百万级文档库。
1.2 Cross-Encoder(Rerank精排模型)
只有一个编码器,输入是拼接后的Query+Document句子对 ,格式:
[CLS] Query [SEP] Document [SEP]
- Query和文档的每一个token互相做Attention交互,模型看得见词与词之间细粒度匹配关系;
- 最后使用
[CLS]位置输出得到一个标量,代表这一组query-doc的相关性; - 每一个候选文档都需要独立跑一次前向传播。
- ✅优点:理解细粒度语义、指代、转折,相关性判断精度远高于Bi-Encoder;
- ❌缺点:速度慢。例如召回100条候选,就要跑100次模型推理;绝对不能直接对百万知识库全量跑Rerank。
2 对称任务 vs 非对称任务(BGE指令机制的根源)
2.1 对称任务 Symmetric Tasks
两个文本地位完全平等,可以互换,相似度(A,B) = 相似度(B,A)。
- 典型场景:文本聚类、信息去重、同义问句判断;
- 输入特点:两段文本长度、句式风格接近;
- BGE使用规则:不需要加任何指令前缀。
2.2 非对称任务 Asymmetric Tasks(检索任务属于此类)
Query和Document本身文本分布差异巨大:
- Query:简短问句、口语化、疑问句式、意图明确;
- Document:长陈述句、书面知识段落、大段上下文。
向量域偏移 :如果不加任何指令,同一个模型直接编码两种不同风格文本,会把语义相关的query和doc映射到向量空间不同子区域;明明语义匹配,但计算余弦距离很大,发生漏召回。
BGE解决方案:不训练两套网络,在Query文本前面拼接固定指令前缀,触发模型切换编码行为,把Query编码到和文档对齐的向量子空间,实现单权重完成非对称检索任务。
对比DPR:DPR采用真正两套独立权重的双塔,参数、显存翻倍,训练成本高;BGE单权重+指令,部署成本更低。
3 Embedding基础概念与分类
3.1 什么是文本Embedding
将离散的文本(字符串)映射为连续的低维数值向量。
- 核心性质1 语义保距:语义越接近的文本,向量在高维空间距离越近;语义无关距离更远。
- 核心性质2 维度权衡 :
- 高维度(1024):表征能力更强,占用显存、存储更大;
- 低维度(384/512):速度快、省内存,复杂语义表达能力会下降。
3.2 Embedding三种类型
- 传统词嵌入(统计):基于词频TF-IDF、BM25;无上下文语义理解,只做关键词匹配。
- 静态词向量(Word2Vec/GloVe) :一个词固定唯一向量;无法处理一词多义,同一个词在不同句子输出向量完全一样。
- 动态上下文嵌入(Transformer,BGE属于此类):根据上下文动态生成向量;相同词语不同语境输出不同向量;RAG主流选择。
BGE系列:基于XLM-Roberta架构;BGE-M3最大支持8192 token输入长度,支持长文档处理。
4 Embedding评测体系 MTEB / C-MTEB
4.1 MTEB(Massive Text Embedding Benchmark)
业界通用大规模嵌入评测基准,覆盖8大类任务:Retrieval检索、STS语义相似度、分类、聚类、重排等;支持上百种语言;提供可复现代码、公开排行榜。
4.2 C-MTEB
MTEB中文专项子集;专门面向中文语料评测。
✅RAG选型优先级指标:
- Retrieval任务 nDCG@10(最重要):衡量检索召回质量;
- STS分数(辅助):衡量语义辨别能力。
4.3 Scifact数据集(MTEB检索任务示例)
scifact是科学事实检索数据集,用于验证模型从论文摘要库检索支持/反驳科学论断的证据文档。
MTEB所有检索数据集统一三件套格式:
- Corpus文档库 :
_id、title、text;待检索全部文档; - Queries查询集 :
_id、text;代表用户查询/科学论断; - Qrels相关性标注 :
query-id、corpus-id、score;score=1代表这条文档对该查询是相关证据。
评测逻辑:输入query,模型召回文档列表,对比qrels人工标注,计算nDCG、Recall指标。
运行评测最简代码
python
import mteb
task = mteb.get_tasks(["SciFact"])
evaluator = mteb.MTEB(task)
model = mteb.get_model("BAAI/bge-base-zh-v1.5")
evaluator.run(model)
5 BGE v1.5核心原理(重点面试)
5.1 BGE v1.5 官方指令(只给Query!文档绝对不加)
中文Query指令:
text
为这个句子生成表示以用于检索相关文章:
英文Query指令:
text
Represent this sentence for searching relevant passages:
关键规则:
- 检索任务:Query拼接指令,知识库Chunk直接裸文本输入;
- 聚类、STS相似度任务:全部文本不加指令;
- 不要自己改写指令字符串,模型只见过训练时固定前缀;
- v1.5可以不带指令跑通,但短问句场景召回指标显著下降。
5.2 BGE 核心本质
- BGE 是单编码器共享权重的检索嵌入模型(区别于 DPR 双塔双编码器)。
- 专门为非对称检索任务训练:短问句(Query)匹配长文档(Passage/Corpus)。
- 核心特性:同一个模型、同一套权重,通过输入指令前缀切换编码行为,无需两套网络。
- 适用场景:RAG 检索、文本相似度匹配;不适用场景无特殊限制,但非检索任务不需要指令。
5.3 为什么 BGE 要区分「带指令 / 不带指令」
根本原因:检索是不对称任务。
- Query:短、问句、口语化、疑问句式
- Passage:长、陈述句、书面化、知识片段
两者文本分布天然不同,直接编码会出现向量域偏移:语义相关但向量距离很远,导致漏召回。
指令的真实作用(极简大白话):
指令 = 向量空间坐标转换器
- Query 加指令:告诉模型「当前是检索提问,请编码到 Query 向量子空间」
- Passage 不加指令:模型默认编码到 Passage 向量子空间
最终让两个不同分布的文本,向量空间可精准匹配、计算相似度有效。
为什么不用「两个独立函数 / 双编码器」?
- 双编码器(DPR 方案)效果好,但参数翻倍、显存翻倍、训练推理成本极高
- BGE 采用单权重 + 指令触发,参数复用、轻量化、部署简单,是性价比最优方案
- 注意:工程层可以封装两个函数,但模型底层仍是同一套权重,不是真双塔
5.4 向量空间三种情况对比
| 使用方式 | 结果 | 现象 |
|---|---|---|
| ✅正确:Query+指令,文档无指令 | 向量子空间对齐 | 相似度计算有效,召回最优 |
| ⚠️错误:全部文本不加指令 | query-doc分布偏移 | 大量相关文档距离拉大,漏召回,Recall下降 |
| ❌致命错误:文档也拼接指令 | 两边同时偏移,匹配规则完全破坏 | 检索结果完全不可用,性能毁灭性下跌 |
5.5 输入输出向量规则
- 输入单字符串:输出一维数组
(768,),一个向量; - 输入字符串list:输出二维数组
(N,768);每条文本独立向量,不会把多条文本融合合并为一个向量;批量只是并行加速; normalize_embeddings=True:开启L2归一化;归一化之后余弦相似度等价向量点积;向量库检索必须开启该参数。
注意:
encode_queries()、encode_corpus()不是库原生函数,属于工程封装,底层调用同一个model.encode,底层模型权重没有两套。
5.6 工程标准函数封装(RAG 通用规范)
python
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-base-zh-v1.5')
QUERY_INS = "为这个句子生成表示以用于检索相关文章:"
# 批量编码提问(加指令)
def encode_queries(queries: list[str]):
return model.encode([QUERY_INS + q for q in queries], normalize_embeddings=True)
# 批量编码知识库文档(无指令)
def encode_corpus(corpus: list[str]):
return model.encode(corpus, normalize_embeddings=True)
6 BGE-M3三合一模型(BAAI)
BGE-M3不再需要Query指令前缀!和v1.5重要区别。
三大能力集成到同一个权重:Dense密集检索 + Sparse稀疏词法检索 + ColBERT多向量细粒度检索。
三种检索模式
-
Dense密集向量检索
输出单个1024维稠密向量;依靠整体语义做匹配;擅长同义词、转述、深层语义;缺点:容易遗忘特殊关键词。
-
Sparse稀疏检索(Lexical词法)
输出token权重稀疏向量,类似BM25;重点捕捉关键词、专有名词、型号;解决dense"关键词遗忘";大部分维度为0。
-
ColBERT多向量检索(Multi-vector)
每一个token输出独立向量矩阵;查询token和文档所有token做late-interaction晚交互匹配;匹配粒度最细,效果最好,存储、计算开销最高。
工程常用Hybrid混合检索 :
hybrid_score = w1*dense_score + w2*sparse_score;同时利用语义能力+关键词能力;Milvus、Vespa原生支持BGE-M3混合检索。
模型规格:维度1024,最大序列长度8192,支持100+语言;依赖库:
pip install -U FlagEmbedding。
BGE-M3编码示例
python
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
sentences = ["什么是BGE-M3"]
out = model.encode(sentences, return_dense=True, return_sparse=True, return_colbert_vecs=False)
# out["dense_vecs"] 稠密向量
# out["lexical_weights"] 稀疏token权重
7 完整工程链路:Embedding召回 → Top-K → Rerank精排
完整RAG链路流程:
- 使用BGE-M3对Query、知识库文档编码,得到dense、sparse向量;
- 计算dense得分、sparse得分,加权得到hybrid混合得分,排序召回候选集;
- 截取召回结果Top-K(例如k=3~10,不能太大);
- 构造
[query,doc]句子对送入FlagReranker; - Reranker输出logits,sigmoid映射0-1相关性分数;
- 使用rerank分数重新排序,输出最终结果给LLM。
关键点:Reranker只做重排,不做召回。
8 BGE-M3与Milvus向量库集成
milvus_model封装好BGEM3EmbeddingFunction,可以直接输出dense/sparse向量,适配Milvus混合检索。
python
from milvus_model.hybrid import BGEM3EmbeddingFunction
model_path = "./bge-m3"
embedding_func = BGEM3EmbeddingFunction(model_name=model_path, device="cpu", use_fp16=False)
texts = ["测试文本"]
emb = embedding_func.encode_documents(texts)
dense_vec = emb["dense"]
sparse_vec = emb["sparse"]
9 总结
- BGE-v1.5:单编码器共享权重 + Query指令前缀,解决非对称检索query-doc向量域偏移;指令只给查询,文档不加。
- BGE-M3一体化模型,支持Dense / Sparse / ColBERT三种检索,不再需要指令,适合混合检索RAG。
- RAG标准流水线:Embedding(Bi-Encoder)快速召回候选 → Rerank(Cross-Encoder)做少量候选精排,兼顾速度与精度。
- 评测看MTEB/C-MTEB,RAG优先看Retrieval任务nDCG@10指标。
一句话极简总结
BGE 依靠单模型权重共享 + Query 指令前缀触发向量空间切换,解决问答与文档的非对称匹配问题;工程上封装为 encode_queries、encode_corpus 区分场景,批量输入生成多条独立向量,是 RAG 检索的标准嵌入方案。
可直接运行代码片段汇总
1. MTEB评测
python
import mteb
task = mteb.get_tasks(["SciFact"])
evaluator = mteb.MTEB(task)
model = mteb.get_model("BAAI/bge-base-zh-v1.5")
evaluator.run(model)
2. BGE v1.5 编码封装
python
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('BAAI/bge-base-zh-v1.5')
QUERY_INS = "为这个句子生成表示以用于检索相关文章:"
def encode_queries(queries: list[str]):
return model.encode([QUERY_INS + q for q in queries], normalize_embeddings=True)
def encode_corpus(corpus: list[str]):
return model.encode(corpus, normalize_embeddings=True)
3. BGE-M3 编码
python
from FlagEmbedding import BGEM3FlagModel
model = BGEM3FlagModel('BAAI/bge-m3', use_fp16=True)
sentences = ["什么是BGE-M3"]
out = model.encode(sentences, return_dense=True, return_sparse=True, return_colbert_vecs=False)
# out["dense_vecs"] 稠密向量
# out["lexical_weights"] 稀疏token权重
4. Milvus 集成
python
from milvus_model.hybrid import BGEM3EmbeddingFunction
model_path = "./bge-m3"
embedding_func = BGEM3EmbeddingFunction(model_name=model_path, device="cpu", use_fp16=False)
texts = ["测试文本"]
emb = embedding_func.encode_documents(texts)
dense_vec = emb["dense"]
sparse_vec = emb["sparse"]