RAG技术
.w7ylV6r5.png)
途中绿色的元素是需要进一步讨论的核心 RAG 技术,黄色的是文本,蓝色的是数据库(向量数据库,结构化数据库等)
一、分块与向量化
1.1、分块
分块的大小是需要考虑的一个参数,这个参数取决于使用的嵌入模型能够处理的最大 token 长度,不同的嵌入模型的最大 Token 是不同的 Qwen/Qwen3-Embedding-8B BAAI/bge-m3
****
当文本块分好之后,我们需要使用嵌入模型将文本块转换为机器能理解的符号,也就是向量
- 一句话,或者一个段落 → 输入到嵌入模型(embedding model)。
- 模型会输出一个固定维度的数字数组,比如
[0.12, -0.53, 0.88, ...]。 - 这个数组就是向量,通常有几百到几千个维度。
向量其实就是一个高维度的坐标,也就是一个固定维度的数字数组
在选择嵌入模型时,需要考虑两个方面
- 向量维度的大小(维度过低会语义丢失,维度过高增加存储和计算成本)
- 模型支持的语言范围(确保支持你的文本语言)
二、搜索索引
2.1、向量存储索引
.C93167_T.png)
图中,为了核心流程的清晰,有。省略一些模块
- 嵌入模型:查询的时候会先将文本使用嵌入模型转换为向量,进入到向量数据库查询
- 查询结果:查询出来的结果其实是前 k 个向量,而不是文本块,这里系统会自动使用向量 ID 去映射回原始的文本块
RAG 流程关键的部分是搜索索引,其有多种实现方式
- 平面索引:直接用暴力方式计算查询向量和所有片段向量的距离
- 向量索引:通常采用近似最近邻(ANN)的方法,比如聚类,树结构和 HNSW 算法
ANN 是一类搜索策略,目标是在海量向量里快速找到最接近的候选,而不是逐一比对
- 聚类倒排(IVF):先把空间分块,缩小候选范围。
- 树结构(Annoy 等):用分割空间的树来加速查找。
- 图结构(HNSW):构建近邻图,贪心搜索路径找到候选。
🌟 同时你也可以根据你的需求和场景,在存储向量的同时保存元数据
元数据的作用有两个:
- 可以用来作为向量查询的附加值,也就是元信息
- 可以利用元数据过滤器来限定检索范围,例如:根据日期或者来源筛选
2.2、分层索引
.CNeWB71K.png)
分层索引的核心思路:先大再小,先粗筛再细筛
分层索引会创建两个索引,一个是“大文档”的摘要索引,一个是“小文档”的文本块索引,摘要索引里面的向量和文本块索引里面的向量是有关联关系,搜索的时候分为两步搜索:
- 先通过摘要索引过滤出相关的文档
- 只在这些相关文档内部进行片段级的检索
2.3、假设性问题(Hypothetical Questions)与假设性文档(HyDE)
.liOnCjDA.png)
1、 假设性问题(Hypothetical Questions),源头是文档片段
- 对于每个文档块,让 LLM 生成几个可能有人会问的问题,把这些问题拿去向量化建立索引
- 在检索的时候,用户的问题去检索上一步构建的“文档问题”向量数据库,命中合适的值之后,根据“文档问题”找到文档块
例子:
- 文档块:支持 7 天无理由退货,定制商品除外
- 生成的问题:
- 多久可以退货
- 定制商品能退吗
- 退货运费谁承担
- 当用户问:“不合适能退吗?”,搜索就可以命中生成的文档问题块
2、假设性文档(Hypothetical Document Embeddings),源头是用户的问题
- 用户输入的问题,使用 LLM 生成一段假设性的答案文档,
- 把这个“答案”拿去文档向量数据库中进行检索,同时也可以把问题也拿去检索,最后合并一下结果即可
例子:
- 用户问题:什么是量子纠缠?
- 生成的答案:“量子纠缠是指两个粒子状态相关联,即使相距遥远,测量一个会立即影响另一个。”
- 根据这个答案再去文档向量数据库中进行检索,这样检索出来的极大概率都可以和问题解答相关
2.4、上下文增强
检索时尽量取更小的片段,以提升搜索精度,但在提供给 LLM 时,再补充周围的上下文,以充足的上下文供 LLM 进行更好的推理
目前有两种实现的方式:
- 句子扩展:在检索到较小的片段的时候,把该片段的上下的片段都加上,组合成为一个完整的上下文块输入给 LLM
- 父文档检索:先把文档分割为较大的父级片段,每一个父级片段再包含更小的子片段,检索的时候使用子片段组成的向量数据库,但是在使用的时候回溯到相应的父级片段,把父级片段周围上下文输入给 LLM,子片段只做一个中间的检索步骤
2.4.1、句子扩展
.yCuAmXpJ.png)
最终的效果会得到一些改良:
- 如果没有前后两句文档块,只给中间一句,那模型的回答可能只是一个观点或者一句话:“是的,苹果可能降低心脏病的风险
- 这个时候加了前后两句文档块,那效果或者观点就会扩大,模型可能会输出:“苹果不仅能增强免疫系统,还能降低心脏病风险。研究显示,经常吃苹果的人往往更长寿。”
🌟 核心思路:检索时保持足够小的文档块,在输入给 LLM 之前,进行上下文的扩展和增强,输入更完整的上下文给 LLM 进行推理
2.4.2、父文档检索- Auto-merging Retriever
.4Ev_Q6CY.png)
这个方法其实和上面的句子扩展的思路是一样的,最后都是将更充足的上下文输入给 LLM 进行推理。
文档被分割成一个分层结构,
- 子文档块,更小的片段,用于承担检索任务,
- 父文档块,更大的片段,与子文档是有联系的,用于决定最正输入给 LLM 的上下文
在查询的操作中,流程如下:
- 先根据问题检索子文档块的向量数据库,这里的文档块都比较小,更有利于精准的检索
- 得到前 k 个相关的子文档块
- 由前 k 个相关的子文档块,查找或者合并它们相应的父文档块
- 最后将整理出的父文档块输入给 LLM,用于生产最终答案
例子:
章节:心脏健康与饮食
段落1:
水果和蔬菜中的抗氧化剂可以减少心脏疾病风险。
苹果尤其被广泛研究,它们含有丰富的类黄酮。
段落2:
类黄酮能帮助降低血压,改善血管功能。
研究表明,每天食用一个苹果可以降低冠心病的发生率。将上面的文档按照向量存储来分块
父块的向量数据库
- 父块 A=段落 1
- 父块 B=段落 2
子块的向量数据库
- 父块 A 再拆为两个子块
- 子块A1 = “水果和蔬菜中的抗氧化剂可以减少心脏疾病风险。”
- 子块A2 = “苹果尤其被广泛研究,它们含有丰富的类黄酮。”
- 父块 B 再拆为两个子块
- 子块B1 = “类黄酮能帮助降低血压,改善血管功能。”
- 子块B2 = “研究表明,每天食用一个苹果可以降低冠心病的发生率。”
用户的问题:“苹果对心脏有帮助吗?”
系统在子块层级搜索,命中子块 B2:
“研究表明,每天食用一个苹果可以降低冠心病的发生率。”
然后系统向上回溯到父块 B,将完整的上下文输入给 LLM
类黄酮能帮助降低血压,改善血管功能。
研究表明,每天食用一个苹果可以降低冠心病的发生率。
2.5、融合检索或混合搜索
.DrlxO9Km.png)
一个相对较早提出的想法:从两种不同的检索方式中汲取优点:第一种是基于关键词的传统搜索,第二种是基于现代的语义搜索和向量搜索
混合检索:结合这两种方式的优点并整理融合两种方式的结果
混合检索的难点在:如何正确的融合来自不同检索方式,且相似度评分标准不同的结果
通常会使用Reciprocal Rank Fusion(RRF)算法来正确融合两种结果,它能对检索结果进行重新排序,生成最终的输出
三、重新排序和过滤
通过上述的任意一种检索方式和思路,我们获得了检索结果,现在需要通过过滤、重排序或某些转换来优化这些结果
目前常见的优化方式是:
- 根据相似度分数排序之后,取出前 k 个值
- 根据元信息中的时间、用户 id 等数据进行过滤筛选
- 也可以直接把向量结果中的文本输入给 LLM,让 LLM 进行最有效的排序
- 可以使用重排序(re-ranking)模型来对结果再次进行筛选优化
四、查询转换
.xhLxEsfX.png)
查询转换:通过使用 LLM 作为推理引擎来修改用户输入,以提高检索质量,有几种不同的方法可以实现这一点。
如果查询比较复杂,LLM 可以将它分解成多个子查询
例如用户会问:
“在 Github 上,Langchain 或 LlamaIndex 哪个框架的星标更多?”
由于在语料库中几乎找不到直接的对比文本,因此将这个问题分解为两个更简单、更具体的子查询是有意义的:
- “Langchain 在 Github 上有多少颗星?”
- “LlamaIndex 在 Github 上有多少颗星?”
这两个子查询会并行执行,然后将检索到的上下文合并在一起,作为一个上下文输入到 LLM 中
还有下面两种思路可以实现查询转换
1、 转换通用查询(Step-back Prompting):这种方法先让 LLM 生成一个更通用的查询,然后利用这个通用查询检索到更通用,更高层次的上下文,这些上下文是有助于为原始查询提供语义基础的
同时,系统也会执行对原始查询的检索,并且在最终生成答案,把这两类上下文一起输入给 LLM 进行推理
2、 查询重写(Query Re-writing):这种方法先利用 LLM 来对初始查询进行改写或重试,以提升检索效果
五、引用来源
在 LLM 生成答案的时候,可以将答案的来源标注出来,方便回溯到原始来源
这种引用来源的方式是非常重要的,可以极大的增加答案的准确性,为用户提供一层“信息审核”的机制
目前可以有以下几种方式实现:
- 在提示词中加入引用任务,要求 LLM 在答案中提到所使用来源的 ID
- 将 LLM 生成的响应与向量数据库中检索出的原始文本块进行匹配
六、查询路由
.CCVwY2Sa.png)
查询路由:是一个由 LLM 驱动的决策步骤,根据用户的查询,决定下一步要做什么,提供给 LLM 的选择通常包括:摘要索引,某些文档块数据索引或结构化数据库,最后在一个答案中综合它们的输出
查询路由还可以运用到搜索策略中去,例如是选择分层检索的方式,还是选择句子扩展的方式,或者父文档检索的方式呢
查询路由最重要的是定义路由器它能够做出的选择范围,也就是能给路由器提供哪些搜索策略或者搜索方式