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_size、chunk_overlap、length_function 和是否把 separator 当作正则。(LangChain 文档)从源码看,它的类说明就是“递归尝试不同字符分隔符,直到找到可用切分方式”。这类策略的典型分隔符顺序通常类似:
先按段落切,再按换行切,再按空格切,最后按字符硬切。
适用场景:
场景 | 是否适合 |
普通 TXT、Markdown、网页正文 | 很适合 |
结构较清晰的说明文档 | 适合 |
表格、代码、PDF 复杂布局 | 需要额外结构解析 |
法律合同、论文、手册 | 建议升级为结构感知或父子分块 |
2. LlamaIndex:SentenceSplitter 与 SemanticSplitter
LlamaIndex 的
SentenceSplitter 更重视语言结构。源码说明其切分顺序大致是:段落分隔符 → 句子 tokenizer → 次级正则 → 空格。
也就是说,它优先保留段落和句子边界,而不是直接按固定长度硬切。(GitHub)
LlamaIndex 还提供 Semantic Splitter。它不是固定长度切分,而是先把文本拆成句子,再通过 embedding 相似度判断哪些句子应该合并、哪里应该断开。源码参数里有
buffer_size 和 breakpoint_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_id 和 page_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文档)5. 语义切分:适合主题变化明显的长文
语义切分的大致流程是:
伪代码如下:
注意,语义切分不是银弹。LlamaIndex 的 Semantic Splitter 需要配置 embedding 模型、
buffer_size、breakpoint_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 粒度调整 |
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_tokensmin_tokensfallback recursive splitmetadata offset
坑 5:检索 chunk 和回答 chunk 用同一个粒度
这是很多 RAG 效果差的根本原因。检索需要小而准,回答需要上下文完整。Dify 的父子分块就是为了解决这个矛盾。(Dify 文档)
十二、完整示例:一个适合中文技术文档的切分器
这段代码适合做 baseline。后续可以继续加:
tokenizer 精确 token 计数Markdown AST 解析PDF 页码映射表格单独 chunkparent-child 结构summary chunkchunk 质量评分
十三、最终实践建议
RAG 长文本切分可以按下面的优先级来做:
- 先解析结构,再谈 chunk size
Markdown 用标题,HTML 用 DOM,PDF 用版面,代码用 AST。
- 默认从 512 tokens 左右起步,但必须评估
512/50 是常见实验起点,不是通用最优解。LlamaIndex 示例中使用过
chunk_size=512、chunk_overlap=50,但最终参数应由数据集和查询集决定。(Developer Documentation)- 长文档优先父子分块
child 用于检索,parent 用于回答,尤其适合技术手册、合同、论文、政策制度。Dify 官方也把 Parent-child Mode 定位为适合需要完整上下文的专业文档。(Dify 文档)
- overlap 不要迷信固定比例
overlap 可以防止边界丢失,但会增加重复和成本;应该用 Recall@K、Precision@K、Faithfulness 评估。
- metadata 和 chunk text 一样重要
没有标题路径、页码、版本、权限、source_id 的 chunk,很难进入生产环境。
- 检索粒度和生成粒度分离
这也是从 Dify 父子分块、Claude Code subagent 隔离上下文、OpenCode auto compact 等机制中能看到的共同趋势:主上下文应该只接收“经过筛选、压缩、扩展后的有效信息”,而不是原始长文本。(Dify 文档)
最终可以用一句话总结:
RAG 长文本切分的目标,不是切得均匀,而是让每个 chunk 成为“可检索、可理解、可溯源、可扩展”的最小知识单元。
分享
