RAG完整构建流程
LLM模型缺陷:
知识是有局限性的(缺少垂直领域/非公开知识/数据安全)
知识实时性(训练周期长、成本高)
幻觉问题(模型生成的问题)
方法:Retrieval-Augmented Generation(RAG)
检索:根据用户请求从外部知识源检索相关上下文
增强:用户查询和检索到的附加上下文填充至prompt
生成:检索增强提示输入到LLM生成请求响应
完整的 RAG 应用流程
一、数据文档准备
现实场景中,我们面对的知识源可能包括多种格式,如Word文档、TXT文件、CSV数据表、Excel表格,甚至是PDF文件、图片和视频等。因此,第一步需要使用专门的文档加载器(例如PDF提取器)或多模态模型(如OCR技术),将这些丰富的知识源转换为大语言模型可理解的纯文本数据。
数据清洗:高性能RAG系统依赖于准确且清洁的原始知识数据。
1)基本文本清理:规范文本格式,去除特殊字符和不相关信息。除重复文档或冗余信息。
2)实体解析:消除实体和术语的歧义以实现一致的引用。
3)文档划分:合理地划分不同主题的文档,便于检索
二、数据处理-向量化
鉴于文档可能存在过长的问题,我们还需执行一项关键步骤:文档切片。我们需要将长篇文档分割成多个文本块,以便更高效地处理和检索信息。这不仅有助于减轻模型的负担,还能提高信息检索的准确性。
文档切片的选择:
1)固定大小分块:是最简单和直接的方法,我们直接设定块中的字数,并选择块之间是否重复内容。通常,我们会保持块之间的一些重叠,以确保语义上下文不会在块之间丢失。
2)内容分块:根据文档的具体内容进行分块,例如根据标点符号(如句号)分割。
3)递归分块:通过重复地应用分块规则来递归地分解文本。例如,在langchain中会先通过段落换行符(\n\n
)进行分割。
4)从小到大分块:把同一文档进行从大到小所有尺寸的分割,然后把不同大小的分块全部存进向量数据库,并保存每个分块的上下级关系,进行递归搜索。
5)特殊结构分块:针对特定结构化内容的专门分割器。这些分割器特别设计来处理这些类型的文档,以确保正确地保留和理解其结构。langchain提供的特殊分割器包括:Markdown文件,Latex文件,以及各种主流代码语言分割器。
#例如langchian提供的切割方式
from langchain.text_splitter import CharacterTextSplitter
text_splitter = CharacterTextSplitter(chunk_size = 200,chunk_overlap = 20)
接下来,将切割出来的文本块(chunks)转换为向量形式(embedding)。这种转换使得我们能够通过简单计算向量之间的差异来识别语义上相似的句子。嵌入模型能帮助我们把文本转换成向量,显然不同的嵌入模型带来的效果也不尽相同。
#例如langchian提供的不同模型的嵌入方式
from langchain.embeddings import HuggingFaceEmbeddings
embeddings = HuggingFaceEmbeddings()
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
三、存储数据库
向量数据库是专门设计用于存储和检索向量数据的数据库系统。在RAG系统中,通过嵌入模型生成的所有向量都会被存储在这样的数据库中。如下图给出一些最常用的数据存储检索:
使用举例:
from langchain.vectorstores import FAISS
db = FAISS.from_documents(documents = "", embedding = embeddings)
四、检索查询
根据用户提问的问题,通过检索查找数据库中相关的文本块。
4.1、元数据
当在向量数据库中存储向量数据时,某些数据库支持将向量与元数据(即非向量化的数据)一同存储。为向量添加元数据标注是一种提高检索效率的有效策略,它在处理搜索结果时发挥着重要作用。例如,日期就是一种常见的元数据标签。它能够帮助我们根据时间顺序进行筛选。
4.2、多级索引
元数据无法充分区分不同上下文类型的情况下,我们可以考虑进一步尝试多重索引技术。多重索引技术的核心思想是将庞大的数据和信息需求按类别划分,并在不同层级中组织,以实现更有效的管理和检索。这意味着系统不仅依赖于单一索引,而是建立了多个针对不同数据类型和查询需求的索引。
4.3、索引/查询算法
利用索引筛选数据,但说到底我们还是要从筛选后的数据中检索出相关的文本向量。
聚类
4.4、查询转化
在RAG系统中,用户的查询问题被转化为向量,然后在向量数据库中进行匹配。不难想象,查询的措辞会直接影响搜索结果。如果搜索结果不理想,可以尝试以下几种方法对问题进行重写,以提升召回效果:
1)结合历史对话的重新表述:在向量空间中,对人类来说看似相同的两个问题其向量大小并不一定很相似。我们可以直接利用LLM 重新表述问题来进行尝试。此外,在进行多轮对话时,用户的提问中的某个词可能会指代上文中的部分信息,因此可以将历史信息和用户提问一并交给LLM重新表述。
2)假设文档嵌入(HyDE):HyDE的核心思想是接收用户提问后,先让LLM在没有外部知识的情况下生成一个假设性的回复。然后,将这个假设性回复和原始查询一起用于向量检索。假设回复可能包含虚假信息,但蕴含着LLM认为相关的信息和文档模式,有助于在知识库中寻找类似的文档。
3)退后提示(Step Back Prompting):如果果原始查询太复杂或返回的信息太广泛,我们可以选择生成一个抽象层次更高的“退后”问题,与原始问题一起用于检索,以增加返回结果的数量。例如,原问题是“桌子君在特定时期去了哪所学校”,而退后问题可能是关于他的“教育历史”。这种更高层次的问题可能更容易找到答案。
4)多查询检索/多路召回(Multi Query Retrieval):使用LLM生成多个搜索查询,特别适用于一个问题可能需要依赖多个子问题的情况。
4.5、检索参数
把查询问题准备好了,可以进入向量数据库进行检索。在具体的检索过程中,我们可以根据向量数据库的特定设置来优化一些检索参数,以下是一些常见的可设定参数:
1)稀疏和稠密搜索权重: 稠密搜索即通过向量进行搜索。
2)结果数量(topK):返回多个检索的结果。
3)相似度度量方法:计算两个向量相似度的方法。
4.6、高级检索策略
最为关键和复杂的步骤——在向量数据库检索之上如何具体开发或改进整个系统的策略。
1)上下文压缩:上下文压缩的思想就是通过LLM的帮助根据上下文对单个文档内容进行压缩,或者对返回结果进行一定程度的过滤仅返回相关信息。
2)多向量检索:多向量检索同样会给一个知识文档转化成多个向量存入数据库,不同的是,这些向量不仅包括文档在不同大小下的分块,还可以包括该文档的摘要,用户可能提出的问题等等有助于检索的信息。
3)多代理检索:多代理检索,简而言之就是选取我们提及的12大优化策略中的部分交给一个智能代理合并使用。
4)Self-RAG:自反思搜索增强是一个全新RAG框架,其与传统RAG最大的区别在于通过检索评分(令牌)和反思评分(令牌)来提高质量。它主要分为三个步骤:检索、生成和批评。Self-RAG首先用检索评分来评估用户提问是否需要检索,如果需要检索,LLM将调用外部检索模块查找相关文档。接着,LLM分别为每个检索到的知识块生成答案,然后为每个答案生成反思评分来评估检索到的文档是否相关,最后将评分高的文档当作最终结果一并交给LLM。
此部分为RAG的研究重点,最新的研究策略还需查询最新论文进行学习研究。
五、生成回答
根据用户的提问和检索到的信息结合,构建出一个prompt,输入到LLM中,得到回答。
5.1、prompt
通过改变提示词的形式,可以有效地影响模型对不同类型问题的接受程度和回答方式。一般情况下,RAG系统中的提示词中应明确指出回答仅基于搜索结果,不要添加任何其他信息例如,可以设置提示词如:“你是一名智能客服。你的目标是提供准确的信息,并尽可能帮助提问者解决问题。
5.2、LLM
LLM生成回答:LLM是生成响应的核心组件。与嵌入模型类似,可以根据自己的需求选择LLM,例如开放模型与专有模型、推理成本、上下文长度等。此外,可以使用一些LLM开发框架来搭建RAG系统,比如,LlamaIndex或LangChain。