← 返回文章
RAGChunkingRetrieval

RAG 切块:先保护语义边界,再谈 chunk_size

切得太碎会丢上下文,切得太大又会稀释证据;先检查结构,再调数字。

面试里常问 chunk_size 和 overlap 怎么设。我不认为存在一个适合所有文档的固定数字。真正要避免的是:检索命中一个片段,却看不到理解它所必需的标题、前提或表头。

比如制度文档中,“三日内提交”如果和上一段的“仅限试用期员工”被拆开,单独命中前者就可能给出错误适用范围。表格里把表头和数据行拆开也一样。

先保结构,再调参数

我会先按标题、段落、列表和表格边界切分,把文档标题、章节路径与页码作为片段元数据。一个段落过长时,再按句子或长度拆分;重叠部分只保留跨边界所需的上下文。表格则尽量保留表头与行的对应关系。

随后拿真实问题检查三个现象:正确片段能否进入候选集、引用是否能独立读懂、送给模型的上下文是否被重复片段挤占。overlap 增大可能救回跨边界信息,也可能使重复内容占满窗口并提高索引成本。

因此切块参数应该随文档类型和查询集一起评估。先修复解析与边界错误,再靠数值微调召回;否则把 500 改成 800,只是在不同尺度上重复同一个问题。

什么时候该合并,什么时候该拆开

切块的单位不是字数,而是一个可被引用的事实单元。FAQ 的一问一答往往适合放在一起;制度条款要保留适用对象、例外与生效日期;操作手册则要避免把“前提条件”和“执行步骤”分在两个互不关联的块里。反过来,如果一整章里同时讲安装、授权和故障处理,整章入库会让大量无关文字一起进入候选。

一个实用办法是给每块附上结构路径,例如“员工手册 / 假期 / 试用期”,并将路径与正文一起参与检索或重排。这样不必为了让模型看到标题而机械地扩大每个块。对于跨段落的条件句,可以保留指向相邻块的关系,在最终组装证据时按需取回邻居,而不是对所有块统一设置很大的 overlap。

用错误样本选择参数

准备几类问题:答案完全落在一段里、答案跨段落、答案在表格中、标题决定适用范围。分别统计正确证据进入候选的比例、最终引用是否包含完整条件,以及每次请求的重复 token 数。若跨段问题经常失败,先检查边界和邻块补全;若简单问题召回变差,则看是否因为块过大而使关键词和语义都被稀释。

我会保留一份原文到片段的映射。切块方案升级后,不只比较“回答好不好”,还要能定位是哪种文档、哪条边界发生了变化。否则一次看似提高平均分的调参,可能恰好破坏最关键的例外条款。

一个具体的回归实验

假设有 50 份制度文件,先挑 30 个已知出处的问题,覆盖普通条款、例外条款、跨页表格和旧版文件。方案 A 按固定长度切,方案 B 按章节和句子切。不要只给两套方案各跑一遍大模型答案,而是先在相同查询、相同索引配置下比候选召回:正确片段是否出现、标题和例外是否完整、一个事实被拆成多少块。随后再比较重排后的引用和回答。

如果 B 的正确片段进入候选更多,却同时产生大量相互重复的块,可能需要收紧邻块补全,而不是退回 A。如果 B 的表格问题更差,就检查表格解析器是否把行列展开错了。这种实验让“切块策略更好”成为可解释的结论,而非看几段示例后的主观印象。

切块还会影响引用体验。用户点击某条引用时,应能回到原文中足够具体的位置;如果一个块横跨十几页,即使答案正确,也难以核对。反过来,一个块只有半句,引用页又缺上下文,同样不能证明答案。因此我会把“能否被人独立核查”与召回率并列为设计目标。