LlamaIndex 核心包中 15 种分块策略完全指南
在 RAG(检索增强生成)系统中,分块(Chunking)是影响检索质量最关键的一步。文本被切割成多大、怎么切,直接决定了嵌入向量的语义完整性和检索的准确性。
LlamaIndex 作为当前最流行的 RAG 框架,提供了非常丰富的分块策略。本文将详细介绍 LlamaIndex 核心包中 15 种可用的分块策略,从原理到代码示例,帮助你在不同场景下选择最适合的方案。
目录
一、继承体系概览
LlamaIndex 的分块器都继承自 NodeParser 抽象基类,整体结构如下:
NodeParser (抽象基类)
├── TextSplitter (文本分块基类)
│ ├── MetadataAwareTextSplitter (元数据感知)
│ │ ├── 1. TokenTextSplitter
│ │ └── 2. SentenceSplitter
│ ├── 3. CodeSplitter
│ ├── 4. LangchainNodeParser
│ ├── 5. SemanticSplitterNodeParser
│ └── 6. SemanticDoubleMergingSplitterNodeParser
├── 7. SimpleFileNodeParser
├── 8. HTMLNodeParser
├── 9. MarkdownNodeParser
├── 10. JSONNodeParser
├── 11. SentenceWindowNodeParser
├── 12. HierarchicalNodeParser
├── 13. MarkdownElementNodeParser
├── 14. UnstructuredElementNodeParser
└── 15. LlamaParseJsonNodeParser
共计 15 种可用分块策略,覆盖了从纯文本到结构化文档,从固定大小到语义感知,从单层到层级索引的各种需求。
二、基础文本分块类
1. TokenTextSplitter
原理:按 token 数量精确切分,不保证句子完整性。使用分词器把文本切成 token,然后按 chunk_size 硬切割。
核心参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
chunk_size |
1024 | 每个块的 token 数 |
chunk_overlap |
200 | 相邻块重叠 token 数 |
separator |
" " |
主要分隔符 |
backup_separators |
["\n"] |
备用分隔符列表 |
算法流程:
- 按分隔符列表逐层切分
- 贪婪合并直到接近
chunk_size - 保留
chunk_overlaptoken 重叠
适用场景:需要严格控制 token 数的场景,比如输入给 LLM 的 prompt 有严格长度限制。
示例代码:
from llama_index.core.node_parser import TokenTextSplitter
splitter = TokenTextSplitter(
chunk_size=512,
chunk_overlap=50,
)
chunks = splitter.split_text(long_text)
优缺点:
- ✅ 简单、快速,token 计数精确
- ❌ 会在句子中间切断,破坏语义连贯性
2. SentenceSplitter
原理:优先保持句子完整,在接近 chunk_size 时才切分,是 LlamaIndex 最常用的默认分块器。
核心参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
chunk_size |
1024 | 目标 token 数 |
chunk_overlap |
200 | 块间重叠 |
separator |
" " |
默认分隔符 |
paragraph_separator |
"\n\n\n" |
段落分隔符 |
secondary_chunking_regex |
[^,.;。?!]+[,.;。?!]? |
句子切分正则 |
分层切分逻辑:
- 先按段落分隔符切分段落
- 再按句子 tokenizer(默认 nltk)切分句子
- 如果句子还太长,按正则切分句片段
- 最后按空格/字符切分
贪婪合并算法:
# 伪代码示意
cur_chunk = []
cur_len = 0
for split in splits:
if cur_len + split.token_size > chunk_size and not new_chunk:
close_chunk() # 输出当前块,并保留最后 overlap 个 token 到下一块
else:
cur_chunk.append(split)
cur_len += split.token_size
适用场景:通用文本、知识文档、技术手册。本项目就使用了这个分块器:
node_parser = SentenceSplitter(
chunk_size=settings.chunk_size, # 512
chunk_overlap=settings.chunk_overlap, # 50
)
优缺点:
- ✅ 尽量保持句子完整,语义连贯性好
- ✅ 支持中文标点(正则包含
。?!) - ❌ 分块大小会略有浮动,不严格等于
chunk_size
3. CodeSplitter
原理:基于 Python AST 语法树切分代码,保留完整的函数/类结构,避免把一个函数切到两个块中。
核心参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
language |
- | 必填,编程语言名称 |
chunk_lines |
40 | 每个块的目标行数 |
chunk_lines_overlap |
15 | 块间重叠行数 |
max_chars |
1500 | 每个块最大字符数 |
count_mode |
"char" |
计数模式:char 或 token |
适用场景:代码仓库、技术文档中含大量代码块。
示例代码:
from llama_index.core.node_parser import CodeSplitter
splitter = CodeSplitter(
language="python",
chunk_lines=50,
chunk_lines_overlap=10,
)
优缺点:
- ✅ 保留代码语法结构,不会把函数拦腰切断
- ❌ 需要对应语言的 AST 解析支持
4. LangchainNodeParser
原理:代理包装器,让你可以在 LlamaIndex 中直接使用 LangChain 生态的任何 TextSplitter。
核心参数:
lc_splitter:LangChain 的 TextSplitter 实例
适用场景:已经在使用 LangChain,想复用已有的分块配置。
示例代码:
from langchain_text_splitters import RecursiveCharacterTextSplitter
from llama_index.core.node_parser import LangchainNodeParser
lc_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50,
)
parser = LangchainNodeParser(lc_splitter)
nodes = parser.get_nodes_from_documents(documents)
优缺点:
- ✅ 无缝集成 LangChain 生态
- ❌ 需要额外安装 langchain
三、语义分块类
5. SemanticSplitterNodeParser
原理:先切成句子,计算相邻句子组的嵌入相似度,在相似度突变处(语义断点)切分。
核心参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
buffer_size |
1 | 比较相似度时滑动窗口大小 |
breakpoint_percentile_threshold |
95 | 断点百分位阈值,越小切分越多 |
embed_model |
- | 必填,用于计算嵌入的模型 |
算法流程:
- 文本切分为句子
- 对每个句子计算嵌入向量
- 计算连续
buffer_size个句子与下一个句子的余弦距离(dissimilarity) - 如果距离大于
breakpoint_percentile_threshold百分位,则在此处切分
适用场景:长文档、知识文章,希望切分边界符合语义段落。
示例代码:
from llama_index.core.node_parser import SemanticSplitterNodeParser
parser = SemanticSplitterNodeParser(
buffer_size=1,
breakpoint_percentile_threshold=95,
embed_model=embed_model,
)
nodes = parser.get_nodes_from_documents(documents)
优缺点:
- ✅ 语义边界更自然,同一主题更可能在一个块
- ❌ 需要对每个句子做嵌入,计算开销大
6. SemanticDoubleMergingSplitterNodeParser
原理:在语义分块基础上增加两轮合并,先按阈值合并句子,再按相似度合并相邻块,减少碎片化。
核心参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
initial_threshold |
0.6 | 初始化新块阈值,相似度低于此才开新块 |
appending_threshold |
0.8 | 追加句子到当前块阈值 |
merging_threshold |
0.8 | 第二轮合并相邻块阈值 |
max_chunk_size |
1000 | 最大块大小(字符数) |
merging_range |
1 | 检查前几个相邻块,可选 1 或 2 |
language_config |
- | Spacy 语言配置(不用 embed_model 时) |
embed_model |
None |
使用嵌入模型代替 Spacy(推荐,多语言支持更好) |
两次合并:
- 第一轮:从句子开始,若当前句子与块相似度 >
appending_threshold就追加,否则开新块 - 第二轮:检查相邻块,若相似度 >
merging_threshold就合并成一个更大块
适用场景:多语言文档、高质量知识库构建。
优缺点:
- ✅ 比单次语义分块更紧凑,减少碎片化
- ✅ 支持 Spacy 或嵌入模型两种后端
- ❌ 计算开销更大
四、文件格式感知类
7. SimpleFileNodeParser
原理:分发器,根据文件扩展名自动选择对应的解析器。目前支持:
.md→MarkdownNodeParser.html→HTMLNodeParser.json→JSONNodeParser
适用场景:批量处理多种格式文件,自动路由。
示例代码:
from llama_index.core.node_parser import SimpleFileNodeParser
parser = SimpleFileNodeParser.from_defaults()
nodes = parser.get_nodes_from_documents(documents)
8. HTMLNodeParser
原理:按 HTML 标签切分,只提取指定标签内的文本。
默认标签:["p", "h1", "h2", "h3", "h4", "h5", "h6", "li", "b", "i", "u", "section"]
适用场景:爬取的网页、HTML 格式文档。
9. MarkdownNodeParser
原理:按 Markdown 标题层级切分,每个标题下的内容作为一个节点,并且会把标题路径写入 header_path 元数据。
核心参数:
header_path_separator:默认"/",用于拼接层级路径
示例结果:
# 第一章
## 1.1 节 → header_path = "第一章/1.1 节"
适用场景:Markdown 文档、笔记、技术文档,天然按标题分段,切分结果语义完整。
10. JSONNodeParser
原理:按 JSON 对象/数组结构递归切分,每个叶子对象成为一个节点。
适用场景:结构化 JSON 数据,比如 API 返回结果、配置文件。
五、关系/层级类
11. SentenceWindowNodeParser
原理:每个句子独立成为一个节点(用于嵌入),但元数据中携带该句子前后 window_size 个句子作为上下文窗口。检索时用句子级嵌入召回,然后把上下文窗口喂给 LLM。
核心参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
window_size |
3 | 每个句子两侧各取几个句子 |
window_metadata_key |
"window" |
存储窗口的元数据键 |
工作流程:
切分:句子1 句子2 句子3 句子4 句子5
↓
节点2: 句子2 → 窗口 = [句子1, 句子2, 句子3]
节点3: 句子3 → 窗口 = [句子2, 句子3, 句子4]
↓
检索:查询 → 嵌入匹配 → 召回节点3 → 取出窗口上下文 → 喂给 LLM
适用场景:追求检索精度,希望召回准确同时给 LLM 足够上下文。
示例代码:
from llama_index.core.node_parser import SentenceWindowNodeParser
parser = SentenceWindowNodeParser.from_defaults(
window_size=3,
)
nodes = parser.get_nodes_from_documents(documents)
优缺点:
- ✅ 嵌入粒度细,检索更精准
- ✅ 回答时仍有完整上下文
- ❌ 节点数量多,存储和检索开销更大
12. HierarchicalNodeParser
原理:递归多层分块,建立父子节点关系,支持从根到叶多粒度检索。
典型配置:[2048, 512, 128] 三层粒度:
- 第一层:大块 2048 token
- 第二层:中块 512 token
- 第三层:小块 128 token
- 自动建立 PARENT/CHILD 关系
辅助函数:
get_leaf_nodes(nodes):取出最细粒度节点用于检索get_root_nodes(nodes):取出顶层节点get_child_nodes(parent_nodes, all_nodes):获取子节点get_deeper_nodes(nodes, depth):获取指定深度节点
适用场景:长篇技术手册、书籍,需要多粒度检索策略(比如先粗检索再细检索)。
示例代码:
from llama_index.core.node_parser import HierarchicalNodeParser, get_leaf_nodes
parser = HierarchicalNodeParser.from_defaults(
chunk_sizes=[2048, 512, 128],
)
all_nodes = parser.get_nodes_from_documents(documents)
leaf_nodes = get_leaf_nodes(all_nodes) # 用叶子节点建索引
13. MarkdownElementNodeParser
原理:专用于处理 Markdown 中的嵌入式元素,把文本和表格等分离,表格单独摘要索引。
工作流程:
- 从 Markdown 提取文本块和表格
- 对表格生成摘要,摘要和原表格分别索引
- 返回文本节点和表格索引节点
适用场景:含大量表格的 Markdown 文档,比如财报、技术参数表。
14. UnstructuredElementNodeParser
原理:依赖 unstructured 第三方库提取文档元素,分离文本、表格、图片等。
依赖:需要安装 unstructured 和 lxml。
适用场景:处理复杂格式 PDF/DOCX 导出的文本,保留元素结构。
15. LlamaParseJsonNodeParser
原理:解析 LlamaParse(LlamaIndex 官方文档解析服务)输出的 JSON,提取文本和表格元素。
适用场景:使用 LlamaParse 管道处理 PDF,已经拿到 JSON 输出。
六、选择指南
根据你的文档类型和需求,参考下表选择:
| 文档类型 | 推荐分块器 | 理由 |
|---|---|---|
| 通用纯文本/知识文档 | SentenceSplitter | 平衡速度和质量,够用 |
| 代码文件 | CodeSplitter | 保留语法结构 |
| Markdown 文档 | MarkdownNodeParser | 按标题天然分段 |
| HTML 网页 | HTMLNodeParser | 按标签提取 |
| JSON 数据 | JSONNodeParser | 按结构切分 |
| 追求检索精度 | SentenceWindowNodeParser | 细粒度嵌入 + 上下文窗口 |
| 长文档、语义连贯优先 | SemanticSplitterNodeParser | 按语义断点切分 |
| 高质量多语言知识库 | SemanticDoubleMergingSplitterNodeParser | 两次合并减少碎片化 |
| 长篇技术手册/书籍 | HierarchicalNodeParser | 多粒度分层索引 |
| 大量表格 | MarkdownElementNodeParser | 表格单独索引 |
| 已用 LangChain | LangchainNodeParser | 复用已有分块器 |
调参建议:
chunk_size:如果 LLM 上下文窗口 4k → 512;8k → 1024chunk_overlap:一般取chunk_size的 1/10 左右,比如 512 → 50- 语义分块
breakpoint_percentile_threshold:越小切分越碎,默认 95 合适
七、总结
LlamaIndex 核心包提供了 15 种分块策略,从简单到智能覆盖各种场景:
| 大类 | 数量 | 特点 |
|---|---|---|
| 基础文本 | 4 | 简单快速,通用性强 |
| 语义分块 | 2 | 更智能但计算开销大 |
| 格式感知 | 4 | 按文件结构天然切分,语义更完整 |
| 关系层级 | 5 | 支持特殊索引策略(句子窗口、层级检索、元素提取) |
对于大多数 RAG 应用,SentenceSplitter 配 chunk_size=512、chunk_overlap=50 就是很好的起点。如果效果不满意,再尝试 SentenceWindowNodeParser 或 SemanticSplitterNodeParser 升级。
希望这篇完整指南能帮助你理解 LlamaIndex 的分块策略体系,在实际项目中做出更好的选择。
本文基于 LlamaIndex 0.10+ 核心包编写
评论区