2.2 RAG 体系:RAG、Chunk 分块、Rerank 重排、混合检索

Embedding 让文字变成了数字,但光有向量还不够------你需要一整套流程把「用户提问」变成「精准答案」。这就是 ​**RAG(检索增强生成)**​,目前大模型落地最成熟、最实用的技术方案。没有 RAG,LLM 只是一个「能聊但不懂你业务」的聊天机器人;有了 RAG,它才能变成真正懂你的业务助手。

📑 目录

  • [RAG(检索增强生成):给 LLM 外挂一个「知识库」](#RAG(检索增强生成):给 LLM 外挂一个「知识库」)
  • [Chunk 分块:文档怎么切才合理](#Chunk 分块:文档怎么切才合理)
  • [Rerank 重排:粗筛之后的精排](#Rerank 重排:粗筛之后的精排)
  • [混合检索:语义 + 关键词双保险](#混合检索:语义 + 关键词双保险)

RAG(检索增强生成):给 LLM 外挂一个「知识库」

一句话定义

Retrieval-Augmented Generation = 先从知识库检索相关文档片段 → 把检索结果作为上下文 → 让 LLM 基于这些材料生成回答。让 LLM 不再只靠「训练时的记忆」,而是可以实时查阅「参考资料」。

本质大白话

复制代码
没有 RAG 的 LLM:
用户:我们公司的报销政策是什么?
LLM:(瞎编一个通用回答)"通常公司报销需要发票..."
→ 因为 LLM 没看过你们公司的内部文件!

有 RAG 的 LLM:
用户:我们公司的报销政策是什么?
① 检索:在知识库里搜索「报销政策」→ 找到 3 篇相关文档片段
② 增强:把这些片段塞进 Prompt 当作参考
③ 生成:LLM 基于这些真实文档来回答
→ 回答有据可查!来源明确!

RAG vs 微调 vs 提示词工程

维度 RAG 微调 Prompt 工程
知识时效性 实时更新 训练时固定 每次手动更新
数据安全 数据不出域 需要训练 安全但有限
成本 最低
幻觉问题 大幅减少 有所改善 无改善
适合场景 私有知识问答 特定风格/格式 通用任务引导
实现难度 中等 较高
python 复制代码
# 最简 RAG 流程代码
def rag_pipeline(user_query: str) -> str:
    # Step 1: 检索 (Retrieval)
    relevant_docs = vector_store.search(
        query=embed(user_query),
        top_k=5
    )
    
    # Step 2: 构建 Prompt (Augmentation)
    context = "\\n\\n".join([doc.text for doc in relevant_docs])
    prompt = f"""基于以下资料回答问题。如果资料中没有相关信息,请说不知道。

参考资料:
{context}

问题:{user_query}
"""
    
    # Step 3: 生成 (Generation)
    answer = llm.generate(prompt)
    return answer

# 一行调用
answer = rag_pipeline("公司年假怎么算?")

❌ 常见误区

  • ❌ RAG 能完全消除幻觉 --- 不能,只是大幅降低。检索不到或检索错了仍然会出错
  • ❌ RAG 只需要向量检索 --- 完整的 RAG 还需要分块策略、重排序等配套(本节后面详细讲)
  • ❌ 所有场景都适合 RAG --- 创意写作、翻译、闲聊等不需要 RAG,纯知识问答才是主战场

Chunk 分块:文档怎么切才合理

一句话定义

将长文档拆分成适合检索和喂给 LLM 的文本小块。切得好,检索准;切不好,要么信息断裂要么噪音太多。

为什么需要分块?

复制代码
不分块的问题:
┌──────────────────────────────┐
│ 整份 PDF(100页/5万字)       │
│                              │
│ 问题:                        │
│ 1. 超出 Embedding 长度限制     │
│ 2. 检索时返回大量无关内容      │
│ 3. 浪费 Context Window 空间     │
│ 4. LLM 无法聚焦关键信息        │
└──────────────────────────────┘

合理分块后:
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│Chunk1│ │Chunk2│ │Chunk3│ │Chunk4│ ...
│~500字│ │~500字│ │~500字│ │~500字│
└──────┘ └──────┘ └──────┘ └──────┘
每个 chunk 可以被独立检索和引用

常用分块策略对比

策略 做法 优点 缺点 适用场景
固定长度 每 N 个字符/Token 切一刀 简单快速 可能切断语义 通用
递归字符分割 按段落 → 句子 → 字符递归切 保持语义完整 实现稍复杂 文档类
语义分割 用 Embedding 判断语义边界再切 最精确 慢,需要额外模型 高精度需求
按文档结构 Markdown 标题/章节为界切 结构清晰 依赖文档格式好 技术文档
python 复制代码
# 推荐的分块策略实现(LangChain 风格)
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 策略1:递归字符分割(推荐起步选择)
splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # 每个 chunk 最大 500 字符
    chunk_overlap=50,   # 相邻 chunk 重叠 50 字符(防丢失上下文)
    separators=["\\n\\n", "\\n", ".", " ", ""]  # 优先在段落处切
)

chunks = splitter.split_documents(documents)
print(f"共分成 {len(chunks)} 个 chunk")

# 策略2:按 Markdown 标题分块(适合技术文档)
from langchain_text_splitters import MarkdownHeaderTextSplitter

md_splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=[("#", "h1"), ("##", "h2")]
)
md_chunks = md_splitter.split_text(markdown_document)

Chunk 参数选择的经验法则

复制代码
Chunk Size 选择:
- 256-512 tokens → 适合短问答(FAQ、定义查询)
- 512-1024 tokens → 适合一般文档检索(最常用)
- 1024-2048 tokens → 适合长文摘要、分析类任务

Overlap 选择:
- 一般是 chunk_size 的 10%-20%
- 太小 → 上下文在边界处丢失
- 太大 → 冗余存储和计算

❌ 常见误区

  • ❌ Chunk 越小越好 --- 太小的 chunk 丢失上下文,语义不完整
  • ❌ Chunk 越大越好 --- 太大的 chunk 包含太多噪音,干扰检索精度
  • ❌ 所有文档用同一种分块策略 --- 不同类型文档需要不同策略

Rerank 重排:粗筛之后的精排

一句话定义

对向量检索返回的 Top-K 结果进行二次精排,用更精细的模型重新打分排序。目的是让最相关的结果排在前面。

为什么需要 Rerank?

复制代码
向量检索(粗筛):
Query: 「如何提升 API 响应速度?」

Top-5 检索结果:
#1 [0.89] 「Redis 缓存加速响应...」 ← 相关度中等
#2 [0.87] 「数据库索引优化指南...」 ← 相关度高!应该排第1
#3 [0.85] 「API 设计最佳实践...」 ← 相关度低(讲设计不讲性能)
#4 [0.84] 「CDN 加速静态资源...」 ← 有点相关
#5 [0.82] 「微服务架构入门...」 ← 不太相关

问题:向量相似度 ≠ 真实相关性
     「缓存」和「速度」语义近,但不一定是最精准的答案

Rerank 后(精排):
#1 [0.95] 「数据库索引优化指南...」 ← 真正最相关的排到第一了
#2 [0.91] 「Redis 缓存加速响应...」 ← 也很相关
#3 [0.78] 「CDN 加速静态资源...」 ← 还行
#4 [0.65] 「API 设计最佳实践...」 → 降下去了
#5 [0.42] 「微服务架构入门...」 → 明显不相关,压到最后

主流 Reranker 对比

模型 类型 语言 效果 成本
bge-reranker-v2 开源 多语 ⭐⭐⭐⭐ 免费本地运行
Cohere Rerank API 多语 ⭐⭐⭐⭐⭐ 付费但效果好
jina-reranker 开源/API 多语 ⭐⭐⭐⭐ 灵活
cross-encoder 开源 英/中 ⭐⭐⭐ 轻量可用
python 复制代码
# Rerank 完整示例
def reranked_search(query, vector_store, top_k=20, rerank_top=5):
    # Step 1: 向量检索(召回更多候选)
    candidates = vector_store.search(embed(query), top_k=top_k)
    
    # Step 2: Rerank 重排(用 CrossEncoder 精排)
    from sentence_transformers import CrossEncoder
    reranker = CrossEncoder("BAAI/bge-reranker-v2-m3")
    
    pairs = [(query, doc["text"]) for doc in candidates]
    scores = reranker.predict(pairs)  # 更精准的相关性分数
    
    # Step 3: 取重排后的 Top-K
    ranked_indices = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)
    return [candidates[i] for i in ranked_indices[:rerank_top]]

# 使用:先召回 20 条,重排后取最优 5 条
final_results = reranked_search("怎么优化数据库?", my_vector_store)

❌ 常见误区

  • ❌ Rerank 会拖慢整个系统 --- Rerank 只处理 Top-20 左右的候选集,延迟增加很小(通常 <100ms)
  • ❌ 有了 Rerank 就不需要好的检索 --- Rerank 是锦上添花,基础检索太差的话 Rerank 也救不了
  • ❌ Reranker 模型越大越好 --- bge-reranker 在多数场景已经足够优秀

混合检索:语义 + 关键词双保险

一句话定义

同时使用向量检索(语义匹配) 和 ​**关键词检索(BM25 精确匹配)**​,然后合并去重排序。两种方法互补,效果优于单一方法。

为什么单一方法不够?

复制代码
向量检索的盲区:
Q: 「iPhone 15 Pro Max 的 A17 Pro 芯片主频是多少?」
向量检索可能找到:「手机芯片发展史...」「苹果产品线介绍...」
→ 语义相关但缺少精确的关键词匹配

关键词检索的盲区:
Q: "怎么让数据库查询更快?"
BM25 可能找到包含「快」「数据库」「查询」的文章
→ 但找不到「加速」「优化」「性能调优」等同义词文章

混合检索 = 两者的优点合并:
✅ 语义理解 + 精确匹配
✅ 同义词扩展 + 专有名词捕获

混合检索架构

复制代码
用户 Query
                    │
       ┌────────────┼────────────┐
       ↓            ↓            ↓
┌──────────┐  ┌──────────┐  ┌──────────┐
│ Embedding│  │  BM25    │  │ (可选)   │
│ 向量检索  │  │ 关键词检索│  │ 其他通道  │
│ Top-K:20 │  │ Top-K:20 │  │          │
└─────┬────┘  └─────┬────┘  └─────┬────┘
      │              │             │
      └──────┬───────┘             │
             ↓ Reciprocal Rank Fusion (RRF)
      ┌──────────────────┐
      │   合并去重排序    │
      └────────┬─────────┘
               ↓
        ┌──────────────┐
        │  Rerank 重排   │
        │  最终 Top-5/10 │
        └──────────────┘
python 复制代码
# 混合检索实现(RRF 算法)
def hybrid_search(query, vector_store, bm25_index, k=10, alpha=0.6):
    """
    alpha: 向量检索权重,(1-alpha): 关键词检索权重
    """
    # 1. 向量检索
    vec_results = vector_store.search(embed(query), top_k=k*2)
    
    # 2. BM25 关键词检索
    kw_results = bm25_index.search(query, top_k=k*2)
    
    # 3. Reciprocal Rank Fusion 合并
    scores = {}
    for rank, doc in enumerate(vec_results):
        doc_id = doc["id"]
        scores[doc_id] = scores.get(doc_id, 0) + alpha / (rank + 60)
    for rank, doc in enumerate(kw_results):
        doc_id = doc["id"]
        scores[doc_id] = scores.get(doc_id, 0) + (1-alpha) / (rank + 60)
    
    # 4. 取最终 Top-K
    final = sorted(scores.items(), key=lambda x: x[1], reverse=True)[:k]
    return final

❌ 常见误区

  • ❌ 混合检索一定比单一检索好 --- 如果你的数据都是自然语言描述且无专有名词,纯向量检索可能就够了
  • ❌ RRF 权重不用调 --- 不同场景下向量和关键词的最佳比例不同,需要根据实际效果调整
  • ❌ 混合检索太复杂不能上线 --- LangChain/LlamaIndex 都有一行代码的实现

📊 本节知识地图

复制代码
┌────────────────────────────────────────────────────────┐
│               RAG 体系 --- 完整检索增强流水线            │
│                                                        │
│   文档输入                                               │
│      ↓                                                  │
│   ┌──────────┐                                          │
│   │ Chunk    │ ← 分块(大小+重叠是关键参数)            │
│   │ 分块策略  │                                          │
│   └────┬─────┘                                          │
│        ↓ Embedding                                      │
│   ┌───────────────────────────────────────┐            │
│   │         检索阶段                      │            │
│   │  ┌─────────┐  ┌─────────┐  ┌───────┐  │            │
│   ️ │ *向量    │  │ *关键词  │  │ Rerank│  │            │
│   │  检索★   │  │  BM25 ☆  │  │ 重排  │  │            │
│   │  (语义)  │  │ (精确)  │  │ (精排)│  │            │
│   │  Top-K:20│  │  Top-K:20│  │ Top-5 │  │            │
│   └────┬─────┘  └────┬─────┘  └───┬───┘  │            │
│        └──────────┬──┘          │        │            │
│                   ↓ RRF 合并    ↓        │            │
│        ┌──────────────────────────────┘        │            │
│        ↓                                        ↓            │
│   ┌──────────┐                              最终结果         │
│   │  LLM 生成 │                                                │
│   │ (基于检索)│                                                │
│   └──────────┘                                                │
│                                                                │
│  核心公式:文档→分块→Embedding→多路召回→Rerank→LLM生成答案   │
└────────────────────────────────────────────────────────┘

🔗 关联推荐

  • 📖 2.1 向量基础 → Embedding 和相似度是 RAG 的前置知识
  • 📖 1.3 交互基础 → 检索结果作为 Context 喂给 LLM
  • 🔜 2.3 向量数据库 → 向量和文档存在哪里?怎么选型?
  • 🔜 8.2 RAG 优化 → 进阶:HyDE、Fusion、多路召回
相关推荐
你不是我我1 小时前
【Agent 学习日记】Agent 的记忆是如何设计的?短期记忆和长期记忆有什么区别?
agent·rag
染指11102 小时前
19.LangChain框架7-LangChain1.0版本使用Agent(中间件实例)
人工智能·python·机器学习·langchain·agent·rag
lianyinghhh20 小时前
FlowGame 从零上手:开源 AI 工作流编排框架与 Vue 3 接入实战
python·低代码·开源·vue·rag·flowgame·ai工作流编排
SL-staff1 天前
企业级私有化AI知识库完整搭建指南:从RAG架构到Llama 3落地实践
人工智能·系统架构·私有化部署·rag·ai知识库·llama3·jvs-ai
逻极1 天前
Hermes Agent深度解析:从ReAct到多智能体系统架构实战
llm·agent·react·rag·多智能体系统
韦东东1 天前
究极方案:油猴脚本实现RAG问答前端图片流式体验
人工智能·大模型·油猴脚本·rag·tampermonkey·userscript
Cosolar1 天前
AutoGen:微软开源的多Agent对话框架详解
人工智能·系统架构·大模型·agent·rag
阿昌喜欢吃黄桃2 天前
Java优质开源AI项目
java·ai·langchain·开源·rag·springai·langchain4j
searchforAI2 天前
Agent Skills知识库检索比RAG强吗?技术原理拆解
人工智能·gpt·ai·agent·rag·skill·claudecode