如何提高LLM应用程序中的RAG结果:从基础到高级

2024年02月01日 由 alex 发表 362 0

如果你正在使用LLM(大型语言模型)构建任何有意义的产品/功能,你可能会使用称为RAG(检索增强生成)的技术。它可以允许你将LLM的训练数据中没有的外部数据整合到LLM的文本生成过程中,这可以大大减少幻觉的噩梦,并提高文本响应的相关性。


RAG的想法似乎很简单:找到并检索最相关的文本块,并将其插入到LLM的原始提示中,以便LLM可以访问这些参考文本片段,并可以使用它们来生成响应。但是,很难获得高质量的RAG管道,以在生产就绪的产品中产生你想要的确切结果。


在本文中,让我们从最基本的到更高级的,探索改进LLM应用程序的RAG结果的技术。我还将根据我自己在现实生活中使用RAG构建产品的经验,提供一些实际操作技巧/案例。


基本RAG


对于那些刚刚开始使用RAG的人来说,让我们从最基本的RAG开始。它是一个简单的三步过程:索引、检索和生成。


1


索引过程正在为检索过程准备数据。你应该收集你想让你的LLM知道的一切,例如,产品文档,产品政策,公司网站等,这取决于你想让你的聊天机器人做什么。然后,你将把它分解成更小的文本块(以便你可以轻松地将这些块放入LLM的上下文大小中)。然后,你将通过嵌入模型将块转换为向量表示(以便以后可以轻松找到类似的块)。然后,你可以将所有这些文本嵌入对保存在索引或向量数据库中,以供以后使用。


检索过程在用户查询LLM时发生。在用户提出问题后,你可以保留该查询,而不是将其直接发送给LLM。相反,你将使用索引中的文本块中的一些附加信息来丰富查询。你将使用相同的嵌入模型对用户的原始查询进行编码,然后执行相似性搜索以查找数据库中最相似(大多数情况下,最相关)的文本块。


然后,为了生成,你将文本块插入到提示符中,该提示符还包括用户的原始查询。LLM将使用检索到的文本块中提供的信息生成答案。


下面是RAG的基本提示:


Answer the following question based on the given information only. If the given information is not enough to answer the question, simply reply "I don't know".on the given information only. If the given information is not enough to answer the question, simply reply "I don't know".
Question: "<user's original query>"
Given information: "<the text chunk you retrieved from the database>"


因为这是目前业界公认的流程,所以许多库将所有这些步骤组合在一起用于RAG流程。LlamaIndex和LangChain是两个流行的库。当你上传你的文档时,OpenAI本身也在其新支持的自定义GPT和助手功能中隐藏了这个过程。许多矢量数据库(如Pinecone和Chroma)也支持在RAG中轻松创建和检索。


这个过程简单而有效,但在实际情况中,你经常会遇到以下问题:


  • RAG无法检索与生成最相关的信息块。当检索到相似但不相关的块时,LLM将对用户的原始查询给出不正确的答案。
  • RAG检索到的块没有正确的上下文。有时,检索到的组块会错过它们周围的上下文,使它们无法生成有用的答案,甚至无法向法学硕士提供矛盾的信息。
  • 不同的用户查询情况需要不同的检索或生成策略。
  • 你的数据结构可能不适合使用嵌入进行相似性搜索。


提高RAG性能的技术


经过近一年的LLM学习,我学到了许多提高RAG性能的技巧,并总结了一些使用RAG的经验教训。接下来我将介绍许多用于在检索前、检索期间和检索后提高RAG性能的技术。


预检索技术


预检索技术包括可在索引步骤中或在搜索数据库中的块之前使用的技术。


第一种技术是提高索引数据的质量。在机器学习领域有一句话“垃圾进,垃圾出”。我认为它也适用于RAG,但许多人只是忽略了这一步,并在这一非常关键的初始步骤之后专注于优化步骤。你不应该期望把每一个文档,无论是否相关,都放到你的矢量数据库中,并期待最好的结果。要提高索引数据的质量,你应该:(1)删除与你的特定任务无关的文本/文档(2)将索引数据重新格式化为类似于你的最终用户可能使用的格式(3)将元数据添加到你的文档中,以实现高效和有针对性的检索。


第二种技术是块优化。根据下游任务,你需要确定块的最佳长度以及每个块的重叠程度。如果块太小,则可能无法包含LLM回答用户查询所需的所有信息。如果块太大,则可能包含太多不相关的信息,从而使LLM感到困惑,或者可能太大而无法适应上下文大小。


2


以下是如何生成假设文档的一些示例:


# if your reference documents are blog articles.
prompt = f"Please generate a paragraph from a blog article on {user_query}"
# if your reference documents are code documentations in markdown.
prompt = f"Please generate a code documentation for {user_query} in markdown format."


使用Query2Doc或Hyde技术时的一个缺陷是,假设的文档可能与实际文档相矛盾或完全不一致,这可能导致不准确的检索。为了解决这个问题,你可以使用假设文档和不使用假设文档来检索文档,以便你可以应用检索后技术(我将在后面介绍)来查找最佳参考文本。


当用户的查询很复杂并且可能需要多个参考文本时,你可以使用LLM将其分解为多个子查询,然后为每个查询找到相关的文本块。例如,如果用户问两个不同的问题“ ChomoDB和Weaviate之间有什么区别?”,则可以将其分解为“什么是ChromaDB?”和“什么是Weaviate?”。


下面是一个要求你的LLM分解查询的示例:


Please rephrase the following query into three or fewer subqueries, so that each sub-query contains only one topic. Show each sub-query in each new line.
Query:"<original user query>"


如果你的聊天机器人或代理可以处理多个下游任务和不同格式的用户查询,你可以考虑使用查询路由,其中你可以将查询动态路由到不同的RAG进程。例如,如果你的用户询问某个问题的特定答案,你可能会将他们路由到查询特定的块。如果你的用户询问总体摘要,你可能会将他们路由到许多检索到的文档的递归创建的摘要。如果用户询问两个文档之间的比较,你可能需要使用上面提到的子查询技术。你可以使用LLM本身进行路由,或者使用关键字匹配/嵌入相似性进行路由。


检索技术


准备好查询后,可以在RAG管道的第二步中进一步改进检索结果。


第一种技术经常被忽视,因为人们只是遵循其他人正在做的事情——始终坚持向量相似性搜索。但是你可以而且应该考虑使用替代搜索方法来替换向量相似性搜索,或者通过混合搜索来补充它。虽然向量相似性搜索在大多数情况下可以找到相关文档,但对于某些情况或数据结构,使用全文搜索、结构化查询、基于图的搜索或混合搜索方法可能会更好。


例如,如果你的文本数据包含许多语义非常相似的块,这些块仅在某些关键字上有所不同,或者如果你的文本数据包含太多非特定的一般文本,则使用精确关键字匹配进行搜索可能会更好。例如,在电子商务中搜索只有一个功能组不同的特定药品名称或成千上万个类似的产品名称,可能会受益于全文匹配和筛选。


另一种经常被忽视的技术是测试和为你的特定任务使用不同的嵌入。许多人甚至不会考虑这一点,因为框架/向量数据库有一个默认的嵌入选项,他们只是跟着它走。但不同的嵌入模型实际上可以捕获不同的语义信息,可能适用于不同的任务。一个有用的嵌入模型是讲师嵌入,它允许你提供关于你正在嵌入的数据类型和你的任务的具体说明[。你还可以参考MTEB排行榜,它是文本嵌入模型的基准。请务必测试模型,因为在排行榜上排名靠前并不意味着它最适合你的特定任务。


除此之外,你还可以在检索过程中进行一些调整,以使检索更具相关性。Small2Big、递归或上下文感知检索是最初检索较小数据块的技术,这些数据块由于更多的特殊性和细节而更有可能匹配查询,然后继续检索这些较小数据块周围的父文档或较大的文本块以包括更多的上下文。它们确保你检索相关的块以及所有重要的上下文。一些框架提供了对这种检索的支持,如LangChain中的ParentDocumentRetriever、句子窗口和LlamaIndex中的节点引用。


如果你可以从小到大检索文档,你也可以用另一种方式来做,即从更一般到更具体的分层检索。例如,你可以创建两个数据层,一个层包含原始块,另一个层包含块的摘要。首先在摘要索引中搜索最相关的文档,然后在这些文档中再次搜索特定的块。通过这种方式,你可以在第一遍中快速过滤掉不相关的文档,然后在第二遍中找到用于问题回答的实际信息。


类似地,你可以将递归搜索与图形搜索结合使用。该方法将相似性搜索与图数据结构相结合。你首先使用向量相似性搜索找到最相关的块,然后探索与这些块相关的节点,以探索更多可能有用的信息。例如,如果你有一个包含相互链接的文档(如Concept或Obsidian)的数据库,你可以通过链接轻松找到LLM的相关文档。llamaindex使用RecursiveRetriever模块支持与此类似的搜索。


还有更多的代理方法来执行检索,首先创建一个检索代理,它具有查询文档的工具/功能,并让它决定是搜索更多信息,还是只将相关的检索块返回给原始代理以回答用户的查询。但这些技术通常需要更长的响应时间,并且可能不稳定,因此可能不是很好的生产选择。希望有了更强大的模型和更快的推理,我们可以在这个方向上得到更好的结果。


后检索技术


在从数据库中检索相关块之后,还有一些技术可以提高生成质量。根据任务的性质和文本块的格式,你可以使用以下一种或多种技术。


如果你的任务与一个特定的块更相关,一种常用的技术是重新排序或评分。正如我前面提到的,向量相似性搜索的高得分并不意味着它总是具有最高的相关性。你应该进行第二轮重新排序或评分,以挑选出对生成答案真正有用的文本块。对于重新排名或评分,你可以要求LLM对文档的相关性进行排名,或者你可以使用一些其他方法(如关键字频率或元数据匹配)来细化选择,然后将这些文档传递给LLM以生成最终答案。


另一方面,如果你的任务与多个块相关—如摘要或比较。在将信息传递给LLM之前,你可以进行一些信息压缩作为后处理,以减少噪音或上下文长度。例如,你可以首先从每个块中总结、解释或提取关键点,然后传递聚合的、将信息浓缩到LLM以供生成。


平衡质量和延迟


我还发现了一些其他的技巧,这些技巧在改善和平衡生成质量和延迟方面非常有用。在实际生产中,你的用户可能没有时间等待多步骤RAG过程完成,特别是当有一连串的LLM调用时。如果你希望改善RAG管道的延迟,以下选择可能会有所帮助。


第一种是对某些步骤使用更小、更快的模型。对于RAG过程中的所有步骤,你不一定需要使用最强大的模型(通常是最慢的)。例如,对于一些简单的查询重写、假设文档的生成或总结文本块,你可以使用更快的模型(如7B或13B本地模型)。这些模型中的一些甚至能够为用户生成高质量的最终输出。


第二种技术是使一些中间步骤并行运行。你不必总是等待一个步骤完成后才进行第二个步骤。你可以并行执行一些中间步骤,如混合搜索或多个块的汇总。要做到这一点,你可能需要大量修改RAG框架或自己创建RAG管道,但这可以大大减少最终输出的时间。


第三种技术是让LLM做出多个选择,而不是在可能的情况下生成长文本。例如,在重新排序/评分中,你可以要求法学硕士只列出文本块的分数/等级,而不是将它们生成回来或包括详细的解释。


结论


在本文中,我介绍了许多可以在LLM支持的应用程序中改进RAG管道的技术,你可以在RAG管道中使用这些技术中的一种或多种,使其更准确、更高效。我希望这些技术可以帮助你为你的应用程序建立一个更好的RAG管道。

文章来源:https://medium.com/design-bootcamp/how-to-improve-rag-results-in-your-llm-apps-from-basics-to-advanced-822818014144
欢迎关注ATYUN官方公众号
商务合作及内容投稿请联系邮箱:bd@atyun.com
评论 登录
写评论取消
回复取消