RAG 进阶:Chunking、向量检索与 Rerank 实战
把文档塞进向量库再 top-k 召回,只是 RAG 的"hello world"。本文讲清提升 RAG 质量的四个杠杆——切分策略、混合检索、Rerank、Query 改写,并给出可复现的评估方法,让你的 RAG 从"勉强能用"到"真的准"。
本文要点
- 主题聚焦:大模型、RAG
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
把公司文档灌进向量库,再让模型 top-k 召回答题——这是 90% 的人对 RAG 的理解,也是 90% 的 RAG demo 效果一般的原因。真正决定 RAG 质量的,从来不是"用了哪个 embedding 模型",而是召回前的切分和召回后的重排。
一、先承认:naive RAG 为什么不准
naive RAG 的链路是:文档 → 固定长度切块 → embedding → 余弦 top-k → 拼进 prompt。它有三处硬伤:
- 切块破坏语义。固定 512 token 硬切,一句完整的论述可能被腰斩,召回的是半截片段。
- 向量召回偏语义、弱关键词。问"Q3 销售额",文档里有"第三季度营收",向量能召回;但问"ErrorCode=E_4021"这种精确串,向量常常输给 BM25。
- top-k 里掺了噪声。top-5 里可能只有 2 条真相关,剩下 3 条拉低回答质量。
下面四个杠杆,逐一解决。
二、Chunking:切得对,召回就对了一半
切分是 RAG 里 ROI 最高的优化,却最常被忽略。常见策略:
- 固定长度:最简单,512 token + 50 overlap。适合文档结构均匀的入门场景,但会切碎语义。
- 递归切分(recursive):按
\n\n → \n → 。 → 空格的层级递归切,尽量沿自然段落边界。LangChain 的RecursiveCharacterTextSplitter是这一类。 - 结构化切分:用 markdown 标题 / HTML 标签 / 代码块边界来切,每块带"它属于哪个章节"的元数据(
{section: "计费/退款规则"})。这是文档型 RAG 的首选。 - 语义切分(semantic chunking):用 embedding 相邻句子的相似度,在相似度骤降处断句。对自由文本效果好,但更慢。
两个容易被忽略的细节:
- chunk 别太短也别太长。太短缺上下文("它支持该功能"——"它"是谁?),太长稀释信号、还吃 token。文档段落级(200–500 token)通常是甜区。
- 保留上下文窗口。每个 chunk 落库时,存上它的"父段落"或"章节路径",召回时可以按需扩展到父级——这就是 small-to-big / 父子文档检索,召回精度用小块,回答上下文用大块。
三、检索:向量不够,上混合检索
生产级 RAG 几乎都是混合检索(hybrid):向量检索(语义)+ 关键词检索(BM25),再融合。
// 伪代码:向量召回 + BM25 召回,RRF 融合
const dense = await vector.search(embed(query), k); // 语义
const sparse = await bm25.search(query, k); // 精确词
const fused = rrf(dense, sparse); // Reciprocal Rank Fusion
RRF(Reciprocal Rank Fusion) 是最常用的融合方法:对每个文档,按它在两个列表里的排名算分 Σ 1/(rank + 60),再合并。它简单、无需调参、效果稳定——PostgreSQL 的 pgvector + 全文检索、Elasticsearch、Qdrant 都内置支持。
为什么必须混合?因为用户的查询是双模态的:既有"帮我找讲退款流程的段落"(语义),也有"排查 ERR_CONNECTION_REFUSED"(精确串)。纯向量解决不了后者。
四、Rerank:召回多、回答只用最好的几条
召回(recall)和重排(precision)要分开。先用快的向量检索召回 20–50 条(追求不漏),再用慢但准的 Reranker 选出最相关的 3–5 条(追求不噪)。
Reranker 是 cross-encoder:它把 [query, doc] 拼起来一起过一遍模型,输出相关性分数。比 bi-encoder(query 和 doc 分别编码再算余弦)准得多,但慢得多——所以只能用在召回之后的小集合上。
常见选择:bge-reranker、Cohere Rerank、jina-reranker。一行调用:
const reranked = await cohere.rerank(query, candidates, topN: 4);
实践经验:Rerank 是 RAG 里收益最大的单点优化之一,加了之后回答准确率往往立竿见影地提升。它不便宜,但只跑在几十条候选上,可控。
五、Query 改写:用户的话不是好的检索词
用户的原始提问,往往不是好的检索 query。几种改写:
- 指代消解:多轮对话里,"它的价格呢" → 补全成"iPhone 17 Pro 的价格"。
- 子问题分解:复杂问题拆成多个原子查询分别检索("对比 A 和 B 的优劣" → 分别查 A、查 B、查对比)。
- HyDE:先让模型生成一个"假设性答案",用它(而非原问题)去做向量检索——假设答案和真实文档在语义空间里更近。
- 同义扩展 / Multi-query:让模型把 query 改写成 3 个不同表述,各检索一次再合并。
这些改写本身要调一次 LLM,有成本,但对模糊提问和多轮场景效果显著。
六、没有评估,就没有优化
RAG 最忌"凭感觉调"。建立一个固定的评估集(50–200 条 问题 → 期望答案/期望引用文档),每次改动跑一遍,才能知道是变好还是变坏。
常用指标(RAGAS 框架):
- Context Precision/Recall:召回到的上下文里,有多少是真正相关的;该被召回的,召回了多少。
- Faithfulness(忠实度):生成的答案是否都能在上下文里找到依据,没"幻觉"。
- Answer Relevancy:答案是否切题。
经验:先把 Context Recall 调到 0.9+(该召回的都能召回),再调 Faithfulness(别瞎编)。前者靠切分+混合检索,后者靠 Rerank + prompt 约束。
小结
RAG 的进阶,本质是把"一个检索调用"拆成一条管线:
query → 改写 → 混合召回(50) → Rerank(5) → 注入 prompt → 生成 → (评估)
四个杠杆——切分、混合检索、Rerank、Query 改写——按 ROI 排序,建议先做切分和 Rerank(改动小、收益大),再上混合检索和 Query 改写。最后,务必建一个评估集,否则所有"优化"都是玄学。
Related