Lazy loaded image
Agent丨RAG 中长文本应该如何切分,如何选择合适的切分策略?
Words 6359Read Time 16 min
2026-5-15
type
Post
status
Published
date
May 15, 2026
slug
agent40
summary
tags
Agent
category
icon
password
RAG 中的“长文本切分”不是简单把文档切成 500 token 一段,而是在 检索精度、上下文完整性、索引成本、生成阶段可用上下文 之间做平衡。切得太小,召回很精准但上下文不足;切得太大,语义被稀释、召回噪声变多、上下文成本升高。LlamaIndex 文档也明确指出:较小 chunk 的 embedding 更精确,较大 chunk 更泛化但可能遗漏细粒度信息;调整 chunk size 时,也可能需要同步调整 similarity_top_k。(Developer Documentation)

一、先给结论:长文本切分的核心原则

在生产级 RAG 中,我建议把切分策略理解成下面这句话:
索引用小粒度,回答用大上下文;切分时保留结构,召回后再做上下文扩展。
也就是说,真正成熟的 RAG 系统通常不会只使用一种 chunk。它会把文档拆成多个层级:
层级
作用
推荐粒度
原始文档
溯源、权限、版本控制
完整文件
结构块
章节、标题、段落、表格、代码函数
Markdown/HTML/PDF 结构
检索块
用于 embedding / BM25 / rerank
200~800 tokens 起步
上下文块
真正塞给 LLM 回答
命中块 + 前后文 / 父块 / 摘要
Dify 的父子分块就是这个思想的典型工程实现:用较小的 child chunk 匹配查询,但返回更大的 parent chunk 给 LLM,从而同时兼顾“匹配精度”和“上下文完整性”。(Dify 文档)

二、为什么长文本不能直接整体 Embedding?

长文本整体 Embedding 的问题主要有三个。
第一,语义稀释。一份 20 页技术文档可能同时包含背景、安装、API、错误码、FAQ。整体向量会变成多个主题的平均表示,用户问“错误码 403 怎么处理”时,整体文档向量未必比某个具体错误码段落更相似。
第二,召回上下文浪费。如果把整篇文档返回给 LLM,可能只有 3% 内容相关,其余都是噪声。上下文窗口越大,这个问题越隐蔽,因为系统看似“能塞下”,但生成模型仍然要在大量无关内容里找答案。
第三,边界信息丢失。如果简单按字符或 token 切,可能把一个定义、公式、代码块、表格行切断。Dify 文档中也提到 chunk overlap 的作用是保留相邻块之间的语义连接,避免重要信息被切在边界外。(Dify 文档)

三、成熟框架中的切分策略调研

1. LangChain:RecursiveCharacterTextSplitter

LangChain 官方文档中,RecursiveCharacterTextSplitter 的核心设计是:按一组分隔符递归切分,并用字符数衡量 chunk size。官方示例中可以配置 chunk_sizechunk_overlaplength_function 和是否把 separator 当作正则。(LangChain 文档)
从源码看,它的类说明就是“递归尝试不同字符分隔符,直到找到可用切分方式”。这类策略的典型分隔符顺序通常类似:
先按段落切,再按换行切,再按空格切,最后按字符硬切。
LangChain 源码中 RecursiveCharacterTextSplitter 的实现就是围绕 separators、keep_separator 和递归切分逻辑展开的。(GitHub)
适用场景:
场景
是否适合
普通 TXT、Markdown、网页正文
很适合
结构较清晰的说明文档
适合
表格、代码、PDF 复杂布局
需要额外结构解析
法律合同、论文、手册
建议升级为结构感知或父子分块

2. LlamaIndex:SentenceSplitter 与 SemanticSplitter

LlamaIndex 的 SentenceSplitter 更重视语言结构。源码说明其切分顺序大致是:
段落分隔符 → 句子 tokenizer → 次级正则 → 空格。
也就是说,它优先保留段落和句子边界,而不是直接按固定长度硬切。(GitHub)
LlamaIndex 还提供 Semantic Splitter。它不是固定长度切分,而是先把文本拆成句子,再通过 embedding 相似度判断哪些句子应该合并、哪里应该断开。源码参数里有 buffer_sizebreakpoint_percentile_threshold,后者用于控制语义断点阈值,阈值越低通常会产生更多节点。(GitHub)
适用场景:
策略
适合文档
优点
风险
SentenceSplitter
技术文档、知识库文章、FAQ
句子不易被切断
对表格/代码弱
SemanticSplitter
主题跳转明显的长文
语义块更自然
成本更高,需限制最大长度
TokenSplitter
需要严格控制模型输入
简单可控
容易破坏语义边界

3. Haystack:DocumentSplitter 的工程化参数

Haystack 的 DocumentSplitter 提供了更工程化的参数:split_by 支持 "word""sentence""passage""page""line""function" 等;split_length 控制长度;split_overlap 控制重叠;split_threshold 可把过短片段合并到前一个 chunk;同时它会保留 source_idpage_number 等元数据,方便溯源。(Haystack文档)
这个设计对生产系统很重要,因为 RAG 不只是“能召回”,还要能回答:
这个答案来自哪份文档?
来自第几页?
原文版本是什么?
用户有没有权限看这段内容?

4. Dify:General、Parent-child、Q&A 三种知识结构

Dify 的知识库支持 General Mode、Parent-child Mode、Q&A Mode。官方文档说明,Parent-child Mode 会使用 child chunk 匹配查询,再返回 parent chunk 提供上下文;这非常适合技术手册、研究报告、专业文档等信息密集型材料。(Dify 文档)
在 Dify 的 Knowledge Pipeline 中,父子分块还可以配置 Parent Delimiter、Parent Maximum Chunk Length、Child Delimiter、Child Maximum Chunk Length,并在父块模式上选择 Paragraph 或 Full Document。官方文档还建议初次创建知识库时优先考虑 Parent-child Mode。(Dify 文档)
这说明成熟 RAG 平台已经从“单层 chunk”走向“层级 chunk”。

5. Claude Code、OpenCode、OpenClaw 给 RAG 的启发

Claude Code、OpenCode、OpenClaw 不是传统 RAG 文档切分框架,但它们在 上下文管理 上给 RAG 很多启发。
Claude Code 文档说明,context window 会包含指令、读取过的文件、模型回复以及用户终端看不到的内容;当 subagent 执行研究任务时,大量文件读取会留在 subagent 自己的上下文里,主上下文只接收摘要和少量元数据。(Claude Code)
OpenCode 仓库说明它有 auto compact 功能:接近模型上下文窗口限制时自动总结会话,并用总结创建新 session,默认在 95% 上下文窗口触发。(GitHub)
OpenClaw 官方仓库把它定位为运行在用户自己设备上的 personal AI assistant,能通过用户已有渠道响应和执行任务。(GitHub)
这些 Agent 系统给 RAG 的启发是:
不要把所有原始材料都塞进主上下文。
应该把“检索、阅读、压缩、摘要、上下文扩展”做成分层流程。
这与父子分块、summary index、chunk expansion 的思想是一致的。

四、长文本切分的标准流水线

一个生产级 RAG 切分流程通常不是“一行 splitter”,而是下面这样的管线:
其中最容易被忽略的是 D、G、K 三步:
D:如果 PDF、Markdown、HTML、代码结构没识别好,后面怎么切都很难救。
G:没有元数据,就无法做权限、版本、页码、标题路径、溯源。
K:检索粒度和生成粒度最好分离,不能直接把检索 chunk 当最终上下文。

五、常见切分策略详解

1. 固定长度切分:最简单,但最容易破坏语义

固定长度切分通常按字符、token、字数切:
适合:
日志、流水文本、格式统一的记录、快速 baseline。
不适合:
合同、论文、技术手册、Markdown 文档、API 文档、代码仓库。

2. 递归字符切分:通用默认方案

递归切分的思想是:先用“大边界”切,切不开再用“小边界”。
这类策略适合作为通用 baseline,尤其是中文技术文章、普通知识库、网页正文。LangChain 官方也把 RecursiveCharacterTextSplitter 作为常用递归切分方式,先按字符列表切,再按长度函数约束 chunk size。(LangChain 文档)

3. 句子 / 段落感知切分:更适合知识问答

句子切分适合 QA 型 RAG,因为用户问题通常命中的是一个完整事实,而事实往往以句子或段落为单位。
相比字符 overlap,句子 overlap 更适合中文说明文、政策文档、业务规则文档,因为不会把半句话切进下一段。

4. 结构化切分:Markdown、HTML、PDF、代码必须优先使用

如果文档有明显结构,不要一上来就按 token 切。
Markdown 应该保留标题路径:
代码应该按函数、类、文件模块切,而不是纯 token 切。Haystack 的 DocumentSplitter 支持按 "function" 切分,这说明工程框架已经把“代码结构”作为一种独立切分单位。(Haystack文档)
PDF 则要尽量保留页码、标题、表格、图注。Haystack 会给切分后的文档添加 source_idpage_number,这是生产环境非常关键的溯源元数据。(Haystack文档)

5. 语义切分:适合主题变化明显的长文

语义切分的大致流程是:
伪代码如下:
注意,语义切分不是银弹。LlamaIndex 的 Semantic Splitter 需要配置 embedding 模型、buffer_sizebreakpoint_percentile_threshold 等参数;阈值会影响生成节点数量,因此必须结合真实查询集评估。(GitHub)

6. 父子分块:长文本 RAG 的强推荐方案

父子分块的核心思想:
工程代码可以这样设计:
Dify 的 Parent-child Mode 正是类似思路:child chunks 用于 query matching,parent chunks 用于提供上下文;并且父块可以按段落或整篇文档构造。(Dify 文档)

六、如何选择合适的 chunk size?

不要问“最佳 chunk size 是多少”,要问:
我的用户问题通常需要多大上下文才能回答?
可以按下面方式选择。

1. FAQ、术语、短知识点

推荐:
参数
建议
chunk size
100~300 tokens
overlap
0~50 tokens
策略
Q&A Mode / 句子切分 / 小段落
top_k
可以稍大
原因:每条知识相对独立,chunk 太大会引入噪声。

2. 技术文档、API 文档、操作手册

推荐:
参数
建议
child chunk
300~800 tokens
parent chunk
一个小节或二级标题下全文
overlap
50~100 tokens 起步
策略
Markdown 结构切分 + 父子分块
top_k
根据 child 粒度调整
LlamaIndex 示例中也常用 chunk_size = 512chunk_overlap = 50 作为一个可实验的起点。(Developer Documentation)

3. 法律合同、政策制度、金融条款

推荐:
参数
建议
chunk size
500~1200 tokens
overlap
按条款/段落,而非固定字符
策略
条款结构切分 + 父子分块
必须保留
条款编号、标题、上下位条款
原因:合同条款之间经常有引用关系,例如“除本条第二款另有约定外”。这类文本不能只切命中的一句话。

4. 论文、研究报告、白皮书

推荐:
参数
建议
chunk size
500~1000 tokens
策略
标题层级 + 段落切分 + 摘要 chunk
特殊处理
图表标题、公式、参考结论
召回后处理
命中段落 + 所属章节摘要
这类文档适合建立多层索引:
摘要索引:用于快速判断相关章节。
子块索引:用于精确定位事实。
父块上下文:用于最终回答。

5. 代码仓库

推荐:
对象
切分单位
函数
函数完整体
类定义 + 方法摘要
配置文件
key/value 或 section
README
Markdown 标题
测试文件
test case 维度
代码 RAG 不应该优先按 token 切,而应尽量使用 AST、函数、类、模块路径。Haystack 支持按 "function" 切分,LlamaIndex 文档也有代码/AST 相关 splitter,这类结构感知能力比纯字符切分更适合代码场景。(Haystack文档)

七、chunk overlap 应该怎么设?

Overlap 的作用是防止边界信息丢失,但它不是越大越好。
Dify 文档中解释了 overlap 可以让上一个 chunk 的末尾出现在下一个 chunk 的开头,从而保留语义连接。(Dify 文档) 但 Chroma 的 chunking 评测也显示,不同 embedding 模型、chunk size、splitter 下,overlap 的效果并不一致;例如他们发现某些 RecursiveCharacterTextSplitter 配置下无 overlap 也表现稳定,而小上下文时 overlap 又可能提升 recall。(Chroma)
我的建议:
场景
overlap 建议
FAQ / Q&A
0~10%
普通知识库
10% 左右起步
技术手册
50~100 tokens
法律/政策
按条款上下文,不建议机械字符 overlap
代码
不建议简单 overlap,优先按函数/类引用扩展
父子分块
child 可少 overlap,靠 parent 扩展上下文

八、一个生产级切分配置决策树


九、评估切分策略:不能靠感觉

切分策略必须通过评估闭环来选,而不是拍脑袋。
建议准备一批真实问题,每个问题标注:
应该命中的文档
应该命中的章节
最小充分证据片段
标准答案或答案要点
然后评估:
指标
含义
Recall@K
正确 chunk 是否在前 K 个召回结果中
Precision@K
前 K 个结果里有多少是真相关
MRR
第一个正确结果排第几
nDCG
排序质量
Context Precision
塞给 LLM 的上下文有多少有用
Faithfulness
生成答案是否忠于证据
Answer Correctness
最终答案是否正确
Cost / Latency
索引、召回、rerank、生成成本
Chroma 的评测也表明,chunking strategy 会显著影响 retrieval performance,而且不同策略在 recall、precision、IoU 等指标上表现不同。(Chroma)

十、推荐的工程默认配置

如果你现在要从 0 搭一个 RAG,我建议先用这个 baseline:
文档类型
默认策略
普通中文技术文档
Markdown/标题切分 + Recursive Split
企业知识库
句子/段落切分 + 10% overlap
长技术手册
父子分块,child 512 tokens,parent 按章节
FAQ
Q&A 切分,一问一答一块
PDF 报告
PDF 结构解析 + 页码 + 段落切分
代码仓库
AST/函数/类切分 + 文件路径元数据
法律合同
条款编号切分 + 父子分块
初始参数:
参数
起步值
child chunk size
300~800 tokens
child overlap
50~100 tokens
parent chunk
一个标题小节或完整条款
top_k
5~10 起步
rerank top_n
3~5
metadata
source、page、title_path、version、permission

十一、容易踩的坑

坑 1:只切正文,不保留标题

很多答案需要标题才能理解。比如正文只有“支持三种模式”,如果没有标题“知识库索引模式”,这个 chunk 的语义会非常弱。
正确做法:
chunk text = 标题路径 + 正文
metadata = title_path、section_id、page_number

坑 2:PDF 先转纯文本再切

PDF 里的表格、页眉页脚、图注、分栏会被打乱。应该先做版面解析,再切分。

坑 3:overlap 设置过大

overlap 太大会导致:
索引膨胀
重复召回
rerank 被重复内容污染
LLM 上下文浪费
Chroma 的实验也说明 overlap 并不是稳定收益项,需要结合模型和数据评估。(Chroma)

坑 4:语义切分没有最大长度兜底

语义切分可能把一个长主题合并成超大 chunk,超过 embedding 模型或 reranker 输入限制。因此必须加:
max_tokens
min_tokens
fallback recursive split
metadata offset

坑 5:检索 chunk 和回答 chunk 用同一个粒度

这是很多 RAG 效果差的根本原因。检索需要小而准,回答需要上下文完整。Dify 的父子分块就是为了解决这个矛盾。(Dify 文档)

十二、完整示例:一个适合中文技术文档的切分器

这段代码适合做 baseline。后续可以继续加:
tokenizer 精确 token 计数
Markdown AST 解析
PDF 页码映射
表格单独 chunk
parent-child 结构
summary chunk
chunk 质量评分

十三、最终实践建议

RAG 长文本切分可以按下面的优先级来做:
  1. 先解析结构,再谈 chunk size
    1. Markdown 用标题,HTML 用 DOM,PDF 用版面,代码用 AST。
  1. 默认从 512 tokens 左右起步,但必须评估
    1. 512/50 是常见实验起点,不是通用最优解。LlamaIndex 示例中使用过 chunk_size=512chunk_overlap=50,但最终参数应由数据集和查询集决定。(Developer Documentation)
  1. 长文档优先父子分块
    1. child 用于检索,parent 用于回答,尤其适合技术手册、合同、论文、政策制度。Dify 官方也把 Parent-child Mode 定位为适合需要完整上下文的专业文档。(Dify 文档)
  1. overlap 不要迷信固定比例
    1. overlap 可以防止边界丢失,但会增加重复和成本;应该用 Recall@K、Precision@K、Faithfulness 评估。
  1. metadata 和 chunk text 一样重要
    1. 没有标题路径、页码、版本、权限、source_id 的 chunk,很难进入生产环境。
  1. 检索粒度和生成粒度分离
    1. 这也是从 Dify 父子分块、Claude Code subagent 隔离上下文、OpenCode auto compact 等机制中能看到的共同趋势:主上下文应该只接收“经过筛选、压缩、扩展后的有效信息”,而不是原始长文本。(Dify 文档)
最终可以用一句话总结:
RAG 长文本切分的目标,不是切得均匀,而是让每个 chunk 成为“可检索、可理解、可溯源、可扩展”的最小知识单元。
回到首页