RAG 深度实战指南
RAG 不只是"检索+生成"这么简单——每个环节的设计选择都会直接影响产品效果。
前置阅读
本文是 AI 应用架构模式 中 RAG 章节的深度扩展。如果你还不了解 RAG 的基本概念,建议先阅读该章节。
RAG 全景架构
┌─────────────────────────────────────────────────────────────┐
│ RAG 完整流程 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 数据提取 │ → │ 文档分块 │ → │ 向量化 │ → │ 索引 │ │
│ │ Ingestion│ │ Chunking │ │Embedding │ │ Indexing │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ↑ 离线处理阶段 │ │
│ │ ───────────────────────────── │ │
│ │ 在线查询阶段 ↓ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 最终生成 │ ← │ 重排序 │ ← │ 检索 │ ← │ 查询理解 │ │
│ │Generation│ │ ReRank │ │Retrieval │ │ Query │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘每个环节都有大量细节和最佳实践。下面逐一深入。
第一步:数据提取(Ingestion)
为什么数据提取是 RAG 的地基?
"Garbage In, Garbage Out"——如果文档解析质量差,后面的分块、检索、生成全部受影响。
多格式文档解析方案
| 文档格式 | 解析难度 | 推荐方案 | 关键挑战 |
|---|---|---|---|
| 纯文本/Markdown | 低 | 直接读取 | 编码问题 |
| 高 | PyMuPDF + 版面分析 | 表格、多栏、扫描件 | |
| Word(.docx) | 中 | python-docx | 嵌套表格、样式继承 |
| HTML/网页 | 中 | BeautifulSoup + 正文提取 | 噪音(导航、广告) |
| PPT | 中高 | python-pptx | 文本框分散、图表 |
| 扫描件/图片 | 高 | OCR(PaddleOCR/Tesseract) | 识别准确率、版面还原 |
| 表格数据 | 中 | 专用表格解析 | 跨页表格、合并单元格 |
PDF 解析的深水区
PDF 是 RAG 中最常见也最棘手的格式:
PDF 解析的四大难点:
1. 多栏布局
┌──────────┬──────────┐
│ 第一栏 │ 第二栏 │ → 文本提取顺序容易错乱
│ 的内容 │ 的内容 │
└──────────┴──────────┘
2. 表格
┌────┬────┬────┐
│ │ │ │ → 单元格关系容易丢失
├────┼────┼────┤
│ │ │ │
└────┴────┴────┘
3. 跨页内容
─────── 第 5 页 ───────
...这段话在这里开始但是
─────── 第 6 页 ───────
在下一页才结束... → 内容被截断
4. 扫描件
[图片形式的文字] → 需要 OCR,准确率有限解析方案选型
你的文档是什么类型?
│
├── 纯数字化 PDF(文本可选中)
│ ├── 简单排版 → PyMuPDF / pdfplumber
│ └── 复杂排版(多栏/表格) → 版面分析模型
│
├── 扫描件 PDF
│ ├── 中文为主 → PaddleOCR
│ └── 英文为主 → Tesseract / Azure Doc Intelligence
│
└── 混合型 PDF
└── 文档智能解析服务(Azure/AWS/阿里云)结构化数据提取
对于包含结构化信息的文档(合同、报告、表单),建议提取后转为结构化格式:
原始 PDF 内容(非结构化):
"甲方:XX科技有限公司
合同金额:人民币 500,000 元
期限:2026年1月1日至2026年12月31日"
↓ 结构化提取
{
"party_a": "XX科技有限公司",
"amount": 500000,
"currency": "CNY",
"start_date": "2026-01-01",
"end_date": "2026-12-31"
}第二步:文档分块(Chunking)
为什么分块如此关键?
分块质量直接决定检索质量:
分块太大:
┌──────────────────────────────────┐
│ 大量不相关的内容被检索出来 │ → 噪音多,模型被干扰
│ 包含了用户需要的信息 │
│ 还有更多不相关内容 │
└──────────────────────────────────┘
分块太小:
┌────────┐
│ 信息不 │ → 上下文丢失,无法理解完整语义
└────────┘
┌────────┐
│ 完整 │
└────────┘
合适的分块:
┌───────────────────┐
│ 完整的语义单元 │ → 信息完整,噪音少
│ 包含必要的上下文 │
└───────────────────┘七种分块方法对比
| 方法 | 原理 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| 固定大小 | 按字符/Token 数切割 | 简单快速 | 语义割裂 | 快速原型验证 |
| 句分隔 | 按句号/换行切分 | 保持句子完整 | 粒度太细 | 短文本、FAQ |
| 递归分割 | 按层级分隔符递归切分 | 较好的语义保持 | 需要调参 | 通用场景(推荐起步) |
| 专门分块 | 按 Markdown/LaTeX 标题切分 | 利用文档结构 | 依赖格式 | 结构化文档 |
| 语义分割 | 按语义相似度切分 | 最佳语义完整性 | 计算量大 | 高质量需求 |
| 滑动窗口 | 固定窗口 + 重叠 | 避免边界信息丢失 | 数据冗余 | 需要上下文连续性 |
| LLM 分块 | 用 LLM 理解文档结构后切分 | 最智能 | 成本高 | 高价值文档 |
递归分割最佳实践(推荐起步方案)
LangChain RecursiveCharacterTextSplitter 参数建议:
文档类型 chunk_size chunk_overlap 分隔符优先级
─────────────────────────────────────────────────────────
技术文档 1000 200 \n\n → \n → 。→ 空格
FAQ/知识库 500 100 \n\n → \n → 。
合同/法律 800 200 \n\n → \n → 。→ ;
聊天记录 300 50 \n → 。
学术论文 1200 300 \n\n → \n → 。语义分割:解决语义割裂问题
语义分割流程:
原始文档
│
▼
按句子切分
│
▼
计算相邻句子的语义相似度
│
▼
相似度
1.0 ─┬──────────────────────────
│ ██ ██ ██ ██ ██
0.8 ─┤ ██ ██ ██ ██ ██
│ ██ ██ ██ █ ██ ██
0.6 ─┤ ██ ██ ██ █ ██ ██
│ ██ ██ ██ █ ██ ██
0.4 ─┤ ██ ██ ██ █ ██ ██ █
│ ██ ██ ██ █ ██ ██ █
0.2 ─┤ ██ ██ ██ █ ██ ██ █
│ ██ ██ ██ █ ██ ██ █
0.0 ─┴──────────────────────────
S1 S2 S3 S4 S5 S6 S7
↑ ↑
语义断点 语义断点
在相似度低谷处切分 → 自然的语义边界分块的常见问题与解决方案
| 问题 | 表现 | 解决方案 |
|---|---|---|
| 分块大小不合适 | 检索结果太泛或太碎 | 根据场景调整 chunk_size |
| 上下文关系被切断 | "它"指代不明 | 增加 overlap 或用父子索引 |
| 语义完整性丢失 | 检索到的内容无法理解 | 使用语义分割 |
| 跨页表格被切断 | 表格数据不完整 | 预处理合并跨页表格 |
第三步:向量化(Embedding)
Embedding 的工作原理
Embedding 将文本转换为数学向量:
"产品经理需要理解用户需求"
↓ Embedding 模型
[0.12, -0.34, 0.56, 0.78, ..., -0.23] (1536 维向量)
语义相似的文本 → 向量空间中距离近:
↑ 维度 2
│
│ ● "用户需求分析"
│ ● "需求调研方法"
│
──────────┼──────────→ 维度 1
│
│ ● "服务器部署"
│ ● "Docker 容器化"
│Embedding 模型选型
| 模型 | 维度 | 中文能力 | 最大长度 | 特点 |
|---|---|---|---|---|
| text-embedding-3-large | 3072 | 好 | 8191 | OpenAI 最新,支持维度裁剪 |
| text-embedding-3-small | 1536 | 好 | 8191 | 性价比高 |
| bge-large-zh-v1.5 | 1024 | 优秀 | 512 | 开源,中文最佳之一 |
| bge-m3 | 1024 | 优秀 | 8192 | 多语言多粒度,支持稀疏+稠密 |
| GTE-Qwen2 | 可变 | 优秀 | 131072 | 阿里,超长上下文 |
| Cohere embed-v3 | 1024 | 好 | 512 | 支持多语言和搜索/分类模式 |
选型决策要点
你的场景是什么?
│
├── 中文为主
│ ├── 需要私有化部署 → bge-large-zh / bge-m3
│ └── 可以用 API → text-embedding-3-large
│
├── 多语言混合
│ └── bge-m3 / Cohere embed-v3
│
└── 英文为主
├── 追求效果 → text-embedding-3-large
└── 追求性价比 → text-embedding-3-smallEmbedding 检索的局限性
纯语义检索并非万能,了解其局限有助于产品设计:
| 局限 | 表现 | 补救方案 |
|---|---|---|
| 关键词盲区 | 搜"型号 XR-7700"找不到 | 混合检索(语义 + 关键词) |
| 否定语义困难 | "不含"和"含有"向量相近 | 结构化过滤 |
| 数值理解弱 | 搜"100万以上"效果差 | 结构化查询 |
| 时效性 | 无法理解"最近"的含义 | 元数据时间过滤 |
混合检索是最佳实践
语义检索 + BM25 关键词检索的混合方案,在绝大多数场景下优于纯语义检索。典型配比是 70% 语义 + 30% 关键词,但需要根据场景调优。
第四步:索引(Indexing)
向量数据库选型
| 数据库 | 类型 | 适用场景 | 特点 |
|---|---|---|---|
| Pinecone | 云托管 | 快速上线 | 全托管,易用 |
| Weaviate | 开源/云 | 中大型项目 | 功能丰富,支持混合搜索 |
| Milvus | 开源 | 大规模 | 高性能,国产 |
| Qdrant | 开源/云 | 中型项目 | Rust 编写,性能优秀 |
| Chroma | 开源 | 原型/小型 | 极简,适合开发 |
| pgvector | PG 扩展 | 已有 PG | 无需新增组件 |
父子索引(Parent-Child Indexing)
解决"检索粒度"和"生成粒度"的矛盾:
问题:
检索需要小块(精确匹配)
生成需要大块(完整上下文)
解决方案 - 父子索引:
┌──────────────────────────────────────────┐
│ 父文档(原始大块) │
│ "AI 产品的定价策略需要考虑多个因素。 │
│ 首先是推理成本,包括 Token 消耗和 │
│ GPU 算力。其次是获客成本..." │
├──────────┬──────────┬────────────────────┤
│ 子块 1 │ 子块 2 │ 子块 3 │
│ "AI 定价 │ "推理成本 │ "获客成本..." │
│ 策略..." │ Token..." │ │
└──────────┴──────────┴────────────────────┘
↑
用子块做检索(精确)
返回父文档做生成(完整)用户反馈闭环优化索引
索引优化循环:
用户查询 → 检索结果 → 生成回答 → 用户反馈
│
┌────────────────┤
↓ ↓
正向反馈 负向反馈
(有帮助) (不准确)
│ │
▼ ▼
提升该文档 分析失败原因
检索权重 │
┌──┴──┐
↓ ↓
文档问题 检索问题
│ │
▼ ▼
更新文档 调整索引/分块第五步:重排序(ReRank)
为什么需要 ReRank?
初始检索(召回阶段):快但粗糙
│ Top 20 结果
▼
ReRank(精排阶段):慢但精确
│ Top 5 结果
▼
送入 LLM 生成回答
比喻:
初始检索 = 从图书馆找到 20 本相关的书
ReRank = 精选最相关的 5 本放在桌上ReRank 模型选型
| 模型 | 中文能力 | 速度 | 部署方式 |
|---|---|---|---|
| Cohere Reranker | 好 | 快 | API |
| bge-reranker-v2-m3 | 优秀 | 中 | 自部署 |
| Cross-Encoder | 取决于训练数据 | 慢 | 自部署 |
| LLM-based Rerank | 优秀 | 最慢 | API/自部署 |
ReRank 的效果提升
典型场景的效果提升:
无 ReRank 有 ReRank 提升
────────── ────────── ─────
知识库问答 Top-3 命中率 72% 89% +17%
文档搜索 MRR 0.65 0.81 +25%
FAQ 匹配准确率 80% 93% +13%RAG vs Prompt Engineering:如何选择
决策矩阵
知识是否在模型训练数据中?
│
┌──────┴──────┐
是 否
│ │
知识是否需要更新? 知识量大吗?
┌──────┴──────┐ ┌──────┴──────┐
否 是 否 是
│ │ │ │
Prompt 就够 需要 RAG Few-Shot 必须 RAG
也许够两个关键影响因素
| 因素 | Prompt Engineering | RAG |
|---|---|---|
| 知识时效性 | 依赖训练数据(有截止日期) | 实时更新 |
| 知识专业度 | 通用知识足够 | 领域知识必须外挂 |
RAG 的局限性与落地挑战
常见失败原因
RAG 失败的五大原因:
1. 上下文整合问题
检索到的多个片段之间互相矛盾或重复
→ 模型不知道该信任哪个
2. 推理问题
检索到了正确信息但模型推理出错
→ 模型"有资料但不会用"
3. 响应格式化问题
信息正确但输出格式不符合要求
→ 需要更强的 Prompt 约束
4. 上下文窗口利用率
塞了太多无关内容,有效信息被稀释
→ 需要更精准的检索和 ReRank
5. 检索失败
根本没找到相关文档
→ 分块策略或 Embedding 选型问题面试高频问题
"你觉得 RAG 可能会在哪些场景失败?"
参考回答:
RAG 的失败模式可以分为检索侧和生成侧:
检索侧失败:
- 文档分块不当导致关键信息被切断
- Embedding 模型对特定领域术语理解不足
- 用户 query 表述和文档表述差异大(语义鸿沟)
生成侧失败:
- 检索到的上下文互相矛盾
- 上下文太多,模型"迷失"在大量信息中
- 模型过度依赖预训练知识,忽略检索结果
应对策略:
- 混合检索(语义 + 关键词)
- 查询改写/扩展
- ReRank 精排
- 上下文压缩
- 回答引用来源,便于验证
RAG 评估体系
RAGAS 评估框架
RAGAS 四大核心指标:
┌──────────────────────────────────────────────┐
│ │
│ 1. 忠实度(Faithfulness) │
│ 回答是否忠于检索到的上下文? │
│ → 衡量是否"编造"了上下文中没有的信息 │
│ │
│ 2. 回答相关性(Answer Relevancy) │
│ 回答是否切中用户问题? │
│ → 衡量回答与问题的匹配度 │
│ │
│ 3. 上下文精确率(Context Precision) │
│ 检索到的文档中有多少是真正相关的? │
│ → 衡量检索的精确度 │
│ │
│ 4. 上下文召回率(Context Recall) │
│ 回答所需的信息是否都被检索到了? │
│ → 衡量检索的完整度 │
│ │
└──────────────────────────────────────────────┘
理想状态:四个指标都 > 0.8生产环境 RAG 评价方法
| 评估方式 | 适用阶段 | 优缺点 |
|---|---|---|
| RAGAS 自动评估 | 开发/测试 | 快速但可能不准 |
| LLM-as-Judge | 开发/测试 | 灵活但有偏差 |
| 人工标注评估 | 上线前 | 最准但成本高 |
| 用户反馈(点赞/踩) | 生产环境 | 真实但有偏差 |
| A/B 测试 | 生产环境 | 最可靠的对比方案 |
评估指标体系
完整的 RAG 评估指标体系:
第一层:检索质量
├── 召回率(Recall@K):相关文档是否被检索到
├── 精确率(Precision@K):检索结果中有多少是相关的
├── MRR:第一个正确结果的排名
└── nDCG:考虑排序的综合指标
第二层:生成质量
├── 忠实度:是否忠于检索上下文
├── 准确率:事实是否正确
├── 完整性:是否回答了全部问题
└── 相关性:是否切中问题
第三层:系统性能
├── 端到端延迟
├── Token 消耗
├── 检索耗时
└── 系统可用性
第四层:业务指标
├── 用户满意度
├── 任务完成率
├── 人工干预率
└── 留存率RAG 优化策略
优化路线图
RAG 优化优先级:
Phase 1: 基础优化(1-2 周)
├── 调整 chunk_size 和 overlap
├── 选择合适的 Embedding 模型
└── 优化 Prompt 模板
效果提升:20-40%
Phase 2: 检索优化(2-4 周)
├── 引入混合检索(语义 + BM25)
├── 添加 ReRank
├── 查询改写/扩展
└── 元数据过滤
效果提升:15-25%
Phase 3: 高级优化(4-8 周)
├── 父子索引
├── 语义分块
├── 微调 Embedding 模型
├── Agent 驱动的多步检索
└── 用户反馈闭环
效果提升:10-20%查询改写策略
用户原始查询可能不适合直接检索:
原始查询:"为什么系统这么慢?"
│
▼ 查询改写
改写 1:"系统性能优化 响应延迟高"
改写 2:"服务器响应时间慢的常见原因"
改写 3:"系统负载过高导致性能下降"
│
▼ 多路检索
合并去重所有检索结果
│
▼ ReRank
精选 Top-K 送入 LLM利用 Agent 优化 RAG
传统 RAG:
查询 → 单次检索 → 生成
Agent-RAG:
查询 → 分析问题 → 制定检索策略
│
├── 子问题 1 → 检索 → 评估结果
├── 子问题 2 → 检索 → 评估结果
└── 子问题 3 → 检索 → 评估结果
│
信息是否充足?
├── 否 → 补充检索
└── 是 → 综合生成知识库问答助手实战案例
案例:企业知识库智能问答
系统架构:
用户提问
│
▼
┌─────────────┐
│ 查询理解 │ ← 意图识别 + 实体提取 + 查询改写
└──────┬──────┘
│
▼
┌─────────────┐ ┌──────────────┐
│ 混合检索 │ ──→ │ 向量数据库 │
│ │ │ Milvus │
│ 语义 + BM25 │ ──→ │ ES/BM25 │
└──────┬──────┘ └──────────────┘
│ Top 20
▼
┌─────────────┐
│ ReRank │ ← bge-reranker-v2-m3
└──────┬──────┘
│ Top 5
▼
┌─────────────┐
│ 答案生成 │ ← Claude Sonnet + Prompt 模板
└──────┬──────┘
│
▼
┌─────────────┐
│ 引用标注 │ ← 标注答案来源于哪个文档
└──────┬──────┘
│
▼
用户看到带引用的回答核心指标看板
知识库问答助手核心指标:
日维度监控:
├── 查询量:______ 次/天
├── 回答率:______%(未回答 = 检索失败)
├── 准确率:______%(人工抽检)
├── 平均延迟:______ ms
└── Token 消耗:______ 万/天
周维度分析:
├── Top 10 高频问题
├── Top 10 失败问题
├── 用户满意度趋势
└── 知识覆盖率变化
月维度复盘:
├── 新增文档数量和质量
├── 模型/Prompt 优化效果
├── 成本趋势
└── 与人工客服对比产品经理的 RAG 决策清单
| 阶段 | 决策 | 考虑因素 |
|---|---|---|
| 规划 | 是否需要 RAG? | 知识时效性、专业度、数据量 |
| 数据 | 数据源和更新频率? | 文档格式、质量、合规 |
| 分块 | 分块策略? | 文档类型、检索粒度 |
| 检索 | 纯语义还是混合? | 精度要求、关键词重要性 |
| 模型 | Embedding 和 LLM 选型? | 中文能力、成本、延迟 |
| 评估 | 如何衡量效果? | 自动评估 + 人工抽检 |
| 上线 | 灰度策略? | 先小范围验证再全量 |
| 运营 | 知识库如何维护? | 更新频率、权责分工 |
延伸阅读
- AI 应用架构模式 — RAG 基础概念回顾
- 数据策略 — 数据质量管理
- 高级 Prompt Engineering — Prompt 在 RAG 中的优化
- AI 评估体系 — 更广泛的 AI 评估方法论
- 模型选型实战指南 — Embedding 和 LLM 选型