Skip to content

RAG技术

RAG技术架构

途中绿色的元素是需要进一步讨论的核心 RAG 技术,黄色的是文本,蓝色的是数据库(向量数据库,结构化数据库等)

一、分块与向量化

1.1、分块

分块的大小是需要考虑的一个参数,这个参数取决于使用的嵌入模型能够处理的最大 token 长度,不同的嵌入模型的最大 Token 是不同的 Qwen/Qwen3-Embedding-8B BAAI/bge-m3

****

当文本块分好之后,我们需要使用嵌入模型将文本块转换为机器能理解的符号,也就是向量

  • 一句话,或者一个段落 → 输入到嵌入模型(embedding model)。
  • 模型会输出一个固定维度的数字数组,比如 [0.12, -0.53, 0.88, ...]
  • 这个数组就是向量,通常有几百到几千个维度。

向量其实就是一个高维度的坐标,也就是一个固定维度的数字数组

在选择嵌入模型时,需要考虑两个方面

  1. 向量维度的大小(维度过低会语义丢失,维度过高增加存储和计算成本)
  2. 模型支持的语言范围(确保支持你的文本语言)

二、搜索索引

2.1、向量存储索引

向量存储索引

图中,为了核心流程的清晰,有。省略一些模块

  1. 嵌入模型:查询的时候会先将文本使用嵌入模型转换为向量,进入到向量数据库查询
  2. 查询结果:查询出来的结果其实是前 k 个向量,而不是文本块,这里系统会自动使用向量 ID 去映射回原始的文本块

RAG 流程关键的部分是搜索索引,其有多种实现方式

  1. 平面索引:直接用暴力方式计算查询向量和所有片段向量的距离
  2. 向量索引:通常采用近似最近邻(ANN)的方法,比如聚类,树结构和 HNSW 算法

ANN 是一类搜索策略,目标是在海量向量里快速找到最接近的候选,而不是逐一比对

  • 聚类倒排(IVF):先把空间分块,缩小候选范围。
  • 树结构(Annoy 等):用分割空间的树来加速查找。
  • 图结构(HNSW):构建近邻图,贪心搜索路径找到候选。

🌟 同时你也可以根据你的需求和场景,在存储向量的同时保存元数据

元数据的作用有两个:

  1. 可以用来作为向量查询的附加值,也就是元信息
  2. 可以利用元数据过滤器来限定检索范围,例如:根据日期或者来源筛选

2.2、分层索引

分层索引

分层索引的核心思路:先大再小,先粗筛再细筛

分层索引会创建两个索引,一个是“大文档”的摘要索引,一个是“小文档”的文本块索引,摘要索引里面的向量和文本块索引里面的向量是有关联关系,搜索的时候分为两步搜索:

  1. 先通过摘要索引过滤出相关的文档
  2. 只在这些相关文档内部进行片段级的检索

2.3、假设性问题(Hypothetical Questions)与假设性文档(HyDE)

假设性问题与假设性文档

1、 假设性问题(Hypothetical Questions),源头是文档片段

  • 对于每个文档块,让 LLM 生成几个可能有人会问的问题,把这些问题拿去向量化建立索引
  • 在检索的时候,用户的问题去检索上一步构建的“文档问题”向量数据库,命中合适的值之后,根据“文档问题”找到文档块

例子:

  1. 文档块:支持 7 天无理由退货,定制商品除外
  2. 生成的问题:
    1. 多久可以退货
    2. 定制商品能退吗
    3. 退货运费谁承担
  3. 当用户问:“不合适能退吗?”,搜索就可以命中生成的文档问题块

2、假设性文档(Hypothetical Document Embeddings),源头是用户的问题

  • 用户输入的问题,使用 LLM 生成一段假设性的答案文档,
  • 把这个“答案”拿去文档向量数据库中进行检索,同时也可以把问题也拿去检索,最后合并一下结果即可

例子:

  1. 用户问题:什么是量子纠缠?
  2. 生成的答案:“量子纠缠是指两个粒子状态相关联,即使相距遥远,测量一个会立即影响另一个。”
  3. 根据这个答案再去文档向量数据库中进行检索,这样检索出来的极大概率都可以和问题解答相关

2.4、上下文增强

检索时尽量取更小的片段,以提升搜索精度,但在提供给 LLM 时,再补充周围的上下文,以充足的上下文供 LLM 进行更好的推理

目前有两种实现的方式:

  1. 句子扩展:在检索到较小的片段的时候,把该片段的上下的片段都加上,组合成为一个完整的上下文块输入给 LLM
  2. 父文档检索:先把文档分割为较大的父级片段,每一个父级片段再包含更小的子片段,检索的时候使用子片段组成的向量数据库,但是在使用的时候回溯到相应的父级片段,把父级片段周围上下文输入给 LLM,子片段只做一个中间的检索步骤

2.4.1、句子扩展

句子扩展

最终的效果会得到一些改良:

  • 如果没有前后两句文档块,只给中间一句,那模型的回答可能只是一个观点或者一句话:“是的,苹果可能降低心脏病的风险
  • 这个时候加了前后两句文档块,那效果或者观点就会扩大,模型可能会输出:“苹果不仅能增强免疫系统,还能降低心脏病风险。研究显示,经常吃苹果的人往往更长寿。”

🌟 核心思路:检索时保持足够小的文档块,在输入给 LLM 之前,进行上下文的扩展和增强,输入更完整的上下文给 LLM 进行推理

2.4.2、父文档检索- Auto-merging Retriever

父文档检索

这个方法其实和上面的句子扩展的思路是一样的,最后都是将更充足的上下文输入给 LLM 进行推理。

文档被分割成一个分层结构,

  • 子文档块,更小的片段,用于承担检索任务,
  • 父文档块,更大的片段,与子文档是有联系的,用于决定最正输入给 LLM 的上下文

在查询的操作中,流程如下:

  1. 先根据问题检索子文档块的向量数据库,这里的文档块都比较小,更有利于精准的检索
  2. 得到前 k 个相关的子文档块
  3. 由前 k 个相关的子文档块,查找或者合并它们相应的父文档块
  4. 最后将整理出的父文档块输入给 LLM,用于生产最终答案

例子:

plain
章节:心脏健康与饮食

段落1:
水果和蔬菜中的抗氧化剂可以减少心脏疾病风险。
苹果尤其被广泛研究,它们含有丰富的类黄酮。

段落2:
类黄酮能帮助降低血压,改善血管功能。
研究表明,每天食用一个苹果可以降低冠心病的发生率。

将上面的文档按照向量存储来分块

父块的向量数据库

  • 父块 A=段落 1
  • 父块 B=段落 2

子块的向量数据库

  • 父块 A 再拆为两个子块
    • 子块A1 = “水果和蔬菜中的抗氧化剂可以减少心脏疾病风险。”
    • 子块A2 = “苹果尤其被广泛研究,它们含有丰富的类黄酮。”
  • 父块 B 再拆为两个子块
    • 子块B1 = “类黄酮能帮助降低血压,改善血管功能。”
    • 子块B2 = “研究表明,每天食用一个苹果可以降低冠心病的发生率。”

用户的问题:“苹果对心脏有帮助吗?”

系统在子块层级搜索,命中子块 B2:

“研究表明,每天食用一个苹果可以降低冠心病的发生率。”

然后系统向上回溯到父块 B,将完整的上下文输入给 LLM

类黄酮能帮助降低血压,改善血管功能。

研究表明,每天食用一个苹果可以降低冠心病的发生率。

2.5、融合检索或混合搜索

融合检索

一个相对较早提出的想法:从两种不同的检索方式中汲取优点:第一种是基于关键词的传统搜索,第二种是基于现代的语义搜索和向量搜索

混合检索:结合这两种方式的优点并整理融合两种方式的结果

混合检索的难点在:如何正确的融合来自不同检索方式,且相似度评分标准不同的结果

通常会使用Reciprocal Rank Fusion(RRF)算法来正确融合两种结果,它能对检索结果进行重新排序,生成最终的输出

三、重新排序和过滤

通过上述的任意一种检索方式和思路,我们获得了检索结果,现在需要通过过滤、重排序或某些转换来优化这些结果

目前常见的优化方式是:

  1. 根据相似度分数排序之后,取出前 k 个值
  2. 根据元信息中的时间、用户 id 等数据进行过滤筛选
  3. 也可以直接把向量结果中的文本输入给 LLM,让 LLM 进行最有效的排序
  4. 可以使用重排序(re-ranking)模型来对结果再次进行筛选优化

四、查询转换

查询转换

查询转换:通过使用 LLM 作为推理引擎来修改用户输入,以提高检索质量,有几种不同的方法可以实现这一点。

如果查询比较复杂,LLM 可以将它分解成多个子查询

例如用户会问:

“在 Github 上,Langchain 或 LlamaIndex 哪个框架的星标更多?”

由于在语料库中几乎找不到直接的对比文本,因此将这个问题分解为两个更简单、更具体的子查询是有意义的:

  • “Langchain 在 Github 上有多少颗星?”
  • “LlamaIndex 在 Github 上有多少颗星?”

这两个子查询会并行执行,然后将检索到的上下文合并在一起,作为一个上下文输入到 LLM 中

还有下面两种思路可以实现查询转换

1、 转换通用查询(Step-back Prompting):这种方法先让 LLM 生成一个更通用的查询,然后利用这个通用查询检索到更通用,更高层次的上下文,这些上下文是有助于为原始查询提供语义基础的

同时,系统也会执行对原始查询的检索,并且在最终生成答案,把这两类上下文一起输入给 LLM 进行推理


2、 查询重写(Query Re-writing):这种方法先利用 LLM 来对初始查询进行改写或重试,以提升检索效果

五、引用来源

在 LLM 生成答案的时候,可以将答案的来源标注出来,方便回溯到原始来源

这种引用来源的方式是非常重要的,可以极大的增加答案的准确性,为用户提供一层“信息审核”的机制

目前可以有以下几种方式实现:

  1. 在提示词中加入引用任务,要求 LLM 在答案中提到所使用来源的 ID
  2. 将 LLM 生成的响应与向量数据库中检索出的原始文本块进行匹配

六、查询路由

查询路由

查询路由:是一个由 LLM 驱动的决策步骤,根据用户的查询,决定下一步要做什么,提供给 LLM 的选择通常包括:摘要索引,某些文档块数据索引或结构化数据库,最后在一个答案中综合它们的输出

查询路由还可以运用到搜索策略中去,例如是选择分层检索的方式,还是选择句子扩展的方式,或者父文档检索的方式呢

查询路由最重要的是定义路由器它能够做出的选择范围,也就是能给路由器提供哪些搜索策略或者搜索方式