Retrieval 决定 RAG 质量的上限
- 年,RAG 项目中「将生成模型从 GPT-4 换成 Claude Opus 4.7 后突然变聪明了」的案例极为罕见。大多数问题出在 Retrieval 阶段未能检索到所需信息,或者检索到噪音导致 LLM 判断混乱。回顾 KGA 过去18个月支持的 RAG 项目,仅凭 Retrieval 改善,用户体感评分即提升30至60%,而更换 LLM 模型带来的平均提升仅约8%。
本文将2026年时点的 RAG Retrieval 定式,从五个层次加以梳理:(1) 混合检索,(2) 重排序器,(3) 查询改写,(4) 分块策略,(5) 评估指标。
层次1:Hybrid Search(BM25 + Dense)
单一的 Dense 检索,在型号("ZX-450B")、缩略词("TLS1.3")、人名、特定数值条件上频繁失败。BM25 则相反,对语义层面的同义表达较弱。将两者通过 Reciprocal Rank Fusion(RRF) 融合,已成为行业标准。
RRF 极为简洁:将各检索结果的排名转换为 `1 / (k + rank)` 后相加(k=60 为默认值)。无需担心分数的量纲差异即可融合,对不同模型组合具有通用效果。
```python from collections import defaultdict
def rrf_fusion(ranked_lists: list[list[str]], k: int = 60) -> list[tuple[str, float]]: scores = defaultdict(float) for results in ranked_lists: for rank, doc_id in enumerate(results, start=1): scores[doc_id] += 1.0 / (k + rank) return sorted(scores.items(), key=lambda x: -x[1])
bm25_top = bm25_index.search(query, k=50) dense_top = vector_db.search(query_embed, k=50) fused = rrf_fusion([bm25_top, dense_top])[:20] ```
KGA 的法务检索项目中,BM25 单独 nDCG@10=0.52,Dense 单独0.61,RRF 混合达到0.73。方法简单,效果显著。
层次2:重排序器(Cohere Rerank-3、Voyage Rerank-2)
混合检索取得 top 50至200后,再用交叉编码器型重排序器进行精确评分的两阶段设计,是2026年的事实标准。Dense 检索采用对查询与文档分别向量化的双编码器结构,丢失了 query-document 间的交互信息。重排序器则将两者同时输入 Transformer,输出真实关联度。
- Cohere Rerank-3(2025年9月发布):支持100种语言,128k 上下文,$2/1k queries
- Voyage Rerank-2:32k 上下文,MTEB Reranking 排名领先,$0.5/1k queries
- Jina Reranker v2:偏向开源的选项,日语性能略逊于 Cohere
- BGE-Reranker-v2-m3:MIT 许可证开源,自托管运营的首选
```python import cohere
co = cohere.Client() results = co.rerank( model="rerank-3", query="数据保护法中跨境传输的要求", documents=[d.text for d in top50], top_n=10, ) reranked = [top50[r.index] for r in results.results] ```
重排序器的效果极为显著,KGA 平均实测 nDCG@10 从 0.65 提升至 0.82 前后。但 top 50 情况下会额外增加200至400ms 延迟,检索 UX 设计上需要异步化,或采用仅对第一阶段高速响应的方案。
层次3:查询改写(HyDE、Query2Doc、Multi-Query)
用户的查询往往短而模糊(「合同解除」「内存使用率」)。先用 LLM 扩展再进行检索的技法,已在2026年的 Retrieval 设计中普及。
- HyDE(Hypothetical Document Embeddings):让 LLM 生成假想的回答文档,将其 embedding 作为检索键
- Query2Doc:将查询与假想文档拼接后取 embedding(HyDE 的变体,在许多场景下更稳定)
- Multi-Query:让 LLM 生成3至5个意图相同的不同表达,并行检索后以 RRF 融合
```python def hyde_search(query: str, vector_db, llm): hypothetical = llm.generate( f"请用3句话写出以下问题的假想回答:{query}" ) combined = query + " " + hypothetical # Query2Doc 化 embed = embedding_model.encode(combined) return vector_db.search(embed, k=50) ```
Multi-Query 的性价比最高,KGA 内部 FAQ RAG 中,相较单一查询检索,Recall@20 提升了12%。代价是每次查询多一次 LLM 调用,但使用 Claude Haiku / GPT-4.1-mini 执行,费用不到0.1日元。
层次4:分块策略
仍在以「每1,000字符分割」方式运营的 RAG,在2026年时点已属过时。分块方式直接决定 Retrieval 质量。
- Fixed-size:经典的按 N 字符分割,重叠率10至20%
- Recursive Character Splitting:按段落→句子→词语的顺序递归切分,为 LangChain `RecursiveCharacterTextSplitter` 的默认方式
- Semantic Chunking:对连续句子进行 embedding,在余弦相似度急剧变化处切分,生成语义上凝聚的分块
- Late Chunking(Jina 2024/2025):先将长文整体输入 embedding 模型,取各 token 的 embedding 后再平均化形成分块,分块保留了周围语境,不会丢失代词与照应关系
- Hierarchical / Parent-Child:以小分块检索,以父分块传入生成阶段,兼顾精度与上下文量
Late Chunking 实现示例:
```python from transformers import AutoModel, AutoTokenizer import torch
model = AutoModel.from_pretrained("jinaai/jina-embeddings-v3", trust_remote_code=True) tokenizer = AutoTokenizer.from_pretrained("jinaai/jina-embeddings-v3")
def late_chunking(long_text: str, chunk_boundaries: list[tuple[int, int]]): inputs = tokenizer(long_text, return_tensors="pt", truncation=True, max_length=8192) with torch.no_grad(): token_embeds = model(**inputs).last_hidden_state[0] chunk_embeds = [] for start, end in chunk_boundaries: chunk_embeds.append(token_embeds[start:end].mean(dim=0)) return torch.stack(chunk_embeds) ```
KGA 在合同 RAG 中引入 Late Chunking 后,在「依第3条规定」等相互引用密集的文档中,Recall@10 从71%提升至88%。
层次5:评估指标与离线评估
Retrieval 无法以「能用就行」来评估,必须有定量指标与回归测试。
- Recall@k:正确文档出现在前 k 位的比例,决定 RAG 上限的最重要指标
- nDCG@10(Normalized Discounted Cumulative Gain):加入排名质量的综合评估,商业检索的事实标准
- MRR(Mean Reciprocal Rank):第一个正确结果的排名倒数的平均值,接近用户体感
- Hit Rate@k:「前 k 位中是否有正确结果」的简单二值指标
评估集至少需要100个查询,理想为500个。以 CSV 保存查询与正确文档 ID 的配对,在 CI 中进行回归测试。
```python import numpy as np
def ndcg_at_k(ranked_ids: list[str], relevance: dict[str, int], k: int = 10) -> float: gains = [relevance.get(doc_id, 0) for doc_id in ranked_ids[:k]] dcg = sum((2 g - 1) / np.log2(idx + 2) for idx, g in enumerate(gains)) ideal_gains = sorted(relevance.values(), reverse=True)[:k] idcg = sum((2 g - 1) / np.log2(idx + 2) for idx, g in enumerate(ideal_gains)) return dcg / idcg if idcg > 0 else 0.0 ```
LlamaIndex 的 `RetrieverEvaluator`、RAGAS、Trulens 等框架已标准化上述计算,比起自行实现,2026年的定式是先引入 RAGAS。
生产参考架构
KGA 在2026年提议的生产 RAG 标准架构如下:
- 导入:文档 → Late Chunking(Jina v3)→ 存储父/子分块
- 索引:在 pgvector 或 Qdrant 中同时存储 Dense 与 Sparse
- 查询:Multi-Query(LLM 生成3个变体)
- 第一阶段:混合 BM25 + Dense → RRF 融合,取 top 100
- 第二阶段:Cohere Rerank-3 压缩至 top 10
- 生成:将 top 10 传入 Claude Opus 4.7 生成最终回答
- 评估:每周在 CI 中对500个查询运行回归测试,nDCG@10 / MRR / Recall@20 低于阈值时阻断
严格落地这一架构,「不换 LLM,RAG 却变聪明了」的体验确实会发生。Retrieval 是 RAG 的核心,是2026年项目投资最优先的层次。