LangChain教程:RecursiveCharacterTextSplitter文本分割器(搭配Docx2txtLoader拆分word) 作者:马育民 • 2026-02-28 14:21 • 阅读:10000 # 介绍 `RecursiveCharacterTextSplitter` 是 LangChain 文本分割器的首选款,核心解决 **长文本拆分时保证语义完整** 的问题: ### 原理 按「优先级分隔符列表」递归拆分文本——先尝试用高优先级分隔符(如空行、段落)拆分,若拆分后的片段仍超过指定长度,再用低优先级分隔符(如标点、空格)拆分,直到所有片段符合长度要求。 ### 优点 尽可能按 **自然语义边界** 拆分(而非生硬截断),避免“2025年销售额500万”被拆成“2025年销售额”和“500万”,适配大模型上下文窗口的同时保证语义连贯。 ### 使用场景 - RAG 知识库构建(拆分长文本,提升检索精度); - 文本长度超过大模型上下文窗口(如 GPT-3.5 4k tokens); - 需按段落/固定长度拆分文本。 # 语法 ``` RecursiveCharacterTextSplitter() ``` ##### 参数详解 | 参数 | 作用 | 中文场景推荐值 | |---------------------|----------------------------------------------------------------------|----------------------| | `chunk_size` | 每段文本的最大长度(按 `length_function` 计算)| 2000-4000(字符)| | `chunk_overlap` | 相邻段落的重叠长度(**避免拆分切断语义**)| 100-300(字符)| | `length_function` | 长度计算函数(中文用 `len()` 按字符数,英文可按 tokens 算)| `len`(中文)| | `separators` | 拆分分隔符列表(优先级从高到低)| 见上方代码(中文专属) | | `add_start_index` | 是否在元数据中添加片段在原文的起始索引 | `True`(便于溯源)| # 常用方法 ### split_documents() 拆分 `Document` 对象列表,返回拆分后的小 `Document` 列表。 ##### **特点**: - 保留原 `Document` 的所有元数据(如文件路径、来源),并新增「片段在原文的起始索引」; - 完全遵循初始化时的拆分规则(字符长度、重叠数、分隔符)。 ##### **适用场景**: - RAG 知识库构建(文件加载器加载后直接拆分,无缝接入向量库); - 需保留元数据的文本拆分(如溯源文本来源、文件路径); - 通用长文本拆分(Word/PDF/CSV 加载后的 `Document` 处理)。 ### split_text() 拆分纯文本字符串,返回纯文本片段列表(无元数据)。 ##### **特点**: - 仅输出文本片段,无任何元数据和索引信息; - 操作极简,无需封装 `Document`,直接处理字符串。 ##### **适用场景**: - 无元数据需求的纯文本拆分(如日志、简单笔记); - 快速验证拆分效果(仅看文本片段,不关注来源); - 纯文本批量处理(如文本统计、简单摘要)。 ### split_text_with_overlaps() 拆分文本并返回「片段内容+起始/结束索引+重叠长度」等精准信息。 ##### **特点**: - 不仅返回文本,还提供片段在原文的位置、重叠部分的长度; - 便于精准溯源和语义校验。 ##### **适用场景**: - 文本溯源(定位片段在原文的具体位置); - 语义完整性校验(检查重叠部分是否覆盖关键信息); - 企业级精准处理(如法律文档、合同拆分后的定位)。 # 方法选择 | 场景 | 首选方法 | 核心原因 | |-------------------------------|-----------------------|-------------------------------------------| | RAG 知识库构建(加载器后拆分) | split_documents() | 保留元数据,适配向量库/大模型生态 | | 纯文本拆分(无元数据需求)| split_text() | 极简操作,仅关注文本片段 | | 纯文本转标准化 Document | create_documents() | 自定义元数据,统一格式 | | 文本溯源/精准定位 | split_text_with_overlaps() | 提供索引/重叠信息,便于精准管理 | # 例子 本例为了便于理解,对字符串进行拆分 ``` from langchain_text_splitters import RecursiveCharacterTextSplitter text = """ 本报告旨在总结2025年度XX科技有限公司(以下简称“公司”)的经营情况、核心成果、存在问题及2026年度发展规划,为公司管理层决策、员工认知及合作方了解提供依据。2025年,公司围绕“数字化转型、提质增效”核心目标,稳步推进各项业务,实现营收、利润双增长,圆满完成年度既定目标。 """ # 初始化递归字符分割器(中文场景核心配置) splitter = RecursiveCharacterTextSplitter( chunk_size=20, # 每段最大字符数 chunk_overlap=10, # 段落重叠字符数(保证语义连贯) length_function=len, # 长度计算方式(中文用 len() 按字符数算) # 中文专属:分隔符优先级(从高到低,优先按自然语义拆分) separators=[ "\n\n", # 空行(优先按段落拆分) "\n", # 换行(按行拆分) "。", # 句号(按完整句子拆分) "!", # 感叹号 "?", # 问号 ";", # 分号 ";", # 分号 ",", # 逗号 "、", # 顿号 " ", # 空格 "", # 最后兜底:硬截断 ] ) # 3. 拆分文本(返回多个小 Document) split_docs = splitter.split_text(text) print("返回结果类型:", type(split_docs)) print(f"原始文本字符数:{len(text)}") print(f"拆分后片段数:{len(split_docs)}") for i, item in enumerate(split_docs): print(f"---第{i}段字符数:{len(item)}") print(f"第{i}段内容:{item}") ``` ##### 执行结果 [](https://www.malaoshi.top/upload/0/0/1GW2rPU4mfKa.png) ### 前几段有重复、后面无重复 的原因 这个问题不是参数设置错误,而是文本本身的「语义边界+长度分布」导致的正常现象,核心原因有3类: 1. **最后几段文本长度不足**: 当拆分到最后几个片段时,剩余文本的总长度 ≤ chunk_size + chunk_overlap,分割器会直接将剩余文本作为最后一段,不再强制重叠(否则会导致片段重复/长度超限)。 例:chunk_size=3000、chunk_overlap=200,最后剩余文本仅 2500 字符 → 最后一段直接取 2500 字符,无后续片段,因此无重叠。 2. **文本末尾无匹配的语义分隔符**: 分割器优先按「高优先级分隔符(空行/句号)」拆分,文本末尾若为无分隔符的连续文本,分割器会硬截断,导致重叠逻辑无法触发(仅按字符数拆分时,末尾易出现无重叠)。 3. **重叠长度超过剩余文本长度**: 若剩余文本长度 < chunk_overlap,分割器会放弃重叠(否则会导致片段内容完全重复)。 例:chunk_overlap=200,最后剩余文本仅 150 字符 → 无足够内容支撑重叠,因此无重复。 # 案例 适配 `Docx2txtLoader` 拆分大文本,中文场景最优配置 ```python from langchain_community.document_loaders import Docx2txtLoader from langchain_text_splitters import RecursiveCharacterTextSplitter # 1. 加载大 Word 文件,传入Word文件路径 loader = Docx2txtLoader("../企业报告.docx") doc = next(loader.lazy_load()) # 大文件用 lazy_load() 更友好 # 2. 初始化递归字符分割器(中文场景核心配置) splitter = RecursiveCharacterTextSplitter( # 核心参数 # chunk_size=3000, # 每段最大字符数(适配 GPT-3.5 4k tokens) chunk_size=200, # 每段最大字符数(适配 GPT-3.5 4k tokens) # chunk_overlap=200, # 段落重叠字符数(保证语义连贯) chunk_overlap=50, # 段落重叠字符数(保证语义连贯) length_function=len, # 长度计算方式(中文用 len() 按字符数算) # 中文专属:分隔符优先级(从高到低,优先按自然语义拆分) separators=[ "\n\n", # 空行(优先按段落拆分) "\n", # 换行(按行拆分) "。", # 句号(按完整句子拆分) "!", # 感叹号 "?", # 问号 ";", # 分号 ";", # 分号 ",", # 逗号 "、", # 顿号 " ", # 空格 "", # 最后兜底:硬截断 ] ) # 3. 拆分文本(返回多个小 Document) split_docs = splitter.split_documents([doc]) print("返回结果类型:", type(split_docs)) print(f"原始文本字符数:{len(doc.page_content)}") print(f"拆分后片段数:{len(split_docs)}") for i, item in enumerate(split_docs): print("-"*50) print(f"第{i+1}段字符数:{len(item.page_content)}") print(f"第{i+1}段内容:{item.page_content}") ``` ##### 执行结果 [](https://www.malaoshi.top/upload/0/0/1GW2rPeO80Cy.png) # 使用技巧 ### 1. 按 Token 数计算长度(适配大模型) 若需精准适配大模型的 tokens 限制(如 GPT-4o 的 128k tokens),可自定义长度函数: ```python import tiktoken # 定义按 tokens 计算长度的函数 def count_tokens(text: str) -> int: encoding = tiktoken.encoding_for_model("gpt-3.5-turbo") return len(encoding.encode(text)) # 初始化分割器(按 tokens 拆分) text_splitter = RecursiveCharacterTextSplitter( chunk_size=4000, # 4000 tokens(GPT-3.5 上限) chunk_overlap=200, length_function=count_tokens, # 改用 tokens 计算长度 separators=["\n\n", "\n", "。", "!", "?", ",", ""] ) ``` ### 2. 拆分纯文本(非 Document 对象) 若仅需拆分字符串(而非 `Document`),用 `split_text()` 方法: ```python # 拆分纯字符串 long_text = full_doc.page_content split_texts = text_splitter.split_text(long_text) print(f"拆分后文本片段数:{len(split_texts)}") ``` ### 3. 保留片段索引(便于溯源) 开启 `add_start_index`,可在元数据中看到每个片段在原文的起始位置: ```python text_splitter = RecursiveCharacterTextSplitter( chunk_size=3000, chunk_overlap=200, add_start_index=True, # 开启起始索引 separators=["\n\n", "\n", "。", ""] ) split_docs = text_splitter.split_documents([full_doc]) # 查看元数据中的起始索引 for doc in split_docs[:2]: print(f"起始索引:{doc.metadata['start_index']}") print(f"内容:{doc.page_content[:100]}...") ``` # 与其他分割器的对比 | 分割器类型 | 核心特点 | 适用场景 | 缺点 | |--------------------------|---------------------------|-------------------------|---------------------------| | RecursiveCharacterTextSplitter | 按语义递归拆分,适配中文 | 通用长文本、Word/PDF/CSV | 配置稍多(需指定分隔符)| | CharacterTextSplitter | 简单按字符数硬截断 | 无结构纯文本 | 易切断语义 | | TokenTextSplitter | 按 tokens 硬截断 | 精准适配大模型 tokens | 不考虑语义边界 | | MarkdownHeaderTextSplitter | 按 Markdown 标题拆分 | Markdown 文档 | 仅适配 MD 格式 | # 常见问题 | 问题 | 解决方案 | |-----------------------|--------------------------------------------------------------------------| | 中文拆分切断语义 | 配置中文专属 `separators`(优先按句号、换行拆分)| | 拆分后片段仍过长 | 减小 `chunk_size`,或补充更低优先级的分隔符(如空字符串)| | 重叠字符导致冗余 | 适当降低 `chunk_overlap`(如从 300 降到 100)| | 计算长度不准确 | 改用 `tiktoken` 按 tokens 计算长度(而非字符数)| # 总结 1. **核心定位**:`RecursiveCharacterTextSplitter` 是 LangChain 处理长文本的「通用最优解」,尤其适配中文场景; 2. **核心配置**:重点调 `chunk_size`(适配大模型)、`chunk_overlap`(保证语义)、`separators`(中文分隔符); 3. **核心优势**:按「自然语义边界」递归拆分,避免硬截断,是 RAG 场景拆分长文本的首选; 4. **使用场景**:搭配 `Docx2txtLoader`/`PyPDFLoader`/`CSVLoader` 等加载器,拆分大文件文本后接入向量库。 原文出处:http://malaoshi.top/show_1GW2rPemHeV1.html