构建一个检索增强生成 (Retrieval-Augmented Generation, RAG) 应用的概念验证过程相对简单,但要将其推广到生产环境中则会面临多方面的挑战。这主要是因为 RAG 系统涉及多个不同的组件,每个组件都需要精心设计和优化,以确保整体性能达到令人满意的水平。在这一过程中,外部非结构化数据的清洗和处理、文本分块、Query 的预处理、是不是每次 Query 都要进行检索、上下文信息的检索和排序能力、如何评估检索生成质量、知识缓存等环节都会影响系统的性能。
『RAG 高效应用指南』系列将就如何提高 RAG 系统性能进行深入探讨,提供一系列具体的方法和建议。同时读者也需要记住,提高 RAG 系统性能是一个持续的过程,需要不断地评估、优化和迭代。
本文是『RAG 高效应用指南』系列的第 1 篇文章,本文将首先对 RAG 系统前置离线环节的文档解析和文本分块进行深入探讨。
2020 年,Meta AI 研究人员提出了检索增强生成(RAG)的方法,用于提高 LLM 在特定任务上的性能。LLM 擅长语言理解、推理和生成等任务,但也存在一些问题:
• 信息滞后:LLM 的知识是静态的,来源于当时训练时的数据,也就是 LLM 无法直接提供最新的信息。
• 模型幻觉:实践表明,当前的生成式 AI 技术存在一定的幻觉,而在一些常见的业务应用中,我们是希望保证事实性的。
• 私有数据匮乏:LLM 的训练数据主要来源于互联网公开的数据,而垂类领域、企业内部等有很多专属知识,这部分是 LLM 无法直接提供的。
RAG 通过将检索到的相关信息提供给 LLM,让 LLM 进行参考生成,可以较好地缓解上述问题。因此,合理使用 RAG 可以拓展 LLM 的知识边界,使其不仅能够访问专属知识库,还能动态地引入最新的数据,从而在生成响应时提供更准确、更新的信息。
如图所示,是一个 RAG 应用的基本架构,可以分为离线和在线两部分。
• 离线:对知识库文档进行解析、拆分、索引构建和入库。这部分可以说是 dirty work,因为需要对多种不同格式的文档数据进行大量的清洗和处理等;但同时又是非常重要的工作,因为 「Garbage in, Garbage out」,数据质量是影响 RAG 最终效果的重要因素。
• 在线:当用户输入问题之后,我们会对 query 进行分析,如关键词提取、意图识别等,然后再根据路由条件进行知识库的多种召回检索或者联网搜索等,接着进行重排以提取最重要最相关的上下文信息,然后将用户问题和上下文信息一起提交给 LLM,让 LLM 根据背景知识提供可靠的回答。另外,为了进一步提高系统的性能,我们也会引入后置处理的环节,如风控检测、结果缓存和 RAG 相关指标的监控上报等。
本系列将根据这幅架构图,对其中的重要环节进行深入探讨,提供一系列具有可操作性的方法和建议,从而提高 RAG 系统的整体性能。
本文是『RAG 高效应用指南』系列的第 1 篇文章,本文将首先对 RAG 系统前置离线环节的文档解析和文本分块进行深入探讨。
拓展阅读
• https://research.facebook.com/publications/retrieval-augmented-generation-for-knowledge-intensive-nlp-tasks/
• https://myscale.com/blog/how-does-retrieval-augmented-generation-system-work/
• https://myscale.com/blog/prompt-engineering-vs-finetuning-vs-rag/
解析文档内容是 RAG 系统最重要的前置工作之一。很多时候,企业内部数据以各种各样的文件格式存在,如 PDF、Word 文档、PPT 和 Excel 表格等。如何从大量非结构化数据中提取出内容,就需要文档智能解析技术了。
文档智能解析是指利用机器学习算法,对文档内容进行自动识别、理解和处理的过程。它不仅包括文本内容的识别,还涉及到图像、图表和表格等非文本元素的解析。
一般而言,对于不同类型的文件,有不同的解析方法,如 HTML/XML 解析、PDF 解析等。这里我们以图片形式的文档(如对纸质文档进行了扫描、拍照等)为例进行说明。
首先,我们需要对文档进行版面分析(Layout Analysis,也称布局分析),用于识别和理解文档中的视觉和结构布局。这里会使用到区域检测和区域分类等技术。区域检测用于识别文档中的不同区域,如文本块、图像、表格和图表等。而区域分类则将检测到的区域进一步分类,如文本可以进一步被细分为标题、副标题、正文文本等;图像可能被分类为图片、图表或公式等;表格需要识别为包含数据的结构化形式。
然后,对这些区域分别进行识别。比如,对于表格区域,它们会被输入表格识别(TSR,Table Structure Recognition)模块进行结构化识别,包含解析表格的行和列,识别单元格边界,提取结构化数据。对于文本区域,使用 OCR 引擎将图像中的文字转为机器可读的字符。
随着大语言模型(LLM)和多模态技术的发展,文档理解领域逐渐出现了端到端的多模态模型,它们将文档内容和文档图像进行联合学习,这样一来,模型可以学习到不同文档模板类型的局部不变性信息,当模型需要迁移到另一种模板类型时,只需要人工标注少量的样本就可以对模型进行调优。
比如,微软的 LayoutLM 系列模型将视觉特征、文本和布局信息进行了联合预训练,在多种文档理解任务上取得了显著提升;OpenAI 的 GPT-4V 能够分析用户输入的图像,并为有关图像的问题提供文本回应,它结合了自然语言处理和视觉理解;微软的 Table Transformer 可以从非结构化文档中提取表格;基于 Transformer 的 Donut 模型无需 OCR 就可以进行文档理解;旷世科技近期发布的 OneChart 模型可以对图表(如折线图、柱状图和饼图等)信息进行结构化提取。
长期来看,大模型和文档理解进行结合应该是一个趋势。但就目前而言,多模态大模型与传统的 SOTA 方案相比,还不具备很好的竞争力,尤其是在处理细粒度文本的场景下。
在这里,笔者推荐几个用于文档解析的开源项目,如果读者有更好的方案推荐,也欢迎留言。
RAGFlow[https://github.com/infiniflow/ragflow] 是一款基于深度文档理解构建的开源 RAG 引擎。RAGFlow 的最大特色,就是多样化的文档智能处理,它没有采用现成的 RAG 中间件,而是完全重新研发了一套智能文档理解系统,确保数据 Garbage In Garbage Out 变为 Quality In Quality Out,并以此为依托构建 RAG 任务编排体系。对于用户上传的文档,它会自动识别文档的布局,包括标题、段落、换行等,还包含图片和表格等。RAGFlow 的 DeepDoc 模块提供了对多种不同格式文档的深度解析。
Unstructured[https://github.com/Unstructured-IO/unstructured] 是一个灵活的Python 库,专门用于处理非结构化数据,它可以处理各种文档格式,包括 PDF、CSV 和 PPT 等。该库被多个项目用于非结构化数据的提取,如网易有道的QAnything、Dify 等。
PaddleOCR[https://github.com/PaddlePaddle/PaddleOCR] 是由百度推出的 OCR 开源项目,旨在提供全面且高效的文字识别和信息提取功能。PaddleOCR 提供了版面分析、表格识别和文字识别等多种功能。PaddleOCR的应用场景广泛,包括金融、教育、法律等多个行业,其高效的处理速度和准确率使其成为业界领先的 OCR 解决方案之一。
文档智能解析技术的应用非常广泛,可用于法律文档的信息提取、财务报表的数据整理、医疗记录的分析等多个领域,它是搭建企业 RAG 系统不可或缺的部分。
拓展阅读
• https://www.infoq.cn/article/hjjm3kv620idoyyobtps
• https://zhuanlan.zhihu.com/p/35910823
• https://www.msra.cn/zh-cn/news/features/layoutlmv3
• https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.6/ppstructure/README_ch.md
• https://arxiv.org/pdf/2111.15664v5
• https://onechartt.github.io
文本分块(text chunking),或称为文本分割(text splitting),是指将长文本分解为较小的文本块,这些块被嵌入、索引、存储,然后用于后续的检索。通过将大型文档分解成易于管理的部分(如章节、段落,甚至是句子),文本分块可以提高搜索准确性和模型性能。
• 提高搜索准确性:较小的文本块允许基于关键词匹配和语义相似性进行更精确的检索。
• 提升模型性能:LLM 在处理过长的文本时可能会遇到性能瓶颈。通过将文本分割成较小的片段,可以使模型更有效地处理和理解每一部分,同时也有助于模型根据查询返回更准确的信息。
因此,文本分块是很重要的一个环节,在 RAG 的众多环节中,它也许是我们容易做到高质量的一个环节。
下面我们来看看有哪些分块的策略。
按大小分块是指将文本按固定字符数或单词数进行分割,这是最直接、最经济的分块方法,但也存在明显的问题,也就是语义不连贯。按大小分块通常不考虑文本的语义内容,因此有可能将相关联的信息切割开,导致分出的文本块在内容上缺乏连贯性和完整性。例如,一个完整的句子或一段函数代码可能会被截断在两个不同的块中,使得单独的块难以理解。
下面是一个使用 Langchain 的 CharacterTextSplitter 按大小分块的示例:
可以看到,CharacterTextSplitter 设置了 3 个参数:
• 分割符(separator):空格
• 文本块的最大长度(chunk_size):7
• 文本块之间的最大重叠长度(chunk_overlap):3,chunk_overlap 这个参数很重要,表示两个切分文本之间的重合度,设置重叠大小可以保持文本块之间的连续性。
特定格式分块是针对具有特定结构或语法特征的文本文件进行分块的一种方法,如 Markdown、LaTeX、Python 代码等。这种分块方式依据各自格式的特定字符或结构标记来实现,以保证分块后的内容在结构上的完整性和逻辑上的连贯性。比如 Markdown 文本可以使用标题(#
)、列表(-
)、引用(>
)等来进行分块。
针对特定格式的分块,langchain 提供了相应的方法,如:
• MarkdownTextSplitter:根据 markdown 的标题、列表或引用等规则来分割文本
• LatexTextSplitter:根据 latex 的 chapter、section 或 subsection 等规则来分割文本
• HTMLHeaderTextSplitter:根据 html 特定字符串分割文本,如 h1、h2、h3 等
• PythonCodeTextSplitter:根据 python 特定的字符串分割文本,如 class、def 等,总共有 15 种不同的语言可供选择
递归分块以一组分隔符为参数,以递归的方式将文本分成更小的块。如果在第一次分割时无法得到所需长度的块,它将递归地继续尝试。每次递归都会尝试更细粒度的分割符号,直到块的长度满足要求。这样可以确保即使初始块很大,最终也能得到较为合适的小块。例如,可以先尝试按照句子结束符来分割(如句号或问号),如果这样分割出的文本块太长,就会依次尝试其他的标记,例如逗号或者空格。通过这种方式,我们可以找到比较合适的分割点,同时尽量避免破坏文本的语义结构。
下面是一个使用 Langchain 的 RecursiveCharacterTextSplitter 进行递归分块的示例:
命题分块(propositional chunking)也是一种语义分块,它的原理是基于 LLM,逐步构建块。
• 首先从基于段落的句法分块迭代开始。
• 对于每个段落,使用 LLM 生成独立的陈述(或者命题),比如我们可以使用简单的提示「这段文字讨论了哪些主题」。
• 移除冗余命题。
• 索引并存储生成的命题。
• 在查询时,从命题语料库中检索,而不是原始文档语料库。
除了上面所说的分块策略,也还有很多其他的分块策略,比如 langchain 提供了根据 OpenAI 的 token 数进行分割的 TokenTextSplitter,还有使用 NLTK 分割器的 NLTKTextSplitter 等。
文本分块并没有固定的最佳策略。选择哪种方式取决于具体的需求和场景,需要根据业务情况进行调整和优化。关键是找到适合当前应用的分块策略,而不是追求单一的完美方案。有时候,为了获得更准确的查询结果,我们甚至需要灵活地使用多种策略相结合。
另外,为了直观分析文本分割器是如何工作的,我们可以使用 ChunkViz 工具进行可视化,它会展示文本是如何被分割的,可以帮助我们调整分割参数。
拓展阅读
• https://python.langchain.com/docs/modules/data_connection/document_transformers/
• https://github.com/FullStackRetrieval-com/RetrievalTutorials/blob/main/tutorials/LevelsOfTextSplitting/5_Levels_Of_Text_Splitting.ipynb
• https://arxiv.org/pdf/2312.06648
• https://chunkviz.up.railway.app/
RAG 是扩展 LLM 知识边界的利器,本文对 RAG 系统前置的文档解析和文本分块两个环节进行深入了探讨。
文档智能理解从各种各样非结构化的文档中提取内容,是构建高质量 RAG 系统的基础。数据质量决定成效。Garbage in, Garbage out. Quality in, Quality out.
文本分块将长文本分解为较小的文本块,这些块被嵌入、索引、存储,然后用于后续的检索。文本分块并没有固定的最佳策略,每种策略各有优缺点,关键在于根据具体的需求和场景,灵活运用不同策略,提高搜索准确性与模型性能。
文章来自微信公众号“ AI花果山” ,作者“ AI花果山 ”
【开源免费】FASTGPT是基于LLM的知识库开源项目,提供开箱即用的数据处理、模型调用等能力。整体功能和“Dify”“RAGFlow”项目类似。很多接入微信,飞书的AI项目都基于该项目二次开发。
项目地址:https://github.com/labring/FastGPT
【开源免费】graphrag是微软推出的RAG项目,与传统的通过 RAG 方法使用向量相似性作为搜索技术不同,GraphRAG是使用知识图谱在推理复杂信息时大幅提高问答性能。
项目地址:https://github.com/microsoft/graphrag
【开源免费】Dify是最早一批实现RAG,Agent,模型管理等一站式AI开发的工具平台,并且项目方一直持续维护。其中在任务编排方面相对领先对手,可以帮助研发实现像字节扣子那样的功能。
项目地址:https://github.com/langgenius/dify
【开源免费】RAGFlow是和Dify类似的开源项目,该项目在大文件解析方面做的更出色,拓展编排方面相对弱一些。
项目地址:https://github.com/infiniflow/ragflow/tree/main
【开源免费】phidata是一个可以实现将数据转化成向量存储,并通过AI实现RAG功能的项目
项目地址:https://github.com/phidatahq/phidata
【开源免费】TaskingAI 是一个提供RAG,Agent,大模型管理等AI项目开发的工具平台,比LangChain更强大的中间件AI平台工具。
项目地址:https://github.com/TaskingAI/TaskingAI
【开源免费】LangGPT 是一个通过结构化和模板化的方法,编写高质量的AI提示词的开源项目。它可以让任何非专业的用户轻松创建高水平的提示词,进而高质量的帮助用户通过AI解决问题。
项目地址:https://github.com/langgptai/LangGPT/blob/main/README_zh.md
在线使用:https://kimi.moonshot.cn/kimiplus/conpg00t7lagbbsfqkq0