点击上方蓝字关注我们
关于RAG应用的一个共识是:你可以在10分钟甚至更短时间内构建一个原型;但是如果你希望它是一个足够健壮、性能卓越,能适用企业知识应用需求与多元数据环境的“生产就绪(Production Ready)”的系统,却是困难的。你可能会面临这些企业级RAG应用的常见挑战:
- 海量知识文档带来的精确检索等问题
- 任务形态不再是简单的事实性知识问答
- 更复杂的知识形态,不仅是文件与文本
- 更高的企业级工程要求,如响应时间与准确率
本文参考一些国内外的实践经验与分享,对构建企业级RAG应用中的一些关键优化考量做总结与解析,希望能够对我们开展实际项目有一定的指导意义(本文仅探讨方案,具体实践后续展开)。
- 选择合适的知识块大小
- 分离用于检索的块与用于生成的块
- 对大文档集知识库做分层检索
- 多模态知识的输入输出处理
- 考虑高级检索与查询重写
- 在投入生产之前对应用作全面评估
考量一
选择合适的知识块大小
首先是一个相对简单,但容易被忽视的基础参数问题。
无 论我们借助LlamaIndex还是LangChain构建RAG应用,在将外部知识特别是文件进行向量化存储时,都会遇到 chunk_size 这个决定把原始知识拆分成多大块(chunk)的简单参数,而chunk也是后续从向量库中检索上下文知识的基本单位。 因此chunk_size在很大程度上会影响到后续的检索与生成质量:
-
较小的chunk_size将会产生更小粒度、更多的知识块。 尽快更小的知识块可以有更精准的语义,但是风险是带有的上下文信息更少,可能导致需要的重要信息不出现在检索出来的顶部知识块中(特别是top_K比较小的时候)
-
较大的chunk_size有利于携带更加完整的上下文,但同时也带来语义检索不准确的隐患。 此外,过大的chunk_size也可能带来性能的下降、注入的上下文过大导致窗口溢出以及tokens成本升高的问题
-
合适的块大小有时候和你的RAG应用需要面临的潜在任务类型有关。 对于常见的事实性问答,你可能只需要少量特定的知识块;而对于摘要、总结、对比之类的任务,你可能需要更大的块甚至全部知识块。
因此,确定最合适的块大小本质上就是取得某种“平衡”。在不牺牲性能的条件下尽可能捕获最重要的知识信息。而在企业级应用场景下,最好的办法就是 借助成熟的LLM应用评估(evaluation)框架与测试数据集,来评估不同的chunk_size下的响应质量。 包括响应答案与检索出上下文的相关性、响应对于输入问题的有用性、响应的正确率等。具体实现可以借助独立的评估框架 RAGAS ,或者LlamaIndex中的 Evaluation 模块,以形成不同的chunk_size下的评估结果,进而作出最优选择。
一个 批量 评估 的过程 输出
考量二
分离用于检索的块与用于生成的块
虽然你可以尝试用最佳的 chunk_size 来试图在检索精准性与语义丰富性之间取得平衡,一劳永逸的解决问题。但企业数据环境的复杂性有时候决定了一个统一的chunk策略很难完全奏效。比如:
-
一个原始知识块包含了LLM生成所需要的详细信息,但同时也包含了一些可能使向量产生偏差的关键词信息,从而导致无法被精确检索
-
在一个问答型应用中,用户的提问方式可能相对简单,比较适合少量块的精准检索;但是在生成时则需要参考更多的上下文才能形成完整答案
因此一种常见的优化策略是: 对检索阶段(Retriever)的知识块与生成阶段(Generation)的知识块做分离考虑,即:输入给LLM的知识块不一定总是直接检索出来的top-k知识块。
这里假设,经典RAG应用的检索与生成过程中对知识块的传递如下:
那么考虑如下几种策略,以分离检索与生成阶段的知识块:
1. 从问题扩充到完整的问答对
如果原始知识是大量结构化的问答对,那么非常适合只对问题作向量嵌入。在精确检索到问题以后,再将关联的答案内容加入后一起输入LLM用作生成。 此外,这里还会有一些常见的检索前(Pre-Retriever)处理:
- 利用LLM生成更多 相似问法 做嵌入,以提高召回能力
- 利用LLM生成 假设性问题 做嵌入,来模拟问答对
2. 动态扩充知识块的上下文窗口
对常见的连续文档型知识,在检索到相关知识块后, 对其进行内容的扩展,将指定大小窗口内的上下知识块也同时带入LLM 。比如,如果设定窗口大小为3,则在检索到这个块后,通过读取块的关系信息或者元数据,获得上下关联的各3个知识块,一起输入给LLM。 显然,这种方案可以把每个知识块的粒度相对减小 ,以提高召回的精准性,但同时又能提供足够的生成上下文给LLM。
3. 从知识摘要扩充到原始内容
这种模式下会构建两种类型的知识块,一种用于保存知识摘要,一种则保存了原始知识内容。 检索基于知识摘要进行,在命中到相关的摘要块后,通过链接获得关联的一个或者多个知识内容块,并把其作为LLM生成的上下文 。这种方案提供了一种在更高层面检索知识的手段,而非直接检索知识。可能更适合原知识内容较为细节,但问题输入则相对概括且单一的场景。
4. 从“小”知识扩充到“大”知识
这是一种多层知识分割与嵌入方案。 即对相同的知识内容在多个不同的粒度上(多个Chunk_size)进 行分割并嵌入,在不同的语义粒度上提供多层的检索能力 ,比如在chunk_size为128,512两个粒度上进行分割与嵌入,同时保存大小块之间的关系。在检索到关联的小知识块后,根据关系信息查找到包含更丰富内容的大知识块,并输入给LLM。
上面几种拆分“检索块”与“生成块”的策略虽然在实现方法上各有差异,但基本思想是一致的: 用相对小的知识块提升检索的精准度,同时又能扩展到更大的知识块,以保证LLM有足够的上下文用来生成。
考量三
对大文档集知识库做分层检索
如果你只是用笔记本电脑上的一个简单pdf文档来构建经典RAG应用,你可能永远不会有这个问题的困惑。但在企业复杂的知识密集型应用中,你可能会面临几百个不同知识来源与类型的知识文档。虽然在管理与维护上,可以通过多级管理进行简化;但是在向量存储与检索的方法上,如果只是简单依赖于传统的文本分割+top-k检索,那么就会遇到精度不足、知识相互干扰等问题导致效果不佳,而这里最主要的问题仍然是检索阶段。一个重要的方法是实现 大文档集下的“分层”过滤与检索。 考虑以下三种不同的分层检索策略:
【元数据过滤 + 向量检索】
这种方案在向量知识库构建时根据文档信息对拆分的每个知识块做元数据标识(比如这里的地区、类型),然后生成向量并存储。那么在检索时的流程变为:
- 利用LLM推理输入问题的元数据信息
- 借助向量数据库的元数据过滤定位到部分文档
- 结合向量语义检索进一步定位到top-k的知识块
这种方案的好处是简单的借助了向量库的能力,可以快速自动过滤并检索;但缺点是:
- 需要设计元数据标签
- 借助LLM推理问题元数据存在一定的不确定性
- 元数据只能精确匹配,不能语义搜索
- 可能需要借助向量数据库
【摘要检索 + 内容检索】
这种方案就是利用了上文提到的“小块”到“大块”的多层向量方案。对每个文档做摘要信息提取,并在摘要与原始文档两个级别上分别做嵌入与向量存储,并做好两者的关联。检索时的流程为:
- 在摘要级别做检索,获得相关的摘要知识块
- 根据摘要块的关联信息,可以关联到原始文档
- 递归查找原始文档的知识块,找到top-k的上下文
这种方案的好处是在两个层次上都可以做语义检索。缺点是:
- 需要借助LLM生成摘要知识块,较为麻烦且成本增加
- 摘要查找时如果语义匹配错误,则后续无法得到有效上下文
【多文档AI Agent】
与上面两种不同的是,这是一种基于AI Agent思想的方案。虽然也是一个“两级”的方案,但是并不是通过简单的两级向量来实现分层递归检索,而是一个两级的AI Agent, 并利用Agent的思维链模式进行推理 。其核心思想是:
-
为每一个文档或知识库实现一个知识Agent。这个Agent的能力是可以使用一个或者多个RAG工具来回答问题
-
在多个知识库Agent之上实现一个语义路由的Agent,这个Agent会借助推理来使用后端的知识Agent完成任务
所以这不是一个简单的知识检索优化方案,而是一个基于RAG之上的多级Agent方案(Agentic RAG)。 其最大的好处是具备了 极大的灵活性与扩展性,几乎可以完成任意基于知识的复杂任务 。知识既可以是这里向量化的知识,也可以是外部系统的结构化/非结构化知识。其主要优势来自于两点:
- 通过对二级Agent的扩展,可赋予其更多的工具能力,而不再局限于简单的回答事实性的问题,可以完成更多的知识型任务。 比如:整理、摘要生成、数据分析、甚至借助API访问外部系统的实时知识等
- 多个Tool Agent可以通过协作完成联合型的任务。 比如:对两个不同文档中的知识做对比与汇总。这也是经典的问答型RAG无法完成的任务
当然这种方案的缺点是具有一定的实现复杂性,后面我们也会用专门的文章与实例单独探讨Agentic RAG的应用。
我们将在【下】篇中继续探讨RAG应用的另外三个常见优化考量。
END
点击下方关注我,不迷路
交流请识别以下名片并说明来源