RAG遇到中文召回差,Embedding模型选型与调优
RAG 项目里中文召回差,最直接的原因通常是三条:用的 Embedding 模型对中文不友好、文档切成小块时机不对、只靠向量相似度做排序。
解决路径也很清晰:换一个中文语料优化的嵌入模型,调整文本切分布局,再引入重排序环节。
这篇教程面向零基础用户,给出了每一步可照做的选型与调优操作,并在最后提供命中率验证方法,让你改完能确认效果。
找出中文召回差的真正位置
先别急着换模型。
把当前检索结果打印出来,观察返回的文档片段和问题是否语义相关。
常见现象有三类:问题词和文档词完全不同但意思接近,说明模型语义理解不行;
返回片段主题对但细节偏,说明切分块过大;
检索结果不稳定,说明相似度阈值或 TopK 设置不适合。
中文 Embedding 模型选型要点
选型只看三点:训练语料是否以中文为主、文本长度支持是否够用、能否在本机直接加载。
常用选项中,BAAI/bge-large-zh、m3e-base 都是中文场景常见的开源模型,具体版本和训练方式以模型仓库说明为准。
以 bge 为例,加载方式:
from sentence_transformers import SentenceTransformer
model = SentenceTransformer("BAAI/bge-large-zh")
vector = model.encode("我需要优化中文检索")
如果服务器显存只有 8G 左右,优先选 bge-base-zh 或更小的 m3e-small。
这里要特别注意的是:更换模型后,向量库中已有数据必须重新编码,否则新旧向量无法比较。
调优核心:切分粒度与检索重排
换模型之外,文本切分对中文召回影响巨大。
中文建议按 300-500 字切块,块之间保留 30-80 字重叠,防止语义断句。
代码示例:
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=400,
chunk_overlap=50
)
检索阶段不要只信向量相似度。
先用向量检索取 top50,再用重排序模型对 query 和候选文档重新打分,取 top5。
如果不想引入额外模型,也可以用 BM25 和向量分数做加权合并,得到混合召回结果。
简单可用的重排代码如下:
from bge_reranker import Reranker
reranker = Reranker("BAAI/bge-reranker-base")
scores = reranker.compute_score([(query, doc) for doc in candidates])
避坑:这些改动越调越差
相似度阈值不要固定用 0.5。
不同模型输出的向量分布差异很大,建议用验证集看命中的分数,再定阈值。不要高频微调 Embedding 模型。
数据集只有几百条时,微调通常不如调切分和重排效果好。每次只改一个变量。
同时改模型和切分长度,出了问题很难定位。
效果验证:用命中率说话
准备 30 个中文问题,每个问题标出正确的文档片段。
对每个问题执行检索,看 top5 里是否包含正确答案。
统计命中率:从 40% 提升到 70%,说明调整有效。
验证脚本的核心代码思路:
hit_count = 0
for q, gold_doc in test_set:
results = search(q, top_k=5)
if any(doc.id == gold_doc.id for doc in results):
hit_count += 1
print(f"top5命中率: {hit_count / len(test_set):.2%}")
如果命中率没变化,回查切分后文本是否有内容被截断,或向量库是否混入了旧模型的向量。
最后补充一句:RAG 中文召回调优不是一次到位,建议按“模型选型→切分调整→重排→命中率验证”的顺序循环,每次记录数据。
具体模型版本和参数以官方文档为准。