Rag系统的发展历程,从朴素、高级到模块化

1 评论 3055 浏览 7 收藏 30 分钟

Rag系统它通过结合信息检索和自然语言生成技术,显著提高了信息处理的效率和准确性。然而,随着应用的深入,Rag系统的局限性也逐渐显现。本文将深入探讨Rag系统的发展、存在的问题及其优化方案,希望能为相关领域的研究者和开发者提供参考和启示。

前段时间,风叔发表了《聊聊炙手可热的Rag:产生原因、基本原理与实施路径》,有读者反馈希望多讲讲Rag系统现存的问题和优化方案,今天风叔就来谈谈这个话题。

从2020年Meta AI 的研究人员提出检索增强生成(RAG)方法,Rag系统也在不断地优化和迭代。

总结来说,Rag的发展经历了三个发展阶段,从最初的Naive Rag(朴素Rag),再到Advance Rag(高级Rag),最后再演变为Modular Rag(模块化Rag)。

下面,风叔详细介绍下这三个阶段的Rag系统都遇到了哪些问题,以及这些问题是如何被解决的。

一、朴素Rag

RAG发展初期,其核心框架由索引、检索和生成构成,这种范式被称作朴素RAG。朴素Rag的原理非常简单,包括以下三个步骤。

  1. 索引:这一过程通常在离线状态下进行,将原始文档或数据进行清洗并分块,然后将分块后的知识通过embedding模型生成语义向量,并创建索引。
  2. 检索:对用户输入的Query问题,使用相同的embedding模型,计算Query嵌入和文档块嵌入之间的向量相似度,然后选择相似度最高的前N个文档块作为当前问题的增强上下文信息。
  3. 生成:将原始Query和相关文档合并为新的提示,然后由大型语言模型基于提供的信息回答问题。如果有历史对话信息,也可以合并到提示中,用于进行多轮对话。

Rag系统的发展历程,从朴素、高级到模块化

朴素RAG是一个非常简单的框架,也很易于理解,但是在实际应用中自然而然地出现了以下挑战。

  • 索引环节:在构建索引的过程中,核心关键词可能被湮没在大量的无效信息中,导致建立的索引,核心知识占的比重比较小,无效信息占的比重大,从而影响生成答案的质量。同时,内容块的语义信息容易受分割方式影响,在较长的语境中,会出现重要信息丢失或掩盖的问题。
  • 检索环节:在使用用户原始query做检索时,由于语义的复杂性以及需要用到专业词汇时,无法准确地理解用户的真实需求,导致用户query和知识索引不能很好匹配。同时,查询Query和文档块之间的语义相似度并非总是高度一致,仅依靠相似度计算进行检索,缺乏对查询Query与文档间关系的深度探究,导致检索质量不高。另外,朴素rag会将所有检索到的块直接输入到大模型,但是过多的冗余和噪声信息会干扰大模型对关键信息的识别,增加生成错误。
  • 生成环节:当没有检索到相关知识或检索知识质量比较差时,大模型会自主对用户Query进行回答,导致幻觉问题产生。或者出现回答内容空洞,无法直接使用的问题。

要解决上面的问题,需要在检索前和检索后做一些优化,于是就产生了高级RAG。

二、高级Rag

高级RAG相比于朴素RAG,增加了检索前、检索中和检索后的优化策略,用于解决索引、检索和生成的问题。

Rag系统的发展历程,从朴素、高级到模块化

下图是更直观的对比,可以看到相比朴素Rag,高级Rag主要优化了黑框中的三个环节。

Rag系统的发展历程,从朴素、高级到模块化

2.1 Pre-Retrieval 检索前

检索前主要有两个事项,建立索引和处理查询。

1)建立索引(Indexing)

在建立索引的环节,为了解决核心关键词被无效信息淹没以及内容分割受语义影响较大的问题,高级Rag引入了索引降噪、知识切分和元数据。

  • 索引降噪:是根据业务特点,去除索引数据中的无效成分,突出其核心知识,降低噪音的干扰。比如原文档内容是“How can I download source code from github.com”,其核心内容是“download source code、github”,其他噪音可以忽略。
  • 知识切分:先训练专门的语义理解小模型,然后把较长的文本,按照语义内聚性的分析切成小块,解决核心知识湮没以及语义截断的问题。
  • 元数据:增加内容摘要、时间戳、用户可能提出的问题等附加信息来丰富知识库,元数据不需要被向量化。此外还可以添加诸如章节或小节的引用,文本的关键信息、小节标题或关键词等作为元数据,有助于改进知识检索的准确性。

2)处理查询(Query)

在处理查询的环节,为了解决用户真实意图识别不准确的问题,高级Rag引入了Query改写的方法,但是对语义相似度的计算,仍然沿用了Embedding模型向量计算的方法。

Query改写:Query改写就是把用户原始的问题转换成适合知识库检索的问题,从而提高检索的精准程度。Query改写的方法主要有Query分解和Query扩展,Query分解是将一个问题分解为多个子问题,Query扩展则是将一个问题转化成多种不同的问法。

2.2 Retrieval 检索中

检索阶段的目标是召回知识库中最相关知识,在朴素Rag中,检索完全基于向量搜索,通过计算查询与索引数据之间的语义相似性,从而获取最相关的内容块,但是存在查询Query和文档块之间的语义相似度并非总是高度一致的问题。

嵌入模型微调:相比朴素Rag,高级Rag仍然沿用了使用Embedding模型计算相似度的方式,但是对嵌入模型做了微调,将嵌入模型定制为特定领域的上下文,特别是对于一些专业术语浓度比较高的场景。

2.3 Post-Retrieval 检索后

在朴素Rag中,会将所有检索到的块直接输入到大模型,导致冗余信息过载,从而干扰大模型对关键信息的识别。在高级Rag中,针对这一问题做了相关优化。

  • 提示压缩:对于检索到的内容块,不要直接输入大模型,而是先删除无关内容并突出重要上下文,从而减少整体提示长度,降低冗余信息对大模型的干扰。
  • 重新排序:对于检索到的内容块,使用专门的排序模型,重新计算上下文的相关性得分。这些排序模型会考虑更多的特征,如查询意图、词汇的多重语义、用户的历史行为和上下文信息等,比如Cohere模型。

尽管高级RAG有所改进,但是仍然与实际应用与需求之间存在差距。比如数据源的质量问题,“Garbage in Garbage out”,如果本身原始数据或文档的质量就很差,最终生成的结果也不会好。

再比如对于结构化数据的处理,随着应用场景越来越丰富,不再局限于单一的非结构化文本数据源类型,而是拓展至涵盖诸如表格之类的半结构化数据,以及像数据库、知识图谱这样的结构化数据。即便是高级Rag,也没法有效捕捉实体之间的复杂关系和层次结构,比如分析一本书的主旨,或者分析某篇长论文的概念之间的关系。

随着 RAG 技术的发展,客户的期望值和需求持续演变,应用的设置也变得更为复杂。在这样的背景下,高级Rag又进一步演变为模块化Rag。

三、模块化Rag

模块化RAG并不是突然出现的,三个范式之间是继承与发展的关系,高级RAG是模块化RAG的一种特例形式,而朴素RAG则是高级RAG的一种特例。

所以,模块化Rag其实仍然是一种Advance Rag,但是模块划分更清晰,对于系统的管理也更加直观。如下图所示,模块化Rag的框架仍然和高级Rag类似,包括索引、检索前、检索中、检索后和生成,同时增加了一个编排环节,一共是六大环节。

同时,对每个环节的能力做了模块化分割,比如索引环节包括了块优化(chunk optimization)、结构组织(structural organization),比如预检索环节包括了查询转化(Query Transformation)、查询扩展(Query Expansion)、查询构建(Query Construction)等等。

Rag系统的发展历程,从朴素、高级到模块化

模块化Rag基本上涵盖了Rag系统的方方面面,下面风叔就围绕模块化Rag的每个模块,详细阐述每个模块遇到的问题和解决方案。

3.1 索引

在早期的Rag中,索引环节仍然面临着三个主要难题。首先,内容表述不完整,内容块的语义信息受分割方式影响,致使在较长的语境中,重要信息被丢失或被掩盖。

其次,块相似性搜索不准确,随着数据量增多,检索中的噪声增大,导致频繁与错误数据匹配,使得检索系统脆弱且不可靠。

最后,参考轨迹不明晰,检索到的内容块可能来自任何文档,没有引用痕迹,可能出现来自多个不同文档的块,尽管语义相似,但包含的却是完全不同主题的内容。

针对索引环节的问题,模块化Rag将其区分成了两个领域,块优化和结构组织,从而从两个不同的方向来解决索引的问题。

1)块优化(chunk optimizaition)

在内容分块的时候,分块大小对索引结果会有很大的影响。较大的块能捕捉更多的上下文,但也会产生更多噪声,需要更长的处理时间和更高的成本;而较小的块噪声更小,但可能无法完整传达必要的上下文。

块优化仍然维持了原有的内容分块的处理方式,主要有三种优化方案。

  1. 滑动窗口:将文档分割为多个滑动段落窗口,每个窗口包括多个连续的句子,前个窗口和后个窗口中存在重叠的句子,然后为每个窗口创造嵌入向量。这种方法能有效优化上下文丢失的问题,但也存在上下文大小控制不精准、单词或句子可能被截断等问题。
  2. 增加元数据:块可以用元数据(比如页码、文件名、作者、时间戳、摘要或问题)进行丰富,让索引更加精准。这个优化方案在高级Rag中即有使用,被继承到了模块化Rag中。
  3. 从小到大:将用于检索的块和用于合成的块区分开,在检索的时候用较小的块,在合成的时候用较大的块。

2) 结构组织

结构组织顾名思义,即改变索引的组织结构,从而提升RAG 系统对于相关数据的检索和处理的速度和准确性。主要有以下几种结构组织优化方式。

  • 多级索引:是指创建两个索引,一个由文档摘要组成,另一个由文档块组成,并分两步搜索,首先通过摘要过滤掉相关文档,然后只在这个相关组内进行搜索。这种多重索引策略使RAG系统能够根据查询的性质和上下文,选择最合适的索引进行数据检索,从而提升检索质量和响应速度。但为了引入多重索引技术,我们还需配套加入多级路由机制,比如对于查询“最新发表的Rag论文推荐”,RAG系统首先将其路由至论文专题的索引,然后根据时间筛选最新的Rag相关论文。
  • 知识图谱:嵌入模型虽然简单,但是没法有效捕捉实体之间的复杂关系和层次结构,所以导致传统RAG在面对复杂查询的时候特别吃力。比如,用户询问“《跨越鸿沟》这本书的主旨是什么”,传统Rag技术是肯定回答不出来的。但是知识图谱技术可以做到,因为利用知识图谱对数据集建立索引的时候,会做提取实体(Entity)以及实体之间的关系(Relationship),这样就能构建一种全局性的优势,从而提升RAG的精确度。

3.2 预检索

在朴素Rag中,往往直接使用原始query进行检索,可能会存在三个问题。

第一,原始query的措辞不当,尤其是涉及到很多专业词汇时,query可能存在概念使用错误的问题;

第二,往往知识库内的数据无法直接回答,需要组合知识才能找到答案;

第三,当query涉及比较多的细节时,由于检索效率有限,大模型往往无法进行高质量的回答。

在高级Rag中,提出了Query改写的解决思路,模块化Rag对于这一思路做了细化,主要从三个方向进行优化,查询扩展、查询转换和查询构建。

1)查询扩展(Query Expansion)

查询扩展就是将单个查询拓展为多个查询,这样可以丰富查询内容,为潜在的Query内容缺失提供更多上下文,从而确保生成答案的最佳相关性。

多查询(Multi-Query):借助提示工程通过大型语言模型来扩展查询,将原始Query扩展成多个相似的Query,然后并行执行。

目前有一种叫Rag-fusion的有效方案,首先对用户的原始query进行扩充,即使用 LLM 模型对用户的初始查询,进行改写生成多个查询;然后对每个生成的查询进行基于向量的搜索,形成多路搜索召回;接着应用倒数排名融合算法,根据文档在多个查询中的相关性重新排列文档,生成最终输出。

Rag系统的发展历程,从朴素、高级到模块化

子查询:通过分解和规划复杂问题,将原始Query分解成为多个子问题。比如原始Query的问题是“请详细且全面的介绍Rag“,这个问题就可以拆解为几个子问题,”Rag的概念是什么?“,”为什么会产生Rag?“,”Rag的原理是怎样的?“,”Rag有哪些使用场景“等等。

2) 查询转换(Query Transformation)

查询转换是将用户的原始查询转换成一种新的查询内容后,再进行检索和生成,相比前面讲的查询扩展,并没有增加查询的数量。

  • 查询重写:即直接利用LLM大模型重新表述问题。在进行多轮对话时,用户提问中的某些内容可能会指代上文中的部分信息,可以将历史信息和用户提问一并交给LLM大模型进行重新表述。
  • HYDE:全称是Hypothetical Document Embeddings,用LLM生成一个“假设”答案,将其和问题一起进行检索。HyDE的核心思想是接收用户提问后,先让LLM在没有外部知识的情况下生成一个假设性的回复。然后,将这个假设性回复和原始查询一起用于向量检索。假设回复可能包含虚假信息,但蕴含着LLM认为相关的信息和文档模式,有助于在知识库中寻找类似的文档。
  • Step-Back Prompting:如果果原始查询太复杂或返回的信息太广泛,我们可以选择生成一个抽象层次更高的“退后”问题,与原始问题一起用于检索,以增加返回结果的数量。例如,对于问题“勒布朗詹姆斯在2005年至2010年在哪些球队?”这个问题因为有时间范围的详细限制,比较难直接解决,可以提出一个后退问题“勒布朗詹姆斯的职业生涯是怎么样的?”,从这个回答的召回结果中再检索上一个问题的答案。

3) 查询构建(Query Construction)

和查询扩展与查询转化不一样,查询构建主要是为了将自然语言的Query,转化为某种特定机器或软件能理解的语言。因为随着大模型在各行各业的渗透,除文本数据外,诸如表格和图形数据等越来越多的结构化数据正被融入 RAG 系统。

比如在一些ChatBI的场景下,就需要将用户的Query内容,转化为SQL语句,进行数据库查询,这就是Text-to-SQL。

再比如工业设计场景下,可能需要将用户的Query转化为设计指令,或者设备控制指令,这就是Text-to-Cypher。

3.3 检索

在检索环节,模块化Rag主要设置了两个优化方向,检索器选择和检索器微调。

1) 检索器选择(RetriverSelection)

检索器本质上是一种计算Query和内容块相似性的算子,这种算子最原始的形态就是基于Embedding模型的向量相似性,但可以通过转化算子的方式进行优化。

  1. 稀疏检索器:用统计方法将查询和文档转化为稀疏向量。其优势在于处理大型数据集时效率高,只关注非零元素。但在捕捉复杂语义方面,可能不如密集向量有效
  2. 密集检索器:使用预训练的语言模型(PLMs)为查询和文档提供密集表示。尽管计算和存储成本较高,却能提供更复杂的语义表示

可以构建这两种检索器,然后通过编排模块,在特定Query的时候采用最合适的检索器

2)检索器微调(RetriverFine-tune)

在上下文可能与预训练语料库有差异的情况下,尤其是在存在大量专有术语的高度专业化领域,需要对检索器进行微调。

  • 监督微调:基于有标记的领域数据微调检索模型,通常借助对比学习来实现。
  • LM 监督检索器:与直接从数据集中构建微调数据集不同,该方法借助语言模型生成的结果作为监督信号,在RAG流程中对嵌入模型进行微调。
  • 适配器:适配器是一种轻量级的模块,可以在不改变原始模型结构的情况下,为模型增加新功能或调整模型行为,可以更好的和下游任务相适配。

3.4 检索后

在朴素Rag中,系统会将所有检索到的块直接输入到 LLM生成回答,导致出现中间内容丢失、噪声占比过高、上下文长度限制等问题。

高级Rag针对这个问题,提出了提示压缩和重新排序的解决思路,模块化Rag也同样继承了这类方法,并将其分成了三个模块,重排序、压缩和选择。

1)重排序(Rerank)

对于检索到的内容块,使用专门的排序模型,重新计算上下文的相关性得分。这些排序模型会考虑更多的特征,如查询意图、词汇的多重语义、用户的历史行为和上下文信息等,比如Cohere模型。

2) 压缩(Compression)

对于检索到的内容块,不要直接输入大模型,而是先删除无关内容并突出重要上下文,从而减少整体提示长度,降低冗余信息对大模型的干扰。

3) 选择(Selection)

与压缩文档内容不同,选择是直接移除无关的文档块。通过识别并剔除输入上下文中的冗余内容,可以精炼输入,从而提升语言模型的推理效率。有一种有效的选择方法叫做LLM-Critique,是在生成最终答案前,通过LLM批评机制,过滤掉相关性不高的文档。

3.5 生成

在朴素Rag和高级Rag的生成环节,可能会出现以下问题:第一,当系统忽略了以特定格式(例如表格或列表)提取信息的指令时,输出可能会出现格式错误;第二,输出错误或者输出不完整,比如对于一些比较类问题的处理往往不尽人意,以及可能出现的幻觉问题;第三,可能会输出一些不太符合人类/社会偏好,政治不正确的回答。

对于这类问题,模块化Rag将解决思路分成了两个方向,生成器微调和验证。

1) 生成器微调(Generator Fine-tune)

  • 指令微调:当大模型在特定领域缺少数据时,可以通过微调为其提供额外的知识。同时,微调可以调整模型的输入和输出,使大模型适应特定的数据格式和风格。
  • 强化学习:通过强化学习来让大模型的输出与人类偏好相契合,对最终生成的答案进行手动标注,然后借助强化学习给予反馈。
  • Prompt优化:RAG系统中的prompt应明确指出回答仅基于搜索结果,不要添加任何其他信息,避免大模型自主回答问题。

2) 验证(Verification)

  • 知识库验证:通过外部知识直接对大型语言模型生成的响应进行验证。
  • 基于模型的验证:运用小型语言模型来验证大型语言模型生成的响应。给定输入问题、检索到的知识和生成的答案,训练一个小型语言模型来判别生成的答案是否正确反映了检索到的知识。

3.6 编排

编排模块,是模块化Rag和高级Rag相比最大的不同。因为有了编排模块,模块化Rag就可以在关键节点进行决策,并依据先前的结果动态选择后续步骤,实现处理流程的自适应。比如对于特定的Query,应该选择哪个知识库、选择哪种检索器、适配器和生成器等等。

按照功能的不同,模块化Rag将编排环节分成了路由、调度和融合三个模块。

1)路由(Routing)

路由的作用,是为每个Query选择最合适的处理管道,以及依据来自模型的输入或补充的元数据,来确定将启用哪些模块。比如在索引环节引入多重索引技术后,就需要使用多级路由机制,根据Query引导至最合适的父级索引。

2)调度(Scheduling)

调度模块在模块化 RAG 中发挥着关键作用,尤其是在需要相对复杂规划和反思的场景下,何时需要递归、何时需要迭代、何时需要反馈、何时停止循环,都是由调度模块来控制的。

  • 规则判断:即提前确定好固定规则,由规则来进行调度,比如设定一个阈值,当评估分数超过阈值时,进行某一后续动作。
  • 知识图谱判断:从知识图谱中提取与问题相关的信息,构建一个由逻辑上相互关联的节点组成的推理链,每个节点都承载着解决问题的关键信息,可以分别进行信息检索和内容生成
  • Agentic-Rag:RAG应用退化成一个Agent使用的知识工具。我们可以针对一个文档/知识库构建多种不同的RAG引擎,比如使用向量索引来回答事实性问题;使用摘要索引来回答总结性问题;使用知识图谱索引来回答需要更多关联性的问题等。

3)融合(Fusion)

随着RAG进程的发展,已经超越了单一的线性流程,经常需要扩大检索范围或通过探索多条流水线来增加多样性。融合模块的作用不仅仅是合并答案,还要确保最终输出内容丰富,并且能够反映出问题的多维度特性。

LLM融合:先对每个分支的答案进行总结,提取关键信息,然后再将其输入到LLM中,以确保即使在长度受限的情况下也能保留最重要的内容。

互反排名融合:将多个检索结果的排名合并成一个统一的列表,然后用加权平均方法来提高整体的预测性能和排名精度。

四、总结

本篇文章重点介绍了Rag技术的发展历程、每个阶段面临的短板和解决方案。Rag系统上手很容易,但是要做好,还是很不容易的。所有的理论知识,都不如大家在实际项目中去摸索和实践。

作者:风叔,微信公众号:风叔云

本文由@风叔 原创发布于人人都是产品经理,未经作者许可,禁止转载。

题图来自Unsplash,基于CC0协议。

该文观点仅代表作者本人,人人都是产品经理平台仅提供信息存储空间服务。

更多精彩内容,请关注人人都是产品经理微信公众号或下载App
评论
评论请登录
  1. 关注公众号“风叔云”,回复关键词“rag优化方案”,获得rag完整的问题和优化资料

    来自浙江 回复