语义分块三条路线踩坑实录
上周帮客户调一个知识库问答系统,准确率从 67% 拉到 89%。没动模型,没换 embedding,只改了一件事——把固定分块换成了语义动态分块。
说起来简单。
踩的坑是真不少。
先交代下背景。我之前一直用 LangChain 0.1.9 自带的 RecursiveCharacterTextSplitter,chunk_size 512,overlap 50,这套配置跑了小半年,没啥大问题。直到 3 月 15 号那天,客户在群里发飙:“搜‘去年 Q3 的退货率’,返回的全是前年的数据,你们这系统是智障吗?”
我查了下日志。好家伙,文档里“2023 年 Q3”和“退货率”被硬生生切在两个 chunk 里,embedding 检索的时候根本对不上。那个“2023 年 Q3”在一个 chunk 末尾,“退货率详细数据如下”在下一个 chunk 开头,中间隔了一道墙。
这就是固定分块的毛病——它只数字符,不认语义边界。跟用尺子量水一样。
后来我开始折腾语义分块,大概试了三条路线:
1. 基于句子嵌入的相似度分块
思路不复杂:把文档拆成句子,算相邻句子的 embedding 余弦相似度,相似度突然掉下来的地方,就是语义边界,一刀切下去。
我用 SentenceTransformers 的 all-MiniLM-L6-v2 试了 200 篇文档,确实比固定分块强。比如一段讲“产品 A 的定价策略”,下一段讲“竞品分析”,相似度从 0.85 直接掉到 0.41,边界清晰得像刀切豆腐。
但坑来了。
计算量爆炸。10 万字的文档库,光分块就跑了 43 分钟,CPU 全程 100%,风扇呼呼转。而且短句子 embedding 不稳定,“好的。”和“接下来我们看...”这种过渡句经常被误判成边界,切出一堆莫名其妙的碎片。我记得有个 chunk 就三个字:“具体来”,后面全断了。
等等,这里我要更正一下——准确说不是“短句子 embedding 不稳定”,是 sentence-transformers 那个模型对 10 个 token 以下的句子,向量表示方差很大。我后来翻他们 GitHub issues 才确认的,2024 年 6 月有人提过类似问题,至今没修。
后来我加了个最小块长限制,设了 200 字符,短句子强制往前合并,准确率才稳住。这个 trick 不优雅,但管用。
2. 基于 NLP 篇章结构的分块
这条路更“聪明”点——用 NLP 模型识别文档的篇章结构,标题层级、段落、列表、表格这些,按结构切分。
我用了 Unstructured 0.12.5,它能把 PDF 里的标题、正文、表格分开。效果确实不错,特别是处理技术文档和合同,标题天然就是最好的 chunk 摘要,省了我好多事。
但碰上一个让人抓狂的 bug:表格被切碎。有个客户的财务报告,表格跨页了,Unstructured 把表头和表身分到两个 chunk。用户搜“2024 年 Q2 毛利率”,只返回了表头那行字,数据全丢了。
我盯着那个 chunk 看了十分钟,骂了句脏话。
后来写了个后处理逻辑,检测到 3. 基于 LLM 的语义感知分块 最近 X 上挺多人吹这个方案,直接让 LLM 读文档,输出“这里应该切”的标记。听起来很美好——毕竟谁能比 LLM 更懂语义呢? 我拿 GPT-4-turbo(gpt-4-0125-preview)试了 50 篇文档。分块质量确实高,边界精准得像人工标注的,有些地方我觉得它比我自己切得都好。 但两个致命问题: 而且 LLM 偶尔会“自作聪明”。有一次它把“风险提示”和“免责声明”合并成一个 chunk,理由是“语义相关”。用户搜“投资风险”的时候返回了一大堆法律术语,点开一看,用户直接关了页面。这用户体验,绝了。 我现在的方案(折腾完觉得最靠谱的) 混合策略,大概长这样: 1. 先用 Unstructured 做粗切(按标题、段落结构走) 2. 对每个块跑 embedding 相似度,检测内部需不需要再切 3. 设硬约束:最小 200 字符,最大 800 字符 4. 表格、代码块强制保持完整,不参与切割 这套组合拳下来,分块速度比纯 embedding 方案快 3 倍左右,准确率比固定分块高了 22 个百分点。最重要的是——稳,不会出现莫名其妙的边界,不用半夜被报警电话叫起来。 数据说话:同一个测试集 200 个查询,固定分块 recall@5 是 0.67,纯 embedding 分块是 0.78,我的混合方案是 0.89。chunk 数量减少了 30%,检索延迟从 2.3 秒降到 1.1 秒。大概是因为 chunk 少了,向量库查得更快。 几个血泪教训,都是熬夜换来的 总结一下 语义动态分块确实能大幅提升 RAG 检索质量,但别一上来就上 LLM,成本扛不住,速度也跟不上。先用 NLP 结构分块 + embedding 相似度的组合,性价比最高。 最重要的是——在你的数据上做 AB test。别信任何人的“最佳实践”,包括我这篇。你的文档格式、用户的查询习惯、embedding 模型的选择,任何一个变量变了,结论可能完全不一样。 兄弟们你们用啥分块方案?有没有遇到过特别奇葩的文档格式?我见过最离谱的是一份 PDF,正文是竖排繁体中文,里面还嵌了横排的英文表格。Unstructured 直接拒绝工作。 评论区聊聊,我请喝咖啡(虚拟的,打工人请不起真的)。 #RAG #语义分块 #知识库 #NLP #LLM应用 #又是为embedding掉头发的一天 本文由 MakeSense 编辑团队撰写并审核。文中引用的数据和观点均经过交叉验证,如有疏漏欢迎在评论区指正。最后更新:2026年06月27日 18:49 标签或者连续的
| 分隔符,就强制合并相邻 chunk,不管 similarity 多低。代码写得很难看,一堆 if-else,但至少表格完整了。嗯...这个其实还可以优化,比如用表格结构做结构化存储而不是纯文本 chunk,但当时赶 deadline,就先这样了。
读者评论 5