1. 什么是文本分割器? #

文本分割器(Text Splitter)是一种将大型文档拆分为较小、可单独检索的文本片段(chunks)的工具。在自然语言处理和机器学习应用中,文本分割器非常重要,因为:

为什么需要文本分割?

  1. 模型上下文窗口限制:大多数语言模型(如 GPT、BERT 等)都有输入长度限制。例如,某些模型只能处理 512 个 token,而另一些可能支持 4096 或更多。当文档超过这个限制时,必须将其分割成更小的片段。

  2. 提高检索效率:在 RAG(检索增强生成)系统中,将长文档分割成小块可以提高检索的精确度。用户查询时,系统只需要检索相关的片段,而不是整个文档。

  3. 保持语义完整性:合理的分割策略可以确保每个文本块包含完整的语义单元(如段落、句子),避免在句子中间切断,从而保持上下文信息的完整性。

文本分割的核心挑战:

2. 安装必要的库 #

在开始之前,我们需要安装 LangChain 的文本分割器库。LangChain 提供了多种文本分割器实现,是最常用的工具之一。

2.1 Windows 系统安装 #

在 Windows PowerShell 或命令提示符中执行:

# 升级 pip 到最新版本(推荐)
python -m pip install --upgrade pip

# 安装 langchain-text-splitters 库
python -m pip install langchain-text-splitters

注意事项:

2.2 macOS 系统安装 #

在 macOS 终端中执行:

# 升级 pip 到最新版本(推荐)
python3 -m pip install --upgrade pip

# 安装 langchain-text-splitters 库
python3 -m pip install langchain-text-splitters

注意事项:

2.3 验证安装 #

安装完成后,可以通过以下代码验证是否安装成功:

# 尝试导入文本分割器类
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 如果没有报错,说明安装成功
print("langchain-text-splitters 安装成功!")

3. 前置知识 #

3.1 什么是 Chunk Size 和 Chunk Overlap? #

Chunk Size(块大小):

Chunk Overlap(块重叠):

为什么需要重叠?

3.2 分隔符(Separators)的作用 #

分隔符是用于分割文本的字符或字符串。常见的分隔符包括:

分隔符的选择直接影响分割质量。通常按照从粗粒度到细粒度的顺序使用分隔符。

4. 文本分割的三种主要策略 #

根据不同的应用场景和文档类型,文本分割可以采用三种主要策略。每种策略都有其独特的优势和适用场景。

4.1 基于长度的分割 #

核心思想: 严格按照指定的长度(字符数或 token 数)进行分割,确保每个块大小一致。

工作原理:

优势:

适用场景:

4.1.2 字符分割器(CharacterTextSplitter) #

字符分割器按照字符数进行分割,是最简单的分割方法。

# 导入字符文本分割器类
from langchain_text_splitters import CharacterTextSplitter

# 创建字符分割器实例,设置每个块最大长度为100个字符、不重叠、使用空字符串作为分隔符(即按字符切分)
text_splitter = CharacterTextSplitter(
    chunk_size=100, # 每个块最大长度为100个字符
    chunk_overlap=0, # 块之间不重叠
    separator="" # 使用空字符串作为分隔符(即按字符切分)
)

# 构造一个需要分割的长文本,这里由100个'1'、100个'2'和100个'3'拼接组成
document = f"""{"1"*100}{"2"*100}{"3"*100}"""

# 使用分割器的split_text方法,将原始文本切分为若干个子块
texts = text_splitter.split_text(document)

# 打印原始文本的长度(字符数)
print(f"原文长度:{len(document)} 字符")
# 打印分割后块的数量
print(f"分割为 {len(texts)} 个块")
# 打印前3个分块的内容(如果存在多于3个块)
print("\n前 3 个块:")
for i, text in enumerate(texts[:3], 1):
    # 打印每个块的编号、该块的字符长度和内容
    print(f"\n块 {i}{len(text)} 字符):{repr(text)}")

4.1.2 基于 Token 的分割 #

对于语言模型应用,按 token 数分割通常更准确,因为模型限制通常以 token 数表示。

# 从 langchain_text_splitters 导入 RecursiveCharacterTextSplitter,用于递归字符/Token分割
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 使用 tiktoken 编码器创建递归字符分割器
# 这里的编码名称为 "cl100k_base",适用于 GPT-4
# chunk_size 设置为 100,表示每块最多 100 个 token
# chunk_overlap 为 0,表示不同块之间没有重叠
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    encoding_name="cl100k_base",  # 编码名称(GPT-4 使用的编码)
    chunk_size=100,               # 每个块最多 100 个 token
    chunk_overlap=0               # 块之间不重叠
)

# 准备需要分割的长文本,由 100 个 "hello "、100 个 "world " 和 100 个 "python " 组成
document = f"""{"hello " * 100}{"world " * 100}{"python " * 100}"""

# 调用分割器的 split_text 方法,将长文本按 token 拆分成多个块
texts = text_splitter.split_text(document)

# 打印分割后的块数量
print(f"分割为 {len(texts)} 个块")
# 遍历分割后的每一个块,并打印块编号和具体内容
for i, text in enumerate(texts, 1):
    print(f"\n块 {i}{repr(text)}")

注意事项:

4.2 基于文本结构的分割 #

核心思想: 利用文本的天然层级结构(段落、句子、词语)进行分割,尽可能保持语义单元的完整性。

工作原理:

  1. 首先尝试按段落分割(使用 \n\n
  2. 如果段落仍然太大,按句子分割(使用 \n.
  3. 如果句子仍然太大,按词语分割(使用空格)
  4. 最后才按字符分割

优势:

适用场景:

4.2.1. 递归字符文本分割器(推荐使用) #

RecursiveCharacterTextSplitter 是 LangChain 中最常用的文本分割器,它实现了基于文本结构的分割策略。对于大多数应用场景,这是推荐的默认选择。

为什么推荐?

下面的示例演示如何使用 RecursiveCharacterTextSplitter 分割文本。

# 从 langchain_text_splitters 模块导入递归字符文本分割器类
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 创建递归字符文本分割器对象,指定参数
# chunk_size 表示每块最大允许的字符数为 100
# chunk_overlap 表示块与块之间没有重叠(重叠字符数为 0)
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=100,
    chunk_overlap=0
)

# 构造待分割的文本,包含多段各自为 99 或 100 个相同字符以及换行符
document = f"""{"1"*100}\n{"2"*99}\n\n{"3"*99}\n{"4"*99}"""

# 使用文本分割器的 split_text 方法将 document 分割为多个字符串块
texts = text_splitter.split_text(document)

# 打印一共分割出的块数
print(f"共分割为 {len(texts)} 个块:")

# 枚举输出每个块的序号及块内容(使用 repr 格式便于查看特殊字符)
for i, text in enumerate(texts, 1):
    print(i, repr(text))

运行说明:

RecursiveCharacterTextSplitter 的主要参数:

chunk_size(块大小):

chunk_overlap(块重叠):

length_function(长度函数):

is_separator_regex(分隔符是否为正则表达式):

separators(分隔符列表):

4.3 基于文档结构的分割 #

核心思想: 利用文档的格式结构(如 Markdown 标题、HTML 标签、JSON 对象)进行分割。

工作原理:

优势:

适用场景:

4.3.1 Split markdown #

在实际工程中,许多文档(如 Markdown、HTML、代码文件)都具有明显的结构性。针对这类结构化文本,直接用通用分割器可能导致优雅的段落、标题等被切割,影响后续检索和上下文理解。因此,LangChain 提供了专门的结构化文本分割器(如 MarkdownHeaderTextSplitter),能够基于文档的结构标签有层次地拆分文本。

以 Markdown 为例,MarkdownHeaderTextSplitter 支持根据不同级别的标题(如 #、##、### 等)对文本进行递归分割,每个分割块能够保持所属标题信息,便于后续内容聚合、检索与问答等应用。

下面的例子展示了如何使用 MarkdownHeaderTextSplitter 按 Markdown 结构拆分文档:

from langchain_text_splitters import MarkdownHeaderTextSplitter
markdown_document = f"""# Foo
Hi this is Bob

## Bar

Hi this is Alice

### Boo 

Hi this is Lance"""   

headers_to_split_on = [
    ("#", "Header 1"),
    ("##", "Header 2"),
    ("###", "Header 3"),
]

markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on,strip_headers=False)
md_header_splits = markdown_splitter.split_text(markdown_document)
for i, text in enumerate(md_header_splits, 1): 
    print(i, repr(text.page_content))

4.3.1 Split code #

对于结构化代码(如 Python、JavaScript、HTML、JSON 等),直接基于字符数或简单的分隔符进行切分往往会破坏代码的结构,造成函数、类、逻辑块等被断裂。LangChain 针对不同的主流编程语言提供了结构感知的分割器(如 RecursiveCharacterTextSplitter.from_language),能够更好地按照语言语法、结构边界(如函数、类、注释分隔)合理拆分代码块。

这种做法的好处包括:

实际应用中,如果要处理大段代码文本,可以优先选择结构化的分割器,提升检索和问答质量。同时,对于数据格式(如 Markdown、JSON 等),建议结合专用分割器和通用分割器,满足不同应用场景的需求。

# 从langchain_text_splitters模块导入Language和RecursiveCharacterTextSplitter
from langchain_text_splitters import (
    Language,
    RecursiveCharacterTextSplitter,
)

# 定义一个包含Python代码的多行字符串
PYTHON_CODE = """
def hello_world():
    print("Hello, World!")

# Call the function
hello_world()
"""

# 创建一个适用于Python语言的递归字符文本分割器,设置分块大小为50,无重叠
python_splitter = RecursiveCharacterTextSplitter.from_language(
    language=Language.PYTHON, chunk_size=50, chunk_overlap=0
)

# 使用分割器将Python代码分割为文档
python_docs = python_splitter.create_documents([PYTHON_CODE])

# 输出分割得到的文档对象
for i, doc in enumerate(python_docs, 1):
    print(i, doc.page_content)

5. 处理无词边界语言 #

某些语言(如中文、日文、泰文)没有明显的词边界,使用默认分隔符可能导致单词被拆分。本节介绍如何处理这些语言。

5.1 问题说明 #

什么是无词边界语言?

为什么需要特殊处理?

5.2 解决方案:自定义分隔符 #

下面的代码演示如何为中文文本添加合适的分隔符。

# 导入递归字符文本分割器类
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 创建递归字符文本分割器对象,专为中文文本优化
text_splitter = RecursiveCharacterTextSplitter(
    # 指定用于分割的分隔符列表,包含常见中英文标点
    separators=[
        "\n\n",    # 两个换行符,分隔段落
        "\n",      # 单个换行符,分隔行
        " ",       # 空格,分隔英文单词
        ".",       # 英文句号
        "。",      # 中文句号
        ",",      # 中文逗号(全角)
        "、",      # 中文顿号
        "\u200b",  # 零宽空格(部分亚洲语种分词用)
        "\uff0c",  # Unicode 全角逗号
        "\uff0e",  # Unicode 全角句号
        "\u3002",  # Unicode 中文句号
        "",        # 最后按字符强制分割
    ],
    # 设置每个块的最大字符数为 100
    chunk_size=100,
    # 设置相邻块之间的重叠字符数为 20
    chunk_overlap=20
)

# 构造待分割的中文文本示例
chinese_text = """
人工智能是计算机科学的一个分支。它试图理解智能的实质,并生产出一种新的能以人类智能相似的方式做出反应的智能机器。

机器学习是人工智能的一个子领域。它使计算机能够在没有明确编程的情况下学习和改进。

深度学习是机器学习的一个子集。它使用神经网络来模拟人脑的工作方式。
"""

# 使用分割器的 split_text 方法对中文文本进行分块
texts = text_splitter.split_text(chinese_text)

# 打印原文字符总长度
print(f"原文长度:{len(chinese_text)} 字符")
# 打印分割后得到的块数
print(f"分割为 {len(texts)} 个块\n")

# 遍历每个分块,依次打印其编号、长度及内容
for i, text in enumerate(texts, 1):
    print(f"块 {i}{len(text)} 字符):")
    print(text)
    # 打印分隔线便于区分
    print("-" * 50)

运行说明:

5.3 多语言混合文本处理 #

如果你的文档包含多种语言(如中英文混合),可以使用更全面的分隔符列表。

# 导入递归字符文本分割器类
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 创建一个递归字符文本分割器对象,支持多语言分隔符
text_splitter = RecursiveCharacterTextSplitter(
    # 指定用于分割的分隔符列表,包含中英文常用符号
    separators=[
        "\n\n",    # 段落分隔符
        "\n",      # 行分隔符
        "。",      # 中文句号
        ".",       # 英文句号
        "!",      # 中文感叹号
        "!",       # 英文感叹号
        "?",      # 中文问号
        "?",       # 英文问号
        ",",      # 中文逗号
        ",",       # 英文逗号
        ";",      # 中文分号
        ";",       # 英文分号
        " ",       # 空格
        "",        # 最后按字符进行分割
    ],
    # 设置每个块的最大字符数为 300
    chunk_size=300,
    # 相邻块之间重叠 50 个字符
    chunk_overlap=50
)

# 定义一段中英文混合的示例文本
mixed_text = """
Python is a popular programming language. 它简单易学,功能强大。

You can use Python for web development, data analysis, and machine learning. 
你可以用它来开发网站、分析数据和进行机器学习。

The syntax is clean and readable. 语法简洁易读。
"""

# 使用文本分割器将文本分割为多个块
texts = text_splitter.split_text(mixed_text)

# 打印分割后块的总数
print(f"分割为 {len(texts)} 个块\n")
# 遍历每一个分割出来的块,逐块打印其编号和内容
for i, text in enumerate(texts, 1):
    print(f"块 {i}:")
    print(text)
    print("-" * 50)

6. 常见问题与解决方案 #

在使用文本分割器的过程中,可能会遇到一些问题。本节列出常见问题及解决方法。

6.1 问题 1:分割后的块大小不一致 #

问题描述: 设置了 chunk_size=500,但实际块的大小可能小于 500。

原因:

解决方案:

6.2 问题 2:中文文本分割效果不好 #

问题描述: 中文文本被不合理地分割,可能在句子中间切断。

原因:

解决方案:

6.3 问题 3:重叠部分导致重复内容 #

问题描述: 设置了 chunk_overlap,但发现相邻块有重复内容。

原因:

解决方案:

6.4 问题 4:分割速度慢 #

问题描述: 处理大文件时,分割速度很慢。

原因:

解决方案:

6.5 问题 5:Token 计数不准确 #

问题描述: 使用 token 分割时,实际 token 数与预期不符。

原因:

解决方案: