如何切分文档
在 RAG(Retrieval-Augmented Generation)系统中,多数检索效果不理想的问题,根源不在 Embedding 模型不够好,也不在向量数据库不够快,而在于文档切分(Chunking)这一步没有做好。切分质量直接决定检索效果——一块被错误截断的文本,无论用什么 Embedding 模型编码,都无法被正确检索到。
文档切分是将长文档拆分成更小单元(Chunk)的过程。这些 Chunk 是 Embedding 和检索的基本单位,也是 LLM 生成回答时的上下文来源。切得太大,一个 Chunk 里混杂了多个主题,检索精度下降;切得太小,上下文信息丢失,即使检索命中也无法提供足够的回答依据。
本文属于《从 0 到 1 AI 产品出海知识库》中的「RAG 知识库与 AI Agent 入门」章节,系统讲清楚文档切分的核心策略、参数选择、不同文档类型的处理方式,以及如何评估和优化切分效果。
为什么需要文档切分
在讨论切分策略之前,先理解为什么不能把整篇文档直接丢给 LLM。
Token 限制
每个 Embedding 模型和 LLM 都有输入 Token 上限。当前主流 Embedding 模型的输入限制通常在 512 到 8192 Token 之间。一篇 50 页的技术文档可能包含 5 万到 10 万 Token,远超模型的处理能力。即使模型支持更长的上下文窗口,将整篇文档作为单次检索单元也不现实——计算量和存储成本都会过高。
检索精度
检索的目标是找到与用户问题最相关的文本段落。如果 Chunk 太大,一个 Chunk 涵盖了多个主题,Embedding 向量实际上编码的是多个主题的「平均值」,与任何单一主题的相似度都不够高。如果 Chunk 太小(比如只有一句话),虽然与特定问题的匹配度可能很高,但缺少足够的上下文来支撑 LLM 生成完整回答。
合适的 Chunk 应该满足一个判断标准:它在脱离上下文的情况下,仍然能让读者理解其含义,并包含回答某一类问题所需的完整信息。
成本与延迟
更大的 Chunk 意味着更多的 Embedding 计算、更大的向量存储开销,以及更长的 LLM 推理时间。合理的切分能在检索质量和系统性能之间取得平衡。
常见切分策略
文档切分策略从简单到复杂,形成了一个递进的光谱。选择哪种策略取决于文档类型、业务需求和可用的计算资源。
固定大小切分(Fixed-size Chunking)
最直接的策略:按固定数量的字符或 Token 切分文本。例如每 500 个 Token 切一刀,遇到边界就断开。
这种策略的实现成本极低,几乎所有框架都支持。但它完全不考虑文本的语义结构,可能在句子中间、段落中间甚至单词中间断开,导致 Chunk 内容不连贯。
适用场景:对切分精度要求不高的通用文本、短文档、快速原型验证。
按句子/段落切分(Sentence / Paragraph Splitting)
利用标点符号或换行符作为切分边界,保证每个 Chunk 在语法上是完整的。句子级别的切分使用句号、问号、感叹号等作为分隔符;段落级别使用换行符或空行作为分隔符。
这种方式比固定大小切分更自然,但产出的 Chunk 大小不均匀——有的段落只有一句话(可能太短),有的段落长达数千字(可能太长)。通常需要配合合并和再拆分逻辑来控制 Chunk 的大小范围。
适用场景:博客文章、新闻报道、FAQ 文档等段落结构清晰的文本。
递归切分(Recursive Splitting)
递归切分是目前使用最广泛的策略之一。它的核心思路是:定义一组优先级递减的分隔符列表(如 \n\n → \n → . → ),先尝试用最高优先级的分隔符切分,如果切分后的块仍然大于目标大小,再用下一级分隔符继续切分,直到每个 Chunk 满足大小要求。
LangChain 的 RecursiveCharacterTextSplitter 就是这种策略的典型实现。它在保持文本语义完整性方面比固定大小切分好得多,同时实现复杂度可控。
适用场景:通用的技术文档、产品手册、结构不固定的长文本。
语义切分(Semantic Chunking)
语义切分不依赖固定的分隔符,而是通过计算相邻句子或段落之间的语义相似度来确定切分边界。具体做法是:先计算每句话的 Embedding,然后计算相邻句子的相似度,在相似度出现显著下降的位置切分——那里通常意味着话题发生了转换。
这种策略能确保每个 Chunk 围绕一个连贯的主题,避免将不同话题的内容混在一起。代价是需要额外的 Embedding 计算,处理速度明显慢于前几种策略。
适用场景:学术论文、长篇报告、法律文档等主题切换频繁且需要精确检索的文本。
按文档结构切分(Structure-based Chunking)
利用文档自身的结构信息来切分。Markdown 文档按标题层级(# → ## → ###)切分;HTML 文档按 DOM 结构切分;PDF 文档按页面和章节切分。
这种策略最大程度地保留了文档的逻辑结构。一个 ## 标题下的内容通常就是一个完整的话题,天然适合作为 Chunk 的边界。如果切分后的块仍然过大,可以在结构切分的基础上叠加递归切分做二次拆分。
适用场景:Markdown 文档、技术手册、API 文档、有清晰标题层级的长文档。
滑动窗口切分(Sliding Window Chunking)
在固定大小切分的基础上,引入相邻 Chunk 之间的重叠(Overlap)。例如 Chunk 大小为 500 Token,相邻两个 Chunk 共享 100 Token 的内容。这样每个位置的文字会出现在多个 Chunk 中,确保不会因为切分边界而丢失关键信息。
Overlap 的比例通常在 10% 到 20% 之间。过小的 Overlap 起不到保护边界的作用,过大的 Overlap 则导致存储冗余和检索重复。
适用场景:对话记录、会议转录、叙事性文本等上下文连续性强的内容。
层级切分(Hierarchical Chunking)
构建一个树状的 Chunk 结构:文档级别、章节级别、段落级别、句子级别。检索时先在大粒度上定位相关章节,再在小粒度上找到具体段落。每个小 Chunk 都携带父级 Chunk 的引用,检索命中时可以向上扩展上下文。
这种策略实现复杂度最高,但对于大型文档集或结构复杂的长文档,它提供了灵活的检索粒度选择。
适用场景:大型技术手册、百科全书、法规汇编等需要多层级检索的场景。
切分策略对比
| 策略 | 实现复杂度 | 语义保持 | 计算成本 | Chunk 大小均匀性 | 典型适用场景 |
|---|---|---|---|---|---|
| 固定大小 | 低 | 差 | 低 | 均匀 | 快速原型、通用短文本 |
| 句子/段落 | 低 | 中 | 低 | 不均匀 | 博客、新闻、FAQ |
| 递归切分 | 中 | 较好 | 低 | 较均匀 | 通用技术文档 |
| 语义切分 | 高 | 优秀 | 高 | 不均匀 | 学术论文、法律文档 |
| 文档结构 | 中 | 好 | 低 | 取决于文档 | Markdown、API 文档 |
| 滑动窗口 | 低 | 中 | 中 | 均匀 | 对话记录、会议转录 |
| 层级切分 | 高 | 优秀 | 高 | 多层级 | 大型手册、百科全书 |
切分参数选择
切分策略确定后,还需要配置两个核心参数:Chunk 大小(Chunk Size)和重叠大小(Chunk Overlap)。这两个参数没有放之四海而皆准的最优值,但有一些经验性的指导范围。
Chunk Size 的选择
Chunk Size 通常以 Token 为单位衡量。主流的建议范围:
| Chunk 大小(Token) | 特点 | 适用场景 |
|---|---|---|
| 100–256 | 信息密度高,检索精确,但上下文少 | 事实型问答、实体检索、信息抽取 |
| 256–512 | 平衡精度与上下文,最常见的默认值 | 通用问答、技术文档、产品手册 |
| 512–1024 | 上下文丰富,适合需要推理的场景 | 长文摘要、分析报告、法律文书 |
| 1024+ | 上下文极丰富,但检索精度下降,Embedding 可能超出有效范围 | 需要大段上下文的特殊场景 |
选择时的核心权衡是:小 Chunk 检索精度高但上下文不足,大 Chunk 上下文丰富但检索精度下降。一个实用的测试方法是:把切分后的 Chunk 单独拿出来读,看它是否能在脱离原文的情况下传达完整的信息。如果不能,说明 Chunk 太小了。
Embedding 模型的有效输入长度也是重要的约束条件。多数 Embedding 模型在输入超过一定长度后,Embedding 质量会显著下降(即使模型标称支持更长的输入)。对于 OpenAI 的 text-embedding-3-small(最大 8191 Token),实践中 Chunk 大小控制在 512 Token 以内的检索效果通常优于 1024 Token 以上。
Chunk Overlap 的选择
Overlap 的作用是防止关键信息在切分边界处丢失。常见的 Overlap 设置:
| Overlap 比例 | 具体值(以 500 Token Chunk 为例) | 适用场景 |
|---|---|---|
| 5%–10% | 25–50 Token | 段落结构清晰的文档,切分策略已经尊重语义边界 |
| 10%–20% | 50–100 Token | 最常用的范围,适合大多数场景 |
| 20%–50% | 100–250 Token | 上下文连续性强的文本(对话记录、叙事文本) |
需要注意,Overlap 直接增加了存储成本和检索时的重复度。如果 Chunk 大小是 500 Token、Overlap 是 250 Token,那么同一句话最多会出现在两个 Chunk 中,向量数据库的存储量接近翻倍。Overlap 不是越大越好,通常 10% 到 20% 就足够覆盖边界风险。
一个常见的误区是把 Overlap 当作解决检索质量问题的万能药。如果检索效果不好,优先检查切分策略是否合理、Chunk 大小是否合适,而不是一味增大 Overlap。
参数调优方法
参数调优不应该是凭感觉猜,而应该基于评估数据做系统性测试:
- 准备评估数据集:收集 20 到 50 个典型的用户问题,标注每个问题的正确答案以及答案出自哪个文档的哪个位置
- 网格搜索:对 Chunk Size(如 256、512、1024)和 Overlap(如 0、50、100)做组合测试
- 评估指标:测量每个参数组合下的命中率(Hit Rate)、精确率(Precision)和召回率(Recall)
- 选择最优:选择综合表现最好的参数组合,同时考虑存储和延迟成本
不同文档类型的切分策略
不同类型的文档有截然不同的结构和内容特征,用同一套切分参数处理所有文档类型是粗放的。
通用文本文档(TXT、Markdown)
Markdown 文档有天然的标题层级结构,优先按标题切分。一级标题(#)和二级标题(##)下的内容通常就是一个完整的话题。切分时保留标题作为 Chunk 的前缀,这样即使段落脱离了原始位置,Embedding 中仍然包含主题信息。
纯 TXT 文档没有结构信息,退回到递归切分作为默认策略。
PDF 文档
PDF 是最难处理的文档格式。文本型 PDF 的解析结果通常保留了段落分隔和页面信息,可以按段落切分后合并到目标 Chunk 大小。扫描型 PDF 需要先经过 OCR 处理,识别质量会直接影响后续的切分效果。
PDF 中的表格是一个特殊挑战。表格内容如果被按行切分到不同 Chunk 中,语义会完全破碎。建议在解析阶段识别表格区域,将表格作为独立的 Chunk 处理,或者将表格转换为结构化文本后单独切分。
代码文件
代码文件的切分需要特别注意语法边界。按函数或类的定义来切分是最自然的方式——每个函数通常就是一个自包含的逻辑单元。如果使用固定大小切分,可能会把函数从中间截断,导致代码片段无法理解。
实践中的做法:先按空行和缩进做初步分割,再按函数/类定义做合并或拆分。保留文件路径和语言类型作为 Chunk 的元数据,有助于检索时做过滤。
| 文档类型 | 推荐切分策略 | 建议 Chunk Size | 建议 Overlap | 注意事项 |
|---|---|---|---|---|
| Markdown | 按标题层级 + 递归 | 512–1024 Token | 50–100 Token | 保留标题作为 Chunk 前缀 |
| PDF(文本型) | 按段落 + 递归 | 256–512 Token | 50–100 Token | 表格单独处理 |
| PDF(扫描型) | OCR → 段落 + 递归 | 256–512 Token | 50–100 Token | OCR 质量直接影响切分效果 |
| Word(DOCX) | 按段落 + 递归 | 256–512 Token | 50–100 Token | 表格和图片需特殊处理 |
| 代码文件 | 按函数/类定义 | 128–512 Token | 20–50 Token | 保留语法完整性 |
| 对话记录 | 按发言轮次 + 滑动窗口 | 512–1024 Token | 100–200 Token | 保留说话人信息 |
| HTML / 网页 | 按 DOM 结构 + 去噪 | 256–512 Token | 50–100 Token | 先去除导航栏等无关内容 |
| 表格数据 | 按行或按记录 | 视数据而定 | 通常不需要 | 转为结构化文本再切分 |
对话记录与会议转录
对话记录的切分需要保留说话人信息和时间顺序。按发言轮次切分是最自然的方式,每一轮对话(一个说话人的连续发言)作为一个基础单元。如果单轮发言过长,可以在轮次内部做二次切分。
Overlap 在对话记录中尤其重要,因为前后发言之间经常有语义上的承接关系。20% 左右的 Overlap 可以有效避免对话上下文断裂。
表格与结构化数据
表格不适合用文本切分的方式处理。将每一行转为「列名:值」的键值对格式,然后按行或按逻辑记录切分。如果表格有明确的分类维度,可以按类别将多行合并为一个 Chunk。
切分效果评估
切分策略和参数确定后,需要量化评估效果。没有评估的切分就是在凭运气工作。
评估指标
| 指标 | 含义 | 关注点 |
|---|---|---|
| 命中率(Hit Rate) | 检索返回的 Chunk 中包含正确答案的比例 | 切分是否让相关内容能被找到 |
| 精确率(Precision) | 检索返回的 Chunk 中,真正与问题相关的比例 | Chunk 是否太泛、包含太多无关内容 |
| 召回率(Recall) | 所有相关 Chunk 中,被检索到的比例 | 是否有相关内容被遗漏 |
| 上下文相关性(Context Relevancy) | 检索到的 Chunk 与用户问题的语义匹配程度 | Chunk 的粒度是否合适 |
| Chunk 利用率(Chunk Utilization) | LLM 生成回答时实际使用了 Chunk 中多大比例的内容 | Chunk 是否太冗余 |
| Chunk 归因(Chunk Attribution) | 系统是否能正确识别回答来源于哪个 Chunk | 检索结果是否指向了正确的位置 |
评估流程
- 构建测试集:从目标用户群体收集 20–50 个典型问题,标注每个问题的期望答案和答案所在的文档位置
- 基线测试:用默认参数(如 Chunk Size = 512, Overlap = 50)跑一遍测试集,记录各项指标
- 参数调优:调整 Chunk Size 和 Overlap,观察指标变化
- 策略对比:尝试不同的切分策略,在相同的测试集上比较效果
- 人工审核:自动化指标之外,安排人工审查部分检索结果,判断语义层面的匹配质量
评估不是一次性的工作。随着文档内容的更新、用户问题模式的变化、Embedding 模型的迭代,切分参数需要持续调整。
实际案例
案例一:跨境电商产品手册的知识库切分
某跨境电商平台需要将 500+ 份产品手册(PDF 格式,平均每份 20–40 页)接入知识库,供客服人员快速检索产品规格和售后政策。
初始方案使用固定大小切分(Chunk Size = 500 Token, Overlap = 50),测试后发现两个问题:一是产品规格表格被截断,检索时表格内容完全匹配不上;二是不同产品的信息混在同一个 Chunk 中,检索返回了错误产品的参数。
调整后的方案:
- 使用 Docling 解析 PDF,识别并提取表格为独立的结构化文本
- 按文档标题层级做初步切分,每个产品章节作为一个独立的 Chunk 候选
- 对超长章节叠加递归切分,Chunk Size 设为 512 Token,Overlap 设为 100 Token
- 表格内容转为「参数名:参数值」的键值对格式,单独作为 Chunk 存储
- 每个 Chunk 附加产品名称、章节标题作为元数据,支持检索时做过滤
调整后的命中率从 62% 提升到 89%。产品规格类问题的改善尤为明显——表格单独切分后,检索可以精确匹配到具体产品的具体参数。
案例二:SaaS 产品 API 文档的切分优化
一家 B2B SaaS 公司将其 API 文档(Markdown 格式,约 200 个页面,托管在 Git 仓库)接入知识库,目标是让开发者能通过自然语言查询 API 用法。
初版使用按段落切分,Chunk 大小参差不齐(从 50 到 2000 Token 不等)。开发者反馈:对于「某个参数的取值范围是什么」这类精确问题,检索效果不错;但对于「如何实现某个功能」这类需要多步骤回答的问题,检索到的 Chunk 往往只覆盖了流程中的一步。
分析后发现问题在于按段落切分的 Chunk 太小,一个完整的操作流程被分散到了多个 Chunk 中。调整后:
- 改用按二级标题(
##)切分,保留标题作为 Chunk 前缀 - 对于标题下内容超过 1024 Token 的章节,叠加递归切分
- Chunk Size 设为 1024 Token,Overlap 设为 100 Token
- 每个 Chunk 附加文件路径和标题层级作为元数据
调整后,「如何实现 XX」类问题的回答完整度明显提升——更大的 Chunk 包含了完整的操作步骤。同时通过标题前缀和元数据过滤,精确问题的检索精度也没有下降。
文档切分完整流程
切分策略选择指南
| 决策因素 | 选择建议 |
|---|---|
| 文档结构清晰(有标题层级) | 优先按文档结构切分,叠加递归切分处理超长章节 |
| 文档结构不清晰(纯文本) | 使用递归切分作为基线,尝试语义切分看是否提升 |
| 需要精确检索(事实型问答) | 较小的 Chunk Size(128–256 Token),较低的 Overlap |
| 需要完整回答(流程说明、分析) | 较大的 Chunk Size(512–1024 Token),适当增加 Overlap |
| 表格密集 | 表格单独提取和切分,不混入正文切分逻辑 |
| 多主题长文档 | 语义切分或层级切分,避免不同主题混在同一个 Chunk |
| 上下文连续性强(对话、叙事) | 滑动窗口切分,Overlap 比例提高到 15%–20% |
| 快速原型验证 | 固定大小或递归切分,Chunk Size = 512, Overlap = 50 |
| 计算资源有限 | 避免语义切分和层级切分,使用递归切分 |
构建文档切分方案的检查清单
- 切分策略匹配文档类型:是否根据文档的实际结构和内容特征选择了合适的切分策略?
- Chunk Size 在合理范围内:是否在 128–1024 Token 的常见范围内,针对业务场景做了测试?
- Overlap 设置合理:是否在 10%–20% 的范围内,既能保护边界又不造成过多冗余?
- 不破坏语义完整性:切分后的 Chunk 是否能脱离上下文独立理解,没有在句子或概念中间被截断?
- 表格单独处理:文档中的表格是否被识别并作为独立单元处理,而不是按行截断?
- 元数据完整:每个 Chunk 是否携带了来源文档、标题、页码等元数据,支持检索时过滤和归因?
- 代码保持语法完整:代码文件的切分是否保留了函数和类的完整性?
- 有评估数据集:是否准备了 20–50 个典型问题用于量化评估切分效果?
- 有基线对比:是否在相同的测试集上对比了不同策略和参数的表现?
- 命中率达标:检索命中率是否经过测量,并且达到了可接受的水平?
- 人工审核:除了自动指标,是否安排了人工审查检索结果的质量?
- 持续调优机制:是否建立了文档更新后重新评估切分参数的流程?
- 存储成本可控:Overlap 带来的存储膨胀是否在可接受的范围内?
- 多语言兼容:如果文档涉及多种语言,切分工具是否正确处理了不同语言的分词规则?
总结
文档切分是 RAG 系统中投入产出比最高的优化环节之一。它不需要更换模型、升级基础设施或调整算法架构,只需要根据文档类型和业务场景选择合适的切分策略和参数,就能显著提升检索效果。
几个核心原则值得记住:第一,切分策略应该匹配文档类型——Markdown 按标题切分,代码按函数切分,表格单独处理,不要用一种策略通吃所有格式。第二,Chunk Size 和 Overlap 的选择应该基于评估数据而不是直觉——准备好测试集,做网格搜索,用指标说话。第三,切分不是一次性的工作——文档在变、用户在变、模型在变,切分参数也需要跟着迭代。
对于出海产品,还需要额外注意多语言文档的分词差异(中文没有空格分隔词边界)、不同地区文档格式偏好的差异,以及数据存储和处理的合规要求。
参考资料
- Pinecone. "Chunking Strategies for LLM Applications." https://www.pinecone.io/learn/chunking-strategies/
- Weaviate. "Chunking Strategies to Improve LLM RAG Pipeline Performance." https://weaviate.io/blog/chunking-strategies-for-rag
- DataCamp. "Chunking Strategies for AI and RAG Applications." https://www.datacamp.com/blog/chunking-strategies
- Microsoft Azure. "Develop a RAG Solution – Chunking Phase." https://learn.microsoft.com/en-us/azure/architecture/ai-ml/guide/rag/rag-chunking-phase
- Unstructured. "Chunking for RAG: Best Practices and Key Methods." https://unstructured.io/blog/chunking-for-rag-best-practices
- IBM. "RAG 中的分块:架构指南." https://www.ibm.com/cn-zh/think/architectures/rag-cookbook/chunking
- Databricks. "The Ultimate Guide to Chunking Strategies for RAG Applications." https://community.databricks.com/t5/technical-blog/the-ultimate-guide-to-chunking-strategies-for-rag-applications/ba-p/113089
- 腾讯云. "Chunking:基于大模型 RAG 系统中的文档分块." https://cloud.tencent.com/developer/article/2464168