目录前言1. RAG 简要介绍2. RAG 的使用场景3. RAG 的基本运行流程4. 分片5. 索引6. 召回7. 重排8. 生成9. RAG 的整体流程结语参考前言学习 UP 主 马克的技术工作坊 的 RAG 工作机制详解——一个高质量知识库背后的技术全流程 视频了解下 RAG 的一些基本概念记录下个人学习笔记仅供自己参考videoRAG 工作机制详解——一个高质量知识库背后的技术全流程1. RAG 简要介绍如果你想要做一个靠谱的知识客服或者想搭建一个能回答问题的知识库那你一定绕不开一个技术RAG它的全称是Retrieval-AugmentedGeneration翻译过来就是检索增强生成听起来挺高大上但说白了也就这么两件事先从资料库里检索相关的内容再基于这些内容来生成答案也就是说它先检索再生成所以叫做检索增强生成RAG 是目前最常用的 AI 问答方案之一很多企业内的知识助手智能客服用的都是这项技术在本文中我们将介绍 RAG 的实现原理主要包括以下三个部分首先我们将总体看一下 RAG 的使用场景和大致链路让你对这门技术有个感性的了解然后我们会逐步拆解这个链路里面的每一个环节深入理解它的原理最后我们会从提问前和提问后两个角度出发复习一下整个链路的运行过程加深理解在这个过程里面我们还会解释一下 RAG 用到的各种专业名词比如说向量、Embedding 模型、向量数据库、向量相似度等等相信在看完这篇文章后你就会明白一个高质量的智能客服或者知识库是如何构建的OK废话少说我们下面正式开始吧2. RAG 的使用场景假设你想做一个智能客服这个智能客服可以回答各种关于你们公司产品的问题那应该怎么实现它呢首先这个客服的内部一定要有个模型比如说 GPT-4o、deepseek 这种不过光有个模型可不够因为模型可不知道你们公司的产品信息你想这个问题好办在给模型发送问题的时候我把产品手册一起发给模型不就好啦没错这确实是一个解决方案不过如果产品手册的字数特别多比如有个上百页乃至上千页的话这就会带来很多问题首先模型可能无法读取所有的内容因为每个模型都只能存储一定量的信息我们通常称这个量为上下文窗口大小如果你的产品手册字数过多超过了这个上下文窗口大小的话模型就会读了后面忘了前面回答的准确率也就无法得到保障除此之外模型的推理成本也会很高输入越多成本越高每次回答问题的时候都要带上一本厚厚的手册那成本可想而知。最后模型的推理速度也会受到影响输入越多模型需要消化的内容就越多模型的输出就会越慢一本上百页的手册扔进来那大概率会对模型的推理速度产生严重的影响看来直接把文档丢给模型是行不通的那我们是不是可以考虑只把文档中相关的内容发给模型呢可以的这就需要 RAG 登场了我们一起来看看 RAG 是如何解决这个问题的3. RAG 的基本运行流程首先 RAG 会把文档切分为多个片段当用户提出问题后我们就用这个问题在所有的片段中寻找相关内容比如在一份上百页的产品手册中可能只有三个片段真正与用户的问题相关我们就把这三个片段单独挑出来把他们和用户的问题一起发给模型这样模型就只会感知三个相关的片段而不是整个文档之前的问题也就迎刃而解了没错RAG 就是这样的是不是很简单不过话说回来这只是一个过度简化的链路我们隐藏了很多实现细节比如说如何分片如何选择相关的片段这里面的学问都不少所以下面我们把整个 RAG 的流程具体来拆分下通常来说 RAG 的整体流程包含两个部分一个是数据准备部分这个发生在用户提问前我们要在这一部分里把相关的文档都给准备好并完成相应的预处理它一共是包含分片和索引两个环节另外一个是回答部分这一部分当然是发生在用户提问之后了在用户问完问题之后我们便会触发回答问题的各个环节分别是召回、重排和生成接下来我们就逐步拆解分片、索引、召回、重排和生成这五个环节看看他们分别是如何工作的4. 分片分片顾名思义就是把文档切分成多个片段我们之前在基本运行流程环节里也简单看过分片的方式有很多种我们可以按照字数来分比如说 1000 个字一个片段按照段落来分比如说一个段落一个片段或者是按照章节分按照页码分除此之外还有很多的切分方式但不管怎么做我们最后都需要把一篇文档切分为多份切好后这个环节就结束了然后我们就要进入到下一个环节索引5. 索引索引就是通过 Embedding 将每一个片段文本转换为向量然后再将片段文本和对应向量都存储在向量数据库的一个过程没错它一共就这两步但这两个步骤所包含的信息量其实是巨大的比如什么是 Embedding什么是向量数据库我们先来把这几个概念解释清楚然后再回来看看索引这个流程相信那时你就会清楚很多首先我们来讲讲这其中最基础的向量向量是数学里面的一个概念相信大家多多少少都学过从概念上来讲它代表一个有大小有方向的量在通常情况下我们可以用一个数组来表示它比如说a ⃗ [ 1.0 , 2.0 ] \vec{a}[1.0,2.0]a[1.0,2.0]每个向量都有维度维度的大小就等于数组中数字的个数比如一维向量例如[ 1.0 ] [1.0][1.0]、二维向量例如[ 1.0 , 2.0 ] [1.0,2.0][1.0,2.0]、三维向量例如[ 1.0 , 2.0 , − 3.0 ] [1.0,2.0,-3.0][1.0,2.0,−3.0]等等对于低维度的向量我们可以直接把它们在坐标轴里面绘制出来如下图所示当向量维度再大一点我们可能就没法演示它在坐标轴里面的位置了毕竟我们生活在一个三维世界里但无法可视化并不代表就不存在实际上我们在 RAG 里面用到的向量维度通常情况下都会比较大比如几百甚至几千一般来说维度越大每个向量所包含的信息也就会越丰富用这些向量做各种工作的可靠性也就越强讲完了向量我们再来看看 EmbeddingEmbedding 就是把文本转换为向量的一个过程比如我们以二维向量为例假设马克喜欢吃水果对应的向量是[ 1 , 2 ] [1,2][1,2]马克爱吃水果对应的向量是[ 1 , 1 ] [1,1][1,1]天气真好对应的向量是[ − 3 , − 1 ] [-3,-1][−3,−1]你会发现前两个句子的向量非常接近而天气真好则距离比较远这说明前两个句子的语义是相近的而后者则完全不相关这正是 Embedding 的目的含义相近的文本在经历了 Embedding 之后它们对应的向量也是相近的因为这样的话当用户询问马克喜欢吃什么的时候我们就可以先把这个问题给 Embedding 将其转换为向量然后再根据向量相似度把与这个问题相关的文本也找出来最后我们就可以把这两个相关文本以及用户的问题一起扔给大模型大模型就可以告诉我们马克喜欢吃水果了。Embedding 这个操作是模型来完成的不过这个模型可不是我们通常所使用的 GPT-4o、deepseek 这样的模型而是专门的 Embedding 模型如果你想知道哪些 Embedding 模型最好用的话可以看一下 MTEB 排行版网站是https://huggingface.co/spaces/mteb/leaderboard它会对各种 Embedding 模型做评测并且把结果做个排行方便我们挑选和使用聊完了 Embedding 的概念我们再来看看向量数据库向量数据库就是用来存储和查询向量的数据库它为存储向量做了很多优化并且还提供了计算向量相似度等相关的函数方便我们使用向量Embedding 后的向量就可以放在向量数据库里面方便后续查询比如我们还是以 “马克喜欢吃水果” 这句话为例在我们给这个话做了 Embedding 之后就得到了一个向量然后我们需要把这个向量存入到向量数据库中不过注意我们要存的不仅有向量还有原始的文本所以原始文本也要发给向量数据库因为只有这样我们才能够在通过向量相似度查询出相似的向量之后把对应的原始文本也抽取出来发给模型让它来处理我们最终需要的还是原始的文本向量只是一个中间结果所以一般的向量数据库表格里面至少都会有原始文本和向量两列内容就像下图展示的这样讲完了向量 Embedding 和向量数据库我们再回头看看我们之前提到过的索引这个概念索引就是通过 Embedding 把每个片段文本转换为向量并且把片段文本和对应的向量都存储在向量数据库的过程这句话的意思想必大家都有个概念了其实就是我们刚才聊的这个过程只不过我们要把一开始的这个文本换成每一个片段的内容比如说我们一开始要处理的是片段一片段一处理完了之后呢我们要处理片段二以此类推直到所有的片段都处理完毕整个索引的流程也就结束了不管是分片还是索引它们都发生在用户提问之前属于都要提前准备的步骤下面我们就来看看用户提问之后发生了什么首先是召回6. 召回召回就是搜索与用户问题相关片段的过程这个环节从用户问题开始首先用户的问题会发给 Embedding 模型Embedding 模型会将它转化为向量然后我们把它发送给向量数据库让他查询与用户问题最为相关的十个片段的内容如下图所示没错召回的结果就是十个与用户问题相关的片段当然 10 这个数字并不是固定的你也可以选择 15、20 等等具体是多少呢不是很重要只要数量不是很多都可以那不管是多少向量数据库都要返回与用户问题最相似的一批片段那向量数据库是怎么知道哪些片段与用户问题最相关的呢这就要计算向量相似度了我们来模拟下整个过程向量库里面的数据以及用户问题和对应向量如上图所示我们要做的就是计算每个片段与用户问题的向量相似度我们通过一个相似度计算公式s i m i l a r i t y ( , ) \mathrm{similarity}(\quad,\quad)similarity(,)可以得出两个向量的相似度这个公式的第一个参数是用户问题所对应的向量第二个参数则是每个片段的向量了例如片段一和问题向量的相似度计算如下s i m i l a r i t y ( [ 11 , 5 , 2 , 3 , 1 ] , [ 1.0 , 2.5 , 3.7 , 5.8 , 2.8 ] ) 0.5113 \mathrm{similarity}([11,5,2,3,1],[1.0,2.5,3.7,5.8,2.8])0.5113similarity([11,5,2,3,1],[1.0,2.5,3.7,5.8,2.8])0.5113其它片段类似我们需要按照这个方法把所有的片段都计算完毕在都计算完毕之后我们就把这个向量相似度排个序取前十个最大的就好了那下一个问题就是这个公式是怎么算出来的呢答案是有很多种目前比较流行的方案包括余弦相似度、欧式距离和点积这里我们大致说一下余弦相似度主要在计算两个向量之间夹角的cos \coscos值然后根据这个cos \coscos值来判断夹角的大小夹角越小相似度就越高欧式距离主要是计算两个向量之间的距离距离越小相似度越高点积是一种通过代数方式衡量两个向量相似度的方法它不仅要考虑两个向量之间的方向关系也要考虑它们的长度比如我们如果要计算过上图中 A 和 B 的点积我们首先要从 A 向 B 引入一条垂线假设垂足为 C然后 A 和 B 的点积就是 OC 和 OB 两段距离的乘积乘积越大就代表相似度越高。如果两个向量方向一致的话那么这两个向量越长点积值就越大如果方向相反那么点积就是负的如果垂直点积就为 0。所以我们可以通过点积的值来判断两个向量是否在同一个方向上努力以及它们努力的程度有多大下面我们再来看看三种方案对应的计算公式(from ChatGPT)假设有a , b \mathbf {a,b}a,b两个向量其定义为a ( a 1 , a 2 , … , a n ) , b ( b 1 , b 2 , … , b n ) \mathbf a (a_1, a_2, \dots, a_n),\quad \mathbf b (b_1, b_2, \dots, b_n)a(a1,a2,…,an),b(b1,b2,…,bn)点积Dot Producta ⋅ b ∑ i 1 n a i b i \mathbf a \cdot \mathbf b \sum_{i1}^n a_i\,b_ia⋅bi1∑naibi欧式距离Euclidean Distanced ( a , b ) ∥ a − b ∥ 2 ∑ i 1 n ( a i − b i ) 2 d(\mathbf a,\mathbf b) \|\mathbf a - \mathbf b\|_2 \sqrt{\sum_{i1}^n (a_i - b_i)^2}d(a,b)∥a−b∥2i1∑n(ai−bi)2余弦相似度Cosine Similaritycos ( θ ) a ⋅ b ∥ a ∥ 2 ∥ b ∥ 2 ∑ i 1 n a i b i ∑ i 1 n a i 2 ∑ i 1 n b i 2 \cos(\theta) \frac{\mathbf a \cdot \mathbf b}{\|\mathbf a\|_2 \;\|\mathbf b\|_2} \frac{\sum_{i1}^n a_i\,b_i} {\sqrt{\sum_{i1}^n a_i^2}\;\sqrt{\sum_{i1}^n b_i^2}}cos(θ)∥a∥2∥b∥2a⋅b∑i1nai2∑i1nbi2∑i1naibi这里∥ a ∥ 2 ∑ i a i 2 \|\mathbf a\|_2 \sqrt{\sum_i a_i^2}∥a∥2∑iai2是a \mathbf aa的二范数余弦相似度取值范围[ − 1 , 1 ] [-1,1][−1,1]越接近 1 表示方向越相似欧式距离越小表示两个向量越接近。向量相似度就讲到这里在这一阶段我们查询出了与用户问题最为匹配的十个片段记住这个结论因为这十个片段会发送到重排阶段继续处理7. 重排重排全称是重新排序它做的事情其实跟召回是一样的前面我们说过召回是从所有的片段里面挑十份与用户问题最相似的而重排则是从召回的这十份里面再挑三份与用户问题最相似的作为重排的结果你可能会想那直接在召回阶段挑三个不就好了这样就不用重排了同样的事情搞两遍干什么呢一次挑出三个当然是可以的不过这样做的效果没有召回加重排的方案好为什么呢因为召回与重排阶段使用的文本相似度计算逻辑不一样下面我们来比较一下首先看一下召回召回阶段使用的是向量相似度我们前面还列举了三个常见的向量相似度计算方法但无论是使用哪一种方法它们的特点都是成本低耗时短准确率低所以呢适合做初步的筛选也就是在短时间内把上千条片段的相似度数值都计算出来从中挑选十个最高的而重排就不是了重排阶段一般是使用一种叫做 cross-encoder 的模型计算每个片段与用户问题的相似度相比之下 cross-encoder 的成本会比较高耗时也比较长那为什么用它呢因为 cross-encoder 的准确率会高很多所以它很适合做精挑细选你可以把它类比成公司筛选人才众所周知公司面试一共是分为两个环节简历筛选加面试召回就跟简历筛选有点像候选人太多公司只能用一些粗略的方法从成千上万份简历里面挑出十个看起来最优秀的准确率可能会大打折扣但这也是没有办法的事情那重排就跟面试很像了公司对这十个人进行面试仔细挑选尽可能地保证判断正确从中挑出最优秀的三个人入职OK重排就讲到这里下面我们进入到生成阶段8. 生成生成什么呢那当然是答案了现在我们有了用户问题也有与用户问题相关的三个片段我们就可以把这两部分一起发给大模型让它根据片段内容来回答用户问题到此整个流程就结束了9. RAG 的整体流程RAG 流程里面的所有环节我们都讲完了下面我们把所有的流程给串联一下整体讲一遍整个流程分为两个部分一个是准备部分它发生在提问前包括分片和索引两个环节另一个是回答部分发生在提问后包括召回、重排和生成三个环节由于整个流程分为两个部分所以我们的整体流程也会有两个先看提问前的准备部分首先我们把相关的资料做个分片然后把所有的片段都扔给 Embedding 模型让它给每个片段都产出一个对应的向量最后我们把向量存入到向量数据库中到这里提问前的准备流程就结束了这就相当于我们的知识库构建已经完毕了就等用户来用了再来看看用户提问之后发生了什么首先用户的问题会给到 Embedding 模型Embedding 模型会把用户的问题转化为一个向量然后我们会把这个向量传给向量数据库让它给我们找到十个与用户问题最相近的片段找到之后我们再把这十个片段送给 cross-encoder 模型让它做个重排从十个里面再筛选出三个相关程度最高的然后我们把这三个与用户问题相关程度最高的片段外加用户的问题一起发给大模型大模型就可以给我们产出最终答案了到这里所有的流程就算是结束了结语这篇文章我们主要是学习了解了下 RAGRAG 翻译过来叫做检索增强生成它做的事情就是先从资料库里检索相关的内容再基于这些内容来生成答案接着 UP 主帮我们梳理了 RAG 的运行流程整个流程分为两部分一个是准备部分另一个是回答部分准备部分包括分片和索引两个环节首先我们把相关资料做分片然后把分片后的片段扔给 Embedding 模型生成向量最后把向量和对应的文本存入到向量数据库中这样就完成了我们的知识库构建工作回答部分包括召回、重排以及生成三个部分首先用户的问题会给到 Embedding 模型转化为向量然后我们会把这个向量和向量数据库中的向量进行相似度计算让它挑选出最符合的十个片段接着这十个片段会送给 cross-encoder 模型让它重排从中再筛选出三个相关度最高的片段将这些片段外加用户问题一起发给大模型大模型就可以给我们生成答案了这便是整个流程整个讲解非常通俗易懂大家感兴趣的可以多关注关注多看看 UP 的视频参考RAG 工作机制详解——一个高质量知识库背后的技术全流程https://huggingface.co/spaces/mteb/leaderboard
RAG 工作机制详解—一个高质量知识库背后的技术全流程
目录前言1. RAG 简要介绍2. RAG 的使用场景3. RAG 的基本运行流程4. 分片5. 索引6. 召回7. 重排8. 生成9. RAG 的整体流程结语参考前言学习 UP 主 马克的技术工作坊 的 RAG 工作机制详解——一个高质量知识库背后的技术全流程 视频了解下 RAG 的一些基本概念记录下个人学习笔记仅供自己参考videoRAG 工作机制详解——一个高质量知识库背后的技术全流程1. RAG 简要介绍如果你想要做一个靠谱的知识客服或者想搭建一个能回答问题的知识库那你一定绕不开一个技术RAG它的全称是Retrieval-AugmentedGeneration翻译过来就是检索增强生成听起来挺高大上但说白了也就这么两件事先从资料库里检索相关的内容再基于这些内容来生成答案也就是说它先检索再生成所以叫做检索增强生成RAG 是目前最常用的 AI 问答方案之一很多企业内的知识助手智能客服用的都是这项技术在本文中我们将介绍 RAG 的实现原理主要包括以下三个部分首先我们将总体看一下 RAG 的使用场景和大致链路让你对这门技术有个感性的了解然后我们会逐步拆解这个链路里面的每一个环节深入理解它的原理最后我们会从提问前和提问后两个角度出发复习一下整个链路的运行过程加深理解在这个过程里面我们还会解释一下 RAG 用到的各种专业名词比如说向量、Embedding 模型、向量数据库、向量相似度等等相信在看完这篇文章后你就会明白一个高质量的智能客服或者知识库是如何构建的OK废话少说我们下面正式开始吧2. RAG 的使用场景假设你想做一个智能客服这个智能客服可以回答各种关于你们公司产品的问题那应该怎么实现它呢首先这个客服的内部一定要有个模型比如说 GPT-4o、deepseek 这种不过光有个模型可不够因为模型可不知道你们公司的产品信息你想这个问题好办在给模型发送问题的时候我把产品手册一起发给模型不就好啦没错这确实是一个解决方案不过如果产品手册的字数特别多比如有个上百页乃至上千页的话这就会带来很多问题首先模型可能无法读取所有的内容因为每个模型都只能存储一定量的信息我们通常称这个量为上下文窗口大小如果你的产品手册字数过多超过了这个上下文窗口大小的话模型就会读了后面忘了前面回答的准确率也就无法得到保障除此之外模型的推理成本也会很高输入越多成本越高每次回答问题的时候都要带上一本厚厚的手册那成本可想而知。最后模型的推理速度也会受到影响输入越多模型需要消化的内容就越多模型的输出就会越慢一本上百页的手册扔进来那大概率会对模型的推理速度产生严重的影响看来直接把文档丢给模型是行不通的那我们是不是可以考虑只把文档中相关的内容发给模型呢可以的这就需要 RAG 登场了我们一起来看看 RAG 是如何解决这个问题的3. RAG 的基本运行流程首先 RAG 会把文档切分为多个片段当用户提出问题后我们就用这个问题在所有的片段中寻找相关内容比如在一份上百页的产品手册中可能只有三个片段真正与用户的问题相关我们就把这三个片段单独挑出来把他们和用户的问题一起发给模型这样模型就只会感知三个相关的片段而不是整个文档之前的问题也就迎刃而解了没错RAG 就是这样的是不是很简单不过话说回来这只是一个过度简化的链路我们隐藏了很多实现细节比如说如何分片如何选择相关的片段这里面的学问都不少所以下面我们把整个 RAG 的流程具体来拆分下通常来说 RAG 的整体流程包含两个部分一个是数据准备部分这个发生在用户提问前我们要在这一部分里把相关的文档都给准备好并完成相应的预处理它一共是包含分片和索引两个环节另外一个是回答部分这一部分当然是发生在用户提问之后了在用户问完问题之后我们便会触发回答问题的各个环节分别是召回、重排和生成接下来我们就逐步拆解分片、索引、召回、重排和生成这五个环节看看他们分别是如何工作的4. 分片分片顾名思义就是把文档切分成多个片段我们之前在基本运行流程环节里也简单看过分片的方式有很多种我们可以按照字数来分比如说 1000 个字一个片段按照段落来分比如说一个段落一个片段或者是按照章节分按照页码分除此之外还有很多的切分方式但不管怎么做我们最后都需要把一篇文档切分为多份切好后这个环节就结束了然后我们就要进入到下一个环节索引5. 索引索引就是通过 Embedding 将每一个片段文本转换为向量然后再将片段文本和对应向量都存储在向量数据库的一个过程没错它一共就这两步但这两个步骤所包含的信息量其实是巨大的比如什么是 Embedding什么是向量数据库我们先来把这几个概念解释清楚然后再回来看看索引这个流程相信那时你就会清楚很多首先我们来讲讲这其中最基础的向量向量是数学里面的一个概念相信大家多多少少都学过从概念上来讲它代表一个有大小有方向的量在通常情况下我们可以用一个数组来表示它比如说a ⃗ [ 1.0 , 2.0 ] \vec{a}[1.0,2.0]a[1.0,2.0]每个向量都有维度维度的大小就等于数组中数字的个数比如一维向量例如[ 1.0 ] [1.0][1.0]、二维向量例如[ 1.0 , 2.0 ] [1.0,2.0][1.0,2.0]、三维向量例如[ 1.0 , 2.0 , − 3.0 ] [1.0,2.0,-3.0][1.0,2.0,−3.0]等等对于低维度的向量我们可以直接把它们在坐标轴里面绘制出来如下图所示当向量维度再大一点我们可能就没法演示它在坐标轴里面的位置了毕竟我们生活在一个三维世界里但无法可视化并不代表就不存在实际上我们在 RAG 里面用到的向量维度通常情况下都会比较大比如几百甚至几千一般来说维度越大每个向量所包含的信息也就会越丰富用这些向量做各种工作的可靠性也就越强讲完了向量我们再来看看 EmbeddingEmbedding 就是把文本转换为向量的一个过程比如我们以二维向量为例假设马克喜欢吃水果对应的向量是[ 1 , 2 ] [1,2][1,2]马克爱吃水果对应的向量是[ 1 , 1 ] [1,1][1,1]天气真好对应的向量是[ − 3 , − 1 ] [-3,-1][−3,−1]你会发现前两个句子的向量非常接近而天气真好则距离比较远这说明前两个句子的语义是相近的而后者则完全不相关这正是 Embedding 的目的含义相近的文本在经历了 Embedding 之后它们对应的向量也是相近的因为这样的话当用户询问马克喜欢吃什么的时候我们就可以先把这个问题给 Embedding 将其转换为向量然后再根据向量相似度把与这个问题相关的文本也找出来最后我们就可以把这两个相关文本以及用户的问题一起扔给大模型大模型就可以告诉我们马克喜欢吃水果了。Embedding 这个操作是模型来完成的不过这个模型可不是我们通常所使用的 GPT-4o、deepseek 这样的模型而是专门的 Embedding 模型如果你想知道哪些 Embedding 模型最好用的话可以看一下 MTEB 排行版网站是https://huggingface.co/spaces/mteb/leaderboard它会对各种 Embedding 模型做评测并且把结果做个排行方便我们挑选和使用聊完了 Embedding 的概念我们再来看看向量数据库向量数据库就是用来存储和查询向量的数据库它为存储向量做了很多优化并且还提供了计算向量相似度等相关的函数方便我们使用向量Embedding 后的向量就可以放在向量数据库里面方便后续查询比如我们还是以 “马克喜欢吃水果” 这句话为例在我们给这个话做了 Embedding 之后就得到了一个向量然后我们需要把这个向量存入到向量数据库中不过注意我们要存的不仅有向量还有原始的文本所以原始文本也要发给向量数据库因为只有这样我们才能够在通过向量相似度查询出相似的向量之后把对应的原始文本也抽取出来发给模型让它来处理我们最终需要的还是原始的文本向量只是一个中间结果所以一般的向量数据库表格里面至少都会有原始文本和向量两列内容就像下图展示的这样讲完了向量 Embedding 和向量数据库我们再回头看看我们之前提到过的索引这个概念索引就是通过 Embedding 把每个片段文本转换为向量并且把片段文本和对应的向量都存储在向量数据库的过程这句话的意思想必大家都有个概念了其实就是我们刚才聊的这个过程只不过我们要把一开始的这个文本换成每一个片段的内容比如说我们一开始要处理的是片段一片段一处理完了之后呢我们要处理片段二以此类推直到所有的片段都处理完毕整个索引的流程也就结束了不管是分片还是索引它们都发生在用户提问之前属于都要提前准备的步骤下面我们就来看看用户提问之后发生了什么首先是召回6. 召回召回就是搜索与用户问题相关片段的过程这个环节从用户问题开始首先用户的问题会发给 Embedding 模型Embedding 模型会将它转化为向量然后我们把它发送给向量数据库让他查询与用户问题最为相关的十个片段的内容如下图所示没错召回的结果就是十个与用户问题相关的片段当然 10 这个数字并不是固定的你也可以选择 15、20 等等具体是多少呢不是很重要只要数量不是很多都可以那不管是多少向量数据库都要返回与用户问题最相似的一批片段那向量数据库是怎么知道哪些片段与用户问题最相关的呢这就要计算向量相似度了我们来模拟下整个过程向量库里面的数据以及用户问题和对应向量如上图所示我们要做的就是计算每个片段与用户问题的向量相似度我们通过一个相似度计算公式s i m i l a r i t y ( , ) \mathrm{similarity}(\quad,\quad)similarity(,)可以得出两个向量的相似度这个公式的第一个参数是用户问题所对应的向量第二个参数则是每个片段的向量了例如片段一和问题向量的相似度计算如下s i m i l a r i t y ( [ 11 , 5 , 2 , 3 , 1 ] , [ 1.0 , 2.5 , 3.7 , 5.8 , 2.8 ] ) 0.5113 \mathrm{similarity}([11,5,2,3,1],[1.0,2.5,3.7,5.8,2.8])0.5113similarity([11,5,2,3,1],[1.0,2.5,3.7,5.8,2.8])0.5113其它片段类似我们需要按照这个方法把所有的片段都计算完毕在都计算完毕之后我们就把这个向量相似度排个序取前十个最大的就好了那下一个问题就是这个公式是怎么算出来的呢答案是有很多种目前比较流行的方案包括余弦相似度、欧式距离和点积这里我们大致说一下余弦相似度主要在计算两个向量之间夹角的cos \coscos值然后根据这个cos \coscos值来判断夹角的大小夹角越小相似度就越高欧式距离主要是计算两个向量之间的距离距离越小相似度越高点积是一种通过代数方式衡量两个向量相似度的方法它不仅要考虑两个向量之间的方向关系也要考虑它们的长度比如我们如果要计算过上图中 A 和 B 的点积我们首先要从 A 向 B 引入一条垂线假设垂足为 C然后 A 和 B 的点积就是 OC 和 OB 两段距离的乘积乘积越大就代表相似度越高。如果两个向量方向一致的话那么这两个向量越长点积值就越大如果方向相反那么点积就是负的如果垂直点积就为 0。所以我们可以通过点积的值来判断两个向量是否在同一个方向上努力以及它们努力的程度有多大下面我们再来看看三种方案对应的计算公式(from ChatGPT)假设有a , b \mathbf {a,b}a,b两个向量其定义为a ( a 1 , a 2 , … , a n ) , b ( b 1 , b 2 , … , b n ) \mathbf a (a_1, a_2, \dots, a_n),\quad \mathbf b (b_1, b_2, \dots, b_n)a(a1,a2,…,an),b(b1,b2,…,bn)点积Dot Producta ⋅ b ∑ i 1 n a i b i \mathbf a \cdot \mathbf b \sum_{i1}^n a_i\,b_ia⋅bi1∑naibi欧式距离Euclidean Distanced ( a , b ) ∥ a − b ∥ 2 ∑ i 1 n ( a i − b i ) 2 d(\mathbf a,\mathbf b) \|\mathbf a - \mathbf b\|_2 \sqrt{\sum_{i1}^n (a_i - b_i)^2}d(a,b)∥a−b∥2i1∑n(ai−bi)2余弦相似度Cosine Similaritycos ( θ ) a ⋅ b ∥ a ∥ 2 ∥ b ∥ 2 ∑ i 1 n a i b i ∑ i 1 n a i 2 ∑ i 1 n b i 2 \cos(\theta) \frac{\mathbf a \cdot \mathbf b}{\|\mathbf a\|_2 \;\|\mathbf b\|_2} \frac{\sum_{i1}^n a_i\,b_i} {\sqrt{\sum_{i1}^n a_i^2}\;\sqrt{\sum_{i1}^n b_i^2}}cos(θ)∥a∥2∥b∥2a⋅b∑i1nai2∑i1nbi2∑i1naibi这里∥ a ∥ 2 ∑ i a i 2 \|\mathbf a\|_2 \sqrt{\sum_i a_i^2}∥a∥2∑iai2是a \mathbf aa的二范数余弦相似度取值范围[ − 1 , 1 ] [-1,1][−1,1]越接近 1 表示方向越相似欧式距离越小表示两个向量越接近。向量相似度就讲到这里在这一阶段我们查询出了与用户问题最为匹配的十个片段记住这个结论因为这十个片段会发送到重排阶段继续处理7. 重排重排全称是重新排序它做的事情其实跟召回是一样的前面我们说过召回是从所有的片段里面挑十份与用户问题最相似的而重排则是从召回的这十份里面再挑三份与用户问题最相似的作为重排的结果你可能会想那直接在召回阶段挑三个不就好了这样就不用重排了同样的事情搞两遍干什么呢一次挑出三个当然是可以的不过这样做的效果没有召回加重排的方案好为什么呢因为召回与重排阶段使用的文本相似度计算逻辑不一样下面我们来比较一下首先看一下召回召回阶段使用的是向量相似度我们前面还列举了三个常见的向量相似度计算方法但无论是使用哪一种方法它们的特点都是成本低耗时短准确率低所以呢适合做初步的筛选也就是在短时间内把上千条片段的相似度数值都计算出来从中挑选十个最高的而重排就不是了重排阶段一般是使用一种叫做 cross-encoder 的模型计算每个片段与用户问题的相似度相比之下 cross-encoder 的成本会比较高耗时也比较长那为什么用它呢因为 cross-encoder 的准确率会高很多所以它很适合做精挑细选你可以把它类比成公司筛选人才众所周知公司面试一共是分为两个环节简历筛选加面试召回就跟简历筛选有点像候选人太多公司只能用一些粗略的方法从成千上万份简历里面挑出十个看起来最优秀的准确率可能会大打折扣但这也是没有办法的事情那重排就跟面试很像了公司对这十个人进行面试仔细挑选尽可能地保证判断正确从中挑出最优秀的三个人入职OK重排就讲到这里下面我们进入到生成阶段8. 生成生成什么呢那当然是答案了现在我们有了用户问题也有与用户问题相关的三个片段我们就可以把这两部分一起发给大模型让它根据片段内容来回答用户问题到此整个流程就结束了9. RAG 的整体流程RAG 流程里面的所有环节我们都讲完了下面我们把所有的流程给串联一下整体讲一遍整个流程分为两个部分一个是准备部分它发生在提问前包括分片和索引两个环节另一个是回答部分发生在提问后包括召回、重排和生成三个环节由于整个流程分为两个部分所以我们的整体流程也会有两个先看提问前的准备部分首先我们把相关的资料做个分片然后把所有的片段都扔给 Embedding 模型让它给每个片段都产出一个对应的向量最后我们把向量存入到向量数据库中到这里提问前的准备流程就结束了这就相当于我们的知识库构建已经完毕了就等用户来用了再来看看用户提问之后发生了什么首先用户的问题会给到 Embedding 模型Embedding 模型会把用户的问题转化为一个向量然后我们会把这个向量传给向量数据库让它给我们找到十个与用户问题最相近的片段找到之后我们再把这十个片段送给 cross-encoder 模型让它做个重排从十个里面再筛选出三个相关程度最高的然后我们把这三个与用户问题相关程度最高的片段外加用户的问题一起发给大模型大模型就可以给我们产出最终答案了到这里所有的流程就算是结束了结语这篇文章我们主要是学习了解了下 RAGRAG 翻译过来叫做检索增强生成它做的事情就是先从资料库里检索相关的内容再基于这些内容来生成答案接着 UP 主帮我们梳理了 RAG 的运行流程整个流程分为两部分一个是准备部分另一个是回答部分准备部分包括分片和索引两个环节首先我们把相关资料做分片然后把分片后的片段扔给 Embedding 模型生成向量最后把向量和对应的文本存入到向量数据库中这样就完成了我们的知识库构建工作回答部分包括召回、重排以及生成三个部分首先用户的问题会给到 Embedding 模型转化为向量然后我们会把这个向量和向量数据库中的向量进行相似度计算让它挑选出最符合的十个片段接着这十个片段会送给 cross-encoder 模型让它重排从中再筛选出三个相关度最高的片段将这些片段外加用户问题一起发给大模型大模型就可以给我们生成答案了这便是整个流程整个讲解非常通俗易懂大家感兴趣的可以多关注关注多看看 UP 的视频参考RAG 工作机制详解——一个高质量知识库背后的技术全流程https://huggingface.co/spaces/mteb/leaderboard