LangChain 文档切分器详解
在 LangChain 中,文档转换器是一种在将文档提供给其他 LangChain 组件之前对其进行处理的工具。通过清理、处理和转换文档,这些工具可确保 LLM 和其他 LangChain 组件以优化其性能的格式接收数据。
加载完文档之后还需要对文档进行转换。文本分割器专门用于将文本文档分割成更小、更易于管理的单元。理想情况下,这些块应该是句子或段落,以便理解文本中的上下文和关系。分割器考虑了 LLM 处理能力的局限性。通过创建更小的块,LLM 可以在其上下文窗口内更有效地分析信息。
主要文本分割器类型
- CharacterTextSplitter
- RecursiveCharacterTextSplitter
- Split by tokens
- Semantic Chunking
- HTMLHeaderTextSplitter
- MarkdownHeaderTextSplitter
- RecursiveJsonSplitter
- Split Code
CharacterTextSplitter
CharacterTextSplitter 根据指定的分隔符拆分文本,默认情况下分隔符设置为 \n\n。chunk_size 参数确定每个块的最大大小,并且只有在可行的情况下才会进行拆分。如果字符串以 n 个字符开头,后跟一个分隔符,然后在下一个分隔符之前有 m 个字符,则如果 chunk_size 小于 n + m + len(separator),则第一个块的大小将为 n。
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
separator="\n\n",
chunk_size=1000,
chunk_overlap=200,
length_function=len,
is_separator_regex=False,
)
参数说明:
separator:这是用于标识文本中自然断点的分隔符。在本例中,它被设置为 \n\n,这意味着分割器将寻找双换行符作为潜在的分割点。
chunk_size:此参数指定每个文本块的目标大小,以字符数表示。在这里,它被设置为 1000,这意味着分割器将旨在创建大约 1000 个字符长的文本块。
chunk_overlap:此参数允许连续块之间重叠字符。它被设置为 200,这意味着每个块将包含前一个块末尾的 200 个字符。这种重叠可以帮助确保在块之间的边界上不会丢失任何重要信息。
length_function:这是一个用于测量文本块长度的函数。在本例中,它被设置为内置的 len 函数,该函数计算字符串中的字符数。
is_separator_regex:此参数指定分隔符是否为正则表达式。它设置为 False,表示分隔符是一个纯字符串,而不是正则表达式模式。
使用示例:
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader("book.pdf")
pages = loader.load_and_split()
from langchain_text_splitters import CharacterTextSplitter
text_splitter = CharacterTextSplitter(
separator="\n",
chunk_size=1000,
chunk_overlap=200,
length_function=len,
is_separator_regex=False,
)
texts = text_splitter.split_text(pages[0].page_content)
print(len(texts))
print(texts[0])
RecursiveCharacterTextSplitter
关键区别在于,如果结果块仍然大于所需的 chunk_size,它将继续分割结果块,以确保所有最终块都在指定的大小限制内。它由字符列表参数化。
from langchain_text_splitters import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
separators=["\n\n", "\n", " ", ""],
chunk_size=50,
chunk_overlap=40,
length_function=len,
is_separator_regex=False,
)
texts = text_splitter.split_text(pages[0].page_content)
print(len(texts))
print(texts[2])
在文本拆分的上下文中,'递归'意味着拆分器将重复将其拆分逻辑应用于生成的块,直到它们满足某些标准,例如小于指定的最大长度。这在处理需要分解成更小、更易于管理的片段(可能在不同的粒度级别)的非常长的文本时特别有用。
Split By Tokens
原文:'The quick brown fox jumps over the lazy dog。'
标记:['The', 'quick', 'brown', 'fox', 'jumps', 'over', 'the', 'lazy', 'dog']
在此示例中,文本根据空格和标点符号拆分为标记。每个单词都成为单独的标记。在实践中,标记化可能更复杂,尤其是对于具有不同书写系统的语言或处理特殊情况(例如,'don't'可能拆分为'do'和'n't')。
有各种标记器。
TokenTextSplitter 来自 tiktoken 库。
from langchain_text_splitters import TokenTextSplitter
text_splitter = TokenTextSplitter(chunk_size=10, chunk_overlap=1)
texts = text_splitter.split_text(pages[0].page_content)
print(texts[0])
SpacyTextSplitter 来自 spacy 库。
from langchain_text_splitters import SpacyTextSplitter
text_splitter = SpacyTextSplitter(chunk_size=1000)
texts = text_splitter.split_text(pages[0].page_content)
NLTKTextSplitter 来自 nltk 库。
from langchain_text_splitters import NLTKTextSplitter
text_splitter = NLTKTextSplitter(chunk_size=1000)
texts = text_splitter.split_text(pages[0].page_content)
利用 Hugging Face 标记器。
from transformers import GPT2TokenizerFast
tokenizer = GPT2TokenizerFast.from_pretrained("gpt2")
text_splitter = CharacterTextSplitter.from_huggingface_tokenizer(
tokenizer, chunk_size=100, chunk_overlap=10
)
texts = text_splitter.split_text(pages[0].page_content)
HTMLHeaderTextSplitter 是一个网页代码分块器,它根据 HTML 元素拆分文本,并将相关元数据分配给分块内的每个标头。它可以返回单个分块或将具有相同元数据的元素组合在一起,以保持语义分组并保留文档的结构上下文。此拆分器可与分块管道中的其他文本拆分器结合使用。
from langchain_text_splitters import HTMLHeaderTextSplitter
html_string = """
<!DOCTYPE html>
<html>
<body>
<div>
<h1>Foo</h1>
<p>Some intro text about Foo.</p>
<div>
<h2>Bar main section</h2>
<p>Some intro text about Bar.</p>
<h3>Bar subsection 1</h3>
<p>Some text about the first subtopic of Bar.</p>
<h3>Bar subsection 2</h3>
<p>Some text about the second subtopic of Bar.</p>
</div>
<div>
<h2>Baz</h2>
<p>Some text about Baz</p>
</div>
<br>
<p>Some concluding text about Foo</p>
</div>
</body>
</html>
"""
headers_to_split_on = [
("h1", "Header 1"),
("h2", "Header 2"),
("h3", "Header 3"),
]
html_splitter = HTMLHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
html_header_splits = html_splitter.split_text(html_string)
print(html_header_splits)
类似于 HTMLHeaderTextSplitter,专用于 markdown 文件。
from langchain_text_splitters import MarkdownHeaderTextSplitter
markdown_document = "# Foo\n\n ## Bar\n\nHi this is Jim\n\nHi this is Joe\n\n ### Boo \n\n Hi this is Lance \n\n ## Baz\n\n Hi this is Molly"
headers_to_split_on = [
("#", "Header 1"),
("##", "Header 2"),
("###", "Header 3"),
]
markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
md_header_splits = markdown_splitter.split_text(markdown_document)
print(md_header_splits)
RecursiveJsonSplitter
用于处理嵌套 JSON 对象,将其递归拆分为较小的 JSON 块。
import requests
json_data = requests.get("https://api.smith.langchain.com/openapi.json").json()
from langchain_text_splitters import RecursiveJsonSplitter
splitter = RecursiveJsonSplitter(max_chunk_size=300)
json_chunks = splitter.split_json(json_data=json_data)
print(json_chunks)
Split Code
LangChain 中的'Split Code'概念是指将代码划分为更小、更易于管理的段或块的过程。支持多种编程语言。
from langchain_text_splitters import Language
[e.value for e in Language]
Python 代码分割示例:
from langchain_text_splitters import (
Language,
RecursiveCharacterTextSplitter,
)
PYTHON_CODE = """
def hello_world():
print("Hello, World!")
# Call the function
hello_world()
"""
python_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.PYTHON, chunk_size=50, chunk_overlap=0
)
python_docs = python_splitter.create_documents([PYTHON_CODE])
print(python_docs)
JavaScript 代码分割示例:
JS_CODE = """
function helloWorld() {
console.log("Hello, World!");
}
// Call the function
helloWorld();
"""
js_splitter = RecursiveCharacterTextSplitter.from_language(
language=Language.JS, chunk_size=60, chunk_overlap=0
)
js_docs = js_splitter.create_documents([JS_CODE])
print(js_docs)
参数调优指南
在实际应用中,选择合适的参数对于 RAG(检索增强生成)的效果至关重要。
Chunk Size(块大小)
- 过小:可能导致语义不完整,检索时无法提供足够的上下文。
- 过大:超出 LLM 上下文窗口,增加计算成本,且可能引入噪声。
- 建议:通常设置在 500 到 1000 个字符之间,具体取决于模型能力。
Chunk Overlap(块重叠)
- 作用:防止在分割边界处切断重要的语义信息。
- 设置:通常设置为 chunk_size 的 10% 到 20%。
- 影响:增加重叠可以提高检索召回率,但会增加存储成本和索引时间。
Separator(分隔符)
- 原则:优先选择自然语言的分隔符(如段落、标题)。
- 层级:可以使用多级分隔符,如先按段落,再按句子。
常见误区与最佳实践
- 忽略元数据:在分割 HTML 或 Markdown 时,务必保留标题等元数据,这有助于后续检索时的重排序。
- 硬编码参数:不要对所有文档使用相同的 chunk_size,应根据文档长度动态调整。
- 过度依赖字符计数:对于多语言环境,建议使用基于 token 的分割器,因为不同语言的字符权重不同。
- 未测试效果:上线前应在小规模数据集上测试检索质量,观察是否出现语义断裂。
总结与选型建议
选择合适的文本分割器是构建高质量 RAG 系统的第一步。
| 分割器类型 | 适用场景 | 优点 | 缺点 |
|---|
| CharacterTextSplitter | 纯文本,结构简单的文档 | 实现简单,速度快 | 可能切断句子 |
| RecursiveCharacterTextSplitter | 长文档,混合结构 | 自动适应层级,保持语义 | 配置较复杂 |
| TokenTextSplitter | 多语言,需精确控制 token 数 | 适配 LLM 上下文限制 | 依赖外部库 |
| HTML/MarkdownSplitter | 结构化网页或文档 | 保留结构元数据 | 仅适用于特定格式 |
| RecursiveJsonSplitter | API 定义,配置文件 | 保持 JSON 结构完整性 | 仅限 JSON 数据 |
开发者应根据数据来源、目标模型及业务需求灵活组合使用上述策略,以达到最佳的检索与生成效果。