趋近智
大型语言模型(LLM)依据其训练过程中编码的知识进行运作,因此无法访问训练之后产生的信息或私有数据。检索增强生成(RAG)提供了一种机制,通过将大型语言模型连接到外部数据源来弥补这一不足。构建RAG系统的首要实际步骤是将这些外部数据转换为适合处理的格式。这包含两个主要阶段:加载文档并将其拆分为易于处理的片段。
在大型语言模型能够使用外部信息之前,这些信息必须首先加载到我们的应用程序中。文档有多种格式,并存储在不同位置。常见来源包含:
专门的工具,通常称为DocumentLoaders(文档加载器),通常用于处理读取不同文件类型或访问数据源的具体事务。这些加载器将解析各种格式的复杂性抽象化。举例来说,PDFLoader会处理从PDF文件中提取文本,而WebBaseLoader则可能从URL获取并解析HTML内容。
在Python中使用加载器可能如下所示:
# 示例 - 具体库可能有所不同
# from some_library.document_loaders import PDFLoader
# 为特定文件类型初始化一个加载器
# loader = PDFLoader("path/to/your_report.pdf")
# 加载文档内容
# documents = loader.load()
# 'documents' 通常是一个列表,其中每个元素
# 代表源文件的一部分(例如,PDF中的一页)
# 并包含文本内容和元数据。
# print(documents[0].page_content) # 第一页的文本
# print(documents[0].metadata) # {'source': 'path/to/your_report.pdf', 'page': 0}
文档加载的一个重要方面是保留元数据。元数据是指有关数据的信息,例如原始文件名、页码、作者、创建日期或网页URL。将这些元数据与文档内容一并保留非常重要,因为它们可以在RAG处理的后续阶段使用。例如,当大型语言模型根据获取到的内容生成答案时,元数据允许您引用原始来源(例如,“根据‘your_report.pdf’的第5页,...”)。
文档加载完成后,它们通常过大,下游组件,特别是大型语言模型本身,无法有效处理。大型语言模型具有有限的上下文窗口,这是它们一次可以考虑的最大文本量(以标记衡量)。将整本书甚至一篇长报告直接输入到大型语言模型提示中通常是不可行的。
此外,为了使RAG的“检索”部分良好运行,我们需要在更小、更集中的文本片段中进行搜索。如果用户提出一个具体问题,获取整个章节不如获取包含答案的精确段落有用。
因此,加载之后,下一步是将大型文档拆分成更小的片段。这个过程也称为分段。拆分的主要原因包含:
没有一种完美的文档拆分方法;最佳策略通常取决于文档类型和具体的应用。以下是常见的方法:
固定大小分段: 最简单的方法是将文本拆分为预定字符长度的片段(例如,每1000个字符)。尽管易于实现,但这种方法可能会不自然地将句子甚至单词截断,可能在边界处丧失语义。
带重叠的固定大小分段: 为缓解上下文截断的问题,相邻的片段可以设置为重叠。例如,一个片段可能包含字符1-1000,下一个包含900-1900,再下一个包含1800-2800,以此类推。这种重叠(在本例中为100个字符)确保了片段边界附近的信息同时存在于两个连续片段中,从而减少了在跨越边界的查询中丢失相关上下文的可能性。
将文档拆分为重叠的片段。重叠有助于保留在拆分点可能丢失的上下文。
内容感知拆分: 这些方法尝试根据文本的结构或语义来拆分。例子包含:
\n\n)、句子结尾标点符号(.、?、!)或自定义标记。\n\n)拆分,然后是单换行符(\n),最后是空格( )。这有助于尽可能保持段落和句子在一起,同时仍遵守大小限制。语义分段: 更先进的方法使用自然语言处理模型(有时甚至是更小的嵌入模型)来识别文本中主题转换的点,旨在创建语义上连贯的意义单元。
理想的片段大小和拆分策略取决于多个因素:
找到最佳的分段参数(如大小和重叠)通常需要进行实验。您可以测试不同的配置,并评估获取结果和最终大型语言模型回应的质量。
框架通常提供实现各种策略的TextSplitter类。使用示例可能如下所示:
# 示例 - 具体库可能有所不同
# from some_library.text_splitter import RecursiveCharacterTextSplitter
# 假设 'documents' 是之前加载的列表
# text_splitter = RecursiveCharacterTextSplitter(
# chunk_size=1000, # 每个片段的目标大小
# chunk_overlap=150, # 片段之间重叠的字符数
# separators=["\n\n", "\n", ".", " ", ""] # 尝试的分隔符顺序
# )
# 将加载的文档拆分为更小的片段
# chunks = text_splitter.split_documents(documents)
# 'chunks' 现在是一个更小文本片段的列表,
# 每个片段仍可能与其原始元数据相关联。
# print(f"将 {len(documents)} 个文档拆分为 {len(chunks)} 个片段。")
# print(chunks[0].page_content) # 第一个片段的内容
# print(chunks[0].metadata) # 从原始文档继承/调整的元数据
重要的是,拆分过程要保留或适当地调整原始文档中的元数据,以用于每个新片段。这可以确保即使在拆分之后,您也能将一段文本追溯到其来源。
加载和拆分是RAG管道中必不可少的准备步骤。通过将原始外部数据转换为带有相关元数据的标准化、易于管理的片段,我们为下一阶段奠定了基础:创建向量嵌入以实现语义搜索和获取。
简洁的语法。内置调试功能。从第一天起就可投入生产。
为 ApX 背后的 AI 系统而构建
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造