第一课:RAG到底是什么?
RAG=先查资料,再让大模型回答。
他的英文全称是Retrieval-Augmented Generation ,中文一般叫:检索增强生成。
先不要管英文有多长,我们先拆成两个部分来看:
- Retrieval:检索,查找资料
- Generation:生成,让模型组织语言输出答案
所有他的大致意思是:大模型回答问题前,不是只靠训练过的知识,而是先去查找相关资料,再根据资料组织语言,进行回答。
为什么会有RAG
先说一个现实的问题,如果你直接问大模型:
公司的内部制度是什么?某个PDF第三章讲了什么?某个最新的政策怎么规定?
这时候模型会遇到3个麻烦
- 他不知道你的私有资料:你公司的内部文档、你的笔记、你的产品手册,这些内容本来就不在他的训练数据里。
- 他的知识可能过时:模型训练完成后,世界仍然在发展,新文档,新政策,新资料出现,他可能根本没见过
- 他可能回答会偏:这就是我们说的幻觉,明明不知道,但却一本正经乱说。
所有RAG的思想就是:能不能让大模型在回答之前,先看一眼我们提供的材料,再回答。
RAG像是开卷考试
不用 RAG 的大模型
像一个学生,考试时:不准翻书只能靠脑子里的记忆答题
那它就可能:记错,忘了,瞎猜
用了 RAG 的大模型
像一个学生,考试时:先看试卷的题目,根据题目去资料库翻书,找到相关内容,再根据书上的内容答题
这时候答案通常会:更准确,更贴近你给的资料,更少胡说八道
所以先记一句:
RAG 本质上,就是给大模型加一个"查资料"的能力。但有一点要知道,好的RAG系统在找不到依据时,不会像考试一样自己编,而是明确说明找不到对应信息。
RAG的知识库是什么?
很多初学者一听到知识库,觉得很玄,其实**知识库=一堆可以被系统搜索的资料 ,**这些资料可能是PDF、Word、网页、Markdown文档、FAQ、数据库里的文本内容等等。所以RAG并不是提升模型智商,而是提升回答时的信息来源质量。
第二课:RAG的完整流程
我们用一个例子来学:
当我们问模型:员工请病假需要提供什么材料?
系统会怎么做?
第一步:接收用户信息
用户输入问题:员工请病假需要提供什么材料?
系统这时知道两件事:
- 你想问的是病假
- 你想知道员工需要哪些材料
第二步:去知识库找相关内容
系统不会直接回答,而是先去知识库里查,知识库里可能有员工手册、请假制度、HR政策文件等。系统会从里面找出类似这些内容:
"员工请病假1天以上需要提供医院证明"
"连续病假申请需要附诊断材料"
"病假审批需要通过OA系统提交"
这一步叫做**检索,**可以先理解为:从很多资料里面,找出和当前问题最相关的几段内容
第三步:把内容当作参考资料
系统找到内容后,不会原封不动的直接甩给你,而是会把这些内容整理为上下文。
比如变成这样:
- 资料 1:员工请病假 1 天以上需提供医院证明
- 资料 2:连续病假申请需附诊断材料
- 资料 3:病假审批通过 OA 系统提交
这些就是给大模型看的"参考资料"。
这一步你需要记住这个词:Context(上下文),它通常是检索出来,准备用来辅助回答的材料
第四步:把问题+参考资料一起发给大模型
系统不是只把你的问题发给模型,而是会一起发:
- 用户问题
- 检索到的上下文
- 一段提示词要求
大概意思像这样:
"用户问:员工请病假需要提供什么材料?
下面是检索到的参考资料,请你仅基于这些资料回答;
- 资料 1:员工请病假 1 天以上需提供医院证明
- 资料 2:连续病假申请需附诊断材料
- 资料 3:病假审批通过 OA 系统提交
如果资料不足,请明确说明。"
这一步就是把 RAG 和普通问答真正区分开的地方。
普通问答是:只给问题
RAG 是:给问题 + 给资料
第五步:大模型生成答案
模型看到问题和资料后,开始组织语言,输出结果。
例如:
根据提供的制度内容,员工请病假时通常需要提交医院证明;如果是连续病假,还需附诊断材料,并通过 OA 系统提交审批。
这时候模型的角色更像是资料整理员与回答生成器,而不是原本的百科全书
RAG的重点
很多初学者以为RAG最重要的是模型,我选好的模型,选参数高的模型就能让模型回答的好一些。其实不是,RAG很多时候成败取决于前面几步:
- 资料找得准不准
- 找出来的内容够不够相关
- 给模型的上下文是否干净清楚
也就是说RAG不只是生成问题,更是检索问题。
第三课:为什么要切块(Chunk)
很多人学RAG都会问,既然要查资料,那我直接把文档丢给他,让他根据文档回答就好了,我弄啥RAG。答案是:因为文档通常太长,太杂,太浪费,且不利于准确检索。所以RAG里又一个特别重要的动作:切块。
RAG不会把整篇文档拿去检索,而是会先把文档切成很多小段,这些小段,就叫chunk。中文可以理解为:文本块、内容片段、文档切片
为什么不能整篇文档直接用
假设有1份100页的员工手册,你问"请病假需要提供什么材料?",如果把这100页都扔给模型,会出现我们提到的问题:
- 太长:模型能接收的上下文是有限的,文档太长的话,可能根本塞不进去
- 太浪费:只是问病假材料,没必要把其他入职流程、报销制度等内容都丢给模型
- 干扰太多:无关的内容太多了,会影响模型的判断,导致抓错重点
- 不利于准确检索:你真正需要的是其中一小段,整篇一起整理不利于定位。
所以要先把大文档拆开。
Chunk可以理解为把大书拆成很多小卡片
我们打一个比方,原来你有一本厚厚的书,现在我们把书按内容切成很多小卡片:
- 卡片 1:年假规则
- 卡片 2:病假流程
- 卡片 3:报销标准
- 卡片 4:加班制度
当用户问"病假材料"时,
系统就更容易找到"病假流程"那张卡片,
而不是抱着整本书乱翻。
所以:chunk 的作用,就是把大文档拆成更适合搜索和检索的小单位。
chunk的本质作用是什么
1、方便检索:系统更容易定位到相关片段
2、降低噪音:只把相关的小段交给模型,而不是整本书
3、节省上下文空间:模型看到更精炼的资料,回答更聚焦
chunk的大小怎么定
chunk太大
假设一个chunk很大,包含了
- 病假制度
- 年假制度
- 婚假制度
这会导致:
- 信息太杂:虽然里面可能包含了正确答案,但也混入了很多无关内容
- 相似度可能被稀释:用户问病假,但这个块还有很多别的内容,系统未必能判断这个块是最相关的
- 给模型时容易啰嗦:我们交给模型的是检索到的资料与用户问题,但检索出来的资料噪音太大,模型输出也不好聚焦用户的问题。
因此chunk太大会让检索变钝
chunk太小
那是不是chunk越小越好?
也不是。比如我们把一句完整的话切的太碎:
原文:
员工请病假 1 天以上需提供医院证明,并通过 OA 系统提交审批。
如果切得太小,变成:
- chunk A:员工请病假 1 天以上
- chunk B:需提供医院证明
- chunk C:并通过 OA 系统提交审批
这时就麻烦了:
- 语义不完整:每一小段单独看,语义都不全,相关性都不高
- 上下文断裂:系统可能只检索到需要医院提供证明,却不知道这是在讲病假
- 模型收到的信息不连贯:把需要提供医院证明交给模型,没有把OA系统提交审批交给模型,这就会导致模型输出的答案是残缺的。
因此chunk太小,会让语义断掉。
最理想的chunk是怎样的
一个好的chunk,需要满足两件事:
- 足够小:方便精确检索
- 足够完整:保留基本语义,不要切断一句话或一段完整的规则的意思
- 可以适当重复: 让相邻两个 chunk 有一部分重复内容 (overlap)
既不能是一整章,也不能只剩半句话。
什么是overlap(重叠)
这是RAG里另一个常见的概念。
文本切块时,很容易在边界把意思切断,所以常常会让相邻两个chunk有一部分内容重叠,这就是overlap。
比如原文是:
员工请病假 1 天以上需提供医院证明,并通过 OA 系统提交审批。连续病假还需附诊断材料。
切块时可能变成:
- chunk1:员工请病假 1 天以上需提供医院证明,并通过 OA 系统提交审批。
- chunk2:并通过 OA 系统提交审批。连续病假还需附诊断材料。
你看,
"并通过 OA 系统提交审批" 这句重复出现在两个块里。
这样做的好处是:
- 减少边界信息丢失
- 让相邻块语义更连续
- 检索时不容易漏掉关键内容
所以overlap可以理解为:切块时故意保留一点重叠内容,避免把意思切断。
先别记数字,先记住原则
你在看视频,听别人交流的时候,可能会听到他们说:
- chunk size 500
- overlap 50
- 1000 tokens
- 200 characters
别被这些数字吓到了,作为初学者,你先记住这些原则
- chunk要能表达一个相对完整的小意思
- 不能太大,否则噪音多
- 不能太小,否则语义不完整
- 必要时要有overlap,避免边界丢失信息
简单的标准判断
看到一段文本时,先问自己:
如果这一段单独拿出来,别人还能大概看懂它在说什么吗?
如果能:这可能是一个合适的chunk
如果不能:那说明切的太碎了
第四课:Embedding是什么
这一节会有一点抽象,我尽量让初学者能看懂。
先记住一句核心的话:
Embedding是把一句话的语义特点变成一串数字。
数字并不是要你做数学题,只是要理解它在RAG里到底是干啥的。
为什么需要Embedding
假设知识库里有一句话**"员工请病假需要提交医院证明。"**
而用户问的是:"请病假要交什么材料?"
这两句话并不是一模一样的。一个说提交医院证明,另一个说叫什么材料。如果系统只会按关键词去找,那就可能会出问题:
"提交"和"交"不是完全一样的
"医院证明"和"材料"也不是同一个词
但人一看就知道,这两句话的意思很接近,但机器不是人,它不能看一眼就知道他俩是一个问题与答案,它需要一个方式来判断意思是否相近,这个方式就是Embedding。
Embedding的本质:把文字转为可比较的语义坐标
计算机不真正懂文字的意思,一切输入对他来说都是数字,它擅长处理数字问题,所以我们把一句话转成数字让他去看懂。这串数字不是乱写的,而是要保留文字的语义信息。
比如
- "员工请病假需要提交医院证明"
- "请病假要交什么材料"
这两句话经过Embedding后,会变成两组数字,虽然数字很长,我们看不懂,但计算机不一样,它是数学能手,它可以去比较这两组数字的关系(怎么比较我们后面再说):
- 如果很接近,说明语义相似
- 如果差很远,说明语义不相关
所以你可以把 Embedding 理解成:
把一句话放到一个"语义空间"里,占一个位置。
意思接近的话,会离得近。
意思差很远的话,会离得远。
用数学的角度的话,就是在一个平面上,我们有(X,Y)坐标来确认位置,而两个点(X1,Y1)与(X2,Y2)之间是有距离的,这个距离就是他们的关系。只不过在语义空间中,这不是二维平面的,而是上千维的。
在RAG里,Embedding用在哪里
在RAG中,Embedding主要用在检索阶段,大致流程是这样的:
- 先处理知识库里的chunk:把每个chunk都做一次Embedding,也就是把每个chunk变成一个向量
- 把这些向量存起来:存到向量库或者检索系统里。
- 用户来提问时:把用户的问题也做一次Embedding
- 比较问题向量与各个chunk向量:看看哪个chunk与问题最接近
- 取出最相关的几个chunk:取出一定数量的chunk交给模型生成答案
文档切块 → 每个 chunk 做 Embedding → 存起来 → 用户提问也做 Embedding → 比较相似度 → 找到相关 chunk
什么是向量
你经常会听到这两个词:"向量"与Embedding,初学者很容易搞混,你可以记为:
- Embedding:把文本转为数字表示的这个过程
- 向量:把文字转为数字表示的这串数字
一段文字,经过Embedding,得到一个向量。
你现在不需要会线性代数,只要知道"向量就是一串能表示语义的数字"就够了。
为什么 Embedding 很重要
因为没有 Embedding,
系统很多时候只能做很死板的关键词匹配。
比如:
知识库里写的是:
"提交医院证明"
用户问的是:
"要交什么材料"
关键词不完全重合。
但语义其实相关。
Embedding 的价值就在这里:
它让系统有机会按"意思相近"来找内容,而不只是按字面是否一样。
这就是为什么 RAG 能做"语义检索"。
Embedding不是理解全文真相,他只是压缩语义特征
我们要明确一个点:Embedding不是说系统真的像人类一样去理解这个句子的含义,而是把一句话的重要语义特征提炼成数学表示。
所以他并不是万能的,可能会出现:
- 相近但不完全准确的内容被召回(初步筛选中)
- 专业术语表示不够好
- 长文本信息被压缩后有损失
这就是为什么会有:
- chunk设计
- 重排rerank
- 混合检索
这些手段出现的原因
一个例子
假设知识库里有 3 个 chunk:
chunk 1
"员工请病假 1 天以上需提供医院证明。"
chunk 2
"国内出差住宿报销上限为每天 600 元。"
chunk 3
"年假需提前 3 天提交申请。"
用户问题是:
"病假要交什么材料?"
系统会做什么?
- 把问题转成向量
- 把 3 个 chunk 的向量拿来比较
- 发现问题和 chunk 1 最接近
- 所以优先取 chunk 1
- 讲chunk1与用户问题交给大模型生成答案
这就是 Embedding 在实际中的作用。
第五课:向量库是什么
向量库,就是专门用来存放向量,并且能快速找出"最相似向量"的地方。
先不要被这个库给吓到,可以先把他理解成一种特殊的数据库。(数据库应该都知道吧,要是数据库也不知道的话,可能得去补补这方面知识)
为什么需要向量库
前面我们说过,知识库里的每个chunk都会做Embedding,变成向量,现在假设你有10万个chunk,这些chunk变成向量后,系统要把它放在哪里?不可能丢给模型让模型记住吧,而且检索过程中系统还要做:从这么多向量里,快速找出和问题最相近的那几个。 这时候,普通的存储方式就不够方便了,需要一个专门做这件事的东西,这个东西就是向量库(Vector Database/Vector Store)
向量库是干什么的
他有两个核心工作:
存向量
把每个chunk的向量存起来,不仅可以存chunk,chunk的文本内容、chunk的id、chunk的来源文档都可以一起存,记住:只有向量是一串数字,其余的是以文本形式存着的。(我们后面会再讲)
查相似向量
当用户问题也变成向量后,向量库会去找:哪些chunk的向量和这个问题向量最接近?
然后返回最相关的几个结果,所以一句话概括:
向量库=存向量+做相似度搜索
他和普通数据库有什么区别
这是初学者必须分清楚的地方。
普通数据库更擅长:精确查询、条件筛选、查id、查日期等,比如你查员工编号=10086,报销金额>500,日期在3月份。这种是普通数据库很擅长的。
向量数据库更擅长:查语义相近的文本、查意思差不多的内容、相似度搜索
比如你问:"病假要交什么材料",知识库的原文可能是 "员工请病假 1 天以上需提供医院证明。" 他们字面不同,但语义接近,这就是向量库擅长处理的场景。
向量库在RAG流程里处于哪里
现在我们把流程串起来
离线准备阶段
先做这些事:
- 拿到文档
- 切chunk
- 每个chunk做Embedding
- 把chunk和向量存到向量库里
这是建库过程
在线问答阶段
用户提问时:
- 用户问题做Embedding
- 去向量库里找最相似的chunk
- 返回top-k结果
- 把这些结果交给大模型生成答案
所以你现在可以看到:
Embedding 负责把文本变成可比较的向量,向量库负责把这些向量存起来并快速检索。
什么是Top-k
我们刚才其实已经碰到了这个概念,当问题向量去向量库里搜索时,系统通常不会只取1个结果,而是取最相关的前几个,这就叫Top-k。
意思就是:
相似度最高的前 k 个结果
比如:
- top-1:最相似的 1 个 chunk
- top-3:最相似的 3 个 chunk
- top-5:最相似的 5 个 chunk
为什么不总是只取 1 个? 因为一个问题的答案,可能分散在多个chunk里,多取几个,能减少漏信息的风险,但也不能取太多,不然噪音会增加太多,这个我们后面还会详细讲。
向量库里不只是向量,通常还有元数据
了向量本身,通常还会存一些附加信息,比如:原始文本内容、文档标题、来源文件名、页码、时间、标签、权限信息等等
这些叫:
metadata(元数据)
这样做的好处是:
- 方便返回原文
- 方便做过滤
- 方便显示来源引用
- 方便权限控制
他的数据结构通常长这样:
- ID (标识符):
1024------ 数字或字符串(用于唯一标记)。 - Vector (向量):
[0.12, -0.55, 0.88, ... 1536维]------ 浮点数数组(这是唯一的"数学暗号",专门给计算机做模糊搜索用的)。 - Payload (元数据/载荷): ------ 文字/JSON 格式(这是给人或大模型看的信息)。
content: "大熊猫主要栖息在四川..." ------ 纯文本。source: "大熊猫百科全书.pdf" ------ 纯文本。page: 12 ------ 整数。
我们现在先不用深记,知道有这个东西就够了
现在为止,我们已经把RAG的核心骨架理清了:
文档切块 → chunk 做 Embedding → 向量存进向量库 → 用户问题做 Embedding → 去向量库查 top-k → 把结果交给大模型回答
第六课:核心组件复盘
这一节课的目的是让你把前面所学到的零散的概念连成一张图
一个最基础的 RAG 系统,通常由 5 个核心部分组成。
分别是
- 文档
- 切块器
- Embedding模型
- 向量库/检索器
- 大模型
下面我们一个一个看
五个核心部分
文档
文档是RAG的知识来源,它可以是PDF、Word、网页、Markdown、产品手册等等,可以理解为RAG要查的原始资料,没有文档,就没有后面的检索。
切块器(Chunker/Text Splitter)
文档通常很长,不能整篇直接用于检索,所以要先i去而成小块,这一步的作用是让检索更精准、降低噪音、保留局部语义、方便后续Embedding。
切完后我们就得到了很多chunk,所以切块器的本质是在做:把大文档拆成适合搜索的小片段
Embedding模型
前面我们已经学了Embedding,但在实际工作中,并不需要我们取手动转成向量,而是通过Embedding模型去转,他负责把每个chunk、用户的问题转成向量。 把文本从"文字形式"变成"可比较的语义数字表示"。 有了它,系统才能做语义检索,而不是只看关键词。
向量库/检索器 (Vector Store / Retriever)
这两个词你会经常一起看到,先简单区分一下
向量库:偏存储和搜索向量的地方
检索器:偏执行检索逻辑的模块
在入门阶段,你可以先把它们看成一类东西:负责把最相关的chunk找出来
他做的事情包括:存储chunk向量、存储元数据、接收问题向量、计算相似度、返回top-k结果
大模型LLM
大模型在RAG里主要负责最后一步:根据问题和检索到的资料,组织出自然语言答案。
注意这里有一个很重要的认知**:在RAG里,大模型不负责记知识,而是负责:理解上下文、阅读问题、总结信息、组织表达**
它是一个阅读理解+答案生成器
还有两个顺手认识的辅助概念
Prompt
也就是给大模型的提示词,提示词不是随便给的,在RAG里,Prompt往往需要包含:用户问题、检索到的上下文、回答要求
比如:
- 只能依据资料回答
- 找不到就说不知道
- 尽量简洁
- 给出引用来源
Context
也就是上下文,在RAG里,它通常指:检索出来并提供给大模型参考的文本内容。与我们常说的大模型容量几百万token不同,大模型的容量叫做Context Window,指的是模型一次推理时可以读取的最大token数量,只是有人会简称为context,我们要分清两个context的区别。
结尾
简单的理论部分就结束了,剩下的就是动手实践环节,我并不推荐去跟着AI,让AI一步步教你做,因为AI会省去很多步骤,连知识库文档这块都是简单生成的,你没有自己亲自上手文档清洗,你无法理解每一步的重要性。
我极力推荐同学们可以参照 datawhale的https://github.com/datawhalechina/all-in-rag来动手学习,有了简单的入门,再去看他的教学会快速很多,一步步跟着做的时候,也能记得每个概念的意思。