一、固定大小切分(Fixed-size chunking)

优势
顾名思义且容易实现。由于直接分割可能会破坏语义流程,建议在两个连续的片段之间保持一些重叠。
劣势
- 破坏语义结构:固定大小切分可能在句子或段落中间进行切分,导致语义信息被割裂,影响模型对文本的理解。
- 上下文信息丢失:由于切分不考虑文本的语义边界,相关信息可能被分散到不同的块中,导致模型在处理时缺乏必要的上下文支持。
- 缺乏灵活性:固定大小切分无法适应不同文本结构的变化,对于结构复杂或格式不一致的文档,可能导致切分效果不佳。
- 影响检索效果:在基于检索的生成任务中,固定大小切分可能导致相关信息被分散,降低检索的准确性和生成结果的质量。
- 处理长文本的挑战:对于包含长句子的文本,固定大小切分可能无法完整保留句子信息,影响模型的理解和处理能力。
缺点较多,不建议在大多数真实生产场景中使用。
二、语义切分(Semantic chunking)

原理概述
语义分块的核心思想是通过计算相邻句子的嵌入向量之间的相似度,识别语义上的断点,从而将文本划分为语义连贯的块。具体步骤包括:
- 句子分割:将文本按标点符号(如句号、问号等)分割成句子。
- 嵌入计算:使用嵌入模型(如 OpenAI Embedding)计算每个句子的向量表示。
- 相似度计算:计算相邻句子之间的语义相似度。
- 断点识别:当相邻句子的相似度低于设定阈值时,认为存在语义断点,进行分块。
- 块生成:将相似度高的句子合并为一个语义块。
这种方法旨在确保每个块内部的语义连贯性,从而提高检索和生成的准确性。
优势
- 语义完整性:通过识别语义断点,避免将相关内容分割到不同的块中,保持信息的完整性。
- 提高检索准确性:语义连贯的块有助于向量检索系统更准确地匹配用户查询,提高检索效果。
- 减少冗余信息:避免将无关信息混合在一个块中,减少噪声干扰。
劣势
- 计算成本高:需要计算大量句子之间的相似度,计算资源消耗大,处理速度较慢。
- 实现复杂:涉及嵌入计算、相似度分析等步骤,算法实现相对复杂。
- 效果依赖数据类型:在某些数据集上,语义分块的效果可能不如固定大小分块或递归分块。
语义分块在保持文本语义完整性方面具有优势,适用于对语义连贯性要求高的任务,如复杂问答系统。然而,其高计算成本和实现复杂度使其在资源受限或对处理速度要求高的场景中不太适用。相比之下,递归切分方法实现简单、处理速度快,适用于结构清晰的自然语言文本。因此,选择合适的分块策略应根据具体任务需求、数据类型和可用资源综合考虑。
三、基于文档结构的切片 (Document structure-based chunking)
文档结构化分块(Document Structure-Based Chunking)是一种利用文档自身结构进行内容切分的策略。其核心思想是根据文档的自然组织形式(如标题、段落、章节、函数等)进行分块,以保留语义完整性和上下文连贯性,从而提升检索和生成的效果。

"Document structure-based chunking"(基于文档结构的切分)不是按固定长度或句子切,而是"按逻辑块"划分内容,常见于 Word、PDF、HTML 等格式的企业文档处理中,尤其适合处理说明书、规约、设计文档的场景。
原理概述
文档结构化分块的基本原理是:
- 结构感知:识别文档中的结构元素(如 Markdown 的标题、HTML 的标签、代码中的函数或类等),并以这些元素作为分块的边界。
- 语义完整:确保每个分块在语义上是完整的,避免将相关内容拆分到不同的块中。
- 上下文保留:通过保留文档的结构信息,维护内容之间的逻辑关系,增强模型对上下文的理解。
例如:在处理包含 Markdown 格式的文档时,可以使用 LangChain 提供的 MarkdownHeaderTextSplitter 类,根据标题层级(如 #、##、###)进行分块,从而保留文档的层次结构。
原理解析
1. 结构识别
从文档中提取结构元素,包括但不限于:
- 标题层级(如 H1/H2/H3、1.、1.1、1.1.2)
- 段落、表格、列表、分隔线
- 样式信息(加粗、缩进、字号、字体)
- 页面结构(页眉页脚、分页、目录)
这些结构在不同文档格式中的表现形式不同,例如:
- Word:段落样式 + Outline Level
- PDF:字体大小、粗细、缩进等视觉特征
- HTML:DOM 节点层级
2. 结构驱动的切分逻辑
常见策略:
- 按 每一个小节(如 1.1、1.2.1)作为一个 chunk,或多个小节合并为一个 chunk
- 同一标题下的所有段落视为一个逻辑块
- 若小节内容过多,则再结合递归切分或 token 限制切分
关键不是按"多少 token"切,而是"从属于哪个结构单元"切。
3. 结构标签保留(可选)
切分后的 chunk 还可保留其结构标识,如:
json
{
"chunk": "本系统支持 7 层安全防护措施......",
"section": "2.3 安全架构设计"
}
这便于:
- 检索时进行 rerank
- LLM 回答时增强上下文定位感(结构提示)
举个例子:
给定这样一段 Word 文档内容:
markdown
1. 系统概述
介绍系统的设计目标与背景。
2. 功能模块
2.1 用户管理
包括登录、注册、权限分配等功能。
2.2 设备管理
支持设备的接入、控制与监控。
Structure-based Chunking 结果可能是:
- Chunk 1: 1. 系统概述 介绍系统的设计目标与背景。
- Chunk 2: 2.1 用户管理 包括登录、注册、权限分配等功能。
- Chunk 3: 2.2 设备管理 支持设备的接入、控制与监控。
docx2python 示例:
示例目录:
我们将把 Word 文档解析成如下结构:
css
[ { "id": "1", "title": "1. 系统概述", "level": 1, "content": "......正文内容......" }, { "id": "1.1", "title": "1.1 功能模块", "level": 2, "content": "......正文内容......" }, { "id": "2", "title": "2. 技术架构", "level": 1, "content": "......正文内容......" }]
完整代码:解析 Word 文档结构和正文内容
python
from docx2python import docx2python
import re
import json
def get_level_and_id(title: str):
"""
从标题行中提取编号和层级(例:1.2.3 → level 3)
"""
match = re.match(r"^(\d+(\.\d+)*)(\s+|$)", title.strip())
if match:
id_str = match.group(1)
level = id_str.count(".") + 1
return id_str, level
return None, None
def parse_docx_structure(docx_path: str):
doc_result = docx2python(docx_path)
body = doc_result.body
parsed_chunks = []
current_chunk = None
for section in body:
for para_group in section:
for para in para_group:
text = para.strip()
if not text:
continue
# 如果段落以编号开头,视为新段落标题
id_str, level = get_level_and_id(text)
if id_str:
# 存储上一段内容
if current_chunk:
parsed_chunks.append(current_chunk)
current_chunk = {
"id": id_str,
"title": text,
"level": level,
"content": ""
}
else:
# 不是新段落标题,加入当前内容
if current_chunk:
current_chunk["content"] += text + "\n"
else:
# 文档开头没有编号,强行起一块
current_chunk = {
"id": "",
"title": "",
"level": 0,
"content": text + "\n"
}
if current_chunk:
parsed_chunks.append(current_chunk)
return parsed_chunks
# 示例使用
chunks = parse_docx_structure("example.docx")
# 美观打印输出
for chunk in chunks:
print(f"\n=== {chunk['id']} ({chunk['title']}) ===")
print(chunk["content"])
# 可选:保存为 JSON
with open("parsed_chunks.json", "w", encoding="utf-8") as f:
json.dump(chunks, f, ensure_ascii=False, indent=2)
在实际应用中,可以根据文档的格式选择合适的分块工具和方法:
- Markdown 文档:使用 MarkdownHeaderTextSplitter,根据标题层级进行分块。
- HTML 文档:使用 HTMLHeaderTextSplitter,根据 HTML 标签(如
<h1>、<h2>
)进行分块。 - 代码文件:使用 PythonCodeTextSplitter 等工具,根据函数或类的定义进行分块。
- 表格数据:将表格内容格式化为模型易于理解的形式(如 HTML 的
<table>
标签、CSV 格式等),以保留表格的结构信息
这些工具通常会在分块的同时添加元数据(如标题、层级信息等),以便在后续的检索和生成过程中提供更丰富的上下文。
优势与适用场景
优势:
- 语义完整性强:每个分块通常对应一个完整的主题或功能单元,便于模型理解。
- 上下文清晰:保留了文档的结构信息,增强了内容之间的逻辑关系。
- 检索效果好:结构化的分块有助于提高向量检索的准确性。
适用场景:
- 结构清晰的文档,如技术文档、API 文档、法律文本等。
- 需要保留文档层次结构的应用,如知识库问答系统、文档摘要生成等。
注意事项:
- 文档结构不清晰时效果有限:对于结构混乱或缺乏明显结构的文档,结构化分块可能无法发挥优势。
- 块大小不均:不同结构单元的长度可能差异较大,需结合其他分块策略(如递归分块)进行优化。
- 实现复杂度较高:需要根据不同的文档格式设计相应的解析和分块逻辑。
文档结构化分块是一种有效的分块策略,特别适用于结构清晰、层次分明的文档。在实际应用中,可以结合其他分块方法(如递归分块、语义分块)进行混合使用,以获得更好的效果。
四、递归切分(Recursive Splitting)
实际上 递归切分(Recursive Splitting) 也是 固定大小文本切块

在 RAG(Retrieval-Augmented Generation)系统中,递归切分(Recursive Splitting)是一种常用的文本分块策略,旨在将长文本有效地划分为适合处理的小块(chunk),以便后续的嵌入、检索和生成任务
- 工具:
RecursiveCharacterTextSplitter
(LangChain) - 参数建议:
- chunk_size=512 (适合多数 Embedding 模型)
- chunk_overlap=128 (平衡上下文连贯性与冗余)
- separators=
["\n\n", "\n", "。", "?", "!", ";", "..."]
(中文场景优化分隔符)
- 优势:先按大结构(标题)切,再按段落/句子细化,避免硬切关键信息。
原理概述
递归切分的核心思想是使用一组预定义的分隔符(如段落符、句号、空格等)按层次递归地将文本拆分成更小的块,直到每个块的长度满足设定的要求。
具体步骤如下:
- 初步切分:使用第一个分隔符(例如换行符 \n)对文本进行初步切分。
- 检查块大小:对于每个切分得到的块,判断其长度是否超过设定的最大块大小(chunk_size)。
- 递归处理:如果某个块的长度仍然超过 chunk_size,则使用下一个分隔符(例如句号 。)对该块进行进一步切分。
- 继续递归:重复上述过程,依次使用预定义的分隔符列表中的下一个分隔符,直到所有块的长度都不超过 chunk_size,或者无法再进行切分。
- 合并块(可选):在某些实现中,如果相邻的文本块合并后长度不超过 chunk_size,则可以将它们合并,以确保块的长度尽可能接近 chunk_size,同时保留上下文完整性。
这种方法的优点在于它能够尽量保留文本的语义结构,例如段落和句子边界,从而在保持上下文连贯性的同时,生成大小合适的文本块。
示例(LangChain):
LangChain 提供了 RecursiveCharacterTextSplitter 类来实现递归切分。以下是一个使用示例:
python
from langchain.text_splitter import RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=50,
length_function=len,
separators=["\n", "。", " ", ""]
)
text = "..." # 待处理的文本
texts = text_splitter.create_documents([text])
for doc in texts:
print(doc)
在这个示例中:
- chunk_size=200:设置每个文本块的最大长度为 200 个字符
- chunk_overlap=50:设置相邻文本块之间的重叠长度为 50 个字符,以保留上下文
- separators=
["\n", "。", " ", ""]
:定义了分隔符的优先级顺序,依次为换行符、句号、空格和空字符串。
这种方式确保了文本在切分时尽量保持语义的完整性和上下文的连贯性。
优势
- 保持语义结构:递归切分优先使用自然语言中的分隔符(如段落、句子等)进行切分,有助于保留文本的语义结构,减少信息碎片化的风险。
- 灵活的分块大小:通过递归使用不同的分隔符,能够根据文本的实际结构动态调整分块大小,适应不同长度和结构的文本。
- 适用于多种文本格式:递归切分方法适用于多种文本格式,包括自然语言文本、Markdown、HTML 等,能够处理结构复杂或层次分明的文档。
- 减少上下文割裂:通过在相邻文本块之间引入重叠部分(chunk_overlap),有助于保留上下文信息,提高模型对文本的理解能力。
适用场景
- 自然语言文本:如新闻文章、博客、书籍等,文本结构清晰,适合使用递归切分方法。
- 结构化文档:如 Markdown、HTML 等,具有明确的结构层次,递归切分能够有效地保留其结构信息。
- 需要保持语义完整性的任务:如问答系统、摘要生成等,对文本的语义连贯性要求较高,适合使用递归切分。
劣势
- 处理速度较慢:递归切分需要多次遍历文本,尤其是在处理大型文档时,可能导致处理速度较慢。
- 块大小不一致:由于依赖于自然语言的分隔符,生成的文本块大小可能不一致,这可能影响后续的处理效果。
- 计算资源消耗大:多次递归切分和合并操作可能导致较高的计算资源消耗,尤其是在大规模数据处理时。
- 对文档结构依赖强:递归切分依赖于文档的结构清晰度,对于结构不明确或格式混乱的文档,效果可能不佳。
不适用场景
- 结构化数据(如 Excel 表格):Excel 表格中的数据通常以行和列的形式组织,递归切分无法有效保留其结构和语义信息。
- 代码或标记语言文档:对于包含代码块或标记语言(如 HTML、Markdown)的文档,递归切分可能会破坏其语法结构,影响后续处理。
- 多语言或混合语言文档:在处理包含多种语言的文档时,递归切分可能无法正确识别和处理不同语言的分隔符,导致切分效果不佳。
- 对块大小有严格要求的应用:某些应用对输入块的大小有严格限制,递归切分生成的不一致块大小可能不满足这些要求。
五、句子窗口检索(Sentence-Window Retrieval)
准确地讲,Sentence-Window Retrieval(检索期再扩窗)不是切片方式,而是 检索策略 ------ 先以"单句 chunks"建索引,命中后再把 前后 N 句 拼回去给 LLM。

原理概述
传统的 RAG 系统通常将文档按固定长度或段落进行切分,并对每个块进行向量化处理。然而,这种方法可能导致语义相关的信息被切割,影响检索效果。
句子窗口检索通过以下步骤优化这一过程:
- 按句子切分文档:将文档按句子进行切分,每个句子作为一个最小的检索单元。
- 构建句子窗口:对于每个句子,记录其前后若干个句子,形成一个"窗口"。这个窗口包含了目标句子及其上下文信息。
- 向量化处理:仅对目标句子进行向量化处理,而将其窗口信息作为元数据存储。
- 检索与生成:在检索阶段,根据用户查询与句子向量的相似度,找到最相关的句子,并将其对应的窗口信息提供给语言模型,以生成更准确的答案。
这种方法结合了细粒度的检索和丰富的上下文信息,提升了检索的精确度和生成的质量。
示例(LlamaIndex)
在 LlamaIndex 中,可以通过 SentenceWindowNodeParser 实现句子窗口检索: from llama_index.node_parser import SentenceWindowNodeParser
python
node_parser = SentenceWindowNodeParser.from_defaults(
window_size=3,
window_metadata_key="window",
original_text_metadata_key="original_text",
)
- window_size=3:表示窗口包含目标句子前后各 3 个句子,共 7 个句子。
- window_metadata_key 和 original_text_metadata_key:用于在元数据中标识窗口内容和原始句子
在检索阶段,可以使用 MetadataReplacementPostProcessor 将检索到的句子替换为其对应的窗口内容,提供给语言模型进行生成。
- LlamaIndex 的 SentenceWindowRetriever 实践显示,对长段落文档回答的 F1 明显提升。
- 常用窗口:中心句 ± 3 句;别忘了把原句位置做 metadata,方便精确引用。
优势
- 提高检索精度:通过细粒度的句子级检索,提升了与查询的匹配度。
- 丰富上下文信息:窗口机制提供了更完整的上下文,有助于语言模型生成更准确的答案。
- 灵活的窗口大小:可以根据具体需求调整窗口大小,以平衡上下文信息量和模型处理能力。
适用于需要高精度检索和丰富上下文支持的场景,如问答系统、文档摘要等。
注意事项
- 窗口大小的选择:窗口过小可能导致上下文不足,过大则可能引入噪声信息。需要根据具体任务进行调整。
- 计算资源消耗:虽然只对目标句子进行向量化处理,但在检索和生成阶段,仍需处理较大的上下文窗口,可能增加计算负担。
- 模型输入限制:需要注意语言模型的输入长度限制,避免窗口内容过长导致截断。
六、自动合并检索(Auto-merging retrieval)

Auto-merging Retrieval(自动合并检索)本质上是一种检索策略,而非单纯的 chunk(文本切分)方法。要实现这一策略,确实需要配合特定的 chunk 策略,尤其是层次化的 chunking(Hierarchical Chunking)。
我们前文说的 "Document structure-based chunking"(基于文档结构的切分)就属于"Hierarchical Chunking"(层次化切分)策略的一种。
原理概述
自动合并检索的核心思想是将文档划分为多个层次的块(chunk),形成父子关系的树状结构。在检索过程中,如果多个相关的子块属于同一个父块,系统会自动将这些子块合并为其父块,从而提供更完整的上下文信息给大语言模型(LLM)
实现步骤
- 文档层次化拆分:使用如 LlamaIndex 的 HierarchicalNodeParser 或 Haystack 的 HierarchicalDocumentSplitter,将文档按预设的块大小(如 2048、512、128)递归拆分,构建出多层级的节点结构。
- 索引构建:将最小的叶子节点(最小块)进行向量化,并存入向量数据库中,供后续检索使用。
- 检索与合并:
- 在用户查询时,系统首先检索与查询最相关的叶子节点。
- 如果检索到的叶子节点中,有多个属于同一个父节点,并且超过设定的阈值(例如,超过该父节点子节点数量的50%),则系统会自动将这些子节点合并为其父节点,作为最终的检索结果返回
优势
- 增强上下文完整性:通过合并相关的子块,提供更连贯的上下文信息,减少信息碎片化。
- 提高生成质量:更完整的上下文有助于大语言模型生成更准确、相关性更高的回答。
- 降低幻觉风险:提供更全面的信息,减少模型因上下文不足而产生的错误回答。
注意事项
- 合理设置层次结构:根据文档的结构和内容,选择合适的 chunk 大小和层数,避免层次过多或过少影响检索效果。
- 调整合并阈值:根据具体应用场景,设置合适的合并阈值,确保在需要时合并相关子块,同时避免引入不相关的信息。
- 利用元数据管理父子关系:在构建层次结构时,确保每个子块都包含其父节点的引用信息,以便在检索时能够正确合并。
Document structure-based chunking 结合 Auto-merging Retrieval
要将"基于文档结构的切分"(Document Structure-Based Chunking)与"自动合并检索"(Auto-merging Retrieval)结合应用于 RAG(Retrieval-Augmented Generation)系统,可以按照以下步骤进行开发:
1 基于文档结构的层次化切分
目标:利用文档的结构信息(如标题、段落、列表等)进行多层次的切分,构建父子关系的树状结构。
实现方法:
- 使用工具:可以使用如 LlamaIndex 的 HierarchicalNodeParser 或 Haystack 的 HierarchicalDocumentSplitter。这些工具允许根据文档结构进行层次化切分。
- 设置参数:
- block_sizes:定义每一层的最大块大小,例如 {2048, 512, 128} 表示父块最大为 2048 个单位,子块最大为 512 个单位,叶子节点为 128 个单位。
- split_by:指定按何种单位进行切分,如按词("word")或句子("sentence")。
- 构建层次结构:通过上述工具和参数设置,将文档切分为多个层次的块,形成父子关系的树状结构。
2 构建向量索引与文档存储
目标:将最小的叶子节点进行向量化,并存入向量数据库中,同时保留父节点的信息以供后续合并使用。 实现方法:
- 向量化叶子节点:使用如 OpenAI 的 text-embedding-3-small 或 SentenceTransformers 等模型,对叶子节点进行向量化。
- 存储向量:将向量化后的叶子节点存入向量数据库中,如 FAISS、Pinecone、Weaviate 等。
- 保留父子关系:在存储过程中,保留每个叶子节点与其父节点之间的关系信息,以便在检索时进行合并。
3 配置自动合并检索器
目标:在检索过程中,根据设定的阈值自动将相关的子块合并为其父块,提供更完整的上下文信息。 实现方法:
- 使用工具:可以使用如 Haystack 的 AutoMergingRetriever 或 LlamaIndex 的 AutoMergingRetriever。
- 设置参数:
- threshold:设定合并的阈值,例如 0.5 表示当超过 50% 的子块被检索到时,合并为父块。
- document_store:指定包含父节点的文档存储。
- 检索流程:
-
根据用户查询,检索与查询最相关的叶子节点。
-
统计被检索到的叶子节点中,属于同一父节点的数量。
-
如果某个父节点下的被检索到的子节点数量超过设定的阈值,则将这些子节点合并为其父节点,作为最终的检索结果返回。
-
总结
在构建基于大语言模型(LLM)的检索增强生成(RAG)系统时,文本分块策略的选择对系统性能和生成质量具有决定性影响。本文深入探讨了六种主流的文本分块方法,分别是:固定大小切分、语义切分、基于文档结构的切分、递归切分、句子窗口检索和自动合并检索。以下是对这些方法的综合比较与应用建议:
1. 固定大小切分(Fixed-size Chunking)
- 优势:实现简单,计算效率高,适用于对语义完整性要求不高的场景。
- 劣势:可能破坏语义结构,导致上下文信息丢失,影响模型理解和检索效果。
- 应用建议:适用于结构简单、对语义连贯性要求不高的文本,如短消息、日志等。
2. 语义切分(Semantic Chunking)
- 优势:保持语义完整性,提高检索准确性,减少冗余信息。
- 劣势:计算成本高,实现复杂,效果依赖于数据类型。
- 应用建议:适用于对语义连贯性要求高的任务,如复杂问答系统、法律文档分析等。
3. 基于文档结构的切分(Document Structure-based Chunking)
- 优势:保留文档的层次结构,增强上下文理解,提高检索效果。
- 劣势:对文档结构依赖强,块大小可能不均,需结合其他策略优化。
- 应用建议:适用于结构清晰的文档,如技术文档、API文档、法律文本等。
4. 递归切分(Recursive Splitting)
- 优势:灵活适应不同文本结构,保持语义结构,减少上下文割裂。
- 劣势:处理速度较慢,块大小不一致,计算资源消耗大。
- 应用建议:适用于自然语言文本和结构化文档,需在处理速度和语义完整性之间权衡。
5. 句子窗口检索(Sentence-Window Retrieval)
- 优势:提高检索精度,丰富上下文信息,灵活的窗口大小。
- 劣势:窗口大小选择需谨慎,计算资源消耗较高,需注意模型输入限制。
- 应用建议:适用于需要高精度检索和丰富上下文支持的场景,如问答系统、文档摘要等。
6. 自动合并检索(Auto-merging Retrieval)
- 优势:增强上下文完整性,提高生成质量,降低幻觉风险。
- 劣势:需合理设置层次结构和合并阈值,依赖元数据管理父子关系。
- 应用建议:适用于结构清晰、层次分明的文档,结合其他分块方法使用效果更佳。
在实际应用中,选择合适的文本分块策略应根据具体任务需求、数据类型和可用资源综合考虑。对于结构清晰的文档,建议优先采用基于文档结构的切分方法,并结合递归切分优化块大小;对于对语义连贯性要求高的任务,可采用语义切分方法;对于需要高精度检索和丰富上下文支持的场景,可采用句子窗口检索或自动合并检索策略。此外,根据实际情况以上 chunk 策略不必拘泥于任何一种,可以混合使用。