AI概念解惑系列 - RAG
本文发布于掘金,转载请声明
随着最近几年AI在各个领域大放异彩,作为一个前端开发,我也对AI里的各种技术概念产生了浓厚的兴趣,希望能详细了解这些技术概念和背后的原理后,找寻和前端结合的一些思路。今天我们就来一起了解下AI领域常用到的一个技术——RAG(Retrieval-Augmented Generation),检索增强生成。
一、 开始之前
在开始介绍RAG之前,我们先来简单回顾一下大语言模型LLM的原理。
LLM通过预训练,让模型学习了全网公开的知识(网页、书籍、论文、代码等等),得到一堆“参数”,之后根据用户的输入来不断地预测下一个token应该是什么,最终得到输出。
这种训练好的模型,在通用领域有着比较好的表现。但是根据LLM的原理,我们也能发现它的一些不足:
- “预训练”:由于是使用了旧的数据进行的训练,对于在训练之后产生新信息,LLM无法回答
- “公开的知识”:LLM使用了公开的知识训练,对于一些非公开的内容,或者专业数据,LLM同样无法回答
- “预测”:因为LLM本质上是一个“看前文、猜下文”的大规模统计生成器,所以对于它不知道的信息,在回答时往往会“胡说八道”,也就是常说的Hallucination,“幻觉”
对于普通用户一些不要求实时性的通用性问题来说,上面这些不是什么大问题,但是一旦涉及到实时性、专业数据、企业内私有数据时,上面的问题就会严重影响用户体验。
为了解决这些问题,通常有下面两个手段:
- 微调大模型
- 手动给LLM添加额外知识
二、 微调 vs 外挂知识库
1. 微调Fine-tuning
所谓微调(Fine-tuning),就是在预训练模型的基础上,使用特定任务的数据(如私有数据、专业领域知识、最新的信息等)进行进一步训练,让模型在特定领域表现更好。
举个比较形象的例子,通过预训练得到的大模型,就是学习了小学到高中的通识教育的知识,学会语言、数学、常识、逻辑思维,知识面广但不够专业。
而微调阶段(Fine-tuning)类似于专业教育,就好像学习了专业领域课程、成为某个领域的专家。
即“通用模型 + 专业数据 + 针对性训练 = 专业模型”:
- GPT + 代码数据 + 代码任务训练 = CodeGPT
- GPT + 医学数据 + 医学任务训练 = MedicalGPT
- GPT + 法律数据 + 法律任务训练 = LegalGPT
2. 外挂知识库
在和LLM交互时,我们可以通过一些特殊手段,从外部知识库检索相关信息提供给LLM,相当于给AI配个"外挂知识库",让它可以获取到专业领域知识、私有数据、实时信息等等。
而RAG就是比较常用的手段。
3. 对比
对比这两个不难发现,微调是内化知识到了模型参数中,最终的效果肯定是稍微好一些的。
但是考虑到微调下面这些点:
- 需要大量的标注训练数据(需要对文档等进行数据清洗、标注等)
- 较高的训练成本(比如训练需要较高的算力,有不小的经济成本和时间成本)
- 较高的实现难度(需要AI领域的专业知识)
- 无法保持数据是最新的(内化的知识需要重新训练才能更新)
对于个人或者中小型企业来说,通过RAG外挂知识库是一个更合适的选择:
- 只需要结构化的文档
- 不需要训练
- 较低的实现难度(大多是工程方面的实现)
- 知识库更新很方便(不需要重新训练,只需要重新向量化即可)
三、 RAG
那我们来看下RAG是什么,所谓**RAG(Retrieval-Augmented Generation)**检索增强生成,就是:
- 检索(Retrieval): 从外部知识库中检索相关信息
- 增强(Augmented): 将检索到的信息作为上下文
- 生成(Generation): 大模型基于检索内容生成回答
它的本质就是上面说的给LLM外挂一个知识库,而它的工作过程,简单来讲就是下面这样:
![]()
而一个RAG完整工程的架构示意图,则如下所示:
![]()
看不懂?没关系,我们一步一步来讲解。
四、 向量和向量化
首先,最基础的一个概念,什么是向量(Vector)和向量化(Vectorization/Embedding)。
向量(Vector)
向量我们应该不陌生,我们最早应该在高中就学过了,就是一个同时具有数值和方向的量。相对应的,标量则只有数值,没有方向。
![]()
向量通常用一个有序的数字数组来表示,比如:
- 2维向量: [3, 4],可以理解为一个有x轴和y轴的平面直角坐标系,从原点指向[3, 4]这个坐标点的一个向量
- 3维向量: [1, 2, 3],可以理解为一个有x、y、z三个轴的空间直角坐标系,从原点执行[1, 2, 3]这个坐标点的一个向量
- 高维向量: [0.2, 0.5, 0.1, ..., 0.8],以此类推,在数学或者计算机领域,一个向量可以有任意多个维度, 比如有几百甚至上千维
而在AI领域,向量是原始数据的数值化表示,用来捕捉语义信息,比如
- 文本“苹果很好吃”,可以用这样一个向量表示:[0.12, 0.34, 0.89, ..., 0.45]
- 图片🍎,可以用这样一个向量表示:[0.23, 0.67, 0.12, ..., 0.91]
而具体是怎么把一串文本或者图片转换为向量的,就是向量化所做的工作了。
向量化(Vectorization/Embedding)
向量化,就是将非结构化原始数据(文本、图片、音频等)转换为向量的过程。
![]()
而为了将一个非结构化数据,变为向量,我们首先需要给这个数据定义好它的维度。
世界上的所有数据,都可以根据它在某一项特点上的程度而打一个分,数值越高代表这个原始数据在这个特点上的特征越强烈。我们规定数值的范围是[-1, 1],1代表完全符合这个特征,-1代表拥有完全相反的特征。
例如下面这个二维的向量空间:
- 横坐标代表情感极性,越靠近1代表越正向的情感,越靠近-1代表越负向的情感
- 纵坐标代表内容类型,越靠近1代表越主观的观点评价,越靠近-1代表越客观的事实陈述
![]()
上面的几个原始文本数据里:
- 这部电影太精彩了,演员表演完美
- 横轴(情感):+0.9(强烈正面)
- 纵轴(内容类型):+0.8(明显观点评价)
- 坐标:[0.9, 0.8]
- 水的沸点是100摄氏度
- 横轴(情感):0.0(完全中性)
- 纵轴(内容类型):-0.9(纯粹事实)
- 坐标:[0.0, -0.9]
- 这个产品质量很差,完全不符合预期
- 横轴(情感):-0.8(明显负面)
- 纵轴(内容类型):+0.7(个人评价)
- 坐标:[-0.8, 0.7]
- 令人失望的是,数据显示销售额下降了20%"
- 横轴(情感):-0.6(负面情绪)
- 纵轴(内容类型):-0.3(偏向事实但带有主观色彩)
- 坐标:[-0.6, -0.3]
- 数据表明用户流失率高达30%
- 横轴(情感):-0.7(负面情绪)
- 纵轴(内容类型):-0.8(事实陈述,且没有明显主观评价在里面)
- 坐标:[-0.7, -0.8]
- 我们彩票中了500万
- 横轴(情感):+0.9(正面)
- 纵轴(内容类型):-0.8(事实陈述,且没有明显主观评价在里面)
- 坐标:[0.9, -0.8]
可以看到,在我们对原始的文本数据从不同维度打分后会出现
- 相似文本会聚集:比如所有正面评价都会集中在第一象限
- 距离/夹角反映相似度:坐标距离/夹角越近,文本在情感和类型上越相似
我们的例子里只有两个维度,而在实际应用中可能有几百上千个维度,每个维度代表不同特征,这样的话就可以得到一个高维的向量空间,在向量空间内可以体现出不同数据之间或近或远的关系。
![]()
另外,向量化除了叫做Vectorization外,也时候也会叫做Embedding,嵌入。这是因为向量化的过程,不只是将一个非结构化数据转为向量数字,更重要的是在这个过程中,将原始数据的语义信息嵌入到了向量空间中,使得语义关系被保留。
举个例子,通常情况下“国王King”和“王后Queen”常出现在相似的上下文中,“男人Man”和“女人Women”经常出现在相似的上下文中。
皇室词汇(King, Queen, Prince)之间的相似度整体大于它们与普通人词汇(Man, Women)的相似度。但是“国王King”和“男人Man”之间又有一些相似,“王后Queen”和“女人Women”之间有一些相似。
而将这四个单词进行向量化后,可能如下图所示:
![]()
“国王King”、“王后Queen”、“男人Man”、“女人Women”的语义以及语义关系被嵌入了向量空间,做到了:
- 保持语义关系: 意思相近的词在向量空间中也靠近
-
支持语义运算: 实现了
king - man + woman ≈ queen这种神奇的运算
如何向量化
向量化是一个很复杂的过程,通常都是使用模型来实现的,即“Embedding models”,比如OpenAI的text-embedding-ada-002(需要API密钥访问),或者也可以使用一些开源的模型。
通常一个模型向量化数据后的维度越高,在后续进行信息检索时就会越精准。
其实除了调用模型这一步之外,完整的向量化过程还有一些其他工作要做,整个过程大概如下:
-
读取文档
- PDF → 提取文本
- Word → 提取文本
- Markdown → 读取内容
- ...
-
清洗文本
- 去除乱码
- 统一格式
- 去除无用内容(页眉页脚等)
-
把文本进行切割,即文本分块 (Chunking)
为什么要分块?因为原始文档可能特别长,几千上万字,不方便后续进行向量化和检索。
而分块的规则也有多种,比如根据标点符号分割,但是可能会存在有的块长度很长,有的又很短的情况;也可以根据固定长度分割,但是可能会导致一个完整的语义被切割到不同的块,每个块里的部分词变得毫无语义价值。
所以RAG 分割一般用相同块大小 + 块重叠的办法,即所谓的滑动窗口分块,这和按长度截断来分割有点像。用这种办法,在一定程度上能避免文档原意被截断导致分出来的片段变得没价值,而且重叠块里有重复的信息,能让模型更好地理解文档内容。

-
使用某个模型进行向量化(Embedding)
-
BGE-base-zh (中文效果好)
-
all-MiniLM-L6-v2 (英文,轻量)
-
text-embedding-ada-002 (OpenAI,需联网)
假设选择BGE-base-zh (中文),输出维度768维,模型大小约 400MB
-
-
将向量化后的数据存入向量数据库,构建索引(Indexing)
普通的数据存储有数据库,那么向量的存储,也有专门的向量数据库,它是专为存储与检索高维嵌入向量而设计的数据库。支持基于相似度的检索并可结合元数据过滤,常用于语义搜索、推荐和 RAG。常见的数据库有:
- Chroma
- Milvus
- Qdrant
假设选择Chroma,那么它的每条记录的存储结构大概为:
{ "id": "doc_1_chunk_1", "vector": [0.23, 0.45, ..., 0.12], // 768维向量 "metadata": { "source": "系统架构文档.pdf", "chunk_index": 1, "text": "第一章 系统架构..." // 原始文本 } }
在做完上述的几个步骤之后,就算是完成了RAG的准备工作,即给LLM准备好了“外挂的知识库”。
![]()
五、 RAG - R (Retrieval)
在准备好知识库之后,就可以和LLM进行问答了。那在提出问题后,如何让LLM知晓知识库的内容呢?答案就是RAG中的R,Retrieval检索。
在上面的知识库的准备过程中,我们使用了某个Embedding Model进行了知识库的向量化,那么在用户提出问题Query之后,也需要使用相同的Embedding Model,对用户的输入Query进行相同的向量化。
在都进行了向量化之后,就可以开始在向量数据库中检索了。
相似度匹配
和普通数据库基于关键字的检索不同,向量数据库是基于语义的检索。那怎么判断两个向量的语义是否接近,常见的衡量标准有:
这三个的具体定义和区别如下所示:
| 算法 | 公式 | 定义 | 适用场景 |
|---|---|---|---|
| 余弦相似度(Cosine Similarity) | 余弦相似度通过测量两个向量的夹角的余弦值来度量它们之间的相似性。 仅反映方向一致性而与长度无关。 |
当你关心“语义方向/形状”而不想被向量长度影响。 典型应用场景有文本/图像嵌入检索、RAG 向量检索、语义去重、语义聚合。 |
|
| 欧氏L2(Euclidean L2/Square L2) | 对两向量差的平方和开平方来度量几何距离(或向量自身长度),反映它们在空间中的直线距离。 | 当你你需要真正的几何距离(度量性质、三角不等式)时。 典型应用场景有K-means 聚类(目标是平方 L2)、最小二乘/MSE、几何邻近、部分 ANN 索引默认。 |
|
| 内积(Inner Product/Dot Product) | 将两个向量对应分量相乘后求和,得到同时受方向与长度影响的对齐程度(线性打分)。 | 典型应用场景有最大内积搜索(MIPS)、推荐召回、因子分解机/embedding 打分。 |
而在RAG语义搜索中,通常使用余弦相似度来计算两个向量是否相似,即通过跑男的两个向量的夹角,来判断两个向量的语义是否接近。还是以上面的这个图为例:
![]()
在这个向量空间里,Chicken和“鸡”的图片之间,向量的夹角很小,Dog和Wolf夹角很小,Banana和Apple夹角很小,Apple和Apple公司的Logo夹角很小。但是动物和水果之间的向量夹角就很大了,语义上相差很远。
为什么用余弦相似度? 因为文本长度不重要(一句话和一段话可能表达同一意思),我们只关心语义方向是否一致。
ANN
在上面的图中,我们还看到了一个叫做Approximate Nearest Neighbor(ANN)近似最近邻检索算法的东西,这个和余弦相似度有什么关系呢?
简单来讲,ANN是“怎么快地找相似向量”(检索算法/索引),余弦相似度是“用什么标准衡量相似”(度量/打分);在RAG中它们是配套使用的:用余弦相似度等度量定义“近邻”,再用ANN在大规模向量库上高效近似地找这些近邻。
完整过程
在完成检索后,通常会拿到Top-K最相关的几条数据。假设用户输入的问题是“如何配置HTTPS?”,那么过程大概如下:
-
用户提问:“如何配置HTTPS?”
-
用户问题向量化,使用和文档向量化时相同的Embedding Model
-
得到Query的向量
query_vector = [0.31, 0.48, 0.73, ..., 0.15] // 768维 -
在向量数据库中搜索
-
计算相似度 Chroma自动计算query_vector与所有向量数据库中向量的相似度
- 向量1: [0.23, 0.45, ...] → 相似度 0.45
- 向量2: [0.31, 0.52, ...] → 相似度 0.89 ⭐
- 向量3: [0.12, 0.34, ...] → 相似度 0.32
- ...
- 向量8234: [0.29, 0.51, ...] → 相似度 0.91 ⭐⭐
- 向量8235: [0.35, 0.48, ...] → 相似度 0.87 ⭐
- ...
-
返回 Top-K 最相关的,假设我们设置 K=3,返回相似度最高的3个文本块
结果: a. (相似度 0.91) "4.2 HTTPS配置步骤:首先需要申请SSL证书..." b. (相似度 0.89) "安全配置章节:HTTPS是保障网站安全的关键..." c. (相似度 0.87) "第五章 证书管理:SSL/TLS证书的申请与部署..."
六、 RAG - A (Augmented)
在得到Top-K相关的数据后,会把这几条结果的原始数据(每条数据的metadata中会有原始数据),和用户的Query,一起给LLM。为什么叫Augmented增强呢?
其实Augmented,就是一个Prompt Engineering提示词工程相关的工作,把Query和知识库中的相关数据,通过一些特殊的Prompt给到LLM,让LLM更好地回答问题。即通过清晰的结构 + 明确的指令,让模型知道该参考什么。例如,一个完整的增强后的提示词可能如下所示:
你是一个技术支持助手。请根据以下参考资料回答用户问题。
## 参考资料
### 文档1(相关度:91%)
来源:网络安全配置手册.pdf
内容:{检索到的文本块1}
### 文档2(相关度:89%)
来源:服务器部署指南.pdf
内容:{检索到的文本块2}
### 文档3(相关度:87%)
来源:证书管理文档.pdf
内容:{检索到的文本块3}
## 回答要求
1. 必须基于上述参考资料
2. 如果资料不足,明确说明
3. 给出具体步骤
4. 标注信息来源
## 用户问题
如何配置HTTPS?
## 你的回答
七、 RAG - G (Generation)
最后一步的Generation生成,就和平时使用LLM没什么区别了,LLM会根据提示词,来回答问题并按照指定的格式来输出内容。即:
- 读取参考资料
- 理解用户问题
- 从资料中提取相关信息
- 组织成结构化回答
- 逐字生成输出
以上面的问题为例,LLM最终生成的内容可能如下所示:
根据参考资料,HTTPS配置步骤如下:
1. **申请SSL证书**
- 可以通过Let's Encrypt获取免费证书
- 或购买商业证书(来源:证书管理文档)
2. **安装证书**
sudo cp cert.pem /etc/ssl/certs/
sudo cp key.pem /etc/ssl/private/
...
3. ...
到这里,整个RAG的过程就算是结束了。
![]()
八、 完整RAG过程
现在把上面几个步骤结合起来:
私有的知识库
→ 文本分块Chunking
→ 向量化Embedding
→ 存储向量构建索引Indexing
→ 用户提问Query
→ 检索Retrieval
→ 增强Augmented
→ 生成Generation
之后就得到了最开始的这个架构示意图:
![]()
九、 FAQ
下面是一些常见的问题:
-
RAG和传统的全文模糊搜索有什么区别?
传统的模糊搜索是关键词匹配的,比如
- 用户搜索:"如何做蛋糕"
- 系统匹配:只能找到包含"蛋糕"字样的文档
- 结果:❌ 找不到"烘焙教程"、"甜点制作"等相关内容
而RAG最重要的一步就是把知识库做了向量化,是根据语义进行搜索的
- 用户搜索:"如何做蛋糕"
- 系统理解:用户想学习烘焙技能
- 结果:✅ 找到"烘焙教程"、"甜点制作"、"面包制作"等相关内容
-
既然RAG里面还是要要通过构造提示词来吧参考资料放入提示词,那这和“直接在问题里把参考文档也输入进去”有什么区别?
这是因为我们要考虑成本和效率,一个知识库可能很大,成百上千个文档,数万数十万字,在实际操作的时候,要考虑到:
- Token限制:GPT-4 最多128K tokens ≈ 10万字,知识库无法塞进去
- 成本问题:即使能塞进去,128K tokens可能一次调用可能要几美元,成本激增
- 效率低下:LLM要读完所有无关内容
- 响应缓慢:处理海量文本需要很长时间 所以RAG中R检索这一步,就是在做过滤,它的本质就是把“无限大”的知识库变成“刚刚好”的上下文
-
在做向量化的时候,使用到的Embedding Model和LLM的Model有什么关联,为什么Embedding Model可以识别原始数据的语义?
简单来讲,Embedding模型和大语言模型,他们的基础架构是相似,但目标和训练方式不同。 两者都基于Transformer架构,即:
- 多头自注意力机制 (Multi-Head Attention)
- 前馈神经网络 (Feed-Forward)
- 层归一化 (Layer Normalization)
- 位置编码 (Positional Encoding)
但是
- Embedding Model:专门学习“理解和表示”,输出是向量
- LLM:专门学习“生成文本”,输出是文字 完整的Transformer可以理解为Encoder编码器 + Decoder解码器。
组成 作用 Encoder编码器 理解输入,提取特征 Decoder解码器 生成输出,逐字生成
Embedding模型主要用的是Encoder,大语言模型主要用Decoder。而Embedding模型之所以比LLM小很多(如BGE-base-zh只有400M),主要是因为它的任务相对简单(映射到向量空间),不需要生成能力,也就不需要记忆海量知识和复杂的推理能力。
十、 小结
这篇文章里,我们一起了解了一下RAG,以及这个技术如何通过外挂知识库解决大语言模型的三大局限:
- 时效性不足(无法处理训练后新数据)
- 私有数据缺失
- 幻觉问题
它的核心机制包含三个阶段:
- 基于语义的向量化检索
- 结构化提示词增强上下文
- 以及大模型的知识整合生成
这样就能实现低成本、易于维护的专业领域知识融合。相比微调方案, RAG无需训练标注数据且支持实时更新,适合用在企业知识库问答、实时政策解读这些场景里。
还有就是这篇文章(或者说《AI概念解惑》这个系列)更专注于概念解惑和原理解析,所以没有包含什么代码实操。
另外这也只是一个简单的原理解析,实际的RAG工程中可能还会包含很多其他的步骤,比如:
- 文本分块时可能会同时使用语义分块+滑动窗口分块
- Retrieval阶段可能不只是用向量相似度检索,会使用向量相似度+关键词(如BM25)的混合检索
- 为什么?因为向量相似度只对语义敏感,对关键词不敏感,比如“Python 3.12”和“Python 3.13”的向量基本相同,但是用户可能是提问具体Python版本的问题
- 在Retrieval检索后还可能存在一个Reranking重排的步骤
- 对候选片段进行精细化打分和排序
- 对于用户的问题Query可能会增加一个Query Rewriting
- 是为了让问题更规范,比如从一个口语化的表达到一个规范化的表达
- 使用多路检索(Multi-Path Retrieval)、上下文压缩(Context Compression)、自我反思(Self-Reflection)等手段进行优化
- 引入RAG评分机制,对RAG的输出结果进行评估
- ...
感兴趣的可以自行搜索了解。
希望这篇文章能帮助你理解RAG的核心原理和应用场景,如果这篇文章有什么问题,辛苦大家指正。