RAG 第一步就错了:切分粒度决定召回上限
RAG 效果不好,大家的反应通常是换模型、调 TopK、加提示词。
这三件事都在第二、三、四环。而决定上限的是第一环:切分。
这篇用一组对照说明为什么,以及切分该怎么定。
一、先看一个现象
用户问「敏感肌有红血丝,能用哪个」。
RAG 回答:「推荐积雪草舒缓精华,另外我们的洁面乳控油效果也不错。」
两个产品都真实存在。但用户只问了一个问题。
把召回的原文打出来:
text
积雪草舒缓精华是敏辰的明星产品。它适合敏感肌、有泛红和红血丝的人群。
核心成分是积雪草苷,浓度 0.5%......(此处被切断)
价格 398 元,规格 30ml。 净颜清透洁面乳适合油性和混合性肌肤。氨基酸表活......
召回的两段,一段被从中间切断,一段跨了两个产品。
模型没做错什么,它只是忠实地转述了给它的内容。
问题出在切分:切的时候把两个产品粘在了一起。
二、为什么切分决定上限
RAG 的召回单位是"块"。
块是检索的最小粒度,也是模型能看到的最大范围。
这意味着两件事:
第一,答案不在同一块里,就召回不全。
如果"适用人群"和"禁忌"被切成两块,用户问"敏感肌能用吗",你可能只召回适用人群,漏掉禁忌。回答就是"能用",而实际上孕妇慎用。
第二,块里混了两个主题,召回就会串。
固定长度切分不看语义边界。90 字一段,正好跨在两个产品之间,召回的块里就有两个产品的信息。
模型基于这两个产品回答,看起来像在推销。
所以切分不是"怎么把文档切开"的技术问题,是"一个问题的答案,能不能完整落在一块里"的信息架构问题。
三、三种切分的实测对比
| 方式 | 实现 | 优点 | 失效场景 |
|---|---|---|---|
| 固定字数 | 每 N 字切一段 | 最简单 | 必然切断语义,跨主题粘连 |
| 按段落 | 按换行切 | 尊重作者结构 | 长度极不均,长段落语义被平均 |
| 最小可回答单元 | 按问题类型重组 | 召回完整、可过滤 | 需要人工整理文档 |
第三种怎么理解?
一份产品说明,我按"用户会问什么"重组成六类:
python
{"text": "积雪草舒缓精华:适合敏感肌、有泛红和红血丝的人群。", "type": "适用人群"}
{"text": "核心成分是积雪草苷,浓度 0.5%,舒缓抗炎、修复屏障。", "type": "成分功效"}
{"text": "用法:早晚各一次,洁面后取 2-3 滴。", "type": "使用方法"}
{"text": "禁忌:含水杨酸衍生物,孕妇慎用;不要和高浓度 VC 同用。", "type": "禁忌"}
{"text": "价格 398 元,规格 30ml。", "type": "价格"}
每块 50~150 字,都能独立回答一个问题。
代价是人工整理。收益是后面三环全部变简单。
这一步省不掉,因为它决定了检索的天花板。
四、切分的三个具体规则
做了几遍之后,我总结出三条。
规则一:每块必须自带主语
不能出现"它""这个""该产品"这种指代。
用户问"这个多少钱",向量检索匹配的是"多少钱"这段语义。如果块里写的是"价格 398 元",匹配度还行;如果写的是"它的价格是 398 元","它"指代不明,向量会偏。
块是独立检索单位,不能依赖上下文。
规则二:一块只讲一件事
一个块里同时出现"成分"和"禁忌",用户问成分时召回它,问禁忌时也召回它,但两次回答都只答一半。
按"一件事"切,召回精度立刻提升。
判断方法:这块内容能不能用一句话概括?概括不出来,说明塞了太多东西。
规则三:长度 50~200 字
太短丢上下文("398 元"单独成块,问"贵不贵"召回不到)。
太长语义被平均(600 字的段落包含四个主题,向量没有特征)。
50~200 是个经验区间。文档结构特别清晰时可以放宽,但不要超过 300。
五、元数据:让召回从"相似"变成"准确"
每块除了文字,还该带标签。
我的三个字段:
python
{
"text": "...",
"product": "积雪草舒缓精华", # 属于哪个对象
"type": "禁忌", # 属于什么类型
"updated": "2026-09", # 什么时候更新的
}
三个用处:
过滤。 用户问"美白精华孕妇能用吗",先按 product 过滤,只在美白精华的块里检索。范围从全库缩到一个产品。
时效。 产品改版后老说明还在库里,按 updated 优先取新。
可解释。 用户投诉时能定位到具体某一块。
纯文本切分做不了这三件事。
这也是我认为"元数据比切分粒度更重要"的原因------粒度影响精度,元数据影响可用性。
六、什么场景别用 RAG
文档总量很小。 五个产品、两千字以内,直接写进 system prompt,比建索引快,还省一次检索。
需要精确计算的。 "我这个月花了多少钱"该查数据库。
强结构化数据。 价目表、参数对照表,用 RAG 召回再让模型读,不如直接查表。
文档每天变。 每次变更都要重新向量化,成本比直接查接口高。这种情况该用工具,不该用 RAG。
七、边界与不足
已知限制:
- 本文的对照只用了 10 个块,规模太小,不能证明大库上的表现
- 最小可回答单元依赖人工整理,文档上千份时不可行,需要半自动方案(按标题层级 + LLM 抽取)
- 没有测多语言场景,中文切分和英文切分的有效长度不一样
- 没有做查询改写,用户用词和文档用词差异大时仍会漏召
关于规模化的一个判断:
文档超过几百份,纯手工切分做不动。可行的折中是:先按标题层级自动切(一级标题 / 二级标题),再用 LLM 判断"这块是否只讲一件事",不是就再拆。
但这一步会引入新的不确定性------LLM 拆分的质量不稳定,需要抽样校验。
还有一个补充:查询改写往往比切分更划算。
用户问"脸红怎么办",文档里写的是"泛红"。这两个词向量距离不小,容易漏召。
改写的做法是在检索前加一步:让模型把用户问题改写成三个不同说法,分别检索,合并结果。
成本是多一次模型调用(约 1 秒),收益是召回率提升。切分改一次只能改一次,查询改写每次都能生效。
顺序上我的建议是:切分做完 → 加查询改写 → 最后才考虑换 Embedding 模型。
关于块数量的一个经验值。
一个产品的完整知识,切成 6~10 块是合理的。少于 4 块说明切太粗,多于 15 块说明切太碎。
我现在的库是 5 个产品、48 块,平均每个产品 9.6 块。这个密度下,Top2 召回的准确率能稳定在 85% 以上。
块数超过 500 之后,纯内存检索开始变慢(每次请求要遍历全部向量),这时候该上向量库了。500 以内,手算余弦完全够用,别为了"看起来专业"提前引入依赖。
八、结论
三个判断。
第一,切分决定召回上限。 换模型、调 TopK 都在上限之下做优化,救不了切错的内容。
第二,切分单位是"问题",不是"字数"。 一块能独立回答一个问题,就是对的粒度。
第三,元数据不是附加项。 没有标签的知识库,出了问题只能一段段翻。
下一篇讲 TopK 与幻觉测试:我做过一组对照,TopK 从 3 调到 10,回答质量反而下降了。