从「检索不到」到「答得对」:RAG 查询改写四层法
用户的原话往往不是好的检索语句。本文拆解 RAG 查询改写的四个层级——同义扩展、多查询并行、意图消解、指代补全,给出适用条件、实现要点和踩坑记录。
RAG 上线后最常见的抱怨是”它找不到我们的文档”。但同一个问题换成人话问一遍,答案就出来了。
问题通常不在向量库,也不在 embedding 模型,而在用户的提问本身不是好的检索语句。人说话带省略、代词、口语,文档写的是规范表述。这道鸿沟就是查询改写要解决的事。
第一层:同义扩展(覆盖词表错配)
用户问”报销要多久”,文档写的是”费用结算时限”。做法是用一次小模型调用把问题扩展成 3–5 个近义表述,并行检索后合并:
原问题:报销要多久
扩展:费用报销 处理时长 / 费用结算 时限 / 差旅报销 到账时间
坑:扩展太多会稀释相关性。控制在 5 条内,并对原问题结果加权(原问题 1.5,扩展 1.0)。
第二层:多查询并行 + RRF(一问多面)
“我们公司能用 AI 做客服吗”,其实包含三件事:场景可行性、合规限制、系统对接。让模型拆成 2–4 个子查询分别检索,再用 RRF 融合:
score(doc) = Σ 1 / (k + rank_i(doc)) k = 60
RRF 的好处是不用处理不同检索结果的分数量纲。坑:拆得太细会丢上下文——如果某个子查询单看没意义,说明拆错了。
第三层:意图消解 + 术语归一(最难,收益最大)
用户和文档用的是不同词表,这层解决”语义对不上”:
| 用户说的 | 文档里写的 |
|---|---|
| 开票 | 发票开具 / 销项税额确认 |
| 押金退不退 | 保证金退还政策 |
| 系统卡 | 响应超时 / 性能瓶颈 |
做法是维护领域映射表,来源有两条:一是人工梳理,二是从线上日志里捞”检索命中但答案被标不满意”的 query,对照正确文档记录词表差异。
这张表看起来土,但它是准确率提升最直接的一件事。很多团队花钱换 embedding 模型,不如认真做两周术语表。
第四层:指代补全(多轮场景必做)
用户:华为的 AI 芯片有哪些
AI:(回答)
用户:那它的功耗呢 ← "它"是谁?检索"功耗"能查到什么?
把最近 2–3 轮对话和当前问题一起喂给模型,输出一个自包含查询:“华为昇腾系列 AI 芯片的功耗参数”。要点:只在有指代/省略时改写;改写后检查是否引入幻觉实体;原始问题一起检索做双保险。最多回看 3 轮,否则用户换话题时会把无关内容带进来。
什么时候别做改写
- 精确查询:输入订单号、错误码、型号时,改写反而破坏它。识别方法:query 里有连续数字或大写串,直接走精确匹配。
- 问题本身规范:内部工具按模板提问,改写是纯浪费。
- 延迟敏感:改写至少多一次模型调用,目标 300ms 内只能用规则映射。
落地顺序与衡量
别一次四层全做,建议:指代补全 → 术语表 → 多查询 → 同义扩展。前两层性价比最高,第四层很多情况被第三层覆盖。
建 100–200 条问题集(覆盖高频与长尾),对比三个指标:Top-5 召回率、首答采纳率、平均延迟。经验值:第四层做完,多轮场景召回提升 15–25 个点;第三层做完,垂类术语场景提升 20 个点以上。
小结
RAG 的短板很少在模型,多半在”用户说的话”和”文档写的字”之间那道缝。先把日志翻出来看真实 query 长什么样,问题会比想象中具体得多。