1. 概述

在检索增强生成(RAG)系统、文档处理系统以及其他基于大语言模型(LLM)的应用中,文本分割器(Text Splitter)是关键组件之一。它负责将长文档按照特定策略切分为更小的语义单元,这些单元随后会被转换为向量表示并存储在向量数据库中。

文本分割器的选择和配置直接影响:

  • 向量检索的准确性和相关性
  • 系统处理性能和资源利用率
  • 最终问答或生成任务的质量

本文档旨在为各类项目提供文本分割器的选型指导和应用建议,涵盖不同类型的分割器、适用场景以及最佳实践。

2. 文本分割器类型及特点

2.1 RecursiveCharacterTextSplitter(递归字符分割器)

特点

  • 按照预定义的字符分隔符列表递归地分割文本
  • 优先保持语义边界(如段落、句子)
  • 可控制块大小和重叠度

优点

  • 简单易用,性能较好
  • 适用于大多数文本类型
  • 保持语义边界,避免在句子中间切分
  • 可配置性强

缺点

  • 无法真正理解语义相似性
  • 对于复杂结构文档可能分割效果不佳

适用场景

  • 通用文本分割场景
  • 对性能要求较高的场景
  • 不需要严格语义连贯性的场景

配置参数

  • chunk_size: 每个文本块的最大长度
  • chunk_overlap: 相邻文本块之间的重叠长度
  • length_function: 用于计算文本长度的函数
  • separators: 用于分割文本的字符分隔符列表

2.2 MarkdownHeaderTextSplitter(Markdown标题分割器)

特点

  • 专门用于分割Markdown格式文档
  • 根据标题层级分割文档
  • 保留文档结构信息

优点

  • 保持Markdown文档结构
  • 生成的块具有上下文信息(标题层级)
  • 适合技术文档、说明文档等结构化文档

缺点

  • 仅适用于Markdown格式
  • 对于标题层级不规范的文档效果不佳

适用场景

  • Markdown格式文档处理
  • 技术文档、说明文档分割
  • 需要保留文档结构信息的场景

配置参数

  • headers_to_split_on: 标题层级映射列表,定义不同级别标题的标记和元数据键名

2.3 SemanticChunker(语义分割器)

特点

  • 基于嵌入模型计算语义相似性进行分割
  • 保持语义连贯性
  • 根据句子间语义差异确定分割点

优点

  • 生成语义连贯的文本块
  • 更符合人类理解方式
  • 适合高质量检索场景

缺点

  • 计算开销大,速度较慢
  • 依赖嵌入模型质量
  • 需要额外的配置参数(如阈值类型)

适用场景

  • 对语义连贯性要求高的场景
  • 长篇文章处理
  • 高质量检索需求场景

配置参数

  • embeddings: 用于计算语义相似性的嵌入模型
  • breakpoint_threshold_type: 确定分割点的阈值类型
  • breakpoint_threshold_amount: 分割阈值的具体数值

2.4 SpacyTextSplitter/NLTKTextSplitter(基于NLP库的分割器)

特点

  • 利用NLP库(SpaCy或NLTK)的句子分割功能
  • 基于自然语言处理技术

优点

  • 准确的句子边界识别
  • 适合自然语言处理任务

缺点

  • 需要额外安装依赖和模型
  • 处理速度相对较慢

适用场景

  • 需要精确句子分割的NLP任务
  • 学术文本处理
  • 对句子边界要求严格的场景

3. 分割器选型对比分析

分割器类型 性能 语义理解 适用文档类型 依赖项 配置复杂度
RecursiveCharacterTextSplitter 通用
MarkdownHeaderTextSplitter Markdown
SemanticChunker 通用 嵌入模型
SpacyTextSplitter 通用 SpaCy及模型
NLTKTextSplitter 通用 NLTK及数据

4. 不同应用场景的选型建议

4.1 通用文档处理场景

对于大多数文档处理场景,推荐使用RecursiveCharacterTextSplitter作为默认选择:

  • 配置简单,易于理解和调整
  • 性能表现良好,资源消耗较低
  • 适用于各种文本格式

典型配置示例:

1
2
3
4
5
6
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
separators=["\n\n", "\n", ". ", " ", ""]
)

4.2 技术文档处理场景

对于技术文档(特别是Markdown格式),推荐使用MarkdownHeaderTextSplitter:

  • 能够保留文档的层级结构信息
  • 有助于提高检索的准确性
  • 适合FAQ、API文档等结构化内容

典型配置示例:

1
2
3
4
5
6
7
8
9
headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]

markdown_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on
)

4.3 高质量语义检索场景

对于对语义连贯性要求较高的场景,推荐使用SemanticChunker:

  • 能够生成语义上更连贯的文本块
  • 提高检索结果的相关性
  • 适合处理长篇文章和复杂文档

典型配置示例:

1
2
3
4
5
6
7
8
from langchain_experimental.text_splitter import SemanticChunker
from langchain_openai.embeddings import OpenAIEmbeddings

text_splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile",
breakpoint_threshold_amount=85
)

4.4 学术或专业文本处理场景

对于需要精确句子边界的场景,推荐使用SpacyTextSplitter或NLTKTextSplitter:

  • 提供准确的句子分割能力
  • 适合处理语法复杂的文本
  • 适用于学术论文、法律文档等专业领域

典型配置示例:

1
2
3
4
5
6
from langchain.text_splitter import SpacyTextSplitter

text_splitter = SpacyTextSplitter(
pipeline="zh_core_web_sm", # 中文处理模型
chunk_size=1000
)

5. 组合策略与最佳实践

5.1 多分割器组合策略

根据不同文档类型采用不同的分割器:

  1. 检测文档类型(如Markdown、纯文本等)
  2. 根据类型选择合适的分割器
  3. 对分割结果进行后处理(如大小调整)

示例实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def get_text_splitter(file_extension):
if file_extension == ".md":
return MarkdownHeaderTextSplitter(headers_to_split_on=[
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
])
else:
return RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
length_function=len,
separators=["\n\n", "\n", "。", "!", "?", ". ", "!", "?", " ", ""]
)

5.2 两阶段分割策略

先按结构分割,再按长度分割:

  1. 使用结构感知分割器(如MarkdownHeaderTextSplitter)进行第一阶段分割
  2. 对分割后的块使用长度控制分割器进行二次处理

示例实现:

1
2
3
4
5
6
7
8
9
# 第一阶段:按标题分割
markdown_splitter = MarkdownHeaderTextSplitter(...)
initial_splits = markdown_splitter.split_text(document)

# 第二阶段:控制块大小
character_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)
final_splits = []
for split in initial_splits:
final_splits.extend(character_splitter.split_text(split))

5.3 参数调优建议

Chunk Size(块大小)

  • 一般建议范围:500-2000字符
  • 过小:增加向量化和检索开销
  • 过大:降低检索准确性,增加上下文负担

Chunk Overlap(重叠大小)

  • 一般建议为块大小的10%-20%
  • 有助于保持上下文连贯性
  • 过大:增加存储和计算负担

Separators(分隔符)

  • 根据文档语言和类型调整
  • 中文文档应包含中文标点符号
  • 从语义较强的分隔符到较弱的分隔符排序

6. 项目集成建议

6.1 当前项目适配

当前项目使用RecursiveCharacterTextSplitter作为默认分割器,针对中文文本进行了优化配置:

  • 使用中文标点符号作为分割符
  • 设置合适的chunk_size和chunk_overlap
  • 无需额外依赖,部署简单

这种配置在大多数场景下表现良好,具有较好的性能和可接受的分割质量。

6.2 扩展性考虑

为支持更多文档类型和分割需求,建议:

  1. 抽象分割器接口,支持插件化扩展
  2. 根据文件类型自动选择合适的分割器
  3. 提供配置选项,允许用户自定义分割参数

6.3 性能优化

  1. 对于大文档,考虑使用异步或批处理方式
  2. 合理设置线程池大小,避免资源竞争
  3. 对分割结果进行缓存,避免重复处理

7. 总结

文本分割器是RAG系统和其他LLM应用中的重要组件,选择合适的分割器对系统性能和效果有重要影响。在实际项目中,应根据具体需求和场景选择合适的分割器:

  1. 默认选择:RecursiveCharacterTextSplitter适用于大多数场景
  2. 结构化文档:MarkdownHeaderTextSplitter适用于Markdown等结构化文档
  3. 高质量需求:SemanticChunker适用于对语义连贯性要求高的场景
  4. 专业领域:SpacyTextSplitter/NLTKTextSplitter适用于需要精确句子分割的场景

建议采用组合策略,根据不同文档类型动态选择合适的分割器,并根据实际使用情况进行参数调优。