Skip to content

第 5 章:RAG 入门

章节定位

这一章开始进入 LangChain 最有实战价值的部分:把模型和你自己的资料结合起来。

前面几章,你已经能用 LangChain 组织提示词、模型和输出格式了。但如果应用要回答“你的课程资料里写了什么”“公司内部文档中怎么定义这个流程”,只靠模型本身不够。RAG,Retrieval-Augmented Generation,解决的就是这个问题。

在主项目 AI 学习助手 中,这一章会把学习助手从“会聊天”升级成“能基于课程资料回答问题”的版本。

配套示例

  • 目录:examples/chapter-05
  • 入口:examples/chapter-05/main.py
  • 依赖:examples/chapter-05/requirements.txt
  • 运行:cd examples/chapter-05 && python3 main.py

示例层级与边界

  • 层级:教学版
  • 本章重点:把基础 RAG 流程拆开,先看懂文档切分、检索和回答是怎么串起来的。
  • 不要误判:这里强调流程理解,不代表真实 Embedding 服务、向量库选型或线上索引维护方案。

本章目标

学完这一章,你应该能理解:

  • 为什么需要 RAG,而不是直接问模型
  • 一条基础 RAG 流水线是怎么组成的
  • 文档切分、向量化、检索、生成分别负责什么
  • 如何搭一个最小可运行的课程资料问答系统
  • 基础 RAG 最常见的问题在哪里

前置知识

你最好已经掌握:

  • LLM 的基本调用方式
  • LangChain 的核心抽象
  • Prompt、Model、Parser 的基本作用

如果你对这些还有点模糊,也可以先看前面的章节,但这章会尽量把核心流程讲清楚。

为什么需要 RAG

直接问模型有三个天然限制:

  1. 模型不知道你自己的私有资料。
  2. 模型的知识会过期。
  3. 模型会“看起来很自信地编造答案”。

RAG 的核心思路不是让模型“记住更多”,而是让模型“先查资料,再回答”。

这个过程很像一个靠谱的学习助手:

  • 先去你的教材、笔记、文档里找相关段落
  • 再把找到的内容交给模型整理答案
  • 最后根据上下文生成自然语言回复

对于 AI 学习助手 来说,这意味着用户可以问:

  • 第 3 章为什么要先做最小链?
  • RAG 和普通问答的区别是什么?
  • 哪些章节适合先学?

而回答不再依赖模型的“记忆”,而是依赖你维护的课程资料。

RAG 的基本流程

一个基础 RAG 系统通常包含五步:

  1. 加载文档
  2. 切分文档
  3. 把文本转成向量
  4. 按问题检索相关片段
  5. 把片段和问题一起交给模型生成答案

你可以把它理解成两段式流程:

  • 离线阶段:准备知识库
  • 在线阶段:根据问题检索并回答

这也是 RAG 和“把整个文档塞进 prompt”最大的区别。后者只能处理很小的内容,前者可以扩展到更多文档。

核心概念

Document Loader

Loader 的作用是把原始资料读进来。

原始资料可能来自:

  • Markdown
  • PDF
  • 网页
  • Notion 导出内容
  • 内部说明文档

在这个项目里,最自然的来源就是 docs/ 里的课程正文、README 和项目说明。

Text Splitter

文档通常太长,不能直接整篇送去做检索和生成,所以要先切分成较小片段。

切分的目标不是“越碎越好”,而是:

  • 保留语义连续性
  • 让每个片段足够独立
  • 方便后续检索时定位相关内容

Embeddings

Embedding 是把文本转成向量表示。

你不用把它理解成某种神秘数学,只需要知道:

  • 相似含义的文本,向量距离更近
  • 检索时可以根据向量相似度找到相关片段

Vector Store

Vector Store 是向量数据库或向量索引。

它负责:

  • 保存向量
  • 保存原始文本片段
  • 根据查询向量返回相似片段

Retriever

Retriever 是检索器,面向“我该找哪些片段”这个问题。

它通常不会直接生成答案,而是负责把最相关的内容找出来。

Generation

最后一步是把:

  • 用户问题
  • 检索到的文档片段

一起交给模型,让模型基于资料生成答案。

基础架构怎么理解

可以把基础 RAG 看成三层:

  • 知识准备层:加载、切分、向量化
  • 检索层:根据问题找相关片段
  • 生成层:把片段组织成回答

这三层职责要分开,原因很简单:

  • 资料更新时,只需要重建知识库
  • 检索策略变化时,只改 Retriever
  • 回答风格变化时,只改 Prompt 或生成链

这也是 LangChain 在工程上的价值:把“知识准备、检索、生成”拆成可组合模块。

最小示例:课程资料问答

下面这个最小示例的目标不是追求完整生产级实现,而是让你看懂 RAG 的骨架。

python
# 伪代码结构,强调流程,不绑定具体版本

documents = load_markdown_files("./docs/chapters")
chunks = split_documents(documents, chunk_size=800, overlap=120)

vector_store = build_vector_store(chunks)
retriever = vector_store.as_retriever(top_k=4)

question = "RAG 为什么比直接问模型更适合课程资料问答?"
relevant_chunks = retriever.retrieve(question)

prompt = """
你是课程学习助手。请只根据给定资料回答问题。
如果资料不足,请明确说明不知道。

资料:
{context}

问题:
{question}
"""

answer = llm.generate(prompt.format(
    context=relevant_chunks,
    question=question,
))

这个示例里最重要的不是某个函数名,而是数据流:

  1. 文档进入系统
  2. 文档被切成片段
  3. 片段被向量化并存入索引
  4. 问题进来后先检索
  5. 检索结果再交给模型生成回答

真实框架版对照

examples/chapter-05/main.py 现在同时保留了 教学版真实框架版

真实框架版和教学版的差别主要在三点:

  • 检索器不再只是普通函数,而是实现成 BaseRetriever 接口
  • 问题和上下文通过可组合链路喂给 ChatPromptTemplate
  • 最终回答走 prompt -> model -> parser 这种更接近真实 LCEL 的形状

这样你能更清楚地看到:教学版是在解释流程,真实版是在解释这些流程最终会以什么接口落到工程代码里。

为什么要切分文档

切分不是可选项,几乎是 RAG 的必需项。

如果你直接把整篇文档都拿去检索,通常会遇到几个问题:

  • 检索粒度太粗
  • 相关内容被无关内容淹没
  • 上下文窗口不够

合理切分后,系统更容易在“正确片段”上做匹配。

但切分也不是越细越好。太碎会导致:

  • 语义断裂
  • 检索到的片段信息不完整
  • 回答缺少上下文

所以切分大小和重叠长度要根据资料类型调节。

为什么要先检索再生成

这是 RAG 的核心价值。

如果直接生成,模型只能基于内部参数记忆作答。
如果先检索,模型就会:

  • 读到当前资料
  • 结合资料回答
  • 更容易保持一致性

对学习站来说,这种方式尤其重要,因为用户问的往往不是“通用知识”,而是“这套课程里怎么安排的”“这份文档里怎么定义的”。

本章实践

建议你先做一个很小的练习:

  1. 选 3 到 5 篇课程 Markdown 文件作为知识源
  2. 按章节语义切分成小块
  3. 做一个最简单的向量索引
  4. 输入一个关于课程结构的问题
  5. 检查系统是不是能返回相关段落

实践的目标不是把系统做大,而是验证这条链路是否成立。

如果你做的是 AI 学习助手,优先问这些问题:

  • 第 5 章的核心流程是什么?
  • 哪几章是 RAG 相关的?
  • 学习路线图里先学哪一章?

常见坑

坑 1:把 RAG 当成“让模型记忆更多”

RAG 不是记忆增强,而是检索增强。
它的重点是“查资料再回答”,不是“把所有知识塞给模型”。

坑 2:文档切得太碎或太粗

太碎,语义丢失。
太粗,检索不准。

最稳妥的做法是先用中等粒度切分,再根据结果调参数。

坑 3:检索到的内容和问题不相关

这通常不是模型问题,而是检索问题。
先检查:

  • 切分是否合理
  • 向量化是否正常
  • top-k 是否合适
  • 提问文本是否足够明确

坑 4:回答没有根据资料作答

这说明生成阶段的提示词约束不够。
你应该明确告诉模型:

  • 只根据检索到的资料回答
  • 资料不足时要说不知道
  • 尽量引用资料中的要点

练习题

  1. 为什么课程站更适合用 RAG,而不是只靠模型直接问答?
  2. 解释一下文档切分、Embedding、Retriever 三者的区别。
  3. 如果检索结果不相关,你会先检查哪三个环节?
  4. 试着把 AI 学习助手 的问答范围限定为“只回答课程文档中的内容”。

本章总结

这一章建立了 RAG 的最基本理解:

  • 先准备资料
  • 再检索相关内容
  • 最后让模型基于资料回答

你不需要一开始就把系统做得很复杂,但你必须理解这条主链路。只要这条链路打通,后面的优化才有意义。

AI 学习助手 中,这一章让系统第一次具备了“基于项目资料回答问题”的能力。

学完本章,你现在应该会

  • 用自己的话讲清楚 RAG 的基本流程:准备资料、检索片段、基于片段生成回答
  • 解释切分、Embedding、Retriever 在链路里的职责差异
  • 判断一个问题是“模型没答好”还是“检索没找对”
  • AI 学习助手 加上“只根据课程资料回答”的基础约束

最小验收 checklist

  • [ ] 我能画出一个最小 RAG 链路并说清每一步输入输出
  • [ ] 我已经用 3 到 5 篇资料做过一次最小检索问答实验
  • [ ] 我会检查检索相关性,而不是只盯着最终回答
  • [ ] 我能解释为什么课程站比纯模型问答更适合 RAG

建议你动手改一版

  • 把知识源从 3 篇扩到 5 篇,再观察检索是否开始混入无关内容
  • 分别试一次“按自然段切分”和“按固定长度切分”,比较哪种更适合课程资料
  • 在回答里新增“依据片段”部分,训练自己观察模型是否真的基于资料作答

卡住时先回看这里

下一章预告

下一章会继续在 RAG 上往前走,不再只看“能不能答”,而是看“答得准不准、稳不稳、能不能解释”。

你会学习:

  • 如何改进切分策略
  • 如何提升检索质量
  • 如何减少无关上下文
  • 如何让回答更稳定、更可信