RAG技术分享

在当今信息爆炸的时代,有效地获取和生成信息变得越来越重要。检索增强生成(Retrieval Augmented Generation,简称RAG)是一种创新的技术,旨在通过集成检索和生成模型,提供更强大的信息处理和创造能力。

检索增强生成为大型语言模型(LLMs)提供了从某些数据源检索到的信息,以此作为生成答案的基础。简而言之,RAG是搜索+LLM提示的结合,即在有搜索算法找到的信息作为上下文的情况下,让模型回答提出的查询。查询和检索到的上下文都被注入到发送给LLM的提示中。

目前,RAG是基于LLM系统中最受欢迎的架构。许多产品几乎完全基于RAG构建,包括将网络搜索引擎与LLMs相结合的问答服务,以及数百种与数据聊天的应用程序。

有两个最突出的开源库用于基于LLM的管道和应用程序——LangChain和LlamaIndex,它们分别于2022年10月和11月成立,并在2023年获得了大量的应用。

RAG可以通过将检索模型和生成模型结合在一起,从而提高了生成内容的相关性和质量。

通俗一点讲就是大模型LLM如何很好的与外部知识源结合在一起, 使其生成的内容质量更高,缓解大模型LLM生成内容「幻觉」的问题。

检索模型

检索模型旨在从一组给定的文档或知识库中检索相关信息。

检索模型的工作就像是在一个巨大的图书馆中寻找信息。设想你有成千上万本书籍和文章,当你提出一个问题时,检索模型就像一个聪明的图书管理员,能迅速理解你的问题并找到与之最相关的最佳信息。

检索的核心分为如下两部分:

  • 索引:嵌入(Embeddings),将知识库转换为可搜索/查询的内容。
  • 查询:从搜索内容中提取最相关的、最佳知识片段。

生成模型

生成模型指的是大型语言模型LLM,例如chatGPT。生成模型是实现高质量、高相关性内容生成的关键。它不仅利用自身的强大语言生成能力,还结合检索模型提供的确切信息,以生成更准确、更丰富的内容。

简单来说,检索模型擅长”找”信息,生成模型擅长”创造”内容。

执行流程

执行流程图

索引生成部分

文档处理:将私有知识库中的文档转换成可以处理的文本块。

嵌入模型:使用嵌入模型(如BERT、GPT等)将文本块转换成向量。

索引:创建文本块向量的索引,以便能够快速检索。

数据库:嵌入向量被存储在一个向量数据库中,通常使用近似最近邻(ANN)搜索来优化检索速度。

用户问答部分

查询:用户在聊天界面输入查询。

查询嵌入:查询也被转换成向量,以便与文档的嵌入向量进行比较。

检索向量:查询的向量在向量索引中被用来找出最相近的文本块向量,最相近的文本块向量代表了与用户查询最相关的知识片段。

提示和回答:生成模型(LLM)接收到用户的查询和检索到的知识片段,然后生成回答。这个回答既包含了用户查询的上下文,也融合了从知识库中检索到的信息。

RAG的作用

保持知识更新

将大型语言模型(LLM)如ChatGPT配备能够随时查阅最新资讯的能力,就好比为它安装了一对能够观察现实世界的“眼睛”。这种能力的增加不仅极大地扩展了模型的知识范围,还提高了其与现实世界同步的能力,让它能够更有效地参与到关于时事的对话中。

提供专业知识

如果你的问题涉及特定的专业领域,RAG就像一个熟练的图书管理员,它不仅掌握着大量的专业书籍,还能够迅速而准确地从这些书籍中找到与你的问题最相关的答案。这样的能力使得RAG在处理复杂和专业性问题时表现出色。

私有知识的安全

随着人工智能的发展,数据安全成为了企业关注的重点。对于企业而言,将长期累积的独有的知识库、敏感的经营数据、合同文件等机密信息上传到互联网上的大型模型可能会带来安全风险。在这种背景下,RAG技术提供了一种有效的解决方案。

增加可信度

RAG赋予机器人在回答问题时提供信息来源的能力,这是一个重要的特性。当你向机器人提问时,它不仅能给出答案,还能明确告诉你这些答案是基于哪些资料或数据得出的。这种透明度极大地增加了机器人提供的信息的可信度,并帮助用户区分信息的真实性和准确性。

减少大模型LLM的“幻觉”

大型语言模型(如GPT系列)在生成文本时偶尔出现的“幻觉”(hallucination)现象,是由于模型在处理特定查询时,可能会生成不准确、不相关或虚构的信息。这种现象往往发生在模型对于回答问题所需的知识不了解或不熟悉的情况下。RAG的出现,通过利用外部知识源,可以有效地弥补这一缺陷。

面临的挑战

RAG在实施过程中确实面临着多项挑战,其中包括嵌入质量、性能优化和上下文理解。这些难点不仅关系到RAG系统的效率,还直接影响到最终生成文本的准确性和可用性。

提升嵌入的质量

将外部知识源转换为向量时,需要保证嵌入(embedding)的高质量至关重要,这一过程对于提高查询与知识库信息匹配的准确性至关重要。技术上,这要求嵌入能够捕获和保留文本的深层语义特征,包括上下文关系和词汇间的微妙联系。

此外,为了避免训练过程中的数据偏见,需要用到平衡和多样化的数据集。只有这样,通过嵌入生成的向量才能真正代表原始文本的意图和内容,从而在查询时提供更准确、更相关的结果。

查找精确知识的挑战

从外部知识源中准确地查找与当前问题最匹配的知识是一个复杂的挑战。这要求大模型LLM能够深入理解用户查询的真实意图,同时利用高效的检索算法在庞大的数据集中快速定位相关信息。此外,确保检索结果的相关性和质量,以及处理大量数据的能力,也是这一挑战的关键部分。同时,系统还需要适应模糊或复杂的查询,并保持知识库的时效性和准确性。

上下文内容的理解

上下文理解是一个核心挑战,要求生成模型不仅深入理解检索到的上下文信息,包括其隐含含义和语境;
在理解检索到的内容后,挑战在于如何将这些信息与原始查询请求以及模型已有的知识库相结合。这要求模型能够在回答生成过程中,不仅准确地引用检索信息,还要保证信息的连贯性和逻辑性。

上下文理解的挑战要求RAG在处理和生成回答时,能够展现出高度的理解能力和灵活的信息整合能力。这对于提升回答的质量和用户满意度至关重要。

应用场景

RAG在未来的应用前景非常广阔,几乎覆盖了所有企业和行业。这是因为RAG能够结合大型语言模型的强大处理能力和企业或行业自身独有的知识体系,从而制定出行业或企业专属的AI解决方案。无论是金融、医疗、法律、教育,还是零售、制造、娱乐等行业,企业都可以利用RAG技术构建专门的小型模型,以满足特定的业务需求。

医疗行业

在医疗领域,RAG可以被用作临床决策支持工具。通过结合医学数据库和研究论文,RAG能够帮助医生快速获得关于疾病诊断、治疗方案和药物信息的最新研究。例如,对于罕见病的诊断,RAG可以通过检索最新的医学文献和病例报告,提供可能的诊断建议和治疗方法。

法律行业

在法律行业,RAG可以辅助律师进行案例研究和提供法律咨询。通过访问法律数据库和历史案例,RAG能够帮助律师找到相关的法律先例和法规,从而提高案件分析的效率和准确性。这对于处理复杂的法律问题,如知识产权纠纷或国际法案,尤为有用。

教育领域

在教育领域,RAG可以作为学习资源和研究辅助工具。教师和学生可以利用RAG快速访问大量的教育资料、学术论文和案例研究,从而丰富教学内容和加深学习理解。例如,学生在准备论文时,可以用RAG来查找相关的研究工作和理论框架。

基础的RAG技术

本文中,我们用一组文本文档的语料库来代表RAG的起点——我们跳过了在此之前的步骤,留给那些开源数据加载器去处理,这些加载器可以连接到任何可想象的来源,从YouTube到Notion。

2.png

简单RAG案例大致如下:

  • 将文本分割成块,然后使用基于Transformer decoder的模型将这些块嵌入到向量中,将所有这些向量放入一个索引中,最后为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提供的提示工程指南[2]。

尽管OpenAI作为LLM领域领导公司,但还有一些替代品,如Anthropic的Claude,最近流行的小型但非常强大的模型,如Mistral的Mixtral,Microsoft的Phi-2,以及许多开源选项,如Llama2、OpenLLaMA、Falcon等,所以您可以为您的RAG管道选择一个“大脑”。

高级RAG技术

现在我们将深入了解高级RAG技术的概述。下面是一个展示核心步骤的示意图。为了保持图表的可读性,省略了一些逻辑循环和复杂的多步骤代理行为。

3.png

图中的绿色元素是接下来讨论的核心RAG技术,蓝色元素代表文本。并非所有高级RAG理念都能轻易在单一图表上可视化,例如,省略了各种扩大上下文的方法——我们将在后面深入探讨这些。

切分和向量化

首先,我们想创建一个向量索引,代表我们文档的内容,然后在运行时搜索这些向量与查询向量之间最小的余弦距离,对应于最接近的语义含义。

切分: Transformer模型有固定的输入序列长度,即使输入上下文窗口很大,一个句子或几个句子的向量也比几页文本的平均向量更好地代表它们的语义含义(也取决于模型,但通常如此),所以要切分你的数据——将初始文档切分为某个大小的块,不会丢失它们的含义(将文本切分为句子或段落,而不是将单个句子切成两部分)。有各种文本分割器实现能够完成这项任务。

块的大小是一个需要考虑的参数——它取决于你使用的嵌入模型及其在令牌上的容量,标准的Transformer编码器模型如基于BERT的句子转换器最多接受512个令牌,OpenAI ada-002能够处理更长的序列,如8191个令牌,但这里的折中是为LLM提供足够的上下文进行推理与执行搜索的足够具体的文本嵌入。最近的一项研究[3]说明了块大小选择的考虑因素。在LlamaIndex中,这是通过NodeParser类来覆盖的,它提供了一些高级选项,如定义自己的文本分割器、元数据、节点/块关系等。

向量化: 下一步是选择一个模型来嵌入切割后的块——有很多选择,例如像bge-large或E5嵌入系列这样的搜索优化模型——只需查看MTEB排行榜上[4]的最新更新。

要了解切分和向量化步骤的端到端实现,请查看LlamaIndex中的一个完整的示例。

搜索索引

向量存储索引: RAG管道的关键部分是搜索索引,它存储了我们在上一步中获得的向量化内容。最简单的实现使用平面索引——在查询向量和所有块向量之间进行暴力距离计算。

4.png

一个为10000+元素规模上的高效检索优化的索引是一个向量索引,如faiss、nmslib或annoy,使用某种近似最近邻实现,如聚类、树或HNSW算法。

还有一些托管解决方案,如OpenSearch或ElasticSearch,以及向量数据库,它们在后台处理第1步中描述的数据摄取管道,如Pinecone、Weaviate或Chroma。根据选择的索引、数据和搜索需求,可以将元数据与向量一起存储,然后使用元数据过滤器来搜索某些日期或来源内的信息。

LlamaIndex支持许多向量存储索引,但还支持其他更简单的索引实现,如列表索引、树索引和关键词表索引——我们将在融合检索部分讨论后者。

分层索引: 如果您需要从许多文档中检索信息,您需要能够有效地在其中搜索,找到相关信息,并将其综合为带有来源引用的单一答案。在大型数据库中做到这一点的有效方法是创建两个索引——一个由摘要组成,另一个由文档块组成,并分两步进行搜索,首先通过摘要筛选出相关文档,然后仅在这个相关组内搜索。

5.png

假设性问题和HyDE: 另一种方法是让LLM为每个块生成一个问题,并将这些问题嵌入向量中,在运行时针对这个问题向量索引进行查询搜索(在我们的索引中用问题向量替换块向量),然后在检索后路由到原始文本块,并将它们作为上下文发送给LLM以获得答案。这种方法通过查询与假设性问题之间更高的语义相似性,提高了搜索质量。

还有一种逆向逻辑方法称为HyDE——让LLM给定查询生成一个假设性回应,然后使用其向量和查询向量来提高搜索质量。

上下文丰富化: 上下文丰富化是检索更小的块以提高搜索质量,但添加周围上下文让LLM进行推理。通常有两种做法——通过在检索到的较小块周围的句子扩展上下文,或者将文档递归地分割成包含较小子块的多个较大的父块。

  • 句子窗口检索: 在这个方案中,文档中的每个句子都分别嵌入,这提供了极高的查询与上下文余弦距离搜索的准确性。为了在找到最相关的单个句子后更好地推理所发现的上下文,我们通过在检索到的句子前后扩展k个句子的上下文窗口,然后将这个扩展的上下文发送给LLM。
6.png
  • 自动合并检索器(又称父文档检索器): 这里的想法与句子窗口检索非常相似——搜索更精细的信息片段,然后在将这些上下文提供给LLM进行推理之前扩展上下文窗口。文档被分割成较小的子块,这些子块引用较大的父块。
7.png

在这种方法中,首先在更细粒度的子块上进行搜索,找到与查询最相关的块。然后,系统会自动将这些子块与它们所属的更大的父块结合起来。这样做的目的是在回答查询时为LLM提供更丰富的上下文。例如,如果一个子块是一段或一小节,父块可能是整个章节或文档的一大部分。这种方法既保留了检索精度(因为是在更小的块上搜索),同时也通过提供更广泛的上下文来增强LLM的推理能力。

在检索过程中首先获取较小的块,然后如果在检索到的前k个块中有超过n个块与同一个父节点(较大的块)相关联,我们就用这个父节点替换提供给LLM的上下文——这就像自动将几个检索到的块合并成一个较大的父块,因此得名。需要注意的是——搜索仅在子节点索引中进行。想要更深入地了解,请查看LlamaIndex关于递归检索器+节点引用的教程[5]。

融合检索或混合搜索: 这是一个相对较老的想法,即从两个世界中各取所长——基于关键字的传统搜索(稀疏检索算法,如tf-idf或搜索行业标准BM25)和现代语义或向量搜索,并将它们结合在一个检索结果中。这里唯一的技巧是正确组合具有不同相似性得分的检索结果——这个问题通常通过使用倒数排名融合算法来解决,重新排列检索结果以获得最终输出。

8.png

在LangChain[6]中,这是通过Ensemble Retriever类实现的,它结合了你定义的一系列检索器,例如faiss向量索引和基于BM25的检索器,并使用RRF进行重排。在LlamaIndex[7]中这种做法也非常类似。

混合或融合搜索通常会提供更好的检索结果,因为它结合了两种互补的搜索算法,同时考虑了查询和存储文档之间的语义相似性和关键词匹配。

重排和过滤

使用上述任何算法得到检索结果后,现在是时候通过过滤、重排或一些转换来精炼这些结果了。在LlamaIndex中,有多种可用的后处理器,可以根据相似性分数、关键词、元数据过滤结果,或者使用其他模型进行重排,比如LLM、句子转换器交叉编码器、Cohere重排端点,或者基于日期的最新性等元数据——基本上,你能想到的都可以。

重排和过滤是在将检索到的上下文提供给LLM以获取最终答案之前的最后一步。现在是时候进入更复杂的RAG技术,如查询转换和路由,这两者都涉及到LLM,因此代表了主动性行为——在我们的RAG流程中涉及到一些复杂的逻辑,包括LLM的推理。

查询转换

查询转换是一系列技术,利用LLM作为推理引擎来修改用户输入,以提高检索质量。

9.png

有几种不同的方式可以做到这一点。如果查询很复杂,LLM可以将其分解成几个子查询。例如,如果你问:

  • “在Github上,Langchain和LlamaIndex哪个框架的星星更多?” 由于我们不太可能在语料库中找到直接的比较,所以将这个问题分解成两个预设简单和具体信息检索的子查询是有意义的:
  • “Langchain在Github上有多少星星?”
  • “LlamaIndex在Github上有多少星星?” 这两个查询将并行执行,然后将检索到的上下文合并成一个提示,供LLM合成最初查询的最终答案。Langchain和LlamaIndex都实现了这一功能——在Langchain中作为多查询检索器,在LlamaIndex中作为子问题查询引擎。

回溯提示使用LLM生成更一般的查询,我们为此检索获得更一般或高层次的上下文,有助于支撑我们对原始查询的回答。也会对原始查询进行检索,两种上下文都在最终生成答案的步骤中输入给LLM。这是LangChain的实现方法。

查询重写使用LLM重构初始查询以改善检索。LangChain和LlamaIndex都有实现,虽然有些不同,但我认为在这里LlamaIndex的解决方案更为强大。

此外, 还有一个概念是参考引用。这一部分不作为单独的一章来介绍,因为它更像是一种工具而不是检索改进技术,尽管它非常重要。如果我们为了回答一个问题而使用了多个来源,可能是因为初始查询的复杂性(我们需要执行多个子查询,然后将检索到的上下文合并成一个答案),或者是因为我们在不同的文档中找到了与单个查询相关的上下文,那么就会出现一个问题:我们能否准确地回溯引用我们的来源。

有几种方法可以做到这一点:

  • 将引用任务插入我们的提示中,并要求LLM提及使用的来源的ID。
  • 将生成的响应部分与我们索引中的原始文本块匹配——llamaindex为这种情况提供了一个基于模糊匹配的高效解决方案。如果你还没有听说过模糊匹配,这是一种非常强大的字符串匹配技术。

聊天引擎

在构建一个能够针对单个查询多次运行的优秀RAG系统中,下一个重要的环节是聊天逻辑,这与前LLM时代的经典聊天机器人一样,需要考虑对话上下文。这对于支持后续问题、指代消解或与先前对话上下文相关的任意用户命令是必要的。这可以通过查询压缩技术来解决,同时考虑聊天上下文和用户查询。

如同往常,有几种处理上述上下文压缩的方法 — 一种流行且相对简单的方法是ContextChatEngine,它首先检索与用户查询相关的上下文,然后将其连同聊天历史记录从内存缓冲区发送给LLM,以便LLM在生成下一个回答时能够了解之前的上下文。

更复杂的一个例子是CondensePlusContextMode — 在这种模式中,每次交互时都会将聊天历史和最后一条消息压缩成一个新的查询,然后这个查询会进入索引,检索到的上下文连同原始用户消息一起传递给LLM,以生成答案。

10.png

值得注意的是,LlamaIndex还支持基于OpenAI代理的聊天引擎,提供更灵活的聊天模式,Langchain也支持OpenAI功能性API。还有其他类型的聊天引擎,如ReAct Agent,但我们在后面再讨论代理本身。

查询路由

查询路由是一个以LLM为驱动的决策步骤,决定针对用户查询接下来要做什么——通常的选项包括概括总结、针对某些数据索引执行搜索,或尝试多种不同的路径,然后将它们的输出合成一个答案。

查询路由器还用于选择索引,或更广泛地说,数据存储位置,以发送用户查询——无论你拥有多个数据来源,例如经典的向量存储、图形数据库或关系型数据库,还是拥有一个索引层次结构——对于多文档存储,一个相当典型的情况可能是一个概要索引和另一个文档块向量的索引。

定义查询路由器包括设置它可以做出的选择。路由选项的选择是通过LLM调用进行的,返回预定义格式的结果,用于将查询路由到给定的索引,或者,如果我们谈论主动性行为,路由到子链或甚至其他代理,如下面的多文档代理方案所示。

LlamaIndex和LangChain都支持查询路由器。

RAG中的代理

Langchain和LlamaIndex都支持的代理(Agents),自从第一个LLM API发布以来就已经存在——这个想法是为一个能够进行推理的LLM提供一套工具和一个要完成的任务。这些工具可能包括一些确定性函数,如任何代码函数、外部API甚至其他代理——LLM链式调用的这个想法是LangChain名字的由来。

代理本身是一个巨大的领域,要在RAG概览中深入探讨是不可能的,所以我将继续讨论基于代理的多文档检索案例,并在OpenAI助手这个相对较新的领域短暂停留,因为它是最近OpenAI开发者大会上作为GPTs介绍的,并在下面描述的RAG系统的底层工作。

OpenAI助手基本上实现了围绕LLM所需的许多工具,我们之前在开源中拥有这些工具——聊天历史记录、知识存储、文档上传界面,以及或许最重要的,函数调用API。后者提供了将自然语言转换为对外部工具或数据库查询的API调用的能力。

在LlamaIndex中,OpenAIAgent类将这种高级逻辑与ChatEngine和QueryEngine类结合起来,提供基于知识和上下文的聊天,以及在一次对话中调用多个OpenAI函数的能力,这确实带来了智能的代理行为。

让我们来看一下多文档代理方案——一个相当复杂的设置,涉及对每个文档初始化一个代理(OpenAIAgent),能够进行文档概要和经典的问答机制,并有一个顶级代理,负责将查询路由到文档代理,并进行最终答案的合成。

每个文档代理都有两个工具——一个向量存储索引和一个概要索引,并根据路由查询决定使用哪一个。而对于顶级代理来说,所有文档代理都分别是工具。

这个方案展示了一个高级的RAG架构,其中每个参与的代理都做出了许多路由决策。这种方法的好处是能够比较不同的解决方案或实体,这些解决方案或实体描述在不同的文档及其概要中,同时包括经典的单文档概要和问答机制——这基本上涵盖了最常见的与文档集合聊天的用例。这种复杂方案的缺点可以从图中猜测——由于涉及代理中LLM的多次来回迭代,它有些慢。顺便说一下,LLM调用总是RAG流程中最长的操作——搜索本身就是为速度优化的设计。所以对于大型多文档存储,我建议考虑对这个方案进行一些简化,使其可扩展。

11.png

响应合成器

这是任何RAG流程的最后一步——基于我们仔细检索的所有上下文和初始用户查询生成答案。最简单的方法可能是将所有获取到的上下文(超过某个相关性阈值的)连同查询一起一次性输入给LLM。但是,像往常一样,还有其他更复杂的选项,涉及多次LLM调用以优化检索到的上下文并生成更好的答案。

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

  1. 通过逐块将检索到的上下文发送给LLM来迭代地完善答案。
  2. 概括检索到的上下文以适应提示。
  3. 基于不同的上下文块生成多个答案,然后将它们连接或概括起来。有关更多细节,请查阅响应合成器模块文档[8]。

编码器和LLM微调

这种方法涉及对RAG流程中的两个深度学习模型之一进行微调——要么是负责嵌入质量和上下文检索质量的Transformer编码器,要么是负责最佳利用提供的上下文来回答用户查询的LLM,幸运的是,后者是一个很好的少量样本学习器。

如今一个很大的优势是能够使用像GPT-4这样的高端LLM来生成高质量的合成数据集。但使用由专业研究团队在精心收集、清洗和验证的大型数据集上训练的开源模型,并使用小型合成数据集进行快速调整,可能会降低模型的整体能力。

编码器微调: 我对编码器微调方法也有些怀疑,因为最新的为搜索优化的Transformer编码器相当高效。所以我在LlamaIndex笔记本设置中测试了对bge-large-en-v1.5(在撰写本文时为MTEB排行榜前4)进行微调的性能提升,结果显示检索质量提高了2%。虽然不是很惊人,但了解这个选项还是不错的,尤其是如果你有一个你正在为之构建RAG的狭窄领域数据集。

排名器微调: 另一个老方法是,如果你不完全信任你的基础编码器,就使用交叉编码器对检索结果进行重排。其工作方式如下——你将查询和前k个检索到的文本块传递给交叉编码器,以SEP令牌分隔,并对其进行微调,以输出1表示相关块,0表示不相关。这里有一个这种调整过程的例子[9],结果显示交叉编码器微调提高了4%的成对分数。

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

一种更复杂的方法在最近的RA-DIT论文[11]中展示:由Meta AI研究提出的检索增强双指导调整技术,建议对LLM和检索器(原论文中的双编码器)进行调整,针对查询、上下文和答案的三元组。有关实现细节,请参考这个指南[12]。这种技术用于通过微调API对OpenAI LLM进行微调,以及对Llama2开源模型进行微调(在原论文中),结果显示在知识密集型任务指标上提高了约5%(与Llama2 65B with RAG相比),以及在常识推理任务上提高了几个百分点。

评估

RAG系统性能评估有几个框架,它们共享一个理念,即拥有几个独立的指标,如整体答案相关性、答案的根据性、忠实度和检索到的上下文相关性。

前一节提到的Ragas使用忠实度和答案相关性作为生成答案质量的指标,以及经典的上下文精确度和召回率用于RAG方案的检索部分。

在Andrew NG最近发布的精彩短课程《构建和评估高级RAG》中,LlamaIndex和评估框架Truelens建议使用RAG三元组——检索到的上下文与查询的相关性、根据性(LLM答案受提供的上下文支持的程度)以及答案与查询的相关性。

最关键且最可控的指标是检索到的上下文相关性——基本上上面描述的高级RAG流程的第1-7部分以及编码器和排名器微调部分旨在改善这一指标,而第8部分和LLM微调则专注于答案相关性和根据性。

一个相当简单的检索器评估流程的例子可以在这里[13]找到,并且已应用于编码器微调部分。一种更高级的方法不仅考虑命中率,还考虑了平均倒数排名(一个常见的搜索引擎指标)以及生成答案的指标,如忠实度和相关性,这在OpenAI cookbook[14]中有所展示。

LangChain有一个相当先进的评估框架LangSmith[15],可以实现自定义评估器,它还监控RAG流程中的运行轨迹,以使你的系统更透明。

如果你在使用LlamaIndex构建,那么有一个rag_evaluator llama包[16],提供了一个快速工具,用公共数据集评估你的流程。

结论

我试图概述RAG的核心算法方法,并且用一些示例来说明它们,希望这能激发一些在你的RAG流程中尝试的新想法,或者为今年发明的众多技术带来一些系统化——对我来说,2023年到目前为止是ML领域最激动人心的一年。

还有许多其他需要考虑的事情,如基于网络搜索的RAG(LlamaIndex的RAGs、webLangChain等),更深入地探讨主动架构(以及最近OpenAI在这个游戏中的份额)以及一些关于LLM长期记忆的想法。

RAG系统的主要生产挑战除了答案相关性和忠实度之外,还有速度,尤其是如果你倾向于更灵活的基于代理的方案。ChatGPT和大多数其他助手使用的这种流媒体功能不是随机的赛博朋克风格,而只是一种缩短感知答案生成时间的方式。这就是为什么我看到小型LLM和最近的Mixtral和Phi-2发布在这个方向上有一个非常光明的未来。

转自 IVAN ILIN

参考资料

[1] Advanced RAG Techniques: an Illustrated Overview:
https://pub.towardsai.net/advanced-rag-techniques-an-illustrated-overview-04d193d8fec6

[2] 提示工程指南:
https://platform.openai.com/docs/guides/prompt-engineering/strategy-write-clear-instructions

[3] chunking-strategies:
https://www.pinecone.io/learn/chunking-strategies/

[4] MTEB排行榜:
https://huggingface.co/spaces/mteb/leaderboard

[5] LlamaIndex关于递归检索器+节点引用的教程:
https://docs.llamaindex.ai/en/stable/examples/retrievers/recursive_retriever_nodes.html

[6] LangChain:
https://python.langchain.com/docs/modules/data_connection/retrievers/ensemble

[7] LlamaIndex:
https://docs.llamaindex.ai/en/stable/examples/retrievers/reciprocal_rerank_fusion.html

[8] 响应合成器模块文档:
https://docs.llamaindex.ai/en/stable/module_guides/querying/response_synthesizers/root.html

[9] cross_encoder_finetuning:
https://docs.llamaindex.ai/en/latest/examples/finetuning/cross_encoder_finetuning/cross_encoder_finetuning.html#

[10] openai_fine_tuning:
https://docs.llamaindex.ai/en/stable/examples/finetuning/openai_fine_tuning.html

[11] RA-DIT:
https://arxiv.org/pdf/2310.01352.pdf

[12] fine-tuning-with-retrieval-augmentation:
https://docs.llamaindex.ai/en/stable/examples/finetuning/knowledge/finetune_retrieval_aug.html#fine-tuning-with-retrieval-augmentation

[13] evaluate.ipynb:
https://github.com/run-llama/finetune-embedding/blob/main/evaluate.ipynb

[14] Evaluate_RAG_with_LlamaIndex:
https://github.com/openai/openai-cookbook/blob/main/examples/evaluation/Evaluate_RAG_with_LlamaIndex.ipynb

[15] LangSmith:
https://docs.smith.langchain.com/

[16] rag_evaluator llama包:
https://github.com/run-llama/llama-hub/tree/dac193254456df699b4c73dd98cdbab3d1dc89b0/llama_hub/llama_packs/rag_evaluator