高级 RAG 技术
朴素 RAG(Naive RAG)遵循"切片 → 向量化 → Top-K 召回 → 生成"的固定流程,在真实业务中常常同时面临两类问题:查不准(用户表达与文档表达存在语义鸿沟)和查不全(单一检索通道无法覆盖所有形态的知识)。高级 RAG 的思路是把这条固定管线拆成多个可独立优化的模块,在检索前、检索中、检索后分别引入针对性技术。
┌────────── 查询变换 ──────────┐
用户问题 ──→ 改写 / 扩写 / 分解 ──→ 多路检索 ──→ 融合排序 ──→ 上下文组装 ──→ 生成
↑ │
向量 + 关键词 + 知识图谱 └── 重排序、过滤、去重
混合检索
向量检索擅长语义匹配,但对型号、编号、人名等精确 token 不敏感;关键词检索(BM25)恰好相反。混合检索同时执行两路召回,再融合结果:
问题 ──┬──→ BM25 关键词召回 Top-K
└──→ 向量检索召回 Top-K
↓
RRF 融合排序
常用融合算法是倒数排名融合(Reciprocal Rank Fusion),不需要调权,对分数尺度不敏感:
RRF_score(doc) = Σ 1 / (k + rank_i(doc)) (k 通常取 60)
一个文档在多路召回中都靠前时,融合分数会显著提高。混合检索几乎是生产 RAG 的默认配置,尤其适合含大量专有名词的领域知识库。
查询变换
用户的原始 query 往往不是好的检索 query。常见变换策略:
| 策略 | 做法 | 适用问题 |
|---|---|---|
| 查询改写 | 补全省略的主语和上下文 | 口语化、指代不明 |
| Multi-Query | 生成多个不同角度的查询,分别召回后合并 | 表达单一导致召回偏斜 |
| 查询分解 | 把复合问题拆成多个子问题 | "A 和 B 的区别"类多跳问题 |
| HyDE | 先让 LLM 生成一个假设性答案,用答案的向量去检索 | 问题与文档表述差异大 |
| Step-back | 先抽象出更泛化的问题检索背景知识 | 细节问题缺乏背景 |
HyDE(Hypothetical Document Embeddings)的直觉是:答案与文档的语义空间比问题与文档更接近。生成假设文档去检索,命中后再用真实片段作答。代价是每次检索多一次 LLM 调用,且生成质量依赖模型能力,应通过评测验证收益。
分块策略进阶
固定长度切片的矛盾:检索需要小而准的片段(语义集中、向量表达鲜明),生成需要大而全的片段(上下文完整)。两种解耦方案:
父子分块(Parent-Child Chunking):文档先切成较大的父块,再在父块内部切成小块;检索时用小块匹配,返回时携带整个父块。
父块(800 tokens,完整论述)
├── 子块 1(200 tokens)── 向量化入库
├── 子块 2(200 tokens)── 向量化入库
└── 子块 3(200 tokens)── 向量化入库
检索命中子块 2 → 返回父块参与生成
句窗口(Sentence Window):以句子为单位向量化,命中后向前后扩展若干句作为上下文窗口。
两者本质上都是"检索粒度"与"生成粒度"的分离,元数据中记录父子关系即可在 LlamaIndex 中直接实现。
重排序
向量检索是双塔结构:问题和文档分别独立编码,速度极快但精度有限。重排序(ReRank)采用交叉编码器:把"问题 + 文档"拼在一起送入模型,逐对打分,精度更高但成本随对数线性增长。
因此工程上采用漏斗结构:
向量召回 Top-50(快、粗)
↓
ReRank 精排 Top-5(慢、准)
↓
进入上下文
选型参考:
| 方案 | 特点 |
|---|---|
| bge-reranker 系列 | 开源、中文效果好、可本地部署 |
| Cohere Rerank | 托管 API、效果稳定 |
| LLM 重排序 | 用强模型逐对判断相关性,灵活但贵 |
上下文工程
召回质量高,组装不当也会浪费。几个实践经验:
- 位置敏感:模型对上下文首尾的注意力高于中部(lost in the middle 现象),最相关的片段应放在最前。
- 控制总量:上下文不是越多越好,无关内容会稀释注意力并推高成本,通常 4-8 个高质量片段优于 20 个混杂片段。
- 结构化呈现:为每个片段附带来源标注(文件、章节),既便于模型区分信息归属,也便于答案溯源。
- 去重:同一事实被多个相似片段重复表达时,应合并压缩。
模块化视角
现代 RAG 已从固定管线演进为模块化架构:每个环节(变换、路由、检索、重排、组装)都是可替换的组件,按问题类型动态编排。这条路线的终点是把"如何检索"的决策权也交给 LLM,即 Agentic RAG,详见后续文章。