RAG 第一步就错了:切分粒度决定召回上限

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,回答质量反而下降了。

相关推荐
LEE1 小时前
别把 Agent 架构画成组织架构图——子代理该不该拆,用任务级 Eval 说话
前端·后端·ai编程
合尘猫1 小时前
词元涨价近一倍后,我把 AI 客服的成本拆成了四个可优化的变量
人工智能·后端·架构
AngusTester1 小时前
自建 Git 之后怎么推代码:配 SSH 或 HTTPS,完成第一次 push
后端
用户574385305111 小时前
# 第 12 步:操作日志 + RBAC 缓存失效 + 业务模块
后端
用户574385305111 小时前
# 织链塔:12 步从零造一个 Koa 后端框架 —— 第 4 步:JSON 条件编译引擎(buildWhere)
后端
coderCN1 小时前
NestJS + Prisma + PostgreSQL
后端
苍何2 小时前
开源微信流 APP,手机微信聊天记录,可以直接给 WorkBuddy 和 Obsidian 了
后端
智驭未来掌门人2 小时前
Qt 项目不建议导入 CLion 说明
后端
Lumos_2 小时前
P1 签名 (PKCS#1) vs 裸签 (Raw Signature)
后端