Skip to content

Text Splitters 文本分割器

文本分割器(Text Splitters)用于将长文档分割成更小的块(chunks)。这是构建 RAG(检索增强生成)系统的关键步骤——合适的块大小和分割策略直接影响检索质量和生成效果。

为什么需要文本分割

在 LangChain 的工作流中,文档加载后通常需要分割成小块,原因如下:

  1. 嵌入限制:嵌入模型有最大输入长度限制(如 512 或 8192 tokens)
  2. 检索精度:较小的块更容易与用户查询精确匹配
  3. 上下文窗口:LLM 的上下文窗口有限,需要选择合适的块大小
  4. 语义完整性:好的分割策略能保持语义单元的完整性

核心概念

文本分割器的工作方式:

文档 → 分割器(根据策略切割)→ 文档块列表

所有分割器都有一个共同的基础接口:

python
texts = text_splitter.split_text(text)           # 分割字符串
docs = text_splitter.split_documents(documents)   # 分割 Document 对象

常用分割器

RecursiveCharacterTextSplitter(推荐)

这是最常用的分割器。它递归地尝试按不同分隔符分割文本,从大到小依次尝试,直到块大小满足要求。

python
from langchain_text_splitters import RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,          # 每个块的最大字符数
    chunk_overlap=50,        # 块之间的重叠字符数
    length_function=len,     # 长度计算函数
    separators=[             # 分隔符优先级(从高到低)
        "\n\n",
        "\n",
        "。",
        ".",
        " ",
        "",
    ],
    keep_separator=True      # 保留分隔符
)

text = """LangChain 是一个强大的框架。
它帮助开发者构建基于 LLM 的应用程序。
文档加载、文本分割、向量存储等功能一应俱全。

在这个示例中,我们将演示如何分割文本。
注意保持语义单元的完整性。"""

chunks = text_splitter.split_text(text)
for i, chunk in enumerate(chunks):
    print(f"块 {i+1} ({len(chunk)} 字符): {chunk[:50]}...")

TokenTextSplitter

基于 token 数量而非字符数进行分割,更精确地控制 LLM 的输入大小。

python
from langchain_text_splitters import TokenTextSplitter

text_splitter = TokenTextSplitter(
    chunk_size=100,      # 每个块的最大 token 数
    chunk_overlap=10,    # 块之间的重叠 token 数
)

docs = text_splitter.create_documents([text])

MarkdownHeaderTextSplitter

按 Markdown 标题结构分割,保持文档的结构层次。

python
from langchain_text_splitters import MarkdownHeaderTextSplitter

headers_to_split_on = [
    ("#", "H1"),
    ("##", "H2"),
    ("###", "H3"),
]

splitter = MarkdownHeaderTextSplitter(
    headers_to_split_on=headers_to_split_on
)

markdown_text = """
# LangChain 入门指南

## 安装
使用 pip 安装 LangChain。

## 快速开始

### 基本用法
创建一个简单的链。

### 高级用法
使用 AgentExecutor 执行任务。

## 常见问题
FAQ 部分。
"""

docs = splitter.split_text(markdown_text)
for doc in docs:
    print(f"标题: {doc.metadata}, 内容预览: {doc.page_content[:40]}...")

RecursiveJsonSplitter

用于分割 JSON 数据,保持 JSON 结构的完整性。

python
from langchain_text_splitters import RecursiveJsonSplitter

splitter = RecursiveJsonSplitter(max_chunk_size=300)

json_data = {
    "title": "技术文档",
    "sections": [
        {"heading": "介绍", "content": "...很长的内容..."},
        {"heading": "方法", "content": "...很长的内容..."},
    ]
}

docs = splitter.split_json(json_data, convert_lists=True)

CharacterTextSplitter

按字符数简单分割。

python
from langchain_text_splitters import CharacterTextSplitter

text_splitter = CharacterTextSplitter(
    separator="\n",
    chunk_size=200,
    chunk_overlap=20,
)

SentenceTransformersTokenTextSplitter

基于 Sentence Transformers 模型的分词器进行分割,确保每个块不超过模型的最大 token 限制。

python
from langchain_text_splitters import SentenceTransformersTokenTextSplitter

splitter = SentenceTransformersTokenTextSplitter(
    chunk_overlap=0,
    model_name="sentence-transformers/all-mpnet-base-v2",
    tokens_per_chunk=256
)

docs = splitter.split_text(text)

分割策略选择

分割器适用场景优势
RecursiveCharacterTextSplitter通用文本灵活性高,效果稳定
MarkdownHeaderTextSplitterMarkdown 文档保持文档结构
RecursiveJsonSplitterJSON 数据保持数据层次
TokenTextSplitter精确控制 token 数与 LLM token 计数一致
CharacterTextSplitter简单文本实现简单

块重叠(Chunk Overlap)

块重叠确保分割边界上下文的连贯性:

python
# 无重叠:可能导致信息丢失
splitter_no_overlap = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=0,  # 无重叠
)

# 有重叠:保持上下文连贯
splitter_with_overlap = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=100,  # 保留部分上下文
)

重叠建议

  • 一般文本:chunk_overlap = chunk_size * 10% ~ 20%
  • 技术文档:chunk_overlap = chunk_size * 15% ~ 25%
  • 需要强连贯性:chunk_overlap = chunk_size * 30%

完整示例:RAG 预处理流程

python
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter

# 1. 加载文档
loader = DirectoryLoader(
    "./data/",
    glob="**/*.txt",
    loader_cls=TextLoader,
    show_progress=True
)
documents = loader.load()
print(f"加载了 {len(documents)} 个文档")

# 2. 分割文档
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", ".", " ", ""],
    keep_separator=True
)
chunks = text_splitter.split_documents(documents)
print(f"分割为 {len(chunks)} 个块")

# 3. 查看分割结果
for i, chunk in enumerate(chunks[:3]):
    print(f"\n--- 块 {i+1} ---")
    print(f"来源: {chunk.metadata.get('source', 'unknown')}")
    print(f"内容: {chunk.page_content[:100]}...")

最佳实践

  1. 选择合适的块大小:根据嵌入模型和 LLM 的 token 限制选择,通常 500-1000 字符
  2. 保持语义完整性:优先使用 RecursiveCharacterTextSplitter 以自然分隔符切割
  3. 合理设置重叠:重叠能保持上下文连贯,但不宜过大(不超过 chunk_size 的 20%)
  4. 结构化文档优先:Markdown、代码等结构化文档使用专门的分割器
  5. 测试不同参数:在不同数据集上测试 chunk_size 和 overlap 的效果

下一步

本站为非官方中文学习站点,不代表 LangChain 官方。部分内容参考官方文档并重新整理为中文学习笔记。