LangChain 学习笔记 · Day 9 文档切分(TextSplitter)
学习 LangChain 的文档切分策略,掌握 RecursiveCharacterTextSplitter 等常见切分器的使用方法,并完成 PDF/Markdown 等文档的分块 Demo。
20 分钟阅读
LangChainPythonTextSplitterRecursiveCharacterTextSplitterPDFMarkdown
🎯 学习目标
- 理解 为什么要进行文档切分
- 掌握 LangChain 常见的 切分器(TextSplitter)
- 重点学习 RecursiveCharacterTextSplitter 的工作机制
- 通过 Demo:把 PDF 切成小块,观察切分效果与参数影响
🔹 为什么要切分?
- 模型上下文有限:长文档不能直接塞给 LLM,需要拆小块
- 检索更精准:小块向量化后命中率更高,减少无关噪声
- 减少割裂:合理的分隔符优先级,能最大程度保留语义边界
🔹 常见切分器对比
| Splitter | 核心思想 | 适用场景 | 优点 | 局限 |
|---|---|---|---|---|
| RecursiveCharacterTextSplitter | 按分隔符优先级递归切分 | 通用文本(默认首选) | 保留语义边界,灵活可调 | 极长句可能被拆 |
| CharacterTextSplitter | 单一分隔符切分 | 简单文本/日志 | 实现简单 | 语义感弱 |
| MarkdownHeaderTextSplitter | 按 Markdown 标题层级切 | 技术文档 | 结构保真 | 需二次切分控长 |
| HTMLHeaderTextSplitter | 解析 HTML 标签结构 | 网页文档 | 保留层级结构 | HTML 噪声需清理 |
| TokenTextSplitter | 按 token 粒度切 | 严格预算场景 | 与模型上下文对齐 | 可能割裂语义 |
| RecursiveJsonSplitter | 结构化切分 | JSON/YAML | 字段级检索 | 仅适合结构化数据 |
🔹 RecursiveCharacterTextSplitter 工作机制
- 设定分隔符优先级(如:
\n\n > \n > 。 > , > 空格 > 空字符) - 尝试用最高优先级切分;若仍超长,降级到下一级分隔符继续切
- 直到满足
chunk_size或退化为逐字符切 - 最终生成结果时加入
chunk_overlap,减少上下文割裂
🔹 参数含义
chunk_size:单个块的最大长度(字符或 token)chunk_overlap:相邻块的重叠部分,保证语义连续性- 经验值:
chunk_size = 800~1200chunk_overlap = 80~200(约 10%~20%)
👉 大块:上下文完整,但可能噪声↑
👉 小块:检索更精准,但上下文容易割裂
🧩 实战代码
1. 基础文本切分
from langchain.text_splitter import RecursiveCharacterTextSplitter
text = "这是一个示例文本。This is an example text.\n```python\ndef add(a, b): return a + b\n```"
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=150,
separators=["\n```", "\n\n", "\n", "。", "!", "?", ";", ",", ". ", "! ", "? ", ", ", " ", ""],
add_start_index=True,
length_function=len
)
docs = splitter.create_documents([text], metadatas=[{"source": "sample"}])
print(len(docs), docs[0].metadata, docs[0].page_content[:80])
2. PDF → 分块
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
pdf_path = "docs/sample.pdf"
pages = PyPDFLoader(pdf_path).load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=150,
separators=["\n```", "\n\n", "\n", "。", "!", "?", ";", ",", " ", ""],
add_start_index=True
)
chunks = splitter.split_documents(pages)
print(f"pages={len(pages)}, chunks={len(chunks)}")
print(chunks[0].metadata, chunks[0].page_content[:100])
3. Markdown 标题 + 二次 RCTS
from langchain.text_splitter import MarkdownHeaderTextSplitter, RecursiveCharacterTextSplitter
md_text = """
# 一、概览
内容 A
## 1.1 背景
内容 B
"""
headers = [("#", "h1"), ("##", "h2")]
md_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers)
sections = md_splitter.split_text(md_text)
rcts = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=120)
chunks = rcts.split_documents(sections)
print(chunks[0].metadata, chunks[0].page_content[:80])
4. Token 粒度切分
from langchain.text_splitter import TokenTextSplitter
tok_splitter = TokenTextSplitter(
encoding_name="cl100k_base",
chunk_size=800,
chunk_overlap=120
)
tok_chunks = tok_splitter.split_text("一大段长文本……")
print(len(tok_chunks), tok_chunks[0][:80])
✅ 今日收获
- 掌握了 为什么要切分,理解 chunk 与 overlap 的作用
- 熟悉了 LangChain 提供的主要切分器,尤其是 RecursiveCharacterTextSplitter
- 能够实际运行 PDF / Markdown / Token 的切分 Demo
- 为 Day 10 的 向量化存储 做好准备
⏭️ Day 10 展望
- 学习 Embedding + 向量数据库(Chroma)
- Demo:把切分后的 chunks 存入向量库,支持相似度检索