做 RAG 优化时,我以前容易把“召回”和“排序”混在一起。
后来才意识到,这是两个不同阶段:
召回率优化:让正确文档先进入候选集合
排序优化:候选已经存在,决定谁排前面
这句话非常重要。
因为 rerank 再强,也只能重排已经进入候选集合的 chunk。如果正确 chunk 一开始没有被召回,rerank 没有机会救它。
1. 当前 RAG 链路怎么分层
一个增强版 RAG 链路大概是:
用户问题
-> Query Rewrite
-> 向量召回 / 多路召回
-> RRF 融合排序
-> rerank 精排
-> 取 TopK 给大模型回答
这里可以分成两类能力:
| 能力 | 主要解决什么 |
|---|---|
| Query Rewrite | 让问题更适合检索 |
| HyDE | 用假设答案增强语义召回 |
| BM25 / 向量 / 标题检索 | 扩大候选来源 |
| RRF | 融合多路召回结果 |
| rerank | 在候选集合内重新排序 |
所以如果正确资料没有进入候选集合,优先查召回链路,而不是先怀疑 rerank。
2. RRF 到底在做什么
RRF 全称是 Reciprocal Rank Fusion,中文可以叫倒数排名融合。
它的作用是把多路检索结果合成一个最终列表。
比如 RAG 里可以同时有:
- 原问题向量检索。
- HyDE 向量检索。
- BM25 关键词检索。
- 标题或标签检索。
每一路都会返回一份排行榜。RRF 不直接看原始分数,而是看排名。
一个文档在多个榜单都靠前,它的融合分数就更高。
简化理解:
某个 chunk 在原问题检索里排第 2
又在 HyDE 检索里排第 3
说明它被多路召回共同认可
RRF 会把它排得更靠前
RRF 的好处是简单、稳定,不太依赖不同检索器的分数尺度是否一致。
3. rerank 更适合解决排序问题
rerank 的输入通常是:
query + 候选 passages
它输出每个候选和问题的相关性分数。
所以它擅长做这件事:
候选集合里已经有正确 chunk
但是原始向量分数排序不够好
rerank 把更相关的 chunk 提到前面
但它不擅长这件事:
正确 chunk 根本没进候选集合
要求 rerank 凭空找回来
这就是为什么评估 rerank 时,不能只看最终回答,还要看 rerank 前的 fusedDocs。
4. 为什么要看 debug trace
RAG 优化必须能看到每一步结果。
我希望 debug 至少能返回:
originalResults
hydeResults
fusedResults
rerankedResults
answerResults
这样才能判断问题在哪里。
如果 originalResults 和 hydeResults 都没有正确 chunk,那是召回问题。
如果 fusedResults 有正确 chunk,但排得很靠后,那是融合排序问题。
如果 rerankedResults 把正确 chunk 往后排了,那是 rerank 问题。
如果检索结果很好,但回答仍然胡说,那是 Prompt 或生成阶段问题。
5. CandidateK 和 TopK 要分开
一个很常见的问题是:检索阶段和最终回答阶段都用同一个 topK。
例如:
检索 topK = 5
rerank topK = 5
回答 topK = 5
这样候选集合太小,rerank 没有发挥空间。
更合理的方式是:
CandidateK = 20 或 30
AnswerTopK = 5
也就是先多召回一些候选,再让 RRF 和 rerank 排序,最后只取少量片段给大模型回答。
这能提高正确 chunk 进入候选池的概率,同时避免最终上下文太长。
6. 优化顺序建议
我现在更倾向于按这个顺序优化 RAG:
- 先保证入库数据完整,旧索引能清理。
- 加 debug trace,能看到每一步结果。
- 做固定问题集,标注正确文章或正确 chunk。
- 提高 CandidateK,观察 Recall@K。
- 加多路召回,例如原问题、HyDE、BM25。
- 用 RRF 融合候选。
- 再接 rerank,并观察排序是否真的改善。
这个顺序的核心是:先让正确答案进候选池,再谈精排。
7. 小结
RAG 效果不好时,不要只盯着最终回答。
应该沿着链路往前拆:
没召回:召回率问题
召回了但排后面:排序问题
排前面但回答错:生成问题
rerank 是重要能力,但不是万能补丁。
只有候选集合足够好,rerank 才能真正发挥作用。
