提高检索增强生成系统性能的10种方法

10 ways to improve performance of retrieval-enhanced generation systems

从原型到生产的工具

快速入门指南不够

“检索增强生成是指将用户的输入补充到大型语言模型(LLM)(如ChatGPT)中的过程,并使用您(系统)从其他地方检索的附加信息进行补充。然后,LLM可以使用该信息来增强其生成的响应。” — Cory Zue

LLM是一个了不起的发明,但存在一个关键问题。它们会虚构东西。通过为其提供事实背景,RAG使LLM在回答查询时更加有用。

使用像LangChain或LlamaIndex这样的框架的快速入门指南,任何人都可以构建一个简单的RAG系统,例如用于文档的聊天机器人,只需写五行代码。

但是,用这五行代码构建的机器人效果可能不太好。RAG易于原型设计,但很难投入生产,即达到用户满意的程度。基本教程可能使RAG的工作达到80%。但是要填补剩下的20%通常需要进行一些严肃的实验。最佳实践尚未完全确定,并且可能因用例而异。但是弄清楚最佳实践很值得,因为RAG可能是使用LLM的最有效的方法。

本文将介绍提高RAG系统质量的策略。它专为使用RAG构建系统并希望填补基本设置和生产级性能之间差距的人员量身定制。对于本文的目的,提高意味着增加系统能够:1.找到适当的上下文,2.生成适当的响应 的查询比例。我假设读者已经了解RAG的工作原理。如果不了解,我建议阅读Cory Zue的这篇文章进行一个良好的介绍。本文还将假设读者对用于构建这些工具的常见框架(LangChain和LlamaIndex)有一些基本了解。但是,这里讨论的思路与框架无关。

我不会详细介绍如何实施每个策略的细节,而是试图给出何时以及为什么可能有用的想法。鉴于这个领域发展迅速,提供全面或完全最新的最佳实践列表是不可能的。相反,我旨在概述一些您在尝试改进检索增强生成应用程序时可以考虑和尝试的事情。

提高检索增强生成性能的10种方法

1. 清理您的数据。

RAG将LLM的功能连接到您的数据上。如果您的数据混乱、内容或布局混乱,则您的系统将受到影响。如果您使用的数据存在冲突或冗余信息,您的检索将难以找到正确的上下文。当检索系统找到正确上下文时,LLM执行的生成步骤可能不够优化。假设您正在为初创公司的帮助文档构建聊天机器人,并发现效果不佳。您应该首先检查输入系统的数据。主题是否逻辑分开?主题是否集中在一个地方还是分散在多个地方?如果作为一个人,您无法轻松判断需要查看哪个文档来回答常见查询,那么您的检索系统也无法做到。

这个过程可以简单地手动合并相同主题的文档,但您也可以进一步进行处理。我见过的比较有创意的方法之一是使用LLM创建提供的所有文档的摘要。然后,检索步骤可以首先对这些摘要进行搜索,仅在必要时深入细节。一些框架甚至已经将此作为内置抽象。

2. 探索不同的索引类型。

索引是LlamaIndex和LangChain的核心支柱。它是保存检索系统的对象。RAG的标准方法涉及嵌入和相似性搜索。将上下文数据划分为块,对所有内容进行嵌入,当收到查询时,从上下文中找到相似的部分。这种方法非常有效,但并不适用于每种用例。查询是否与特定项相关,例如电子商务商店中的产品?您可能需要探索基于关键字的搜索。它不必是二选一,许多应用程序使用混合方法。例如,您可以将基于关键字的索引用于与特定产品相关的查询,但对于一般客户支持,则依赖于嵌入。

3. 尝试你的分块方法。

分块是构建RAG系统的核心部分。框架可以抽象出分块过程,使你无需考虑它。但你应该考虑它。分块大小很重要。你应该探索适合你的应用程序的最佳分块方式。一般来说,较小的分块通常可以提高检索效果,但可能导致生成过程缺乏周围上下文。有很多方法可以处理分块。唯一行不通的方法是盲目处理。PineCone的这篇文章介绍了一些值得考虑的策略。我有一个测试问题集。我通过运行实验来处理这个问题。我循环遍历每个集合一次,使用小、中、大三种分块大小,并发现小分块效果最好。

4. 尝试不同的基本提示。

LlamaIndex中使用的一个基本提示的例子是:

“上下文信息如下。根据上下文信息而非先前知识,回答问题。”

你可以覆盖这个提示,尝试其他选项。你甚至可以修改RAG,以便在上下文中找不到好答案时,允许LLM依靠自己的知识。你还可以调整提示,以帮助引导它接受特定类型的查询,例如指示它对主观问题以某种特定方式回答。至少覆盖提示是有帮助的,这样LLM就能了解它正在做什么工作的上下文。例如:

“你是一名客户支持代理。你的设计目标是尽可能提供有帮助的信息,同时只提供事实信息。你应该友善,但不要过于健谈。上下文信息如下。根据上下文信息而非先前知识,回答问题。”

5. 尝试元数据过滤。

提高检索效果的一种非常有效的策略是在分块中添加元数据,然后使用它来帮助处理结果。日期是一个常见的元数据标签,因为它允许你按照时间顺序进行过滤。想象一下,你正在构建一个允许用户查询其电子邮件历史的应用程序。最近的邮件可能更相关。但我们不知道它们是否从嵌入的角度来看与用户的查询最相似。这引出了一个在构建RAG时要记住的一般概念:相似≠相关。你可以将每封邮件的日期附加到其元数据中,然后在检索过程中优先考虑最近的上下文。LlamaIndex内置了一类称为Node后处理器的工具,可以帮助处理这个问题。

6. 使用查询路由。

拥有多个索引通常是有用的。然后,当查询到达时,将其路由到适当的索引。例如,你可以有一个索引处理摘要问题,另一个处理具体问题,还有一个适用于日期敏感问题。如果你试图优化一个索引以满足所有这些行为,最终会降低其在所有行为上的表现。相反,你可以将查询路由到适当的索引。另一个用例是根据第2节讨论的关键词将一些查询定向到基于关键词的索引。

一旦你构建好了你的索引,你只需要在文本中定义每个索引应该用于什么目的。然后在查询时,LLM将选择适当的选项。LlamaIndex和LangChain都提供了相应的工具。

7. 研究重新排序。

重新排序是解决相似性和相关性之间差异的一种方法。通过重新排序,你的检索系统通常会获取上下文的前几个节点。然后根据相关性对它们进行重新排序。Cohere Rereanker通常用于此。这是我经常看到专家推荐的一种策略。无论用例如何,如果你正在使用RAG构建系统,你应该尝试重新排序并查看它是否改进了你的系统。LlamaIndex和LangChain都提供了简化设置的抽象。

8. 考虑查询转换。

你已经通过将用户的查询放入基本提示中来修改查询。进一步修改它也是有意义的。以下是一些例子:

改写:如果你的系统找不到相关的上下文,你可以让LLM改写查询并重试。对人类来说,两个看起来相同的问题在嵌入空间中并不总是看起来那么相似。

HyDE:HyDE是一种策略,它接受一个查询,生成一个假设的回答,然后同时使用它们进行嵌入查找。研究人员发现这可以显著提高性能。

子查询:LLMs在拆分复杂查询时往往表现更好。您可以将此功能集成到您的RAG系统中,使查询被分解为多个问题。

LLamaIndex有关于这些类型的查询转换的文档。

9. 微调嵌入模型。

基于嵌入的相似度是RAG的标准检索机制。您的数据被分解并嵌入在索引中。当有查询进来时,它也会被嵌入以与索引中的嵌入进行比较。但是,谁来进行嵌入呢?通常使用预训练模型,如OpenAI的文本嵌入ada-002。

问题是,预训练模型在嵌入空间中的相似性概念可能与您的上下文中的相似性不太吻合。想象一下,您正在处理法律文件。您希望嵌入的相似性判断更多地基于您的领域专用术语,如“知识产权”或“合同违约”,而不是一般术语,如“特此”和“协议”。

您可以微调您的嵌入模型来解决这个问题。这样做可以提高您的检索指标5-10%。这需要更多的努力,但可以显著提升您的检索性能。这个过程比您想象的要简单,因为LlamaIndex可以帮助您生成训练集。要了解更多信息,您可以查看Jerry Liu关于LlamaIndex如何进行微调嵌入的帖子,或者查看这篇帖子,其中介绍了微调过程。

10. 开始使用LLM开发工具。

您可能已经在使用LlamaIndex或LangChain构建您的系统。这两个框架都有有用的调试工具,可以让您定义回调函数,查看使用的上下文,检查检索来自哪个文档等。

如果您发现这些框架内置的工具不够用,还有一个不断发展的工具生态系统,可以帮助您深入了解RAG系统的内部工作原理。Arize AI提供了一个笔记本工具,可以让您探索检索的上下文以及原因。Rivet是一个提供视觉界面的工具,可以帮助您构建复杂的代理。这个工具刚刚由法律技术公司Ironclad开源。新的工具不断推出,值得尝试,看看哪些对您的工作流程有帮助。

结论

使用RAG构建可能令人沮丧,因为它很容易实现,但很难实现良好的效果。我希望上面的策略能为您提供一些灵感,帮助您弥合差距。这些想法并不是总是有效的,这个过程需要实验、试验和错误。在这篇文章中,我没有深入探讨如何评估系统的性能,即您如何测量系统的性能。评估目前更多地是一门艺术而不是科学,但建立一种可以持续检查的系统是很重要的。这是唯一能告诉您所实施的更改是否产生了变化的方法。我之前写过如何评估RAG系统的文章。要了解更多信息,您可以探索LlamaIndex Evals、LangChain Evals和一个非常有前途的新框架RAGAS。