文档切分怎么切:chunk 大小、重叠和语义边界
Document Splitting 章节,讲清 RAG 知识库为什么要切分文档,chunk_size、chunk_overlap、递归字符切分、Token 切分和 Markdown 标题切分如何选择。
相关工具
切分不是把长文档剪短,而是保住可检索的语义
文档加载之后,资料已经变成了统一的 Document。下一步不是立刻丢进向量库,而是要把它切成合适的片段。相关概念 在“文档分割”章节里说得很直接:这一步听起来简单,但里面有很多细节会影响后续效果。做 RAG 时,这句话很实在。切得不好,检索会拿错片段;检索拿错片段,模型后面写得再顺也不可靠。
为什么不能把整篇文档直接交给模型?一是上下文窗口有限,长文档可能超过模型一次能处理的 token 数;二是计算成本会变高,把一整本手册塞进每次问答里既慢又贵;三是检索本身需要粒度,用户问的是一个具体问题,系统应该找到相关小节,而不是每次召回整篇文档。
但切分也不是越碎越好。文中提醒,单一字符切分容易丢掉语义信息,回答时可能出现偏差。比如一段退款政策本来包含适用条件、时限、例外情况,如果被切成几块,模型只拿到“支持退款”那一句,就可能漏掉“仅限 7 天内”或“不含定制商品”。文档切分的目标,是让每个 chunk 足够小,方便检索;又足够完整,能让模型读懂。

长文档被切成带重叠的 chunk,进入向量库后按问题召回相关片段。chunk 太粗或太碎,都会影响最终回答。
先理解 chunk_size 和 chunk_overlap
文中介绍 LangChain 文本分割器时,反复出现两个参数:chunk_size 和 chunk_overlap。chunk_size 是每个文本块的大小,可以按字符、token 或其他长度函数计算。chunk_overlap 是相邻文本块之间共享的部分,用来保留上下文,避免切分点附近的信息被硬生生截断。
可以把 chunk_size 理解成每张资料卡片最多能放多少内容。太大,卡片里可能混进多个主题,用户问“发票怎么开”,检索回来却同时包含退款、开票、合同、付款方式,模型需要在一堆内容里自己挑。太小,卡片可能只有一句话,缺少标题和前后条件,模型知道某个规则,却不知道它属于哪个场景。
chunk_overlap 则像卡片之间的交接部分。比如上一段末尾写“申请退款需要满足以下条件”,下一段才列条件。如果没有重叠,某个 chunk 可能只保留标题,另一个 chunk 只保留条件,检索时命中其中一个都不完整。适当重叠可以让上下文不断裂。但重叠太大也会增加重复内容,让索引变胖,召回结果更相似。
一个片段包含多个主题,检索命中后模型容易抓错重点。
片段缺少上下文,模型可能漏掉条件、范围和例外。
保留切分点附近的承接信息,减少上下文断裂。
普通字符切分适合简单文本,但别机械使用
字符切分最容易理解:按照固定长度或指定分隔符把文本拆开。文中演示了 CharacterTextSplitter,如果不设置合适分隔符,它可能不会按预期切;设置逗号作为分隔符后,又可能出现某个块比指定长度更长的提示。这说明字符切分虽然直观,但它很依赖文本本身的标点和格式。
如果资料是短 FAQ、字段清楚的商品描述、每段结构差不多的说明文,字符切分可以作为第一版方案。比如每条 FAQ 本来就只有一个问题和一个答案,切分策略不需要复杂。只要确保问题、答案和必要标签在同一个片段里,检索通常不会太差。
但对于制度文件、技术手册、课程讲义和合同条款,机械按字符切很容易把标题、条件和解释拆散。尤其是中文资料,有时一个长句里包含多个限制条件,简单按标点切也未必稳。工程上更常见的做法,是把字符切分当兜底策略,而不是唯一策略。
递归字符切分更适合通用文本
资料 对 RecursiveCharacterTextSplitter 的解释很有用:它会按分隔符优先级递归切分,比如先按双换行,再按单换行,再按空格,最后才按空字符。这样做的目的,是尽量把语义相关的内容保留在一起。作者也给出建议:通用文本里更推荐使用递归字符文本分割器。
为什么递归切分更稳?因为真实文档天然有层次。段落之间的双换行,通常比句子中的空格更适合做边界;小节标题和正文之间,也比任意字符位置更适合做边界。递归切分先尝试大的自然边界,实在太长再往更小的边界切,比较符合人读文档的方式。
不过,递归切分也不是放进去就万事大吉。你仍然要看切分结果。比如一篇 资料 抽取后没有换行,递归切分就只能退到更细的分隔符;一篇文档里列表编号混乱,也可能把步骤拆坏。调 chunk 的第一步不是猜参数,而是抽样看切出来的片段像不像人能读懂的小资料卡。
优先保留标题、章节、段落和列表。
超过 chunk_size 的内容继续向句子或标点下探。
在相邻 chunk 中保留少量承接信息。
看每个片段是否能独立支撑一个回答。
Token 切分要服务模型限制,不要只看字符数
很多大模型的上下文限制按 token 计算,而不是按字符计算。资料 在 Token 分割部分提到,token 长度和字符长度不一样,基于 token 的切分更接近 LLM 的视角。对英文、代码、混合文本来说,这一点很明显。同样长度的字符,在不同语言和符号密度下,token 数可能差很多。
如果你的知识库会把多个片段一起塞进模型上下文,就必须估算 token。比如每次召回 5 个 chunk,每个 chunk 约 800 token,再加系统提示、用户问题、历史对话和输出空间,整体就可能接近上下文上限。chunk_size 不能只凭肉眼看长短,最好按模型实际 token 预算倒推。
中文资料在不同分词器里表现会有差异,文中也提示当时 LangChain 的 Token 分割器对中文支持有限。实际应用中可以采用更保守的策略:中文 chunk 不要设得过大,保留标题和上下文,测试时关注真实输入 token。不要等线上回答被截断,才发现每次检索带进去的上下文太长。
Markdown 标题切分适合结构化文档
如果资料已经是 Markdown,标题就是很好的切分边界。文中展示了 MarkdownHeaderTextSplitter:它会根据一级、二级、三级标题切分内容,并把标题作为 metadata 加到每个 chunk 里。这个做法很适合知识库,因为标题本身就是语义提示。
比如一篇内部制度有“报销范围”“审批流程”“发票要求”“特殊情况”几个小节。按标题切分后,每个片段都带着它所属的小节。用户问“住宿发票能不能报”,检索不仅能命中发票要求的正文,也能通过 metadata 知道它属于哪篇制度、哪个小节。模型回答时更容易说清依据。
所以,在资料入库前,把 资料 或网页整理成 Markdown 往往很值得。不是为了好看,而是为了让后续切分更自然。标题清楚、段落清楚、列表清楚,RAG 才更容易检索到正确片段。资料结构对人友好,对机器通常也更友好。
一套可落地的切分检查方法
调切分参数时,可以先准备 20 个真实问题,然后用不同切分方案建索引,看每个问题召回的 Top-K chunk 是否包含答案依据。不要只看最终模型回答,因为模型可能把检索问题掩盖掉。先看召回,再看生成,定位会清楚很多。
检查 chunk 时有几个简单标准:一个片段最好只围绕一个主题;标题和正文不要分离;条件、例外、步骤不要被切断;表格要尽量保持行或字段关系;相邻 chunk 的重叠不要大到几乎重复;metadata 要能指回原文。满足这些标准,后面的 Embedding 和检索才有比较好的基础。
第一版可以先保守一些。对普通中文文档,用递归字符切分,chunk_size 设在一个不会太长的范围,保留少量 overlap;对 Markdown,优先按标题切;对 FAQ 和表格,尽量按记录或问答对切。上线后再根据失败问题调整,而不是一开始追求一个万能参数。
常见问题
chunk_size 应该固定多少?
没有统一答案。要看文档类型、模型上下文、召回数量和问题复杂度。更可靠的方法是用真实问题测试召回结果。
chunk_overlap 越大越好吗?
不是。适当重叠能保留上下文,过大重叠会增加重复内容和索引成本,也可能让召回结果过于相似。
Markdown 文档为什么适合按标题切?
标题天然表示语义层级。按标题切可以保留章节关系,并把标题写入 metadata,方便检索、引用和排查。