Featured image of post RAG进阶:Chunking、召回、Hybrid Search 与 Rerank

RAG进阶:Chunking、召回、Hybrid Search 与 Rerank

RAG 做得好不好,很多时候不是模型问题,而是 chunking、召回策略和 rerank 设计的问题。

很多人第一次做 RAG,默认流程都是:

  1. 文档切 chunk
  2. 生成 embedding
  3. 向量检索 top_k
  4. 拼 prompt
  5. 让模型回答

这条路径能跑,但往往不稳。真正让 RAG 质量提升一个台阶的,通常是三件事:

  • chunking 做得更合理
  • 召回从单路 dense 变成多路召回
  • 用真正的 rerank 做精排

一、Chunking 不是“切短一点”这么简单

chunking 的目标不是为了凑 embedding 输入长度,而是为了让“一个 chunk 刚好能独立成为证据”。

一个好的 chunk,通常要满足:

  1. 语义相对完整
  2. 粒度不要太大,避免噪声多
  3. 粒度不要太小,避免信息不够
  4. 能保留来源上下文

1. 固定长度切分

优点:

  • 简单
  • 稳定
  • 易于实现

缺点:

  • 容易把一个完整段落切断
  • 标题与正文可能被拆开
  • 结构信息丢失

适合第一版系统,但不适合长期停留。

2. 按句子、段落、标题层级切分

这种方式通常比纯固定长度更好,因为它尽量保留了自然语言结构。

例如:

  • FAQ 适合按问答对切
  • 技术文档适合按标题和段落切
  • 法规合同适合按条款切

3. 语义切分

如果文档结构复杂,可以进一步用语义切分,让 chunk 边界更贴近自然主题变化。

但语义切分的代价是:

  • 实现复杂度更高
  • 处理时间更长
  • 不一定总比结构化切分更稳定

4. 做 chunk 时一定要补 context

实践里很常见的一个问题是:单个 chunk 本身信息不够。

例如正文里写着:

它的默认超时时间是 30 秒。

如果没有标题和对象上下文,这句话几乎没法独立检索。

所以 chunk 最好补上:

  • 文档标题
  • 小节标题
  • 上级章节
  • 关键实体

你可以把这些信息放进 metadata,也可以直接拼到 chunk 文本前面。

二、为什么单路 Dense Retrieval 经常不够

dense retrieval 很擅长语义相似,但它有几个短板:

  1. 对编号、术语、缩写、版本号不总是稳定。
  2. 对关键词精确匹配场景不够强。
  3. 对长尾实体和 OOV 内容不总是可靠。

这就是为什么现在越来越多系统都会走 hybrid retrieval。

三、Hybrid Retrieval 的思路

hybrid 的核心思想是:不要让一种检索方式决定全部结果。

常见组合:

  1. dense retrieval:负责语义相关性
  2. sparse retrieval:负责关键词、实体、字面匹配
  3. metadata filtering:负责业务边界收缩

也就是说,真正的召回往往是:

候选集 = dense召回 + sparse召回 + metadata过滤

然后再对这些候选做融合。

一个很实用的组合方式

  1. 先用 dense 召回一批候选
  2. 再用 BM25 或 sparse 向量召回一批候选
  3. 把两路结果做 fusion
  4. 进入 rerank

这种方式往往比单路 dense 更稳,尤其适合:

  • 文档里有很多专业术语
  • 用户会问具体字段、编号、参数名
  • 文本既有自然语言,也有表格和半结构化内容

四、Rerank 为什么是关键

检索的第一阶段更像“召回”,目标是别漏掉。

但第一阶段的 top_k 里,往往会混进不少噪声。这个时候就需要 rerank。

rerank 的目标不是扩大候选,而是重新排序,优先保留真正与问题最相关的 chunk。

没有 rerank 时会发生什么

最典型的问题是:

  • 看起来相关,但其实不回答这个问题的 chunk 被放得很前
  • 关键词相似但主题不对的内容混进 prompt
  • 真正答案在 top_20 里,但没有进入最终 prompt

真正的 rerank 和“假 rerank”的区别

很多系统会在代码里留一个 rerank 模块,但实际还是按原始向量分数排序。这种做法在工程结构上没错,但效果上不算真正的 rerank。

真正的 rerank 通常需要单独的语义相关性模型,输入是:

  • query
  • candidate chunk

输出是更适合排序的相关性分数。

五、推荐的多阶段检索架构

一个比较稳的架构通常长这样:

阶段 1:候选召回

  • dense top_50
  • sparse top_50
  • metadata 过滤

阶段 2:结果融合

  • reciprocal rank fusion
  • 或者简单去重后拼接

阶段 3:rerank 精排

  • rerank top_20
  • 输出 top_5 或 top_8

阶段 4:上下文组装

  • 去重
  • 多 source 保持适度多样性
  • 控制上下文总长度

六、top_k 和阈值怎么调

不要把 top_k=5score_threshold=0.3 这种数字当成真理。

它们只是在某个数据集、某个 embedding 模型、某个向量库配置下的经验值。

更合理的做法是:

  1. 把候选召回数和最终注入数拆开。
  2. 阈值不要写死在所有 query 上。
  3. 用离线数据集去看 Recall@K 和最终答案质量。

推荐思路:

  • 召回阶段:宁可多一点,先别漏
  • 精排阶段:重点降噪
  • 最终 prompt:严格控制预算

七、metadata 过滤是“低成本高收益”的优化

很多质量问题并不需要更强模型,只需要更聪明的过滤。

比如:

  • 按知识源过滤
  • 按租户过滤
  • 按语言过滤
  • 按时间版本过滤
  • 按文档类型过滤

一旦 metadata 完整,检索空间会大幅缩小,噪声也会明显下降。

八、上下文组装时的 3 个建议

1. 不要无脑全拼

召回到 10 个 chunk,不代表 10 个都应该进 prompt。最终 prompt 应该更像“精选证据包”,而不是“召回结果 dump”。

2. 尽量保留来源信息

建议至少附带:

  • 文档名
  • chunk 序号
  • 标题或章节

这对引用展示和后续排查都很重要。

3. 控制上下文预算

上下文不是越长越好。长上下文会带来:

  • 成本更高
  • 延迟更高
  • 模型注意力更分散

更推荐的做法是“高质量少量上下文”。

九、一条可执行的升级路线

如果你当前还是单路 dense,建议按这个顺序升级:

  1. 先把 chunking 做合理
  2. 给 chunk 补 metadata
  3. 引入 sparse / BM25
  4. 做 hybrid fusion
  5. 接入真正的 rerank
  6. 最后再做 query rewrite、decomposition 等高级策略

十、本文小结

RAG 做得不好,很多时候不是模型不够强,而是:

  • chunk 没切好
  • 候选召回太单一
  • rerank 没真正落地
  • 上下文组装过于粗糙

如果你把 chunking、hybrid retrieval、rerank 这三步做好,RAG 的整体质量往往会有非常明显的提升。

下一篇会继续讲:如何做 RAG 的评测、Inspect 和可观测性,才能知道问题到底出在哪一层。

使用 Hugo 构建
主题 StackJimmy 设计