9.2 高级检索策略与上下文组装

在 RAG 系统中,“检索”的质量决定了最终效果的上限。如果你给 LLM 提供了错误的上下文,再强大的模型也无法生成正确的答案。本节将深入探讨提升检索质量的关键策略。

单一的向量搜索虽然擅长捕捉语义,但在处理精确匹配(如特定的人名、产品型号)时往往表现不佳。混合检索通过结合关键词搜索和向量搜索来弥补这一缺陷。

核心算法:融合策略

如何将两种搜索结果合并?目前业界主流采用 RRF (Reciprocal Rank Fusion) 算法。

RRF 的核心思想是:如果不确定哪个对,就相信两个系统都认为靠前的结果。其公式为:

score(d)=rR1k+rank(d,r)score(d) = \sum_{r \in R} \frac{1}{k + rank(d, r)}

其中 $k$ 是常数(通常取 60)。RRF 不依赖具体的相似度分数,只依赖排名,因此具有很好的鲁棒性。

2. 重排序:Reranking / Two-Stage Retrieval

这是提升 RAG 效果最核心的手段。标准的 RAG 检索通常分为两个阶段:

  1. 初排 (Retrieval):使用 Bi-Encoder(双塔模型)快速从海量文档中召回 Top-K(如 50-100)。它计算速度快,但因为 Query 和 Document 是独立编码,丢失了深层语义交互。

  2. 重排 (Reranking):对初排结果进行精细打分,筛选出 Top-N(如 5-10)给大模型。

2.1 核心算法与模型

  • Cross-Encoder (交叉编码器):目前最主流的重排方案。

    • 原理:将 Query 和 Document 拼接输入模型(如 BERT),让模型逐个 Token 深度关注两者的交互。

    • 特点:精度极高,但计算慢( Latency 高)。

    • 代表模型BGE-Reranker(BAAI),Cohere RerankJina Reranker

  • ColBERT (Late Interaction / 晚期交互)

    • 特点:介于 Bi-Encoder 和 Cross-Encoder 之间的折中方案。保留了 Token 级别的交互,比纯 Cross-Encoder 快,比纯向量检索准。

  • LLM Reranking (大模型重排序)

    • 原理:直接让 GPT-4 或 Claude 对段落进行 Pointwise/Listwise 排序。

    • 特点:效果通常是天花板,但极慢且贵。

2.2 多样性策略: MMR ( Maximal Marginal Relevance)

  • 问题:有时召回的 Top-5 全是极其相似的重复内容,导致给 LLM 的信息单一。

  • MMR 原理:在重排序时,不仅看 相关性 (Relevance),还要看 新颖性 (Novelty)。它会惩罚那些与已选文档过于相似的候选者,确保上下文包含不同角度的信息。

3. 查询转换 ( Query Transformation)

用户的原始提问往往是不完整或含糊的。我们需要在检索前优化 Query。

HyDE ( Hypothetical Document Embeddings)

  • 原理:先让 LLM 编造一个“假设性回答”,然后用这个回答去检索。

  • 优势:解决了 Query 和 Document 语义空间不匹配的问题( Query 是问题, Document 是陈述)。

多查询扩展:Multi-Query

  • 原理:将一个复杂问题拆解为多个子视角。

    • User: “对比一下 LangChain 和 LlamaIndex”

    • AI: 生成两个 Query -> “LangChain 的优缺点”, “LlamaIndex 的优缺点”

  • 执行:并行执行搜索,然后去重合并结果。

4. 分块与索引优化:Chunking 与 Indexing

分块是 RAG 的基石。分块太小会导致语义破碎,分太大则噪音过多。

4.1 常用分块策略

  • 固定大小分块 (Fixed-size Chunking):最基础的方法。设定固定的 Token 数(如 512)和重叠率( Overlap ,如 10%)。简单高效,但在切断语义连贯性上容易出问题。

  • 内容感知分块 (Content-aware Chunking)

    • 基于句法 (NLTK/spaCy):利用 NLP 工具按句子或自然段落切分,确保句子的完整性。

    • 递归分块 (Recursive Chunking): LangChain 中常用的 RecursiveCharacterTextSplitter。它会尝试按顺序使用不同的分隔符(如 \n\n, \n, , "")进行切分,直到块大小满足要求。这是目前最推荐的通用策略。

  • 结构化分块 (Structural Chunking):针对 Markdown、Code 或 LaTeX ,利用其特定的语法结构( Header, Class, Function)进行切分,能极大保留原有逻辑结构。

4.2 父文档检索 ( Parent Document Retriever)

  • 问题:大文档块包含丰富上下文但向量不精确,小文档块向量精确但丢失上下文。

  • 方案:索引时切分成小块,检索到小块后,返回其所属的父文档块给 LLM。这种“小块索引,大块返回”的策略兼顾了检索精度和上下文完整性。

5. 上下文组装 ( Context Construction)

拿到文档后,如何喂给模型也是一门学问。

“Lost in the Middle” 现象

研究表明, LLM 往往更容易关注上下文的 开头结尾,而忽略中间的信息。

  • 优化策略:将相关度最高的文档放在 Prompt 的开头或结尾,将相关度较低的放在中间。

长上下文的处理

如果检索回来的内容超过了窗口限制:

  • Map-Reduce:先对每个文档块进行摘要,再将摘要汇总。

  • Refine:串行地让 LLM 阅读文档并逐步优化答案(速度较慢)。

9.2.1 关键参数推荐

针对企业级知识库,以下是一组经过验证的经验参数:

参数
推荐值
说明

Chunk Size

512 ~ 1024 tokens

需要涵盖完整语义段落

Overlap

10% ~ 20%

防止切分点断开语义

Retrieval Top-K

50 ~ 100

召回更多候选集给重排序

Rerank Top-K

5 ~ 10

最终喂给 LLM 的数量

延伸思考

  1. 语义搜索(向量)和关键词搜索( BM25)各自的盲区是什么?在什么类型的查询上混合检索优势最明显?

  2. 重排序增加了额外延迟——你会如何决定“值不值得加这一步”?

最后更新于