【AI】七.RAG检索增强生成之文档切割 小滴课堂讲师 2025年09月18日 ai大模型, aigc 预计阅读 21 分钟 #### RAG系统链路构建之文档切割转换 * 构建RAG系统:**涉及的技术链路环节: 文档加载器->文档转换器->文本嵌入模型->向量存储->检索器** * RAG数据流水线示意图 ```python 原始数据 → 数据加载 → 预处理 → 向量化 → 存储 → 检索增强生成 ↗ ↗ ↗ PDF 文本清洗 嵌入模型 数据库 分块 网页 ```  ##### 需求背景,为啥要用 * 模型输入限制:GPT-4最大上下文32k tokens,Claude 3最高200k * 信息密度不均:关键信息可能分布在长文本的不同位置 * 格式兼容性问题:PDF/HTML/代码等不同格式的结构差异 ##### 文档转换器(Document Transformers) * 文档转换器是 LangChain 处理文档流水线的核心组件,负责对原始文档进行结构化和语义化处理, * 为后续的向量化存储、检索增强生成(RAG)等场景提供标准化输入。 * 核心任务:文本清洗、分块、元数据增强 * 关键操作 * **文本分块**:按固定长度或语义分割(防止截断完整句子) * **去噪处理**:移除特殊字符、乱码、广告内容 * **元数据注入**:添加来源、时间戳等上下文信息 * 效果 * **保留语义完整性**:避免因分割导致上下文断裂或信息丢失 * **适配模型输入限制**:确保分割后的文本块长度符合大语言模型(LLM)的token限制 * **优化向量化效果**:通过合理分块提升向量表示的语义精度,从而提高检索匹配率 | 问题类型 | 原始文档示例 | 转换前问题 | 转换后效果 | | :--------: | :-----------: | :-----------------------: | :--------------------: | | 长文本溢出 | 500页法律合同 | 直接输入导致API报错 | 分割为上下文合规的段落 | | 信息碎片化 | 产品手册PDF | 技术参数分散在不同页面 | 按功能模块重组内容 | | 噪音污染 | 网页抓取内容 | 包含广告/导航栏等干扰信息 | 提取纯净正文内容 | | 格式混乱 | 代码仓库文档 | Markdown/代码片段混合 | 分离代码与说明文本 | ##### 基础类和核心参数说明 ```python from langchain_text_splitters import TextSplitter #源码 class TextSplitter(BaseDocumentTransformer, ABC): """Interface for splitting text into chunks.""" def __init__( self, chunk_size: int = 4000, chunk_overlap: int = 200, length_function: Callable[[str], int] = len, keep_separator: Union[bool, Literal["start", "end"]] = False, add_start_index: bool = False, strip_whitespace: bool = True, ) -> None: ``` * 方法说明 * `TextSplitter`本身没有实现`split_text`,要文档分割器按自己的分割策略实现分割 * 关键方法调用 `split_documents()->create_documents->()->split_text()` * `split_text()`是基础文本分割方法 * `create_documents()`在`split_text()`基础上封装了元数据绑定逻辑 * `split_documents()`内部调用`create_documents()`并自动处理元数据传递 | 方法 | 输入类型 | 输出类型 | 元数据处理 | 典型使用场景 | | :----------------------- | :------------------- | :--------------- | :------------------------- | :------------------------------------ | | **`split_text()`** | **单个字符串** | `List[str]` | ❌ 不保留元数据 | 仅需分割纯文本内容时使用 | | **`create_documents()`** | **字符串列表** | `List[Document]` | ✅ 需手动传递元数据 | 从原始文本构建带元数据的文档对象 | | **`split_documents()`** | **Document对象列表** | `List[Document]` | ✅ 自动继承输入文档的元数据 | 分割已加载的文档对象(如PDF解析结果) | * `chunk_size` * 定义:每个文本块的最大长度(字符数或token数),用于控制分割后的文本块大小。 * 作用 * 防止文本过长超出模型处理限制,影响检索精度。 * 较小的chunk_size能提高检索细粒度,会导致上下文缺失。 * 例子 ```python # 设置chunk_size=100,分割文本为不超过100字符的块 text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20) #输入文本:"Python是一种解释型语言,适合快速开发。它支持面向对象编程,语法简洁。" #分割结果:["Python是一种解释型语言,适合快速开发。", "开发。它支持面向对象编程,语法简洁。"](假设每个块接近100字符) ``` * `chunk_overlap` * 定义:相邻文本块之间的重叠字符数,用于保留上下文连贯性。 * 作用:避免因分割导致关键信息被切断(如句子中间被截断) * 案例一 ```python 如果chunk_size设为1024,chunk_overlap设为128, 对一个长度为2560的文本序列,会切分成3个chunk: chunk 1: 第1-1024个token chunk 2: 第897-1920个token (与chunk 1重叠128个) chunk 3: 第1793-2560个token (与chunk 2重叠128个) ``` * 案例二 ```python # 设置chunk_size=50,chunk_overlap=10 text = "深度学习需要大量数据和计算资源。卷积神经网络(CNN)在图像处理中表现优异。" text_splitter = CharacterTextSplitter(chunk_size=50, chunk_overlap=10) #分割结果:["深度学习需要大量数据和计算资源。卷积神经", "计算资源。卷积神经网络(CNN)在图像处理中表现优异。"] # 重叠部分"计算资源。"确保第二块包含前一块的结尾 ``` * `separators` * 定义:分隔符优先级列表,用于递归分割文本。 * 作用:优先按自然语义边界(如段落、句子)分割,减少语义断裂。 * 例子 ```python # 默认分隔符:["\n\n", "\n", " ", ""] text_splitter = RecursiveCharacterTextSplitter( separators=["\n\n", "。", ",", " "] ) #输入文本:"第一段\n\n第二段。第三段,第四段" #分割流程:先按\n\n分割为两段,若仍超长则按。继续分割 ``` #### 字符文档转换器TextSplitter案例实战 ##### `CharacterTextSplitter` 字符分割器 * 核心特点 * 是 LangChain 中最基础的文本分割器,采用**固定长度字符切割**策略。 * 适用于结构规整、格式统一的文本处理场景,强调**精确控制块长度** * 适用于结构清晰的文本(如段落分隔明确的文档)。 * 核心参数详解 | 参数 | 类型 | 默认值 | 说明 | | :------------------: | :--: | :------: | :----------------------: | | `separator` | str | `"\n\n"` | 切割文本的分隔符 | | `chunk_size` | int | `4000` | 每个块的最大字符数 | | `chunk_overlap` | int | `200` | 相邻块的重叠字符数 | | `strip_whitespace` | bool | `True` | 是否清除块首尾空格 | | `is_separator_regex` | bool | `False` | 是否启用正则表达式分隔符 | ##### 案例代码 * 长文本处理 ```python from langchain.text_splitter import CharacterTextSplitter text = "是一段 需要被分割的 长文本示例....,每个文本块的最大长度(字符数或token数)Document loaders are designed to load document objects. LangChain has hundreds of integrations with various data sources to load data from: Slack, Notion, Google Drive" splitter = CharacterTextSplitter( separator=" ", chunk_size=50, chunk_overlap=10 ) chunks = splitter.split_text(text) print(len(chunks)) for chunk in chunks: print(chunk) ``` * 日志文件处理 ```python from langchain.text_splitter import CharacterTextSplitter log_data = """ [ERROR] 2026-03-15 14:22:35 - Database connection failed [INFO] 2026-03-15 14:23:10 - Retrying connection... [WARNING] 2026-03-15 14:23:45 - High memory usage detected """ splitter = CharacterTextSplitter( separator="\n", chunk_size=60, chunk_overlap=20 ) log_chunks = splitter.split_text(log_data) for chunk in log_chunks: print(chunk) ``` 优缺点说明 | 特性 | 优势 | 局限性 | | :--------: | :----------------------: | :----------------------------------------: | | 分割速度 | ⚡️ 极快(O(n)复杂度) | 不考虑语义结构 | | 内存消耗 | 🟢 极低 | 可能切断完整语义单元, 对语义关联性保持较弱 | | 配置灵活性 | 🛠️ 支持自定义分隔符和重叠 | 需要预定义有效分隔符 | | 多语言支持 | 🌍 支持任意字符集文本 | 对表意文字计算可能不准确 | ##### 适合场景 * 推荐使用: - 结构化日志处理 - 代码文件解析 - 已知明确分隔符的文本(如Markdown) - 需要精确控制块大小的场景 * 不推荐使用: - 自然语言段落(建议用RecursiveCharacterSplitter) - 需要保持语义完整性的场景 - 包含复杂嵌套结构的文本 #### 递归字符文档转换器TextSplitter案例实战 ##### `RecursiveCharacterTextSplitter` 递归字符分割器 * 核心特点 * **递归字符分割器**采用**多级分隔符优先级切割**机制,是 LangChain 中使用最广泛的通用分割器。 * 递归尝试多种分隔符(默认顺序:`["\n\n", "\n", " ", ""]`),优先按大粒度分割 * 若块过大则继续尝试更细粒度分隔符,适合处理结构复杂或嵌套的文本。 * 核心参数说明 ```python from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( chunk_size=1000, # 目标块大小(字符)每个块最多包含 1000 个字符 chunk_overlap=200, # 块间重叠量,最多有200个字符重叠 separators=["\n\n", "\n", "。", "?", "!", " ", ""], # 优先级递减的分割符 length_function=len, # 长度计算函数 keep_separator=True, # 是否保留分隔符 ) ``` ##### 案例实战 * 基础案例测试, 处理后chunk之间也有overlap ```python from langchain_text_splitters import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter( #separators=["\n\n", "\n", " ", ""], #首先按段落分割,然后按行分割,最后按空格分割,如果都不行则按字符分割 chunk_size=20, #每个块的最大大小,不超过即可,如果超过则继续调用_split_text分隔(以字符数为单位) chunk_overlap=4 #块与块之间的重叠部分的大小 ) text = "I Love English hello world, how about you? If you're looking to get started with chat models, vector stores, or other LangChain components from a specific provider, check out our supported integrations" chunks = splitter.split_text(text) print(len(chunks)) for chunk in chunks: print(chunk) ``` * 学术论文处理 ```python from langchain.text_splitter import RecursiveCharacterTextSplitter paper_text = """ 引言机器学习近年来取得突破性进展...(长文本)若块过大则继续尝试更细粒度分隔符,适合处理结构复杂或嵌套的文本 方法我们提出新型网络架构...(技术细节)按优先级(如段落、句子、单词)递归分割文本,优先保留自然边界,如换行符、句号 实验在ImageNet数据集上...处理技术文档时,使用chunk_size=800和chunk_overlap=100,数据表格 """ splitter = RecursiveCharacterTextSplitter( chunk_size=20, chunk_overlap=4 ) paper_chunks = splitter.split_text(paper_text) print(len(paper_chunks)) for chunk in paper_chunks: print(chunk) ``` ##### 避坑指南 ```python # 错误示范:不合理的separators顺序 bad_splitter = RecursiveCharacterTextSplitter( separators=[" ", "\n"], # 空格优先会导致过早分割 chunk_size=500 ) # 正确写法:从大结构到小结构 good_splitter = RecursiveCharacterTextSplitter( separators=["\n\n", "\n", "。", " ", ""] ) ``` ##### 核心优势对比 | 特性 | CharacterTextSplitter | RecursiveCharacterTextSplitter | | :------------: | :-------------------: | :----------------------------: | | 分隔符策略 | 单级固定分隔符 | 多级优先级递归分隔符 | | 语义保持能力 | ★★☆☆☆ | ★★★★☆ | | 复杂文本适应性 | 简单结构化文本 | 混合格式/长文本/多语言 | | 典型应用场景 | 日志/CSV | 论文/邮件/网页/混合代码文 | ##### 推荐场景 * 学术论文/技术文档解析 * 多语言混合内容处理 * 包含嵌套结构的文本(如Markdown) * 需要保持段落完整性的问答系统 #### 分割器常见问题和优化最佳实践 ##### 其他常见分割器 * 递归字符分割(RecursiveCharacterTextSplitter) * 原理:按优先级(如段落、句子、单词)递归分割文本,优先保留自然边界(如换行符、句号) * 适用场景:通用文本处理,尤其适合逻辑紧密的长文档(如论文、技术手册) * 固定大小分割(CharacterTextSplitter) * 原理:按固定字符数分割,简单但可能打断句子结构。 * 优化:通过重叠(chunk_overlap)和智能截断(如优先在标点处分隔)减少语义断裂 * 适用场景:结构松散或句子独立性强的文本(如产品说明书) * Token分割(TokenTextSplitter) * 原理:基于LLM的token限制分割,避免超出模型输入长度。 * 优势:更贴近模型处理逻辑(如GPT系列) * 结构化文档分割 * HTML/Markdown分割器:按标题层级分割,保留元数据(如`HTMLHeaderTextSplitter`) * 适用场景:网页、技术文档等结构化内容 * 还有很多,可以查看官方文档拓展(如果不可访问,百度搜索) * https://python.langchain.com/docs/how_to/ ##### 关键参数chunk_size、chunk_overlap | 参数 | 作用 | 默认值 | 关键限制条件 | | :------------------ | :----------------------------------------------------------- | :----- | :------------------------ | | **`chunk_size`** | 定义每个文本块的最大长度(根据`length_function`计算,默认按字符数) | 1000 | 必须为正整数 | | **`chunk_overlap`** | 定义相邻块之间的重叠长度 | 20 | 必须小于`chunk_size`的50% | ##### 重叠内容未出现的常见原因 * 文本总长度不足:当输入文本长度 ≤ chunk_size时,不会触发分割。 ```python #解释:文本长度远小于chunk_size,不触发分割,无重叠。 from langchain_text_splitters import CharacterTextSplitter text = "这是一个非常短的测试文本。" text_splitter = CharacterTextSplitter( chunk_size=100, chunk_overlap=20, separator="。", # 按句号分割 length_function=len ) chunks = text_splitter.split_text(text) print(chunks) # 输出:['这是一个非常短的测试文本。'] ``` * 递归分割策略:RecursiveCharacterTextSplitter优先保证块大小,可能牺牲重叠。 ```python # 解释:当无法找到分隔符时,按字符数硬分割,强制保留重叠。 from langchain_text_splitters import RecursiveCharacterTextSplitter text = "这是一段没有标点的超长文本需要被分割成多个块但是因为没有分隔符所以分割器会尝试按字符递归分割直到满足块大小要求" text_splitter = RecursiveCharacterTextSplitter( chunk_size=30, chunk_overlap=10, separators=["", " "], # 无有效分隔符时按字符分割 length_function=len ) chunks = text_splitter.split_text(text) for i, chunk in enumerate(chunks): print(f"块{i+1}(长度{len(chunk)}): {chunk}") # 输出示例: # 块1(长度30): 这是一段没有标点的超长文本需要被分割成多个块但是因为没有分隔 # 块2(长度30): 个块但是因为没有分隔符所以分割器会尝试按字符递归分割直到满足 # 块3(长度15): 字符递归分割直到满足块大小要求 # 重叠部分:"个块但是因为没有分隔"(10字符) ``` * `enumerate()` * 函数是一个内置函数,用于在迭代过程中同时获取元素的索引和值。 * 它返回一个枚举对象,包含了索引和对应的元素 ```python # enumerate(iterable, start=0) #参数:iterable:必需,一个可迭代对象,如列表、元组、字符串等。 #参数:start:可选,指定索引的起始值,默认为 0。 fruits = ['apple', 'banana', 'orange'] for index, fruit in enumerate(fruits): print(index, fruit) ``` * 分隔符强制分割:在分隔符处切割时,剩余文本不足以形成重叠。 ```python #解释:分隔符优先切割,每个块正好为7个字符,无法形成重叠。 from langchain_text_splitters import CharacterTextSplitter text = "abcdefg.hijkllm.nopqrst.uvwxyz" text_splitter = CharacterTextSplitter( chunk_size=7, chunk_overlap=3, separator=".", # 按句号分割 is_separator_regex=False ) chunks = text_splitter.split_text(text) print("分割块数:", len(chunks)) for i, chunk in enumerate(chunks): print(f"块{i+1}: {chunk}") ``` ##### 参数调优最佳实践 * 通用文本处理 * 参数建议:`chunk_size=500-1000字符,chunk_overlap=10-20%` * 案例: * 处理技术文档时,使用`chunk_size=800和chunk_overlap=100` * 确保每个块包含完整段落,同时通过重叠保留跨段落的关键术语 * 代码分割 * 参数建议:根据编程语言特性调整分隔符。 * 案例: * 分割Python代码时,`RecursiveCharacterTextSplitter.from_language(Language.PYTHON)` * 会自动识别函数、类等结构,避免打断代码逻辑 ```python from langchain_text_splitters import Language, RecursiveCharacterTextSplitter python_splitter = RecursiveCharacterTextSplitter.from_language( language=Language.PYTHON, chunk_size=200, chunk_overlap=50 ) ``` * 结构化文档(如Markdown) * 参数建议:结合标题层级分割。 * 案例: * 使用MarkdownHeaderTextSplitter按标题分割,保留元数据 * 输入Markdown内容将按标题层级生成带元数据的块 ```python headers_to_split_on = [("#", "Header 1"), ("##", "Header 2")] markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on) ``` ##### 常见问题与解决方案 * 过长导致语义模糊 * 表现:检索时匹配不精准。 * 解决:缩小chunk_size,增加chunk_overlap * 块过短丢失上下文 * 表现:回答缺乏连贯性。 * 解决:合并相邻块或使用ParentDocumentRetriever,将细粒度块与父文档关联 * 参数选择原则: * 密集文本(如论文):chunk_size较大(如1000),chunk_overlap约15%。 * 松散文本(如对话记录):chunk_size较小(如200),chunk_overlap约20%。 * **实验验证:通过AB测试对比不同参数的检索准确率与生成质量**
评论区