RAG召回不准?向量检索后加一层rerank救回来
2026/6/30 7:54:21 网站建设 项目流程

先说结论:如果你的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_k=3): # 1. 向量粗排,先捞回20条 candidates = vector_search(query, top_k=20) # 2. query 和每条候选拼一起打分 pairs = [[query, doc.text] for doc in candidates] scores = reranker.predict(pairs) # 3. 按 rerank 分数重排,取前3 ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True) return [doc for doc, _ in ranked[:top_k]]

就这么几行。粗排把范围圈到20,精排在20里精挑3条交给大模型。那段"试用期不享受年假",rerank分数直接飙到第1。

召回前后对比:数据摆这

我手攒了60个真实问题做评测集,标好每个问题对应的标准答案chunk,量了三个指标。纯向量 vs 向量+rerank:

指标

纯向量Top5

向量Top20+rerank Top3

Hit@3(对的进前三)

58%

91%

MRR(正确答案平均排名倒数)

0.61

0.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。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询