检索增强生成技术(RAG)概述

引言生成式人工智能(GenAI)在文本生成、文本到图像生成等领域展现出强大的能力,但同时也存在一些局限性,如幻觉倾向、数学能力不足和缺乏可解释性。为了克服这些问题,**检索增强生成(Retrieval-Augmented Generation, RAG)**技术应运而生。

RAG技术简介RAG技术通过结合检索算法和大型语言模型(LLM),为AI提供更加准确和有依据的信息。这一技术主要包括两个阶段:

  1. 检索阶段:利用检索算法从数据源中找到与用户查询相关的信息。
  2. 生成阶段:将检索到的信息与用户查询一起,作为输入提供给LLM,以生成更准确的回答。

RAG技术的重要性RAG技术在2023年成为基于LLM的系统架构中的热门选择。它不仅被应用于问答服务,还广泛应用于与数据交互的各种应用程序中。

矢量搜索与RAG矢量搜索技术的发展为RAG技术提供了强有力的支持。一些基于现有开源搜索索引(如faiss和nmslib)的矢量数据库初创公司,如chroma、weavaite.io、pinecone等,通过为输入文本添加额外的存储空间和其他工具,进一步提升了RAG技术的性能。

开源库与RAG基于大型模型的应用程序中,有多个著名的开源库支持RAG技术,包括但不限于:

  • LangChain
  • LlamaIndex
  • AutoGen
  • PromptAppGPT 这些库为开发人员提供了深入研究和实现RAG技术的基础。

结语本文旨在系统化地介绍RAG技术的关键点和实现方法,以便其他开发人员能够更好地理解和应用这一技术,推动人工智能领域的发展。

基础RAG流程图

基础RAG流程简单来说如下:将文本分割成块,然后使用编码模型将这些块嵌入到向量中,将所有这些向量放入索引中,最后为LLM创建一个提示,告诉模型根据我们在搜索步骤中找到的上下文来回答用户的查询。

在运行时,我们使用相同的编码器模型对用户的查询进行矢量化,然后针对索引执行该查询向量的搜索,找到前k个结果,从数据库中检索相应的文本块,并将它们作为上下文输入到LLM提示中。

下面是一个示例提示信息:

def question_answering(context, query):

prompt = f""" Give the answer to the user query delimited by triple backticks ```{query}```\ using the information given in context delimited by triple backticks ```{context}```.\ If there is no relevant information in the provided context, try to answer yourself, but tell user that you did not have any relevant context to base your answer on. Be concise and output the answer of size less than 80 tokens. """

response = get_completion(instruction, prompt, model=“gpt-3.5-turbo”)

answer = response.choices[0].message[“content”]

return answer

提示工程是可以尝试改进RAG系统的最便捷便宜的方法。

显然,尽管OpenAI是LLM提供商的市场领导者,但仍有许多替代方案,例如Anthropic的Claude、最近流行的较小但功能强大的模型,例如Mixtral形式的Mistral、Microsoft的Phi-2以及许多开源选项,例如Llama2、OpenLLaMA、Falcon。

3 - 高级检索增强生成(RAG)技术

现在我们将深入了解高级RAG技术的核心步骤和所涉及算法。为了保持方案的可读性,我们省略了一些逻辑循环和复杂的多步骤代理行为。

高级RAG的关键流程图

在方案图中,绿色元素代表核心RAG技术,蓝色元素代表文本。由于篇幅限制,一些高级RAG技术未能在图中展示,例如各种上下文放大方法,但将在文中详细讨论。

3.1 分块和矢量化

3.1.1 分块

Transformer模型的输入序列长度固定,因此需要将文档分割成块,以便更好地表示其语义。分块应尽量保持语义完整性,避免将单个句子分割。块的大小是一个关键参数,取决于使用的嵌入模型及其令牌容量。例如,基于BERT的句子转换器最多需要512个令牌,而OpenAI ada-002能处理更长的序列。分块策略的选择对于确保搜索结果的准确性和相关性至关重要。

a. 分块因素

确定最佳分块策略时,需要考虑以下因素:

  • 被索引内容的性质

  • 使用的嵌入模型及其最佳块大小

  • 用户查询的长度和复杂性

  • 检索结果在特定应用程序中的使用方式

b. 分块方法

分块方法有多种,包括固定大小分块、内容感知分块等。每种方法都有其适用场景。例如,固定大小分块简单直接,而内容感知分块则利用文本的性质进行更复杂的分块。

b.1 固定大小分块

固定大小分块是最常见的方法,通过决定块中的标记数量和是否保留重叠来实现。固定大小分块计算成本低,易于使用。

b.2 内容感知分块

内容感知分块利用文本的性质,例如句子分块、递归分块和专门分块等。这些方法可以更好地保留文本的上下文和结构。

c. 分块优化

如果常见分块方法不适用,可以通过预处理数据、选择块大小范围和评估每个块大小的性能来优化分块。

3.1.2 矢量化

选择一个适合搜索优化的模型进行块的嵌入,例如bge-large或E5嵌入系列。可以参考MTEB排行榜获取最新模型效果。

3.2 搜索索引

3.2.1 向量存储索引

构建向量索引以实现高效的语义搜索。

索引检索流程概述

索引检索是信息检索系统中的一个关键环节,它涉及到将查询内容与存储的索引进行匹配,以快速找到相关信息。以下是索引检索的基本流程:

1. 索引的创建索引的创建基于内容的矢量化表示。在这一步骤中,文本或其他形式的内容被转换成数学上的向量,以便于进行后续的索引和检索。

2. 索引存储创建的索引需要被存储,以便进行快速检索。可以使用平面索引或更高级的近似最近邻算法,如Faiss、nmslib、annoy等,这些算法通过聚类、树或HNSW等技术提高检索效率。

3. 索引检索技术

  • 近似最近邻算法:适用于大规模数据集,通过牺牲一定的精确度来提高检索速度。
  • 托管解决方案:如OpenSearch或ElasticSearch,提供完整的搜索和索引服务。
  • 矢量数据库:例如Pinecone、Weaviate或Chroma,专门处理向量数据的存储和检索。

4. 元数据的使用在索引中,除了向量数据外,还可以存储元数据,如日期或来源等信息。通过元数据过滤器,可以进一步细化搜索结果。

5. 索引类型

  • 平面索引:简单的索引实现,适用于小规模数据集。
  • 层次索引:结构化的索引方法,可以提高检索效率。
  • LlamaIndex:支持大规模向量存储,同时提供其他索引实现,如列表索引、树索引和关键字表索引。

6. 索引检索的优化根据索引选择、数据特性和搜索需求,可以对索引进行优化,以提高检索的速度和准确性。

7. 结论索引检索是信息检索系统中的重要组成部分,通过合理的索引设计和优化,可以显著提升检索效率和用户体验。


在处理大量文档检索任务时,有效的检索策略至关重要。以下是几种提高检索效率和质量的方法:

层次索引流程图为了在大型数据库中进行有效检索,可以创建两个索引:摘要索引和文档块索引。检索过程分为两步:1. 通过摘要索引过滤出相关文档。2. 在相关文档组内检索,获取具体信息。

3.2.3

  • 假设问题和HyDE这种方法要求大型语言模型(LLM)为每个文档块生成问题,并将这些问题转化为问题向量。在查询时,使用问题向量进行索引检索,然后根据检索结果将原始数据文本块作为上下文发送给LLM,以获得更准确的答案。 此外,HyDE是一种反向逻辑方法,它要求LLM根据查询生成假设的响应,然后利用响应向量和查询向量来提高检索的准确性。

3.2.4

  • 上下文丰富为了提高搜索质量,可以采用以下两种策略:
  • 检索较小的文档块,并为LLM提供足够的上下文进行推理。
  • 通过扩展检索到的句子周围的上下文,或者将文档拆分为包含较小子块的较大父块。

a

  • 句子窗口检索在这种方法中,文档中的每个句子都被单独嵌入,以实现高精度的上下文余弦距离搜索。检索到最相关的句子后,通过扩展前后k个句子的上下文窗口,然后将扩展的上下文发送给LLM,以便更好地进行推理。

注意事项

  • 检索过程中,确保问题与文档块之间的语义相似性,以提高搜索质量。
  • 在使用HyDE方法时,注意生成的假设响应与查询的匹配度,以确保检索结果的准确性。 以上方法有助于在处理大量文档时,快速准确地找到所需信息,并提供参考来源。

    在自然语言处理领域,句子窗口检索是一种高效的信息检索方法。它通过在索引中搜索特定的句子嵌入,然后扩展搜索范围以包含整个段落,从而为大型语言模型(LLM)提供更丰富的上下文信息。以下是对这一概念的详细阐述:
  1. 句子窗口检索:这是一种信息检索技术,它首先在索引中定位关键句子的嵌入,然后扩展搜索窗口以包含整个段落。这种方法可以为语言模型提供更全面的上下文,从而提高推理的准确性。

  2. 自动合并检索器(也称为父文档检索器):这种方法与句子窗口检索相似,但有所不同。它将文档分割成更小的子块,同时引用一个更大的父块。这样,检索器能够在细粒度上搜索信息,然后扩展上下文窗口,并将这个扩展后的上下文提供给LLM进行更深入的推理。 通过上述方法,可以有效地提高信息检索的精度和深度,为语言模型提供更加丰富的上下文信息,从而增强其对复杂查询的处理能力。

    在文档检索领域,一种有效的检索策略是父子块检索。这种策略首先将文档分解成层次化的块结构,然后通过索引最小的叶块来实现快速检索。当进行搜索时,系统会检索出k个叶块,如果发现有n个叶块引用同一个父块,系统将这些叶块通过该父块进行合并,然后将其作为整体发送到LLM(Large Language Model)进行答案生成。这个过程类似于自动合并检索到的块,形成更大的父块,因此得名’父子块检索’。值得注意的是,搜索操作仅在子节点索引内执行。 在检索策略的进一步发展中,出现了融合检索或混合搜索的概念。这种方法结合了基于关键字的传统搜索算法(如tf-id或BM25)和现代的语义或向量搜索技术。关键在于如何将这两种不同来源的检索结果,根据它们的相似度分数正确地结合起来。通常,这一问题可以通过Reciprocal Rank Fusion (RRF)算法来解决,该算法会对检索结果进行重新排序,以得到最终的输出结果。 例如,当我们在进行文档检索时,如果检索到的前k个叶块中有超过n个块指向同一个父块,我们可以通过以下步骤来处理:1. 检索k个叶块。2. 检查这些叶块是否引用同一个父块。3. 如果是,将这些叶块合并为一个父块。4. 将合并后的父块发送到LLM进行答案生成。 此外,融合检索或混合搜索的策略可以这样实现:1. 利用基于关键字的搜索算法和语义或向量搜索技术,分别进行检索。2. 检索结果根据各自的相似度分数进行评估。3. 使用RRF算法对两种结果进行综合排序。4. 输出最终的检索结果。

融合检索技术概述

在LangChain框架中,融合检索技术是通过Ensemble Retriever类实现的,它结合了多种检索器,例如基于Faiss的向量索引和基于BM25的检索器。这些检索器通过RRF算法进行重新排序,以提高检索结果的相关性。

3.1 融合检索的优势混合或融合搜索通过结合两种互补的搜索算法,同时考虑了查询与存储文档之间的语义相似性和关键字匹配,从而提供更优的检索结果。

3.2 检索结果的优化在获取初步检索结果后,需要进一步通过过滤、重新排序或转换来优化它们。在LlamaIndex中,提供了多种后处理器,这些后处理器可以基于相似度得分、关键词、元数据等条件进行过滤,或者使用LLM等模型对结果进行重新排名。

3.3 后处理技术

  • 相似度过滤:根据相似度得分过滤结果。
  • 关键词过滤:根据文档中的关键词进行过滤。
  • 元数据过滤:基于文档的元数据,如日期新近度等,进行过滤。
  • 模型重新排名:使用句子转换器、交叉编码器、Cohere重排序端点等模型对结果进行重新排序。

3.4 RAG技术随着技术的发展,现在可以学习更复杂的RAG(Retrieval-Augmented Generation)技术。RAG技术涉及使用LLM作为推理引擎,进行查询转换和路由,这些技术都代表了Agent行为,涉及复杂的逻辑推理。

3.5 查询转换查询转换是使用LLM来修改用户输入的技术,目的是提高检索质量。有多种方法可以实现查询转换,包括但不限于:

  • 使用LLM对用户查询进行语义增强。
  • 根据上下文信息调整查询内容。

结语融合检索和后处理技术是提高检索系统性能的重要手段。通过合理利用这些技术,可以显著提升检索结果的相关性和准确性。


在处理复杂查询时,我们可以采用多种策略来提升检索效率和准确性。以下是一些关键技术及其应用的概述:

查询分解对于复杂的查询,如比较两个框架在GitHub上的受欢迎程度,我们可以通过分解为更简单的子查询来处理。例如:

  • 子查询1:LangChain在GitHub上的星数。
  • 子查询2:LlamaIndex在GitHub上的星数。这些子查询可以并行执行,之后合并结果以合成最终答案。

后退提示通过生成更通用的查询,我们可以检索到更广泛的上下文信息,这有助于回答原始查询。同时,原始查询也会被执行,最终将两个上下文信息一并提供给LLM以生成答案。

查询重写利用LLM重新制定查询,以期获得更好的检索结果。LangChain和LlamaIndex都提供了这种功能,其中LlamaIndex在这方面表现得更为强大。

参考文献引用在生成答案时,如果使用了多个来源,重要的是能够准确地追溯到这些来源。这可以通过以下方法实现:

  • 将引用任务加入提示中,要求LLM在生成答案时提及所用来源的ID。
  • 将生成的响应与索引中的原始文本进行模糊匹配,LlamaIndex为此提供了一种高效的解决方案。

聊天引擎构建一个优秀的RAG系统,需要考虑对话上下文,类似于传统聊天机器人。这涉及到对先前对话的考虑,以及用户命令的处理。上下文压缩技术在此过程中扮演重要角色:

  • ContextChatEngine:检索与用户查询相关的上下文,并结合聊天历史记录,一并发送给LLM。
  • CondensePlusContextMode:在每次交互中,将聊天历史和最后一条消息压缩成新查询,检索上下文后与原始用户消息一起提供给LLM。 这些技术的应用,旨在提高信息检索的准确性和效率,同时确保对话的连贯性和逻辑性。

3.6 查询路由

查询路由是大型语言模型(LLM)支持的决策机制,它根据用户输入的查询决定下一步操作。这可能包括总结信息、在特定数据索引中搜索,或者尝试多种不同的路由策略,并将结果综合成单一答案。 在查询路由中,需要选择索引或数据存储,以确定将用户查询发送到何处。例如,如果存在多个数据源,如向量存储、图形数据库或关系数据库,或者存在索引层次结构,如文档摘要索引和文档块向量索引,查询路由器将决定使用哪一个。 查询路由的选择通过LLM调用执行,返回预定义格式的结果,用于将查询定向到相应的索引。或者,如果采用代理行为,路由可能指向子代理或其他代理,如多文档代理方案所示。LlamaIndex和LangChain都支持查询路由。

3.7 RAG中的Agent

自第一个LLM API发布以来,Agent的概念就已经存在。Agent是一个能够进行推理的LLM,它配备了一组工具和任务。这些工具可能包括确定性函数,如代码函数、外部API或其他Agent。LangChain的名称来源于这种LLM链的思想。 在RAG系统中,基于Agent的多文档检索案例描述了底层工作。OpenAI Assistants已经实现了许多LLM所需的工具,包括聊天记录、知识存储、文档上传接口,以及最重要的函数调用API。 在LlamaIndex中,OpenAIAgent类结合了高级逻辑与ChatEngineQueryEngine类,提供了基于知识和上下文感知的聊天能力,以及在对话轮中调用多个OpenAI函数的能力,这实现了智能Agent行为。 多文档Agent方案涉及在每个文档上初始化一个代理,能够进行文档摘要和问答机制,以及一个顶层Agent,负责将查询路由到文档代理,并用于最终答案的合成。 每个文档Agent配备了两个工具:向量存储索引和摘要索引,并根据路由查询决定使用哪一个。而顶层Agent则将所有文档Agent视为工具。 这种方案展示了一种先进的RAG架构,其中包含大量路由决策,由每个相关Agent做出。这种方法的优势在于能够比较不同文档中描述的不同解决方案或实体,以及它们的摘要和单文档的问答机制,从而覆盖了与文档集合聊天的常见用例。

多文档Agent的方案,涉及查询路由和代理行为模式

这种复杂方案的缺点可以从图片中猜到,由于我们Agent内部使用LLM进行了多次来回迭代,它有点慢。以防万一,LLM调用始终是RAG管道中最长的操作,搜索在设计上针对速度进行了优化。因此,对于大型多文档存储,我们建议对该方案进行一些简化,使其具有可扩展性。

3.8 - 响应合成器

这是任何RAG管道的最后一步-根据我们仔细检索的所有上下文和初始用户查询生成答案。最简单的方法是将所有获取的上下文(高于某个相关阈值)与查询一起连接并立即提供给LLM。

但是,与往常一样,还有其他更复杂的选项,涉及多个LLM调用,以细化检索到的上下文并生成更好的答案。

响应合成的主要方法包括:

1.通过将检索到的上下文逐块发送到LLM来迭代地细化答案

2.总结检索到的上下文以适应提示

3.根据不同的上下文块生成多个答案,然后将它们连接或总结。

更多详细信息,可以参考响应合成器模块文档:网页链接

4 - RAG系统模型微调

RAG系统涉及多个模型,可以考虑对它们进行微调:

**编码器模型(Transformer):**负责内容向量编码,从而负责上下文检索质量

**大语言模型(LLM):**负责最好地使用所提供的上下文来回答用户查询

**排序器(Ranker):**负责对检索结果排序优选

如今的一大优势是可以使用GPT-4等高端LLM来生成高质量的合成数据集。但应该始终意识到,采用由专业研究团队在仔细收集、清理和验证的大型数据集上训练的开源模型,并使用小型合成数据集进行快速调整可能会缩小模型的总体能力。

4.1 - 编码器微调

我们在LlamaIndex设置中测试了通过微调bge-large-en-v1.5(撰写本文时MTEB排行榜前4名)带来的性能提升,结果显示检索质量提高了2%。没什么戏剧性的,但很高兴有这个选项,特别是如果你有构建RAG的领域数据集。

4.2 - 排序器微调

另一个好的选项是如果您不完全信任基本编码器,则可以对检索到的结果进行重新排名。

它的工作方式如下:将查询和检索到的前k个文本块传递给交叉编码器,并用SEP令牌分隔,并将其微调为输出1表示相关块,输出0表示不相关。

结果表明,通过交叉编码器微调,成对分数(pairwise)提高了4%,这里可以找到这种调整过程的一个很好的例子:网页链接

4.3 - LLM微调

最近OpenAI开始提供LLM微调API,LlamaIndex有一个关于在RAG设置中微调GPT-3.5-turbo的教程,以“提炼”一些GPT-4知识。这里的想法是拿一份文档,用GPT-3.5-turbo生成一些问题,然后使用GPT-4根据文档内容生成这些问题的答案(构建一个由GPT4驱动的RAG管道),然后进行细化-在该问答对数据集上调整GPT-3.5-turbo。用于RAG管道评估的ragas框架显示忠实度指标增加了5%,这意味着经过微调的GPT3.5-turbo模型比原始模型更好地利用提供的上下文来生成答案。

最近的论文RA-DIT:MetaAIResearch的检索增强双指令调整中演示了一种更复杂的方法,提出了一种同时调整LLM和Retriever的技术,优化关于查询、上下文和答案的三元组。具体实现方式请参考本指南: 网页链接

该技术用于通过微调API和Llama2开源模型(在原始论文中)对OpenAI LLM进行微调,从而使知识密集型任务指标增加约5%(与带有RAG的Llama265B相比)常识推理任务也增加了几个百分点。

5 - RAG系统评估

RAG系统性能评估有多个框架,它们共享一些独立指标的想法,例如总体答案相关性、答案基础性、忠实性和检索到的上下文相关性。上一节提到的Ragas使用忠实度和答案相关性作为生成的答案质量指标,并使用经典的上下文精确度和召回率作为RAG方案的检索部分。

在AndrewNG、LlamaIndex和评估框架Truelens最近发布的优秀短期课程《构建和评估高级RAG》中,他们提出了RAG三元组——检索到的与查询的上下文相关性、接地性(LLM答案在多大程度上受所提供的上下文支持)并回答与查询相关的问题。

关键和最可控的指标是检索到的上下文相关性。上述高级RAG技术的第3.1-3.7部分加上编码器和Ranker微调部分旨在改进该指标,而第3.8部分和LLM微调则注重回答相关性(relevance)和事实性(groundedness)。

可以在此处找到一个非常简单的检索器评估管道的好示例,并将其应用于编码器微调部分:网页链接

OpenAI演示了一种更先进的方法,不仅考虑命中率,还考虑平均倒数排名(一种常见的搜索引擎指标)以及生成的答案指标(例如忠实度和相关性)。

LangChain有一个非常先进的评估框架LangSmith,可以在其中实现自定义评估器,并且它监视RAG管道内运行的跟踪,以使您的系统更加透明。

如果您使用LlamaIndex进行构建,可以使用rag_evaluator llama包,它提供了一个使用公共数据集评估管道的快速工具。

6 - 总结展望

我们试图系统化梳理RAG的核心方法,并举例说明其中的一些方法,希望这可能会激发一些新颖的思路,以便在RAG系统中进行探索。

还有很多其他事情需要考虑,比如基于网络搜索的RAG(由LlamaIndex、webLangChain等提供的RAG)、深入研究代理架构以及关于LLM长期记忆的一些想法。

除了答案相关性(relevance) 和忠实性(faithfulness)之外,RAG系统的主要生产挑战是速度,特别基于更灵活的Agent方案的RAG系统。ChatGPT和大多数其他助手使用的这种流媒体回复仅仅是一种缩短感知答案生成时间的方法。这就是为什么我们看到较小的LLM也有一个非常光明的未来,最近发布的Mixtral和Phi-2正在引领我们朝这个方向发展。