理解 RAG

从 LLM 的根本缺陷出发,5集走完 RAG 核心路径:原理 → 架构 → 检索优化 → 评估 → 生产化。每集 5–10 分钟,跟着真实代码输出学。

5 集 · 约 34 min | 含真实输出 | Why→What→How→What if


Ep1 · LLM 的两个缺陷,RAG 是什么解法

⏱ 5 min

学完这集你能回答:RAG 解决了 LLM 的哪两个根本问题?用一句话解释什么是 RAG?什么情况下不需要用 RAG?

WHY · 为什么需要 RAG

LLM 有两个根本缺陷

缺陷说明
缺陷 1:知识截止日期GPT-4o 训练截止 2024 年初。你问它”昨天 A 股发生了什么”,它不知道——不是不聪明,是物理上没有这段记忆。
缺陷 2:私有数据盲区你公司的内部报销规定、产品文档、合同模板,LLM 从没见过。问它”我们公司报销需要几天审批”,它只能猜。

具体场景: 问”RAG 分块推荐用多大?“——LLM 会给一个模糊的通用答案(“通常 256-512 tokens……“),因为它不知道你的知识库用的什么框架、存的什么内容。

WHAT · RAG 是什么

RAG = 每次回答前,先去检索相关资料,再基于资料回答。 全称 Retrieval-Augmented Generation(检索增强生成)。

说明
类比:闭卷考试纯 LLM = 闭卷考试。考生只能靠脑子里记住的知识作答。知识截止日期之后的事,以及你公司的私有信息,永远答不上来。
类比:开卷考试RAG = 开卷考试。考试时可以翻书。LLM 仍然负责”理解题意、组织语言、给出答案”,但知识来自实时检索的文档。

HOW · 真实输出对比

── 无 RAG ──────────────────────────────
Q: RAG 分块推荐用多大?
A: 通常建议 256-512 tokens,具体取决于您的使用场景。
   对于长文档建议 512 tokens,短文档可以用 256 tokens。
   (模糊通用答案,无来源)

── 有 RAG(v1 运行结果)───────────────────
Q: RAG 分块推荐用多大?
[检索] 命中文档片段:
   "推荐200-500字符,overlap设置10%-20%以保持上下文连贯"
A: 根据知识库文档,推荐分块大小 200-500 字符,
   overlap 设置 10%-20%。这样既保证检索精度,
   又能保留句子间的上下文连贯性。

关键差别: 有 RAG 的回答有来源、有具体数字、可以追溯。这是因为检索到了你自己写进知识库的文档片段。

WHAT IF · 什么时候不用 RAG

三种不需要 RAG 的情况:

情况原因更好的方案
知识量 < 50 页直接塞进 context window 就够了Long-context LLM(Gemini 1M token)
需要改变 LLM 行为RAG 只补充知识,不改变模型”性格”Fine-tuning
秒级实时数据建索引本身有延迟,无法做到实时Tool Calling + 数据库直查

自测: RAG 解决了 LLM 的什么问题?

RAG 解决了 LLM 的两个根本缺陷:知识截止日期(训练后的新信息 LLM 不知道)和私有数据盲区(企业内部文档、产品手册等 LLM 从未见过)。通过在每次推理时检索相关文档并注入 prompt,RAG 让 LLM 能基于最新的、特定的知识回答问题,而不是仅凭训练时的记忆猜测。


Ep2 · RAG 怎么运作,四层拆解

⏱ 8 min

学完这集你能回答:RAG 的四层架构分别是什么?离线阶段做什么、在线阶段做什么?每层的核心决策点是什么?

WHY · 10 行够用,但不够好

v1 的 RAG 只有约 10 行核心代码就能工作。但生产环境中你会遇到:文档太长塞不进 context相似问题找到不相关的内容模型”脑补”了知识库里没有的答案。这就是为什么每一层都有必要被认真设计。

WHAT · 四层架构

离线阶段(一次性)

  • 数据层:文档加载、清洗、分块(Chunking)
  • 索引层:Embedding → 存入向量数据库

在线阶段(每次请求)

  • 检索层:Query Embedding → ANN 搜索 → Top-K 文档
  • 生成层:构造 Prompt + 调用 LLM → 最终回答

HOW · 逐层拆解,每层一个决策点

🗂 数据层:Chunking — 为什么不把整本书扔进去?

LLM 有 context 上限(即使 1M token 也贵);更重要的是检索精度问题——一大段文字的向量是”平均值”,无法精确匹配用户的具体问题。分块让每个片段语义聚焦,检索命中率更高。

推荐:200-500 字符,overlap 10-20%

📐 索引层:Embedding — 文字怎么变成可搜索的数字?

Embedding 模型把文本映射到高维向量空间。语义相似 = 向量方向相似(余弦相似度高)。“分块大小”和”chunk size”的向量在空间里彼此靠近,即使一个中文一个英文。

🔍 检索层:Vector DB — 为什么不用 MySQL?

MySQL 全表扫描计算余弦相似度复杂度 O(n),百万文档时慢到不可用。向量数据库(ChromaDB、Pinecone、Weaviate)用 ANN(近似最近邻)索引,复杂度接近 O(log n),毫秒级返回 Top-K。

✍️ 生成层:为什么要约束”仅基于以下内容回答”?

不加约束,LLM 会混合知识库内容和训练时的记忆,悄悄”脑补”。加上 仅基于以下检索内容回答,如无相关信息请说"我不知道" 这行约束,可以大幅降低幻觉概率,让回答可以追溯到具体文档片段。

WHAT IF · 每层出问题的诊断信号

症状根因层诊断方向
回答内容是编造的(有文档但答错)生成层检查 Prompt 约束是否够强;检索到的内容是否被完整传入
找到的内容不相关检索层检查 Embedding 模型;考虑混合检索;查看 Top-K 文档
分析表格、图片、代码失败数据层PDF 解析器是否支持结构化提取;图片需要多模态处理
相关内容存在但没被检索到索引层检查 Chunking 策略;文档是否成功入库;Embedding 维度是否匹配

自测: 用户问了一个问题,RAG 执行了哪几步?

在线阶段执行两步:

  1. 检索层:将用户问题 Embedding 成向量 → 在向量数据库中 ANN 搜索 → 返回 Top-K 最相关文档片段。
  2. 生成层:将检索到的文档片段 + 用户问题拼接成 Prompt → 调用 LLM → 得到基于文档的回答。

离线阶段(数据层 + 索引层)在用户提问之前就已完成,是一次性的预处理工作。


Ep3 · 检索为什么会失败,三个递进解法

⏱ 10 min

学完这集你能回答:BM25 和向量检索各自的盲区是什么?混合检索如何互补?MRR 从 0.71 提升到 1.00 经历了哪些步骤?

WHY · 先看一个真实的失败案例

── v3.5 基线评估(纯向量检索)────────────────

Query: "RAG 中推荐的分块大小和 overlap 是多少?"
Top-1: "embedding 模型选型建议..."           ← 排第1(错)
Top-2: "推荐200-500字符,overlap 10%-20%"    ← 正确答案排第2

MRR@3: 0.50  (正确答案排第2,得分0.5,不是1.0)

原因分析: 问题包含"RAG"这个主题词,污染了向量方向
         向量模型把"RAG"当成主要语义,而非背景信息

这个案例由 Andrej Karpathy 在演讲中展示过类似模式:越精确的词越容易被稀释。用户真正想问的是”分块大小”,但”RAG”这个词的向量权重把检索方向拉偏了。

WHAT · 三种检索方式的盲区对比

检索方式优势盲区
稀疏检索(BM25)精确词匹配强;不受语义方向污染;无需 GPU不理解语义;“汽车”找不到”automobile”
稠密检索(向量)理解语义和同义词;支持跨语言精确词弱;长 query 语义被平均;主题词污染
混合检索(RRF)两路互补;精确 + 语义双保险需要调权重;偶尔有平局问题

RRF(Reciprocal Rank Fusion):把两路检索的排名倒数相加。比如某文档在 BM25 排第2、向量检索排第1,则 RRF 得分 = 1/(60+2) + 1/(60+1) ≈ 0.0323。取所有文档 RRF 得分最高的 K 个。

HOW · MRR 数字演进,步步为营

从 0.71 到 1.00 的四个版本:

版本MRR@3关键改动解决的问题
v3.5 基线0.71人工标注 8 条 Query,纯向量检索
v5 混合检索0.85加入 BM25,RRF 融合两路结果精确词被稀释的问题
v6 加权 RRF0.92w_bm25=1.5,解决排名平局两路得分相同时的模糊排序
v6 + Reranking1.00Cross-encoder 精排 Top-K 结果双路召回后的排序精度问题

Reranking 的原理: Cross-encoder 模型同时”看”Query 和文档,计算两者的相关性得分。比 Bi-encoder(向量)的近似相似度更精准,但更慢,所以只在 Top-K 上做精排,不做全量搜索。

WHAT IF · 检索好了但回答还是差?

MRR 提升到 1.00 之后,如果回答质量还是不够好,原因可能是:用户的 Query 本身不清晰、包含多个子问题、或者使用了知识库里没有的术语。这就需要 Query 变换——在检索之前先对问题进行改写、拆解或扩展。

自测: BM25 和向量检索各自的盲区是什么?

BM25(稀疏检索)的盲区: 只做词频匹配,不理解语义。“汽车”和”automobile”、“分块”和”切割”对它来说是完全不同的词,无法召回语义相关但用词不同的文档。

向量检索(稠密检索)的盲区: 精确词匹配弱。当 Query 中有强主题词(如”RAG”)时,向量方向会被平均拉偏,导致包含精确答案但不那么”主题相关”的文档排名靠后。

混合检索的价值: 两路互补——BM25 保住精确词命中,向量检索保住语义理解,RRF 融合后综合排序。


Ep4 · 怎么知道系统好不好,从 MRR 到 RAGAS

⏱ 6 min

学完这集你能回答:MRR 和 RAGAS 各自衡量什么?RAGAS 四个指标分别代表什么?什么时候从 MRR 切换到 RAGAS?

WHY · “感觉变好了”不够

从 v1 到 v7,我们做了分块优化、混合检索、Reranking、Query 变换……每次修改后都感觉”好了一点”。但感觉不是指标。没有量化评估,你不知道:各项改进叠加后总效果如何?检索好了但生成还在出错吗?新功能是否引入了退步?

WHAT · 两种评估工具的分工

MRR(平均倒数排名)RAGAS(4 维度评估框架)
评估检索排名质量检索 + 生成全链路
成本无 LLM 调用每条需调用 LLM 评判
速度毫秒级分钟级(取决于 Query 数量)
适用早期迭代,频繁调整检索参数阶段性全面评估,上线前验收
说明只看”正确文档排第几”,不管生成质量覆盖完整 RAG 链路,有 ground truth 更准

HOW · RAGAS 四个指标,直观解释

Context Recall · 该找到的信息找到了多少?

把 ground truth 答案分解成若干信息点,检查每个信息点是否出现在检索到的文档里。衡量检索的覆盖率——重要信息有没有遗漏。

Context Precision · 找到的里有多少是有用的?

检索到的文档中,有多少比例真正被用于回答问题。衡量检索的精准率——有没有引入噪音文档把 LLM 搞糊涂。

Faithfulness · 回答有没有编造内容?

把生成的回答拆解成若干陈述,检查每个陈述是否可以从检索到的文档中推断出来。衡量生成层的幻觉率——是否基于文档作答。

Answer Relevancy · 回答有没有切题?

检查生成的回答是否真正回答了用户的问题(而不是答非所问或过于宽泛)。衡量回答的相关性——有没有废话、有没有跑题。

── v8 RAGAS 评估结果(8条标注Query)─────────

Context Recall:    0.87  ← 87% 的关键信息被检索到
Context Precision: 0.79  ← 79% 的检索内容有用
Faithfulness:      0.91  ← 91% 的回答陈述可追溯到文档
Answer Relevancy:  0.88  ← 88% 的回答切题

瓶颈:Context Precision 0.79,说明检索引入了
      约 21% 的噪音文档,可进一步优化 Reranking

WHAT IF · 没有 ground truth 怎么办?

没有人工标注的 ground truth 时,常见做法是用 LLM 从文档自动生成 Query-Answer 对。但要注意:合成 Query 评估出来的 RAGAS 指标会系统性虚高 10-30%。原因是合成的问题天然契合文档内容,和真实用户提问的分布完全不同。合成评估适合回归测试(防止退步),但不能替代真实用户标注。

自测: 为什么不一直用 MRR,什么时候切换到 RAGAS?

一直用 MRR 的问题: MRR 只评估检索排名,完全不看生成质量。即使检索 MRR = 1.0,LLM 还是可能基于检索到的文档产生幻觉、答非所问或遗漏关键信息。MRR 无法发现这些生成层的问题。

切换时机: 早期迭代(频繁调整 Chunking、Embedding、检索参数)用 MRR——快、无成本。阶段性全面评估时(每隔几个版本、或上线前)切换到 RAGAS,用 4 个维度全面检查整个链路,找出瓶颈所在。


Ep5 · Demo 到生产,差了什么

⏱ 5 min

学完这集你能回答:生产 RAG 系统有哪四个核心关注点?语义缓存和精确缓存有什么区别?数据秒级更新时 RAG 怎么办?

WHY · v1 能跑 ≠ 生产能用

v1 跑通了,但部署到生产环境会遇到:相同问题反复调 API 浪费钱出问题不知道哪一步错了A 部门看到 B 部门的文档一篇文档更新要重建整个索引。这四个问题是从 Demo 到生产必须解决的工程关键。

WHAT · 四个生产关注点

关注点说明
语义缓存相似问题命中缓存,不重复调 LLM API。“RAG 分块多大”和”RAG chunk size 推荐”语义相似,第二个问题直接返回缓存结果。
请求追踪每次请求打一个 trace ID,记录检索了哪些文档、生成了什么 Prompt、LLM 返回了什么。出问题时能定位到具体步骤。
多租户隔离不同部门的文档打上 tenant_id 标签。检索时自动过滤,保证 A 部门只能看到 A 部门的内容。
增量索引文档更新时只重新 Embedding 修改的部分,不重建全量索引。大知识库尤其关键(全量重建可能需要数小时)。

HOW · 语义缓存效果对比

── v10 语义缓存效果(生产压测)────────────────

请求 1: "RAG 的分块推荐用多大?"
  状态: MISS  (未命中缓存,完整执行检索+生成)
  耗时: 624ms
  → 结果写入语义缓存

请求 2: "RAG 分块大小推荐是多少?"
  状态: HIT   (命中缓存,跳过检索和 LLM 调用)
  耗时: 117ms  节省 81% 延迟

相似度阈值: 0.85(低于此值不命中,防止误匹配)

81% 的延迟节省在实际业务中意味着什么:高峰时段 80% 的问题是同类问题的变体(不同措辞问同一件事),语义缓存可以让这些请求不花一分 API 费用、不占用 LLM 资源。

WHAT IF · 数据秒级更新怎么办?

RAG 的索引有延迟——即使是增量索引,也需要几秒到几分钟。对于股票价格、实时库存、当前系统状态这类秒级更新的数据,RAG 不适合。

正确方案:Tool Calling + 数据库直查。让 LLM 生成 SQL 或调用 API,实时查询数据库。这是 RAG 和 Agent 的边界——静态知识用 RAG,动态数据用 Tool。

自测: 语义缓存和精确缓存(Redis key-value)的区别?

精确缓存(Redis key-value): 以完整的问题字符串为 key。“RAG 分块多大”和”RAG分块多大”(少个空格)是不同的 key,不会命中。只有一字不差才能命中。

语义缓存: 先把问题 Embedding 成向量,在缓存向量库中搜索语义相似的历史问题。“RAG 分块推荐用多大”和”RAG chunk size 推荐是多少”虽然措辞完全不同,但向量相似度 > 阈值(如 0.85),可以命中同一条缓存。

适用场景: 用户提问措辞多变时,语义缓存命中率远高于精确缓存,但需要多一次向量查询的开销。


系列完结

你已走完 RAG 核心路径:从两个缺陷出发 → 四层架构 → 检索优化 → 量化评估 → 生产化

学完这 5 集,你已经走完了 RAG 的核心路径:理解了为什么需要 RAG、它的四层架构是什么、检索为什么失败以及如何修复、如何量化评估系统好坏、以及 Demo 和生产之间的差距。