文档切分怎么做?Chunk 不是越小越好
2026/7/13 16:03:55 网站建设 项目流程

在 RAG 系统里,很多问题表面上看像是模型回答不准,实际根源可能更早。

用户问的问题明明在文档里,系统却检索不到;检索到了内容,但答案只说了一半;模型拿到上下文后仍然答偏;引用来源看起来相关,但点进去发现不是用户真正需要的那一段。

这些问题不一定是大模型不够聪明,也不一定是向量数据库不够强。

很多时候,是文档切分出了问题。

文档切分,也就是常说的 Chunking,是 RAG 里非常容易被低估的一步。

很多初版知识库系统会直接按固定字数切,比如每 500 字切一段,每段之间重叠 50 字。这样做实现简单,Demo 也能跑起来,但一旦文档复杂起来,就很容易出现问题。

因为 Chunk 不是普通文本碎片,而是 RAG 系统里的检索单元。

切分方式决定了后面系统能检索到什么、模型能看到什么、答案能不能完整、引用能不能准确。

为什么文档一定要切分?

在理想情况下,我们当然希望模型能直接读取完整文档,然后回答用户问题。

但真实系统里通常不能这样做。

原因很简单。

第一,文档可能很长。

一份制度文档、产品手册、接口说明、项目资料,动不动就是几千字、几万字,甚至更长。直接把完整文档塞进模型上下文,成本高、速度慢,也容易超过上下文限制。

第二,用户问题通常只对应文档中的局部内容。

比如用户问“试用期员工能不能申请年假”,真正相关的可能只是员工手册里的两三段。如果把整本文档都给模型,模型反而会被大量无关内容干扰。

第三,检索需要一个合理的最小单位。

向量检索不是在整份知识库里凭空找答案,而是要在一个个可检索片段中计算相关性。如果每个片段太大,相关信息会被淹没;如果每个片段太小,完整语义又会被拆散。

所以文档切分的目标不是把文档切得越碎越好,而是把文档切成适合检索和回答的语义单元。

一个好的 Chunk,应该尽量满足几个条件:

  • 能表达一个相对完整的意思。
  • 包含足够回答问题的上下文。
  • 不混入太多无关内容。
  • 可以被用户问题准确召回。
  • 能作为答案引用的依据。

这几个条件看起来简单,但在真实文档里并不好做到。

常见误区一:按固定字数粗暴切分

最常见的切分方式是固定长度切分。

比如:

每 500 字切一个 chunk。 相邻 chunk 重叠 50 字。 超过长度继续切。

这种方式最大的优点是简单。

不管文档是什么格式,都能快速切成一批片段,马上进入 Embedding 和索引流程。

但它的问题也很明显:固定字数不理解文档结构。

假设一段制度是这样写的:

员工入职未满一年时,年休假按实际工作月份折算。 试用期员工如满足折算条件,可以在系统中提交年休假申请。 申请需经直属负责人和人事部门审批后生效。

如果系统刚好在第一句和第二句之间切开,可能就会出现两个不完整的 Chunk:

员工入职未满一年时,年休假按实际工作月份折算。
试用期员工如满足折算条件,可以在系统中提交年休假申请。 申请需经直属负责人和人事部门审批后生效。

用户问“试用期员工能不能申请年假”,第二个 Chunk 能回答一部分,但缺少“按实际工作月份折算”的条件;第一个 Chunk 有条件,但没有直接提到试用期。

如果检索只召回其中一个,答案就可能不完整。

再比如,一个标题下面有几个连续段落,共同描述一个流程。如果按固定字数切分,很可能把流程步骤拆到不同 Chunk 里。

模型拿到其中一段后,只能回答局部内容。

这就是固定字数切分的核心问题:

它保证了长度可控,但不保证语义完整。

常见误区二:Chunk 越小越精准

很多人会觉得,Chunk 越小,检索越精准。

这个想法只对了一半。

Chunk 小确实可以减少无关内容,让每个片段更聚焦。但如果太小,就会失去上下文。

比如一个文档里写:

以下情况不允许远程办公: 1. 涉及生产环境变更期间。 2. 涉及客户现场交付期间。 3. 部门负责人明确要求现场协作期间。

如果切得太小,可能变成:

以下情况不允许远程办公:
1. 涉及生产环境变更期间。
2. 涉及客户现场交付期间。
3. 部门负责人明确要求现场协作期间。

这些片段单独看都不完整。

用户问“客户现场交付期间能远程办公吗”,系统可能召回“涉及客户现场交付期间”这个片段,但模型如果没有看到前一句“以下情况不允许远程办公”,就无法判断这是允许还是不允许。

这就是 Chunk 过小带来的问题。

它让召回结果看起来很精确,但缺少判断答案所需的语义背景。

所以 Chunk 的目标不是“越小越好”,而是“刚好能承载一个可回答问题的最小语义单元”。

这句话非常重要。

一个好的 Chunk 应该让模型看到它时,能理解这段内容在说什么,而不是只看到孤立的一句话。

常见误区三:Overlap 越大越安全

为了避免切分时把语义拆断,很多系统会使用 overlap,也就是相邻 Chunk 之间保留一部分重复内容。

比如:

chunk size = 500 overlap = 100

Overlap 是有用的。

它可以缓解边界处语义被切断的问题,让前后片段之间有一定连续性。

但 overlap 不是越大越好。

如果 overlap 太大,会带来几个问题。

第一,索引体积变大。

同一段内容被重复写入多个 Chunk,Embedding 成本和存储成本都会增加。

第二,检索结果容易重复。

用户提问后,TopK 里可能出现多个高度相似的片段,占用候选位置,却没有提供更多有效信息。

第三,引用来源会变得混乱。

如果多个 Chunk 都包含同一段文字,系统很难判断到底应该引用哪个片段。

第四,模型上下文被重复内容浪费。

当多个相似 Chunk 一起进入 Prompt,模型看到的是重复信息,而不是更多互补信息。

所以 overlap 应该是补偿切分边界的工具,而不是掩盖粗糙切分策略的万能办法。

如果一个系统需要非常大的 overlap 才能勉强回答问题,往往说明切分方式本身需要重新设计。

Chunk 的本质是检索单元

要做好文档切分,首先要改变一个理解:

Chunk 不是为了让文本长度整齐,而是为了让检索结果可用。

一个 Chunk 会经历这样的链路:

原始文档片段 ↓ Embedding 向量化 ↓ 进入向量索引 ↓ 被用户问题召回 ↓ 进入模型上下文 ↓ 支撑最终答案

所以设计 Chunk 时,要从后面的使用场景倒推。

用户可能会问什么?

答案通常需要多长上下文?

一个制度条款是否必须和标题一起出现?

一个表格是否能拆成多行?

一个流程是否必须保留步骤顺序?

一个代码示例是否必须和说明文字放在一起?

这些问题比“每个 Chunk 切多少字”更重要。

如果切出来的 Chunk 不能被准确召回,或者召回后不能支撑答案生成,那么长度再漂亮也没有意义。

普通段落怎么切?

普通文本段落是最容易处理的一类文档。

比如说明文、制度说明、产品介绍、FAQ 文档等。

这类文档可以优先按自然段切分。

自然段通常表达一个相对完整的意思,比固定字数更符合语义结构。

但也不能完全无脑按段落切。

有些段落太短,单独作为 Chunk 信息量不足;有些段落太长,包含多个主题,需要进一步拆分。

比较实用的做法是:

  • 优先保留自然段。
  • 太短的段落可以和相邻段落合并。
  • 太长的段落再按句子或子主题拆分。
  • 标题要和下面的正文建立关联。
  • 切分后检查每个 Chunk 是否能独立表达意思。

比如:

## 年假规则 员工入职未满一年时,年休假按实际工作月份折算。 试用期员工如满足折算条件,可以在系统中提交年休假申请。 申请需经直属负责人和人事部门审批后生效。

这里最好不要只保留正文,还要让 Chunk 知道它属于“年假规则”这一节。

因为标题往往提供了重要语义。

如果标题丢失,正文里的“申请”“折算”“审批”可能会变得不够清楚。

标题层级很重要

很多文档是有层级结构的,比如:

1. 请假制度 1.1 年假 1.2 病假 1.3 调休 2. 考勤制度 2.1 打卡规则 2.2 异常处理

如果切分时只保留正文,不保留标题路径,就会丢失上下文。

比如正文里写:

申请需提前三个工作日在系统中提交。

这句话单独看并不知道是在说年假、病假、调休,还是其他流程。

如果 Chunk 中包含标题路径,就更清楚:

请假制度 > 年假 申请需提前三个工作日在系统中提交。

标题路径可以显著提升检索和生成质量。

对 RAG 来说,标题不只是排版元素,而是重要的语义信息。

所以切分 Markdown、Word、网页等结构化文档时,建议尽量保留标题层级。

一个常见做法是给每个 Chunk 附加 metadata:

document_title: 员工手册 section_path: 请假制度 > 年假 chunk_text: 申请需提前三个工作日在系统中提交。

这样后续检索、过滤、引用都会更清楚。

列表不要随便拆散

列表在企业文档里非常常见。

比如流程步骤、条件说明、风险点、操作清单、权限规则等。

列表最怕被切散。

比如:

报销申请需要满足以下条件: 1. 发票真实有效。 2. 金额与申请单一致。 3. 审批流程已完成。 4. 未超过报销期限。

如果每个列表项单独成为一个 Chunk,用户问“报销申请需要满足哪些条件”,系统可能只召回其中两三项,答案就会漏。

列表通常有两种处理方式。

第一,如果列表不长,尽量整体保留。

第二,如果列表很长,可以按小组拆分,但每个小组都要带上列表主题。

比如:

报销申请需要满足以下条件: 1. 发票真实有效。 2. 金额与申请单一致。
报销申请需要满足以下条件: 3. 审批流程已完成。 4. 未超过报销期限。

这样即使拆开,每个 Chunk 也知道自己属于“报销申请条件”。

这比单独保留“3. 审批流程已完成”要可靠得多。

表格是 RAG 切分里的难点

很多真实知识库都包含表格。

比如价格表、参数表、权限矩阵、版本对照、接口字段说明、配置清单等。

表格处理不好,RAG 效果会明显下降。

一个常见错误是直接把表格转成普通文本,然后按字数切。

比如原表格是:

| 角色 | 权限 | 审批人 | | --- | --- | --- | | 普通员工 | 提交申请 | 直属负责人 | | 部门经理 | 审批部门申请 | 人事部门 | | 财务人员 | 复核报销单 | 财务负责人 |

如果切分后丢了表头,某一行可能变成:

普通员工 提交申请 直属负责人

模型看到这句话时,很难知道三个字段分别是什么意思。

表格切分时至少要注意:

  • 保留表头。
  • 保留行和列的对应关系。
  • 避免把同一行拆断。
  • 长表格可以按行分组,但每组都要带表头。
  • 表格标题和说明文字也要保留。

比较可用的形式是:

表格:角色权限说明 字段:角色、权限、审批人 记录:普通员工,权限为提交申请,审批人为直属负责人。 记录:部门经理,权限为审批部门申请,审批人为人事部门。

这样比简单拼接表格文本更适合模型理解,也更适合检索。

代码块和接口文档要保持结构

技术文档里经常会有代码块、接口示例、JSON 结构、错误码表等内容。

这类内容不能随便切。

比如一个接口文档通常包括:

  • 接口名称。
  • 请求路径。
  • 请求方法。
  • 请求参数。
  • 返回字段。
  • 示例请求。
  • 示例响应。
  • 错误码说明。

如果把请求参数和接口路径切开,用户问参数含义时,系统可能不知道这个参数属于哪个接口。

如果把返回示例和字段说明切开,模型可能无法准确解释字段。

所以接口文档切分时,要尽量围绕接口粒度组织。

一个接口不长时,可以把接口说明整体作为一个 Chunk。

如果接口很长,可以按请求参数、返回参数、错误码拆分,但每个 Chunk 都要保留接口名称和路径。

比如:

接口:创建订单 路径:POST /orders 部分:请求参数 参数 user_id 表示用户 ID,必填。 参数 product_id 表示商品 ID,必填。 参数 quantity 表示购买数量,默认值为 1。

这样用户问“创建订单接口的 quantity 参数是什么意思”,系统更容易召回正确片段。

技术文档的切分目标不是让文本变短,而是让结构关系不丢。

长文档可以先分层,再切分

长文档最容易让 RAG 系统失控。

比如一份几十页的员工手册、一份完整产品白皮书、一份项目交付规范。

如果直接按固定字数切,系统会得到大量碎片,后续检索很容易召回相似但不准确的内容。

对长文档,更推荐分层处理。

可以先按文档结构拆成章节,再在章节内部切分。

大致流程是:

完整文档 ↓ 按一级标题拆分 ↓ 按二级标题拆分 ↓ 按自然段、列表、表格继续细分 ↓ 为每个 Chunk 附加标题路径和文档 metadata

这样做的好处是,每个 Chunk 都知道自己属于哪个章节。

后续检索时,可以结合标题路径、文档类型、更新时间、权限范围等 metadata 一起使用。

这比单纯把文档切成几百个相似片段要可控得多。

Chunk size 应该怎么定?

很多人最关心的问题是:chunk size 到底设置多少合适?

这个问题没有统一答案。

因为不同文档、不同模型、不同业务问题,对 Chunk 的要求都不一样。

但可以有一些判断方向。

如果 Chunk 太小,常见表现是:

  • 检索结果命中了关键词,但缺少上下文。
  • 模型回答很短,只能答局部信息。
  • 列表、规则、步骤经常漏项。
  • 引用片段看起来像孤立句子。

如果 Chunk 太大,常见表现是:

  • 检索结果主题相似,但包含大量无关内容。
  • 多个问题都召回同一大段文本。
  • 模型答案容易混入不相关信息。
  • Token 成本升高。
  • Rerank 和引用定位变困难。

所以设置 chunk size 时,不能只看字数,而要看效果。

一个实用的判断方法是:拿真实用户问题测试。

对于每个问题,检查:

  • 正确 Chunk 是否能被召回。
  • 召回 Chunk 是否包含完整答案依据。
  • 召回内容是否混入太多无关信息。
  • 模型是否能基于该 Chunk 生成准确答案。
  • 引用是否能指向用户可以核查的范围。

如果这些指标不好,就需要调整切分策略,而不是只改一个数字。

切分策略要和后续链路配合

文档切分不是孤立模块。

它会影响后面的 Embedding、Retriever、Rerank、Prompt 和引用展示。

比如:

Chunk 太碎,Retriever 可能需要召回更多片段,Rerank 压力也会变大。

Chunk 太大,Embedding 的语义表示会变得模糊,检索命中可能不够精准。

标题路径保留得好,Prompt 里就可以展示更清楚的来源信息。

metadata 设计得好,后续就能做权限过滤、版本过滤、文档类型过滤。

引用边界设计得好,用户就更容易信任答案。

所以做 RAG 时,不要把文档切分当成预处理脚本里的小功能。

它应该被当成知识库工程里的核心设计。

一个实用的切分检查清单

在实际项目里,可以用下面这个清单检查切分结果。

第一,每个 Chunk 是否能独立表达一个完整意思?

如果一个 Chunk 只有半句话、一个列表项、一个孤立参数,很可能不够好。

第二,标题和章节路径是否保留?

很多正文只有结合标题才有意义。

第三,列表和表格是否被拆坏?

流程、条件、权限矩阵、参数表都需要保留结构。

第四,Chunk 是否包含过多无关内容?

如果一个 Chunk 里混了多个主题,检索和生成都会变差。

第五,是否保留 metadata?

文档名、章节、版本、更新时间、权限范围、来源链接,都可能影响后续系统能力。

第六,是否用真实问题验证过?

不要只看切分结果整不整齐,要看它能不能支撑真实问答。

第七,是否便于引用?

如果用户无法根据引用追溯答案来源,RAG 的可信度会下降。

这个清单不复杂,但能避免很多早期 RAG 系统的问题。

总结

文档切分是 RAG 系统里非常关键的一环。

Chunk 不是越小越好,也不是越大越省事。

它的本质是检索单元,必须能承载相对完整的语义,并且能支撑模型生成可验证的答案。

固定字数切分可以作为起点,但不能解决所有文档结构问题。

真正可用的切分策略,应该结合自然段、标题层级、列表、表格、代码块、接口说明和业务问题来设计。

一个好的 Chunk,应该让系统更容易召回正确资料,也让模型更容易基于资料回答。

如果 Chunk 切不好,后面的 Embedding、Retriever、Rerank、Prompt 都会被拖累。

所以在构建 RAG 知识库时,不要急着堆模型和工具。

先把文档切分做好,很多问题会在源头上少很多。

下一篇文章,可以继续讨论 Embedding 模型怎么选。

因为文档切好之后,下一步就是让问题和文档在语义空间里真正匹配起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询