策略方面 chunking
对于本地 RAG,先从每块 300 到 500 个 token、重叠比例为 0 到 10% 的文本块开始,按段落和标题边界切分,而不是按字符切分,再用您自己的问题评估效果,然后调整。语义分块尚未证明其收益值得付出的成本:2024 年的一项研究得出的结论是,其收益不足以证明额外计算开销是合理的。最重要的设置是让切分位置落在文本的自然边界上。
将文档切分成片段的方式决定了检索能找到什么:切分位置不当的片段无法包含完整答案,过大的片段则会让答案淹没在噪声中。本指南比较常见的切分策略,依据已发表的研究给出起始片段大小,指出单位(token 或字符)容易混淆的问题,并提供一套在您自己的文档上验证所选方案的流程。
#为什么分块与嵌入模型同样重要
分块是在建立索引之前,将文档切分成片段的操作:每个片段都会获得一个向量,被检索出来并传给语言模型的始终是片段,而不是整篇文档。后续的一切都取决于这种切分方式。如果回答问题的句子被切分到两个片段中,那么两个片段都不包含完整的句子,检索就会漏掉它;如果一个片段混合了三个主题,它的向量就会成为一个模糊的平均值,无法与任何问题高度匹配。更强大的嵌入模型也无法弥补有问题的切分,因为它只能对提供给它的内容进行向量化。
Chroma 一项评估文本分块策略的研究表明:像 RecursiveCharacterTextSplitter 这样的启发式方法,在参数设置得当时,往往能在实际应用中取得良好效果,而结果会随所选分块大小和重叠量的不同而显著变化。因此,本指南不承诺任何普遍适用的量化收益:唯一可靠的做法是用您的文档进行测量。
#应选择多大的文本块
你的文档,你的 AI:基于你的 PDF、笔记和邮件的可靠本地 RAG——无需向云端发送任何内容。
- 在线空间,终身可用
- PDF + 文件
- 30 天内退款
没有适用于所有情况的分块大小,但有一些参考依据。在 Chroma 的研究中,分块大小为 400 个 token 或更少且无重叠时,递归分块策略优于按 token 分块;对于更大的分块和带重叠的情况,表现则有所不同。OpenAI 文件搜索工具的默认配置为每块 800 个 token、重叠 400 个 token;在这项测试中,该配置的召回率略低于平均水平,其他各项指标的得分则最低。下面的参考建议由此得出,但并不声称对您的语料库也同样准确。
| 大小 | 适用于 | 风险 |
|---|---|---|
| 200至300个标记 | 信息密集的文档中的具体事实问题(日期、条款、数值) | 丢失回答周围的上下文;请考虑添加章节标题 |
| 300至500个token | 适用于文档、操作流程和文章的通用起始设置 | 风险较低;这是应优先测试的区域 |
| 500至800个token | 观点跨越多个段落展开的议论性文本 | 多个概念混在同一个向量中,各自的特征被淡化;提示词的空间会更快被占满 |
| 超过 1000 个 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 实现
#为每个数据块重新提供上下文
从文档中抽离的片段会失去上下文:“期限为 30 天”并未说明具体指什么。三种简单做法可以解决这个问题:在每个文本块前加上文档标题和章节路径;将这些信息存储为元数据,以便按日期、来源或文档类型筛选;对于以代词或指代语开头的片段(如“该条款”),考虑采用层级切分中的父级文本块。通常只需在正文前加一行上下文,成本就远低于更换模型。
#在确定分块方案前先进行评估
- 01编写 30 到 50 个真实问题编写您的用户可能提出的问题,并为每个问题指定预期对应的来源文本片段;请在查看结果之前写好这些问题。
- 02使用两种或三种配置建立索引例如250、400和700个token,重叠率为0%和10%。其他参数保持不变。
- 03衡量前 5 条结果的召回率每条问题对应的预期答案是否出现在前5个结果中?该比例即为5个结果的召回率。
- 04也要关注查准率和冗余程度在召回率相同的情况下,检索结果更多样的设置更好。统计前 5 个结果中的重复项数量。
- 05逐个查看失败案例对于每个未能正确回答的问题,打开本应提供答案的文本块:是否被截断、范围过大,或提取出的内容有损坏?原因决定了修正方法。
#常见陷阱
- 结构被破坏的表格
- 如果 PDF 提取器将表格扁平化,就会生成失去意义的单元格行:应在分块之前先提取表格结构。
- 代码块被分割
- 不识别语法的分块工具会从中间切断代码块;请采用按结构分块的方式。
- 混合文档
- 一个内容一半是法语、一半是英语的文本块,会生成一个用处不大的平均向量:如果语料库包含多种语言,请按语言拆分。
- 分块内容几乎为空
- 仅包含标题(如「3.2.1 义务」)而无后续文本是噪音:请过滤过短的片段或将其与下一个片段合并。
- 更改分块方式却不重新索引
- 切分在索引时是固定的:任何变更都要求重新计算向量。请从一开始就准备一个重新索引脚本。
#关于分块的常见问题
本地 RAG 应选择多大的文本块?+
语义分块值得付出相应的成本吗?+
chunk 之间是否需要重叠?+
tokens 还是字符:如何设置 chunk_size?+
如何对包含表格的 PDF 进行分块?+
更改chunk大小时是否需要重新索引?+
有反馈、发现了错误,或想补充说明?请告诉我们,让这份指南对每个人都更有帮助。