Recursive Chunking

Recursive Chunking is a hierarchical text splitting strategy that uses a priority list of separators (e.g., ["\n\n", "\n", " ", """]), starting with the highest-level (semantically strongest) separator. If a chunk still exceeds the chunk_size limit after splitting, it recursively applies the next-level separator to that chunk, continuing until all chunks meet the size requirement.
递归分块是一种层次化的文本分块策略,它使用一个分隔符优先级列表(如 ["\n\n", "\n", " ", ""]),从最高级(语义最强的分隔符)开始尝试分割文本。如果分割后的某个块仍然超过 chunk_size 限制,则对该块递归地使用下一级分隔符继续分割,直到所有块都符合大小要求

Core Content

Separator Priority

The separator list is the “brain” of the Recursive Chunking algorithm. It is not a random list—it follows a strict semantic hierarchy. Higher-priority separators represent stronger semantic boundaries.

默认分隔符: ["\n\n", "\n", " ", ""]
优先级分隔符含义语义强度
1 (最高)\n\n双换行 → 段落边界最强
2\n单换行 → 行边界中等
3空格 → 单词边界较弱
4 (最低)""空字符串 → 字符级别最弱(保底)

Core Parameters

参数说明建议值
chunk_size每个块的最大大小(默认按字符数计算)
The maximum size of a chunk. In LangChain, the default length_function is len() (character count). However, for LLMs, token count is more accurate because different LLMs have different tokenizers. A rule of thumb: for OpenAI gpt-4, use 512–1024 tokens (approx 2000–4000 characters for English). For Chinese, since each character is often 1-2 tokens, use smaller character counts (e.g., 500 chars).
200-500 字符
chunk_overlap块之间的重叠字符数,防止上下文在边界丢失

The number of overlapping characters/tokens between adjacent chunks. Why is this needed? Consider a sentence that spans the boundary between chunk 1 and chunk 2. Without overlap, when the user asks a question about that sentence, both chunks lack the full context. Overlap ensures that boundary sentences appear in full in at least one chunk. Best practice: set to 10-20% of chunk_size. If your data has long, continuous paragraphs, use a higher overlap (e.g., 20%).
chunk_size 的 15-20%
separators自定义分隔符列表根据文档语言调整
length_function计算块大小的函数,默认 len
The function used to measure the length of a piece of text. The default is len() (characters). You can replace it with a token counter (e.g., using tiktoken) for precise token budgeting. You can also use a word counter for multilingual documents where character counts are misleading.
可换为 token 计数
is_separator_regex If set to True, the separators are treated as regular expressions. This is powerful for complex splits like “split by one or more newlines” (\n+) or “split by punctuation followed by space” ([.!?]). Default is False for literal matching.

Chinese Language Support

中文分隔符列表

chinese_separators = [
“\n\n”, # 段落
“\n”, # 行
“。”, # 句号
“?”, # 问号
“!”, # 感叹号
“,”, # 逗号
“、”, # 顿号
” “, # 空格(如果有)
“” # 保底
]

4. Code Implementation

4.1 Basic Usage

</>bash
pip install langchain-text-splitters
%pip install langchain-text-splitters

# ============================================================
# 导入 RecursiveCharacterTextSplitter
# Import RecursiveCharacterTextSplitter
# ============================================================
from langchain_text_splitters import RecursiveCharacterTextSplitter

# ============================================================
# 准备示例文本
# Prepare example text
# ============================================================
sample_text = """
第一章:人工智能概述

人工智能是计算机科学的一个分支,致力于创建能够执行通常需要人类智能的任务的系统。
这些任务包括视觉感知、语音识别、决策制定和语言翻译等。

第二章:机器学习基础

机器学习是人工智能的核心子领域。它通过算法让计算机从数据中学习规律。
机器学习主要分为监督学习、无监督学习和强化学习三大类。
"""

# ============================================================
# 初始化 RecursiveCharacterTextSplitter
# Initialize RecursiveCharacterTextSplitter
# 
# 参数说明 (Parameter Description):
#   - chunk_size: 每个块的最大字符数 / Maximum characters per chunk
#   - chunk_overlap: 块之间的重叠字符数 / Overlap characters between chunks
#   - separators: 分隔符优先级列表 / Separator priority list
#   - length_function: 计算长度的函数 / Function to measure length
# ============================================================
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=100,           # 每块最多100字符 / Max 100 chars per chunk
    chunk_overlap=20,         # 块间重叠20字符 / 20 chars overlap
    length_function=len,      # 用 len() 计算长度 / Use len() for length
    separators=["\n\n", "\n", "。", ",", " ", ""],  # 中文分隔符 / Chinese separators
    is_separator_regex=False, # 是否按正则解析 / Whether to parse as regex
)

# ============================================================
# 方式1: split_text() - 返回字符串列表
# Method 1: split_text() - Returns list of strings
# ============================================================
chunks = text_splitter.split_text(sample_text)

print(f"共生成 {len(chunks)} 个块")
print("-" * 50)
for i, chunk in enumerate(chunks):
    print(f"块 {i+1} (长度: {len(chunk)}):")
    print(f"  {chunk[:50]}...")  # 只显示前50字符
# debug
print ('-'*50)
print (chunks)
--------------------------------------------------
['第一章:人工智能概述\n\n人工智能是计算机科学的一个分支,致力于创建能够 ....、无监督学习和强化学习三大类。']



# ============================================================
# 方式2: create_documents() - 返回 Document 对象列表
# Method 2: create_documents() - Returns list of Document objects
# 
# Document 对象包含 page_content 和 metadata,适合下游 RAG 流程
# Document objects contain page_content and metadata, suitable for downstream RAG
# ============================================================
from langchain_core.documents import Document

# 创建 Document 对象 / Create Document objects
documents = text_splitter.create_documents([sample_text])

# 可以附加元数据 / Can attach metadata
documents_with_meta = text_splitter.create_documents(
    [sample_text],
    metadatas=[{"source": "sample.txt", "chapter": "AI基础"}]
)

#debug
for doc in documents_with_meta:
    print(f"内容: {doc.page_content[:50]}...")
    print(f"元数据: {doc.metadata}")
    print("-" * 50)

内容: 第一章:人工智能概述

人工智能是计算机科学的一个分支,致力于创建能够执行通常需要人类智能的任务的系...
元数据: {'source': 'sample.txt', 'chapter': 'AI基础'}
内容: 第二章:机器学习基础

机器学习是人工智能的核心子领域。它通过算法让计算机从数据中学习规律。
机器学...
元数据: {'source': 'sample.txt', 'chapter': 'AI基础'}

4.2 从文件加载并分块(Load from File and Chunk)

<Bash>pip install langchain_community
<python>%pip install langchain_community

# ============================================================
# 从文件加载文档并分块
# Load document from file and chunk
# ============================================================
from langchain_community.document_loaders import TextLoader

# ============================================================
# 1. 加载文档 / Load document
# ============================================================
loader = TextLoader("my_document.txt")
documents = loader.load()  # 返回 List[Document]

# ============================================================
# 2. 配置分块器 / Configure splitter
# ============================================================
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,          # 技术文档用较大块 / Larger chunks for tech docs
    chunk_overlap=80,        # 重叠约16% / ~16% overlap
    separators=[
        "\n\n",              # 段落 / Paragraphs
        "\n",                # 行 / Lines
        "。", "?", "!",    # 中文句末标点 / Chinese sentence endings
        ",", "、",          # 中文句中标点 / Chinese mid-sentence punctuation
        " ",                 # 空格 / Spaces
        ""                   # 保底 / Fallback
    ]
)

# ============================================================
# 3. 执行分块 / Execute chunking
# ============================================================
chunked_docs = text_splitter.split_documents(documents)

print(f"原始文档数: {len(documents)}")
print(f"分块后文档数: {len(chunked_docs)}")

# ============================================================
# 4. 查看分块统计 / View chunk statistics
# ============================================================
chunk_lengths = [len(doc.page_content) for doc in chunked_docs]
print(f"块长度统计: 最小={min(chunk_lengths)}, 最大={max(chunk_lengths)}, 平均={sum(chunk_lengths)/len(chunk_lengths):.0f}")

4.3 按 Token 数分块(Token-based Chunking)

%pip install langchain_openai
%pip  install tiktoken

# ============================================================
# 按 Token 数分块(更精确控制 LLM 上下文)
# Token-based chunking (more precise control for LLM context)
# ============================================================
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings

# ============================================================
# 使用 tiktoken 编码器计算 token 数
# Use tiktoken encoder to count tokens
# ============================================================
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(
    encoding_name="cl100k_base",  # OpenAI 的编码器 / OpenAI's encoder
    chunk_size=512,               # 每块 512 tokens
    chunk_overlap=50,             # 重叠 50 tokens
)

chunks = text_splitter.split_text(sample_text)

#debug
print(f"共生成 {len(chunks)} 个块")
print(chunks)
共生成 1 个块
['第一章:人工智能概述\n\n人......无监督学习和强化学习三大类。']


# 验证 token 数 / Verify token count
import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
for i, chunk in enumerate(chunks):
    token_count = len(enc.encode(chunk))
    print(f"块 {i+1}: {token_count} tokens")

块 1: 172 tokens

4.4 自定义长度函数(Custom Length Function)

# ============================================================
# 自定义长度函数:按单词数而非字符数
# Custom length function: count words instead of characters
# ============================================================
def word_count(text: str) -> int:
    """计算文本中的单词数 / Count words in text"""
    return len(text.split())

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=100,           # 每块100个单词 / 100 words per chunk
    chunk_overlap=20,         # 重叠20个单词 / 20 words overlap
    length_function=word_count,  # 使用自定义长度函数 / Use custom length function
    separators=["\n\n", "\n", "。", ",", " ", ""]
)

chunks = text_splitter.split_text(sample_text)
for i, chunk in enumerate(chunks):
    print(f"块 {i+1}: {word_count(chunk)} 个单词")

Complete RAG Chunking Pipeline

# ============================================================
# 完整 RAG 分块流程:加载 → 分块 → 向量化 → 存入向量库
# Complete RAG chunking pipeline: Load → Chunk → Embed → Vector Store
# ============================================================

from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma

# ============================================================
# Step 1: 加载文档 / Load documents
# ============================================================
loader = TextLoader("knowledge_base.txt",encoding="utf-8")
docs = loader.load()
print(f"✅ 加载了 {len(docs)} 个文档")

# ============================================================
# Step 2: 递归分块 / Recursive chunking
# ============================================================
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=400,           # 适中大小 / Moderate size
    chunk_overlap=80,         # 20% 重叠 / 20% overlap
    separators=[
        "\n\n", "\n", 
        "。", "?", "!", 
        ",", "、", 
        " ", ""
    ]
)

chunked_docs = text_splitter.split_documents(docs)
print(f"✅ 分块完成: {len(chunked_docs)} 个块")
✅ 分块完成: 1 个块


%pip install chromadb
%pip install langchain-chroma
# ============================================================
# Step 3: 向量化并存入向量数据库 / Embed and store in vector DB
# ============================================================
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
from langchain_community.embeddings import OllamaEmbeddings   # <- I installed Ollama,runslocal
import os
#embeddings = OpenAIEmbeddings(model="text-embedding-3-small", api_key=your_API_KEY )

# I installed Ollama, running local
embeddings = OllamaEmbeddings(
    model="nomic-embed-text",
    base_url="http://localhost:11434",
    show_progress=True  # 显示下载进度
)

vectorstore = Chroma.from_documents(
    documents=chunked_docs,
    embedding=embeddings,
    persist_directory="./chroma_db"  # 持久化目录 / Persist directory
)

print(f"✅ 向量库创建完成,共 {vectorstore._collection.count()} 个向量")
OllamaEmbeddings: 100%|██████████| 1/1 [00:43<00:00, 43.13s/it]
✅ 向量库创建完成,共 1 个向量


# ============================================================
# Step 4: 测试检索 / Test retrieval
# ============================================================
query = "什么是机器学习?"
results = vectorstore.similarity_search(query, k=3)

print(f"\n🔍 查询: {query}")
print("-" * 50)
for i, doc in enumerate(results):
    print(f"结果 {i+1}: {doc.page_content[:100]}...")

Key Takeaways

要点 (Key Point)ENCN
分隔符顺序决定语义完整性Separator order determines semantic integrity. Always go from coarse (paragraph) to fine (character).分隔符顺序决定语义完整性。永远从粗(段落)到细(字符)。
chunk_overlap 防止边界信息丢失chunk_overlap prevents information loss at boundaries. Set to 10-20% of chunk_size.chunk_overlap 防止边界信息丢失。设置为 chunk_size 的 10-20%。
中文必须自定义分隔符Chinese text must use custom separators (). Never use the default English list.中文文本必须使用自定义分隔符()。切勿使用默认的英文列表。
元数据重复是隐藏的陷阱Metadata duplication is a hidden trap. When using split_documents(), be careful with unique ID fields.元数据重复是隐藏的陷阱。使用 split_documents() 时,注意唯一 ID 字段。
大文件要用流式处理For large files (> 1 GB), implement streaming chunking to avoid OOM errors.对于大文件(> 1 GB),实现流式分块以避免 OOM 错误。
字符数 vs Token 数Character count is fast but inaccurate. Token count is accurate but slower. Choose based on your use case.字符数快但不准确。Token 数准确但较慢。根据您的用例选择。