RAG(检索增强生成)落地时,分块(chunking,把长文档切成喂给向量库的小段)大概是工程师踩的第一个参数坑。怎么切、切多大,网上的答案是清一色的理论推演:“语义完整性”、“粒度平衡”、“建议 512”。这些建议有没有数字撑腰?我把自己的 10 篇技术长文切了,跑了个对照实验。
一、实验怎么搭的
语料:2026 年本人写的 10 篇实测长文(DeepSeek Harness 系列 4 篇、ARC-AGI-3 harness 实测、DseWiki 数据实拉、SkillSpector 扫描、两篇模型对比、一篇 Code Review 规范重写),其中 7 篇已发在本账号,3 篇是没发的稿子——语料按域选齐,不看发布状态。共 74,727 字符,清洗掉导航行和状态注记,代码块和表格原样保留——中文技术文,带代码、带表格,是分块策略最容易出事故的文体。
测试集:100 道题。每篇文章出 10 题,分两档——A 档 30 道主题级(问文章主线话题),B 档 70 道细节级(必须读到正文里的具体数字、命令、报错才能答,比如"DSH 用 npm 发了多少个版本")。题目按真实搜索口吻写,不允许出现"这篇文章"这种元指涉。ground truth(标准答案)标到文章级。
变量与固定量:被测的是三种分块策略 × 三种粒度——
| 固定窗口 | 递归切分 | 结构感知 | |
|---|---|---|---|
| 实现 | 字符滑窗 | LangChainRecursiveCharacterTextSplitter,中文分隔符 | 自建,按 Markdown 标题分节,代码块不切断 |
| 粒度 | 256 / 512 / 1024 字符 | 同左 | 同左 |
嵌入模型固定 bge-small-zh-v1.5,向量库 Chroma,top-k=5,重叠(overlap)固定 50 字符。3×3 共 9 组配置,每组 100 题全量检索,共 900 次。CPU 跑完一组 10~15 秒。
二、主结果:总体差异小得意外
| 配置 | 块数 | Hit@1 | A 档 Hit@1 | B 档 Hit@1 | MRR@5 | 代码块切断次数 |
|---|---|---|---|---|---|---|
| 固定-256 | 366 | 0.830 | 0.767 | 0.857 | 0.877 | 23 |
| 固定-512 | 165 | 0.830 | 0.767 | 0.857 | 0.877 | 10 |
| 固定-1024 | 80 | 0.830 | 0.867 | 0.814 | 0.885 | 7 |
| 递归-256 | 471 | 0.820 | 0.767 | 0.843 | 0.875 | 12 |
| 递归-512 | 203 | 0.820 | 0.767 | 0.843 | 0.875 | 4 |
| 递归-1024 | 90 | 0.810 | 0.767 | 0.829 | 0.865 | 2 |
| 结构-256 | 347 | 0.810 | 0.767 | 0.829 | 0.867 | 10 |
| 结构-512 | 188 | 0.810 | 0.767 | 0.829 | 0.867 | 8 |
| 结构-1024 | 97 | 0.820 | 0.767 | 0.843 | 0.872 | 2 |
九组配置的 Hit@1 全挤在 0.81~0.83。选错分块策略的代价,比网上说的小得多——至少在万字级语料、文章级 ground truth 这个口径下,策略之间拉不开数量级差距。
但 A/B 分层看,结论才站得住。
三、A/B 档方向相反
A 档(主题级)只有固定-1024 突出:0.867,其余八组齐刷刷 0.767。大窗口把整篇文章的主题语境塞进一个块里,主题级查询的语义匹配更稳。
B 档(细节级)反过来:固定-1024 掉到 0.814,是三种策略的 1024 粒度里最差的;256 和 512 粒度下三策略的差距压在 2 题以内(0.829~0.857)。细节事实分散在全文各处,窗口越大,单块里被"稀释"掉的上下文越多。
说直白点:如果你的 RAG 主要答"这份文档讲了什么"级别的问题,大窗口是免费收益;如果答细节,大窗口开始亏。
四、1024 字符组藏着截断伪影
实验里最反直觉的一组数字:bge-small-zh-v1.5 的输入窗口是 512 token,我用 tokenizer 逐块实测——固定-1024 有 90% 的块超长被模型静默截断,递归-1024 是 72%,结构-1024 是 61%。也就是说,1024 粒度组测的根本不是"1024 字符的分块效果",而是"1024 字符的块被截成前半段的效果"。
那固定-1024 的 A 档优势是真的吗?我补了个对照:同样的分块,一组保留前 510 token(模型默认行为),一组保留后 510 token——
| 截断方向 | A 档 Hit@1 | B 档 Hit@1 |
|---|---|---|
| 保头(默认) | 0.867 | 0.814 |
| 保尾 | 0.833 | 0.729 |
保尾让 B 档掉了 8.5 个百分点。细节事实散布在块的全文,截掉哪头都丢内容;而主题词天然集中在块的开头,静默截断"恰好"护住了主题信号——固定-1024 的 A 档领先,一部分是截断方向的运气,不是大窗口本身的功劳。顺带一个实操提醒:选大 chunk 前先查嵌入模型的 max_seq_length,否则你在测一个自己没意识到的混合体。
五、结构感知的真实价值在代码块
结构感知策略在命中率上没赢,但它赢了另一张表:代码块被切断的次数。
同样是 256 字符,固定窗口切了 23 次代码块,结构感知只切 10 次;拉到 1024,结构感知和递归都只剩 2 次,固定窗口还有 7 次。切断的代码块在检索层看不出来——数字上它照样命中——但下游生成层拿到半截package.json或半条命令,就是幻觉的直接原料。如果 RAG 检索结果要喂给模型生成答案,代码完整性比 2 个点的命中率值钱。
六、5 道题,9 组配置全部失手
有 5 道题在所有配置下 Hit@1 全军覆没。一个个拆开看,死法几乎一样:全死在跨文章语义重叠。“dsh 启动后 web 界面默认开在哪个端口”,答案在 DSH 上手实录,top1 却是 197 包架构解读;"dsh 官方自带哪几个预设模式"问的也是上手实录,被插件市场文抢走;"dsh 的 patch 文件里空数组后面能追加条目吗"指向插件市场文,top1 是上手实录;"DSH 的 197 个包按功能分成了哪几大类"问 197 包文,还是被上手实录压过去;"只读沙箱里的 agent 怎么把 wiki 页面改掉的"指向 DseWiki 数据实拉,top1 是 DSH 插件开发——沙箱执行语境强重叠。五道题里四道死在 DSH 系列内部互抢:同一个系列写多了,篇与篇的检索边界自然糊掉。
这不是分块的锅。任何 chunking 配置都救不回语义层的歧义,这个锅在下游——重排序(rerank,拿原查询对候选块精排)或元数据过滤。这也是系列第 2 篇的入口:chunking 榨不出来的分,得去重排序那层取。
七、工程结论
- 别为分块策略焦虑:9 组配置的差距被普遍高估,先把嵌入模型、测试集和 top-k 调明白,收益更大;
- 按问题类型选粒度:答主题选大窗口,答细节选 256~512;两种都要,就上父子分块(粗粒度检索、细粒度喂给模型);
- 大 chunk 先查嵌入窗口:超窗的截断是静默的,必要时把 chunk 控制在模型 max_seq_length 的 token 数内;
- 带代码的语料优先结构感知:命中率没赢,代码完整性完胜,生成质量的钱在这省;
- 分块解决不了语义歧义:留预算给重排序。
数据核验说明
- 语料 10 篇共 74,727 字符,清洗规则与文件清单见 rag-lab 仓库
corpus/; - 测试集 100 题(A 档 30 / B 档 70),LLM 按篇生成、人工全检,元指涉与 schema 错误均为 0;跨篇撞题用 6 字滑窗初筛后人工复核为 0;
- 全实验 900 次检索为单次运行(无重复),CPU 每组 10~15 秒;嵌入模型 bge-small-zh-v1.5(512 维),向量库 chromadb 1.5.9,递归切分为 langchain-text-splitters 实现;
- ground truth 标文章级,top-k 命中目标文章即记 hit,是宽松口径——数字只用于 9 组横向对比,不代表端到端问答质量;
- 语料 10 篇全部为本人原创的中文技术文(AI 实测域,已发布 7 篇、未发稿 3 篇),Java 老文尚未混入,跨域干扰指标本期为 0、留待语料扩充后补测;结论不外推英文语料与其他文体;
- 截断对照只覆盖固定-1024 的头/尾两方向,未做 3×2 全因子;n=100 报分层观察,不做显著性检验。
一条命令复现:
gitclone https://github.com/ethanliang2016/rag-lab&&cdrag-lab pipinstall-rrequirements.txt python src/run_eval.py模型自动从 hf-mirror 下载,全部结果(含 100 题逐题命中记录、5 道顽固题明细、截断对照数据)在results/目录。
留个问题:你们的 RAG 里 chunk 大小是怎么定的,是拍的还是测的?评论区聊聊,下一篇我拿重排序来救那 5 道顽固题。
相关实测:
- 《RAG 重排序实测:双编码召回 + CrossEncoder 精排,500 条真实查询上的收益与代价》—— 分块撞墙之后往下游要,精排能救回多少
- 《RAG 生成层实测:把答案递到模型嘴边,7B 还是漏掉一半》—— 检索修到 0.506 之后,生成层还漏多少
完整导航:博客导航|agent 安全 / RAG 实测 / AI 代码治理,都在这