RAG召回不准?向量检索后加一层rerank救回来

📅 2026/6/28 15:04:38
RAG召回不准?向量检索后加一层rerank救回来
先说结论:如果你的RAG答非所问,八成不是大模型的锅,是召回的那几段根本没把对的内容排到前面。最省事的解法,是在向量检索之后、丢给大模型之前,塞一层重排序(rerank),把真正相关的拎到Top3。我上周就靠这一步,把一个内部问答的命中率从一半多干到九成。下面是完整过程和前后数据。问题:向量召回回来的,看着像、其实不对背景是这样。我给团队做了个查公司制度的小工具,知识库是47篇规章,切成大概600个chunk,用的BGE做embedding,余弦相似度召回Top5,然后塞给大模型让它总结回答。跑demo挺好,一上线就翻车。有个同事问试用期能不能请年假,召回回来的Top5里,前三段全是年假天数怎么算年假过期作废这种——主题词全中,年假两个字密度拉满,可就是没答到试用期那个限制条件上。真正写着试用期员工不享受年假的那段,排在第6,压根没进Top5。大模型拿着前五段一本正经地编,告诉人家试用期能请5天。我当场就有点慌。排查下来,根因是向量相似度只管语义像不像,不管能不能回答这个问题。年假怎么算和试用期能不能请年假,在向量空间里挨得特别近,但对用户来说一个有用一个没用。bi-encoder(双塔)把query和文档分开编码,本来就丢信息,细粒度的相关性它分辨不出来。加rerank那一步:让模型重新读一遍query和文档思路很简单:向量检索负责召得全(粗排,捞回Top20),rerank负责排得准(精排,从20里挑出真正相关的Top3)。关键区别在于,rerank用的是cross-encoder——把query和每篇候选文档拼在一起喂进模型,让它直接打一个相关性分。query和文档之间的token能互相看见,试用期和不享受年假的对应关系,这下能被捕捉到了。代价是慢,所以只在小范围候选上做。代码改动其实就夹一层,我用的sentence-transformers的CrossEncoder:from sentence_transformers import CrossEncoder reranker CrossEncoder(BAAI/bge-reranker-base) def retrieve(query, top_k3): # 1. 向量粗排,先捞回20条 candidates vector_search(query, top_k20) # 2. query 和每条候选拼一起打分 pairs [[query, doc.text] for doc in candidates] scores reranker.predict(pairs) # 3. 按 rerank 分数重排,取前3 ranked sorted(zip(candidates, scores), keylambda x: x[1], reverseTrue) return [doc for doc, _ in ranked[:top_k]]就这么几行。粗排把范围圈到20,精排在20里精挑3条交给大模型。那段试用期不享受年假,rerank分数直接飙到第1。召回前后对比:数据摆这我手攒了60个真实问题做评测集,标好每个问题对应的标准答案chunk,量了三个指标。纯向量 vs 向量rerank:指标纯向量Top5向量Top20rerank Top3Hit3(对的进前三)58%91%MRR(正确答案平均排名倒数)0.610.88答案被同事吐槽不对19次/天2次/天单次检索耗时~80ms~430ms最直观的是那个吐槽数,从一天被戳19次降到2次。MRR从0.61到0.88,意味着对的内容基本稳定排在第一第二位,大模型不用再从一堆噪声里猜。代价也写在表里了:延迟从80ms涨到430ms,翻了五倍多。reranker毕竟要逐条过模型。真实取舍:它不是免费的说几个我踩过、你大概率也会遇到的点。慢是真的慢。候选从20加到50,延迟能到800ms以上,体感就有点卡了。我的折中是粗排只给20条,够用了——再多rerank也救不回向量没召回来的东西,精排不背粗排的锅。小模型够用,别上来就堆大的。我先试了bge-reranker-large,效果好一丢丢,但慢一截。base版在我这60题上只差2个点,果断用base。reranker也不是神。有几个问题是知识库本身就没写清楚,rerank排得再对,大模型也答不出来,这跟检索没关系。别指望一层rerank包治百病。还有个小插曲:我一开始把rerank分数当成概率卡了个0.5的阈值,结果好多对的被滤掉了——bge-reranker输出的是logits,不是0~1的概率,直接拿来排序就行,别瞎卡阈值。这坑我debug了俩小时。最后补一句。这套东西我没全自己写。粗排精排串起来、知识库挂上去、最后发布成一个能用的问答接口,我是在一个零代码就能拖配智能体的平台上搭的,可视化配检索流程,rerank这层勾一下就接进去了,省了我不少胶水代码。它干的是杂活,真正调参挑模型还得自己来,但确实让我把精力留在了刀刃上。底层大模型API我走的讯飞星辰MaaS,现成调,没自己折腾算力部署。你们的RAG翻车,是召回没召全,还是召全了排不准?评论区聊聊,我顺手帮看看是不是也该夹层rerank。