高级 11 分钟优化

策略方面 chunking

直接回答

对于本地 RAG,先从每块 300 到 500 个 token、重叠比例为 0 到 10% 的文本块开始,按段落和标题边界切分,而不是按字符切分,再用您自己的问题评估效果,然后调整。语义分块尚未证明其收益值得付出的成本:2024 年的一项研究得出的结论是,其收益不足以证明额外计算开销是合理的。最重要的设置是让切分位置落在文本的自然边界上。

将文档切分成片段的方式决定了检索能找到什么:切分位置不当的片段无法包含完整答案,过大的片段则会让答案淹没在噪声中。本指南比较常见的切分策略,依据已发表的研究给出起始片段大小,指出单位(token 或字符)容易混淆的问题,并提供一套在您自己的文档上验证所选方案的流程。

作者: Mohamed Meguedmi·更新于 2026-09-30·已在 Windows、macOS 和 Linux 上测试

#为什么分块与嵌入模型同样重要

分块是在建立索引之前,将文档切分成片段的操作:每个片段都会获得一个向量,被检索出来并传给语言模型的始终是片段,而不是整篇文档。后续的一切都取决于这种切分方式。如果回答问题的句子被切分到两个片段中,那么两个片段都不包含完整的句子,检索就会漏掉它;如果一个片段混合了三个主题,它的向量就会成为一个模糊的平均值,无法与任何问题高度匹配。更强大的嵌入模型也无法弥补有问题的切分,因为它只能对提供给它的内容进行向量化。

Chroma 一项评估文本分块策略的研究表明:像 RecursiveCharacterTextSplitter 这样的启发式方法,在参数设置得当时,往往能在实际应用中取得良好效果,而结果会随所选分块大小和重叠量的不同而显著变化。因此,本指南不承诺任何普遍适用的量化收益:唯一可靠的做法是用您的文档进行测量。

i
一个良好的分块
好的文本片段应包含一个完整的意思,单独阅读也能理解。片段太小,意思会被截断,向量表达也会变得模糊;片段太大,多个意思会混在一起,检索就会返回噪声。

#应选择多大的文本块

本地 RAG 工具包

你的文档,你的 AI:基于你的 PDF、笔记和邮件的可靠本地 RAG——无需向云端发送任何内容。

  • 在线空间,终身可用
  • PDF + 文件
  • 30 天内退款

没有适用于所有情况的分块大小,但有一些参考依据。在 Chroma 的研究中,分块大小为 400 个 token 或更少且无重叠时,递归分块策略优于按 token 分块;对于更大的分块和带重叠的情况,表现则有所不同。OpenAI 文件搜索工具的默认配置为每块 800 个 token、重叠 400 个 token;在这项测试中,该配置的召回率略低于平均水平,其他各项指标的得分则最低。下面的参考建议由此得出,但并不声称对您的语料库也同样准确。

根据文档和问题类型选择的初始分块大小
大小适用于风险
200至300个标记信息密集的文档中的具体事实问题(日期、条款、数值)丢失回答周围的上下文;请考虑添加章节标题
300至500个token适用于文档、操作流程和文章的通用起始设置风险较低;这是应优先测试的区域
500至800个token观点跨越多个段落展开的议论性文本多个概念混在同一个向量中,各自的特征被淡化;提示词的空间会更快被占满
超过 1000 个 token很少适合检索;仅应在分层切分中用作父级层向量过于宽泛,文本片段难以筛选
→
初始参考
先采用 400 个 token 的分块大小,重叠部分设为 0 到 50 个 token,再测试 250 和 700 个 token 的分块大小;只有用您自己的问题测量召回率后,才调整设置。

#Token 还是字符:单位陷阱

各个库并不都使用相同的单位计数。根据 LlamaIndex 的文档,其 SentenceSplitter 的 chunk_size 和 chunk_overlap 以 token 为单位,默认值分别为 1024 和 200。其他工具,包括 LangChain 的 RecursiveCharacterTextSplitter,默认按字符计数;请检查您所用版本的 length_function 参数。在这两种工具中分别将数值设为“500”,得到的文本块大小会相差约四倍。第二个限制是:嵌入模型有自己的最大输入长度。根据 BGE-M3 的模型说明,它支持最多 8 192 个 token 的输入;其他较旧或较轻量的嵌入模型接受的输入长度要短得多,超出限制后,段落末尾会被忽略。选择文本块大小之前,请检查模型的长度限制,并使用该模型的分词器计数,而不是凭目测估算。

#重叠:有用,但并非总是有用

重叠会将一个 chunk 的结尾重复到下一个 chunk 的开头,让在边界处被截断的句子至少在一侧仍能完整读懂。其代价是更多的 chunk、更大的索引,以及结果中的重复项。Chroma 的研究指出,减少重叠可以提高 IoU 分数,这一指标会惩罚冗余信息。如果按固定大小切分,使用重叠是有理由的;如果已经按段落和标题切分,重叠就不那么重要,因为切分点此时落在自然边界上。

0 个标记
通过段落或结构分段即可满足需求,是最节省资源的方式。
大小的 10% 至 15%
按句子或字符切分时,这是一个不错的折中选择。
超过25%
通常没有必要:结果中冗余很多,会出现几乎相同的段落。

#分块策略:从最简单到成本最高

#1. 固定字符长度

不考虑文本内容,每隔 N 个字符或 token 就切分一次。这种方法最简单,也最容易破坏文本结构:它会从词语、句子或表格的中间切开。仅限用于原型。

#2. 按段落或按句子

在双换行符或标点符号处切分,再将各单元合并至目标大小。这能在不增加成本的情况下带来明显改善,因为每个文本块都在自然边界处开始和结束。

#3. 递归

先尝试按较大粒度的分隔符切分(段落、行、句子、空格),只有在别无选择时才退到字符级切分。这是主流框架的默认行为,也是可靠的基础:Chroma 的研究发现,这类切分方式在参数设置得当时通常表现良好。

#4. 根据文档结构

保留标题、列表、表格和代码块的结构。例如,LlamaIndex 的 MarkdownNodeParser 按标题切分,并为每个节点附上通向该节点的标题层级路径,从而为文本片段补充其自身内容所不具备的上下文。这是处理技术文档、Wiki 和导出的 HTML 页面的最佳选择。

#5. 语义分块

为每个句子计算一个嵌入向量,然后在相邻两句的相似度明显下降处切分。在 LlamaIndex 中,SemanticSplitterNodeParser 接受 buffer_size(一起比较的句子数量,默认为 1)和 breakpoint_percentile_threshold(默认为 95;较低的值会产生更多节点)。代价是在建立索引时需要额外计算嵌入向量,而收益尚未得到证实:2024 年 10 月一项针对三项检索任务的研究得出结论,语义切分并未带来足够稳定的性能收益,无法证明其计算成本是值得的。采用前,请先在您的语料库上测试。

#6. 分层结构(父级与子级)

为提高检索精度,我们对较小的文本块建立索引,再将更大的父级文本块返回给模型,以提供上下文。LlamaIndex 的 HierarchicalNodeParser 可以生成这样的层级结构,例如其文档所述的三层结构,文本块大小分别为 2048、512 和 128 个 token。这就是处理长文本段落的方法:既不只用小块,也不只用大块。

针对不同文档的策略选择
文档类型推荐策略
文档、Wiki、Markdown、HTML先按标题结构切分,再在较长章节内部递归切分
合同、法律文本按条文或条款划分结构;每个文本块为 300 至 500 个 token;每个文本块中重复注明条文标题
无结构的自由文本(邮件、笔记、转录文本)递归、10% 重叠,可选分层
带表格的PDF预先提取结构;根据问题,将整张表格作为一个分块,或按行分块
源代码按函数或类切分,绝不在代码块中间切断

#使用 LlamaIndex 实现

按句子分块,保留块间重叠内容
from llama_index.core.node_parser import SentenceSplitter

splitter = SentenceSplitter(chunk_size=400, chunk_overlap=40)  # en tokens
nodes = splitter.get_nodes_from_documents(docs)
根据 Markdown 结构
from llama_index.core.node_parser import MarkdownNodeParser

parser = MarkdownNodeParser()
nodes = parser.get_nodes_from_documents(docs)
# le chemin des titres est stocké dans les métadonnées de chaque nœud
分层结构
from llama_index.core.node_parser import HierarchicalNodeParser, get_leaf_nodes

parser = HierarchicalNodeParser.from_defaults(chunk_sizes=[2048, 512, 128])
nodes = parser.get_nodes_from_documents(docs)
leaves = get_leaf_nodes(nodes)  # ce sont les feuilles qu'on vectorise
语义分块(采用前需测试)
from llama_index.core.node_parser import SemanticSplitterNodeParser
from llama_index.embeddings.huggingface import HuggingFaceEmbedding

embed = HuggingFaceEmbedding(model_name="BAAI/bge-m3")
splitter = SemanticSplitterNodeParser(embed_model=embed, buffer_size=1, breakpoint_percentile_threshold=95)
nodes = splitter.get_nodes_from_documents(docs)

#为每个数据块重新提供上下文

从文档中抽离的片段会失去上下文:“期限为 30 天”并未说明具体指什么。三种简单做法可以解决这个问题:在每个文本块前加上文档标题和章节路径;将这些信息存储为元数据,以便按日期、来源或文档类型筛选;对于以代词或指代语开头的片段(如“该条款”),考虑采用层级切分中的父级文本块。通常只需在正文前加一行上下文,成本就远低于更换模型。

#在确定分块方案前先进行评估

  1. 01
    编写 30 到 50 个真实问题
    编写您的用户可能提出的问题,并为每个问题指定预期对应的来源文本片段;请在查看结果之前写好这些问题。
  2. 02
    使用两种或三种配置建立索引
    例如250、400和700个token,重叠率为0%和10%。其他参数保持不变。
  3. 03
    衡量前 5 条结果的召回率
    每条问题对应的预期答案是否出现在前5个结果中?该比例即为5个结果的召回率。
  4. 04
    也要关注查准率和冗余程度
    在召回率相同的情况下,检索结果更多样的设置更好。统计前 5 个结果中的重复项数量。
  5. 05
    逐个查看失败案例
    对于每个未能正确回答的问题,打开本应提供答案的文本块:是否被截断、范围过大,或提取出的内容有损坏?原因决定了修正方法。

#常见陷阱

结构被破坏的表格
如果 PDF 提取器将表格扁平化,就会生成失去意义的单元格行:应在分块之前先提取表格结构。
代码块被分割
不识别语法的分块工具会从中间切断代码块;请采用按结构分块的方式。
混合文档
一个内容一半是法语、一半是英语的文本块,会生成一个用处不大的平均向量:如果语料库包含多种语言,请按语言拆分。
分块内容几乎为空
仅包含标题(如「3.2.1 义务」)而无后续文本是噪音:请过滤过短的片段或将其与下一个片段合并。
更改分块方式却不重新索引
切分在索引时是固定的:任何变更都要求重新计算向量。请从一开始就准备一个重新索引脚本。
!
不要盲目优化
没有一组测试问题,就无法判断某项设置会改善还是恶化效果。一个“看起来合理”的分块大小,也会对召回率产生可测量的影响,可能提高,也可能降低。

#关于分块的常见问题

FAQ
本地 RAG 应选择多大的文本块?+
先采用 300 到 500 个 token 的分块大小,重叠比例设为 0% 到 10%,并在段落或标题处分块。随后用您实际会提出的 30 到 50 个问题,分别测试 250 和 700 个 token 的分块大小,比较前 5 个结果的召回率。没有通用的分块大小:它取决于您的文档和问题。
语义分块值得付出相应的成本吗?+
不一定。语义分块在建立索引时需要额外计算嵌入,而一项于 2024 年 10 月针对三项检索任务开展的研究得出结论:与固定大小分块相比,它并未带来稳定的收益,无法证明这项额外开销值得。在您的语料库上测试:如果没有优势,就保留递归分块。
chunk 之间是否需要重叠?+
不一定需要。如果您已经按段落和标题分块,通常不设置重叠也可以。如果按固定长度或按句子分块,设置 10% 到 15% 的重叠可以避免在边界处丢失句子。过多的重叠会增加结果中的重复项,并使索引体积增大。
tokens 还是字符:如何设置 chunk_size?+
请检查所用库采用的计量单位。LlamaIndex 的 SentenceSplitter 按 token 计数,其他工具默认按字符计数,这会使实际大小相差约四倍。也请检查嵌入模型的最大输入长度:超过这一长度时,文本片段会在建立索引时被截断。
如何对包含表格的 PDF 进行分块?+
先使用能识别表格的工具提取结构,再进行切分:如果表格较小,每个文本块包含一张表格;如果表格较大,每个文本块包含一行,并附上表头。将表格展平为纯文本的提取工具会产生无法使用的文本片段,无论之后采用哪种切分方式。
更改chunk大小时是否需要重新索引?+
是的,每次都需要重新索引。向量是根据索引时各文本块的内容计算的:更改文本块大小、重叠量或分割器,都必须重新计算所有向量。请保留一个能从源文档重建索引的脚本,并对所使用的分割参数进行版本控制。
这份指南对您有帮助吗?

有反馈、发现了错误,或想补充说明?请告诉我们,让这份指南对每个人都更有帮助。