为什么将RAG扩展到生产环境如此困难?
将RAG扩展到生产环境是一项复杂的挑战,需要考虑多个方面,本文将深入探讨这些挑战,并提供解决方案。
RAG如何演变?
RAG(Retrieval-Augmented Generation,检索增强生成)是一种技术,通过为大型语言模型(LLM)提供额外的上下文信息,使其能够生成更准确、更具体的响应。LLM在公开数据上进行训练,本身非常智能,但由于缺乏特定领域知识,无法回答特定问题。RAG通过提供必要的上下文信息,帮助LLM正确回答查询。
RAG是向LLM注入新知识或能力的一种方式,这种注入不是永久性的。另一种方法是通过微调LLM来添加新知识或能力。
通过微调添加新知识,难度大,成本高,且是永久性的。通过微调添加新能力,还会影响LLM之前拥有的知识。在微调过程中,我们无法控制哪些权重会被改变,因此无法控制哪些能力会增强或减弱。
无论是选择微调、RAG,还是两者结合,都取决于具体的任务。没有一种方法适用于所有情况。
一个基本的RAG系统通常包含以下步骤:
- 将文档拆分成更小的片段。
- 每个片段都是一段原始文本。
- 使用编码器(例如OpenAI embeddings、sentence_transformer)为每个片段生成嵌入,并将其存储在数据库中。
- 找到Top-K个最相似的编码片段,获取这些片段的原始文本,并将它们与提示一起作为上下文提供给生成器。
RAG基本架构
最初的RAG面临的问题是,这些组件并非设计为无缝协作,因为它们都是独立设计的,因此各个组件之间存在很多摩擦。
RAG 2.0的演变
简而言之,RAG 2.0开始对系统进行端到端训练,从而使所有组件更加流畅,彼此协调。
一个好的生产级RAG系统应该能够做到以下几点:
- 开放域问答:能够很好地处理自然的人类语言查询。能够正确检索相关知识,并以人类语言准确地生成答案。
- 忠实度:系统必须能够保持对检索到的证据的依赖,避免幻觉。一些好的基准测试包括HaluEvalQA和TruthfulQA。
- 新鲜度:这些系统应该能够通过使用网络搜索索引来适应快速变化的全球知识,并在最近发生的事件中表现出准确性。
每个方面对于构建生产级RAG系统都至关重要。
真正的问题不在于技术,而在于流程
人们犯的一个最重要的错误是不理解他们试图自动化哪些流程。
每个流程都处于完全确定性到极度随机性的范围内。根据具体任务,整个流程都会发生变化。例如,创意写作是极度随机的,而湍流计算需要高度确定性。
想象一下,你正在尝试自动化医疗行业的流程。系统需要始终工作(几乎)。没有幻觉的容错空间,这种系统的系统设计将与随机系统的系统设计完全不同。你将尝试在流程中最关键的部分集成人工干预。但如果系统是为餐厅设计的,你就不太在意这一点。
我经常看到,人们对他们的解决方案需要多少确定性一无所知。他们不断地构建最常见或最著名的解决方案,这就是为什么这些POC无法扩展的原因。
在你构建任何RAG系统或任何其他系统之前,都要确定你真正需要什么。如果说的是RAG系统,主要有三个方面:
- 速度:一个包含一百万份文档的RAG系统,不可能设计成每次查询都需要5分钟才能得到答案,没有人会使用它。尝试将一百万份文档整合在一起,看看你POC的速度,它会惨败。这种系统的系统设计需要使用最快的检索机制。其他一切都是次要的。
- 基础性:随着文档数量的增加,找到Top-K个相关文档变得极其困难。特别是如果文档包含很多交叉引用,这个问题就更加困难。
- 处理不同数据类型的能力:处理不同文档所需的流程会随着数据格式的变化而发生显著变化。例如,PDF可能具有非常复杂的结构,包含大量表格数据、图像,甚至布局。PDF中最难解决的挑战之一是如何处理图表、流程图,以及文本出现在图像中的情况。
没有一个系统能够解决所有这些挑战,这就是主题领域专业知识发挥作用的地方。根据用例,整个流程将被设计为处理速度、基础性和数据类型方面不同的问题。
主要挑战
以下是人们在设计生产级RAG时面临的一些基本挑战:
与响应质量相关的挑战
- 知识库中缺少上下文
- 初始检索过程中缺少上下文
- 重新排序后缺少上下文
- 未提取上下文
- 输出格式错误
- 输出的具体程度不正确
- 输出不完整
可扩展性
- 无法扩展到更大的数据量
- 速率限制错误
所有这些问题都在这两篇博文中进行了详细讨论。
但让我们讨论更多挑战,并了解下面流程中每个组件的作用。
RAG系统组件
1. 查询分类
并非所有查询都需要检索。一些查询,例如“梅西是谁?”,答案存储在LLM的内部知识库中,因此不需要检索。这就是查询分类的概念,根据查询的复杂程度来确定是否需要检索。
王等人将查询划分为15个任务类别,并使用二元分类器来评估一个查询是“足够”(LLM本身可以回答)还是“不足”(需要检索)。这一初始步骤简化了流程,确保系统在LLM有足够信息的情况下不会执行不必要的检索。
2. 分割
这里的挑战是找到适合你的检索过程的理想片段大小。如果你的片段太大,你可能会在检索过程中增加噪音和不必要的成本。太小?你可能会丢失关键的上下文信息。
通常情况下,256到512个token之间的尺寸是最佳的。但是,这会根据你的数据而有所不同,因此评估是关键。使用从小到大的方法——从小的片段开始搜索,然后转向较大的片段进行生成,这在调整流程时可能会有所帮助。
另一种技术是使用滑动窗口来创建重叠的片段,确保在各个片段之间不会丢失任何重要信息。
3. 利用元数据和混合搜索
元数据在检索方面可以改变游戏规则。添加标题、关键词或假设问题等元数据可以提高检索到的文档的相关性。将元数据与混合搜索(将_向量搜索_用于语义匹配,将BM25用于基于关键词的匹配)相结合,可以实现精确结果的完美平衡。
另一种有趣的技术是HyDE(假设文档嵌入),它生成伪文档来提高检索质量。虽然它可以提供更好的结果,但它效率低下,会增加显著的延迟。对于大多数情况,混合搜索是兼顾效率和质量的最佳选择。但请记住,每个案例都不同,因此要进行适当的评估。
4. 嵌入模型选择
选择合适的嵌入模型至关重要。你可以选择闭源模型,例如GPT、Cohere的Command R或Meta的Llama。仔细决定你要使用哪种模型,因为其他组件可能依赖于它。
5. 向量数据库
我将提供一个比较表,供你选择。
向量数据库比较
向量数据库比较(续)
6. 查询转换
在将你的查询发送到检索引擎之前,我们需要进行查询转换来提高准确性。方法包括:
- 查询重写:重新措辞或简化查询,使其更清晰。
- 查询分解:将复杂的查询分解成更小、更容易管理的子查询。
- HyDE:根据查询生成伪文档,但这会增加显著的延迟。
每种转换技术都会对检索结果产生重大影响,因此值得根据你的延迟和准确性需求进行试验。
7. 重新排序
检索之后,我们可能会有几个结果需要根据相关性进行排序。这就是重新排序的作用。
以下是一些用于重新排序的选项:monoT5、RankLLaMA、TILDEv2
8. 文档重新打包
重新排序后,我们需要重新打包文档,以优化它们呈现给LLM的方式。一项研究表明,“反向”方法,即最相关的文档放在序列的开头或结尾,效果最好。这有助于LLM在生成过程中有效地处理最重要的信息。按照相关性升序排列文档可以提高生成任务的性能。
9. 摘要
将大型提示发送给LLM既昂贵又往往是多余的。在将检索到的文档传递给LLM之前,摘要是一个至关重要的步骤,可以减少文档的大小。
Recomp是一个出色的工具,它提供提取式摘要和抽象式摘要。提取式摘要选择最重要的句子,而抽象式摘要将来自多个来源的信息综合成更简洁的格式。但是,如果你在时间限制下工作,可以考虑跳过此步骤。
10. 微调生成器
你是否应该为生成任务微调LLM?当然!使用相关文档和不相关文档的混合进行微调,有助于生成器变得更加健壮。虽然我们不知道相关文档与不相关文档的确切比例,但结果很明显:微调会导致更准确、更具上下文意识的响应。
此步骤与领域相关,因此请确保你在特定领域的数据库上微调模型,以获得最佳性能。
11. 多模态
如果你正在处理多模态输入,例如图像和文本,那么多模态检索至关重要。例如,在处理图像到文本的任务时,你可以检索类似的图像及其对应的标题,以帮助LLM将其响应与真实数据联系起来。
结论
本文讨论的每个组件都需要花费更多的时间思考,你可能需要所有这些组件,甚至可能需要更多组件。选择合适的组件对于生产级RAG的成功至关重要。市面上有很多拖放式的AI代理和RAG,但它们往往在生产环境中无法正常工作。
此外,如果它真的那么容易,那么每个人都可以做到,那么你比其他企业有什么优势?没有。这就是为什么系统设计是你的技术栈在拥挤的AI代理领域脱颖而出的唯一因素。祝你好运,构建你的生产级RAG流程。
本文转载自 DevOpsAI,作者: RAG