12 RAG 搜不准?面试官想听的是“混合检索+重排“,不是换个向量库
候选人:"我的 RAG 知识库效果不好,用户问的问题经常搜不到正确答案。"
面试官:"你怎么优化的?"
"我换了更好的向量库,从 Milvus 换成了 Elasticsearch。"
"换库解决了吗?"
"……没有,还是搜不准。"
面试官在心里叹了口气。向量库只是存储,不是检索质量的瓶颈。搜不准,90% 的问题是检索策略的问题——而检索策略的终极答案,是"混合检索 + 重排"。
这篇把 RAG 检索质量的完整方案讲透。这也是 RAG 系列的收官之篇。
为什么纯向量检索会搜不准?
先看一个场景。用户问:
"iPhone 15 Pro 256G 现在什么价格?"
你的知识库里有一篇文档:《iPhone 15 Pro 各版本价格表(256G 版本 ¥8999)》。
纯向量检索会怎样?
它把问题变成向量,去库里找"语义最接近"的文档。问题里有"iPhone 15 Pro"、"256G"、"价格"这些关键词,文档里也都有——理论上应该能搜到。
但有两个情况向量检索会翻车:
情况一:精确匹配失效。用户问的是"IP15 Pro 256G 多少钱"(口语化缩写)。向量检索能理解"IP15 Pro"≈"iPhone 15 Pro",但如果你问的是订单号"PO-20240715-001"、型号"RTX4090"这种必须精确匹配的信息,向量检索的"模糊"反而成了缺点——它可能给你返回语义相似但编号不同的文档。
情况二:语义漂移。两个文档都"看起来相关",向量检索分不清哪个才是真正回答了用户的问题。比如用户问"退货政策",库里有《退货政策 v2.0》和《售后常见问题(含退货案例)》,向量距离可能差不多,但前者才是权威答案。
纯向量检索的命门:它只懂"像不像",不懂"对不对"。
方案:混合检索(Hybrid Search)
思路很简单:用两种检索器各搜一遍,把结果融合。
-BM25(关键词检索):精确匹配,订单号、型号、人名、专有名词它最强。不懂同义词,但命中即精准。
-向量检索(语义检索):理解同义词、口语化表达。模糊但覆盖面广。
两个检索器各返回 Top-50,然后融合成一份 Top-10。
融合算法推荐 RRF(Reciprocal Rank Fusion,倒数排名融合):
score(d) = Σ 1 / (k + rank_i(d))k 一般取 60。核心思想:不看分数看排名。一个文档在 BM25 里排第 3、在向量检索里排第 5,它的融合分就是 1/63 + 1/65。两边都靠前的文档胜出。
为什么用 RRF 而不是加权平均?因为 BM25 的分数和向量相似度根本不是一个量纲,没法直接加权。RRF 只看排名,天然免疫两个检索器的"分数尺度不一致"问题,还不用调权重。
Spring AI 里启用混合检索:
// 配置 ES 的混合检索(关键词 + 向量) SearchRequest request = SearchRequest.builder() .query(question) .topK(50) .searchType(SearchType.HYBRID) // 混合检索 .build(); List<Document> docs = vectorStore.similaritySearch(request);生产上常见组合:Elasticsearch(自带 BM25 + 向量)、pgvector(BM25 用 PostgreSQL 全文检索 + 向量列)、或 Milvus + 独立 ES。
进阶:两阶段检索(召回 + 重排)
混合检索解决了"搜得全不全",还没解决"排得准不准"。融合后的 Top-10 里,可能还是混着几个"相关但不对"的文档。
这时候上重排(Rerank)。
第一阶段(召回):BM25 + 向量检索,各取 Top-50,RRF 融合去重成 Top-30。要求:快、全,宁可多捞不能漏。
第二阶段(精排):用 Rerank 模型(如 bge-reranker-v2-m3)对 Top-30 逐条精算相关性,取 Top-5 送入 LLM。要求:准。
为什么 Rerank 比向量检索准?因为向量检索是"把问题和文档分别编码成向量,再算距离"——两个独立编码,信息在编码过程中被压缩丢失了。Rerank 模型是把"问题+文档"拼接在一起,做交叉编码(Cross-Encoder),模型能看到问题和文档的完整交互,相关性判断精细得多。
# bge-reranker 用法示意(伪代码) from sentence_transformers import CrossEncoder reranker = CrossEncoder('BAAI/bge-reranker-v2-m3') pairs = [(question, doc) for doc in top30] scores = reranker.predict(pairs) # 逐条打分 # 按分数排序,取 Top-5 top5 = sorted(zip(top30, scores), key=lambda x: -x[1])[:5]成本账:Rerank 是逐条计算,30 条大概多花 200ms。但这 200ms 换来的是:进 LLM 的文档从 10 条变 5 条,Token 消耗减半,回答质量明显提升,总成本反而降了。
查询改写:容易被忽略的第一步
混合检索之前,还有一个前置环节——查询改写(Query Rewriting)。用户的原始提问往往不适合直接检索:
- "那个 256G 的手机多少钱" —— "那个"指代不明
- "跟刚才说的那款比呢" —— 依赖上下文
- "帮我看看" —— 太口语化,检索词太少
不改写就检索,召回质量天然受限。常见的改写策略:
策略一:指代消解。结合历史对话,把"那个"、"它"替换成实际指代的对象:
String REWRITE_PROMPT = """ 结合历史对话,把用户的问题改写成适合检索的独立问句。 如果问题已经清晰,原样输出。 【历史对话】 {history} 【用户问题】 {question} 输出改写后的问句,不要任何解释。 """;策略二:扩展关键词。用户问"退款",改写时补充同义词"退货、退款流程、退款政策",提高 BM25 的命中率。
策略三:拆解复合问题。"帮我查一下订单状态和物流信息" → 拆成"订单状态"、"物流信息"两个检索请求,分别召回再合并。
查询改写的成本:每次多一次小模型调用(几厘钱 + 几百毫秒)。值得吗?我们实测:加上改写后,检索命中率提升了 8 个百分点。性价比很高,尤其是用户提问口语化严重的场景。
什么时候可以不用重排?
重排不是银弹,两个场景可以省掉:
场景一:知识库规模小(几百条以内)。暴力检索 + 人工维护的文档质量够高,Top-K 直接送 LLM 也行。杀鸡不用牛刀。
场景二:延迟极度敏感。Rerank 多花 200ms,如果产品要求首字延迟 < 1 秒,就要权衡。可以降级方案:只对 Top-10 做 Rerank,而不是 Top-30。
判断标准:检索结果里"相关但不对"的噪声多不多。噪声多 → 上重排;噪声少 → 省掉。用测试集评估,别拍脑袋。
一个完整的生产链路
把前面所有知识串起来,一个生产级 RAG 检索链路:
用户问题 ↓ ① 查询改写(可选):口语化 → 标准问法 ↓ ② 混合召回:BM25 取 Top-50 + 向量检索取 Top-50 ↓ ③ RRF 融合去重 → Top-30 ↓ ④ Rerank 精排 → Top-5 ↓ ⑤ 注入 Prompt(带引用来源)→ LLM 生成回答 ↓ ⑥ 回答后校验:引用是否真实存在每一步都有它的职责:
- 查询改写解决"用户问得糙"
- 混合召回解决"搜不全、精确匹配失效"
- RRF 融合解决"两个检索器怎么合并"
- Rerank 解决"排得不准"
- 引用校验解决"幻觉"
面试官问"RAG 检索质量怎么优化",你能把这条链路讲出来,就是 P6 级别的答案。
🎯 面试官视角的标准回答
如果面试官问:"RAG 检索质量差,你怎么优化?"
先说结论:检索质量差,90% 不是向量库的问题,是检索策略的问题。我的方案是混合检索 + 两阶段精排。<br><br>第一步,混合检索。纯向量检索的弱点是"只懂像不像,不懂对不对",订单号、型号这类精确信息容易翻车。所以我用 BM25 关键词检索 + 向量语义检索并行,各取 Top-50。BM25 保精确,向量保语义,互补。<br><br>第二步,RRF 融合。两个检索器的分数不是一个量纲,不能直接加权。RRF 只看排名,score = Σ 1/(k+rank),两边都靠前的文档胜出,不用调权重。<br><br>第三步,Rerank 精排。融合后的 Top-30 用交叉编码模型逐条精算相关性,取 Top-5 送 LLM。Rerank 把问题和文档拼接编码,比向量检索的独立编码精细得多。<br><br>补充效果数据:混合检索 + 重排后,我们的检索命中率从 62% 提升到 91%,进 LLM 的 Token 少了,成本也降了。<br><br>补充一点:检索质量是系统工程,查询改写、混合召回、融合、精排、引用校验,每一环都值得做。只换向量库解决不了问题。
AIGC 面试系列 13-17 篇:流式输出、上下文记忆、幻觉治理、Embedding、混合检索重排,到这里告一段落。
从"AI 回答怎么蹦出来的"到"向量怎么来的",再到"搜不准怎么办"——这些不是孤立的知识点,而是一个完整 AIGC 后端工程师的实战地图。你把这五篇吃透,配合前面 12 篇,AIGC 方向的面试基本能平趟。
老规矩,面试官问 AIGC,不背答案,要能聊。
