RAG回答错误:问题出在召回、重排还是生成?
RAG回答错误常因召回阶段未获取正确片段,而非生成问题。通过元数据过滤排除无关数据,混合检索扩大候选后经重排确定上下文,回答核验检查生成依据。权限隔离需服务端控制,而非Prompt约束。优化需逐层拆解问题,评估改进前后效果。
最近几天,一直在学习 RAG。

文档需要先清洗、切片,再通过 Embedding 模型向量化并写入向量数据库。用户提问后,系统检索相关片段,把片段和问题一起交给 LLM,才形成完整的 RAG。这些基本流程已经清楚,但在分析错误案例时,一个更具体的问题浮现出来:
以前容易直接归因于“LLM 回答不准确”。跟着一个可运行 Demo 逐步分析后,发现最终回答之前至少经过了几道不同的关口:
用户问题→ 硬条件过滤→ 关键词检索 / 向量检索→ 合并候选→ 重排→ 截取最终 Top-K→ LLM 生成回答→ 可选的回答核验→ UI 展示
只有把这些阶段拆开,才能知道一次改动究竟改善了什么。
先看一个回答错误的案例
知识库中有两个片段:
片段 A:标准商品付款后 30 天内可申请退款。片段 B:定制商品一旦进入生产,不支持退款。
用户问:
定制商品进入生产后可以退款吗?
如果检索系统返回片段 A,LLM 很可能回答:
可以,因为仍在付款后 30 天内。
这个回答虽然引用了真实规则,却把“标准商品”的规则错误地用到了“定制商品”上。
问题并不是 LLM 没有读懂片段 A,而是正确的片段 B 根本没有进入它的上下文。
因此,这个案例首先应该定位为:
Demo 中用一个固定生成器模拟这个过程:
function generateFromContext(contextFragmentIds) {if (contextFragmentIds.includes("片段B")) {return {text: "不可以,定制商品进入生产后不支持退款。[片段B]",answerGrounded: true};}return {text: "可以,因为付款后 30 天内可申请退款。[片段A]",answerGrounded: false};}
这里故意不改变生成逻辑,只改变传给它的检索片段:
const baselineRun = runPipeline({strategy: "仅关键词匹配",retrievedFragmentIds: ["片段A"]});const improvedRun = runPipeline({strategy: "商品类型过滤后再做关键词匹配",retrievedFragmentIds: ["片段B"]});
改进前,目标片段没有被召回;改进后,同一个生成器拿到片段 B,才生成有依据的答案。
这让人第一次很具体地看到:
“元数据过滤”其实就是先按硬条件筛数据
学习过程中,看到“元数据过滤”这个词时,一度觉得很陌生。换到具体场景后,马上发现这个知识其实早就理解了。
假设每个片段除了正文,还带有这些字段:
{tenantId: "company-a",docType: "policy",language: "zh-CN",indexVersion: "v2"}
当前用户属于 company-a,本次只查询 policy 类型文档,那么 Runtime 应该先排除其他公司和其他类型的片段,再进行关键词或向量相似度排序。
即使 company-b 的某个片段相似度是 0.99,也不应该进入候选池。
所以,元数据过滤用白话说就是:
它不只是为了提升相关性,还可能参与权限、版本和业务范围控制。
权限隔离不是让 LLM 自觉保密
如果登录用户属于 company-a,但他手动把前端请求中的 tenantId 改成 company-b,Runtime 不能相信这个字段。
安全的数据范围应该来自服务端已经核验的登录身份:
服务端确认当前用户属于 company-a→ Runtime 强制加入 company-a 的查询条件→ company-b 的数据不进入候选池
“权限隔离”是安全目标,服务端元数据过滤只是它的一种实现方式。
更强的隔离还可以使用:
- 每个租户独立索引;
- 每个租户独立数据库;
- 数据库行级权限;
- 服务端统一的授权策略。
不能采用的方式是:
先查询所有公司的数据→ 全部交给 LLM→ 在 Prompt 中要求它不要泄露
Prompt 是软约束,不能代替服务端权限边界。
混合检索先扩大候选,重排再决定最终 Top-K
单独使用关键词或向量检索,各有适合的场景。
例如用户问:
ERR_AUTH_403 是什么原因,应该怎样恢复权限?
关键词检索擅长命中准确的错误码:
片段 E:ERR_AUTH_403 表示当前令牌缺少 invoices:read 权限。
向量检索更容易命中自然语言含义接近的恢复说明:
片段 F:访问被拒绝时,请检查角色权限,并在授权后重新获取令牌。
同时,它也可能召回一个只有少量语义关联的干扰片段:
片段 G:发片模板支持调整页眉颜色和公司 Logo。
混合检索先把多路候选合并:
function mergeUniqueResults(...resultGroups) {return [...new Map(resultGroups.flat().map((fragment) => [fragment.id, fragment])).values()];}
候选更多,不等于全部都应该交给 LLM。下一步还需要重排:
function rerankCandidates(candidates, scores, finalK) {return [...candidates].sort((left, right) => scores[right.id] - scores[left.id]).slice(0, finalK);}
Demo 使用固定分数:
片段 E:0.99片段 F:0.96片段 G:0.18
按分数从高到低排序并取 Top-2,最后留下 E 和 F,淘汰 G。
这里的固定分数只是为了展示控制流,并不代表真实重排模型的计算方式。真实系统可能使用向量相似度、关键词分数、专用 Reranker,或者多种信号组合。
只看一个成功案例,不能证明改进可靠
修好“定制商品退款”案例后,很容易产生一种错觉:
但一次成功只能证明一个案例。
因此,又在 Demo 中加入了三个固定问题:
| 案例 | 期望片段 | 改进前 | 改进后 |
|---|---|---|---|
| 定制商品退款 | B | A,失败 | B,通过 |
| 标准商品退款 | A | A,通过 | A,通过 |
| 权限错误恢复 | E、F | 只有 E,失败 | E、F,通过 |
运行结果是:
改进前:1 / 3改进后:3 / 3
这个结果至少提供了两条证据:
- 新策略修复了两个已知失败案例;
- 原本正确的标准商品案例没有在这次评测中退化。
但不能据此声称“线上不会再出问题”。
三个案例太少,也不代表真实用户流量。更严谨的结论只能是:
这也是 Eval 很重要的一点:它不仅衡量结果,还限制能够下多大的结论。
生成前的召回,和生成后的回答核验不是一件事
今天还把两个阶段混在了一起。
原本把下面三种方式都理解为“召回方案”:
- 只用 Prompt 约束模型;
- Runtime 按句子或片段检查;
- 等完整回答生成后,统一质检再输出。
后来重新梳理才发现:
- 召回通常发生在 LLM 生成之前,决定哪些知识进入上下文;
- 回答核验发生在生成期间或生成之后,检查模型已经生成的结论是否有依据。
它们解决的是不同问题。
| 方案 | 优点 | 代价与边界 |
|---|---|---|
| Prompt 约束后直接流式输出 | 实现简单,首字快 | 不能保证每个结论都有依据 |
| 按结论块核验后再发送 | 可以拦截当前块的无依据内容 | 跨块语义复杂,增加延迟 |
| 完整缓存回答,全部核验后再输出 | 最容易做整体核验 | 真实首字延迟最高,无法即时展示原始模型流 |
“完整核验后再输出”也不能直接叫作最准确。
它只是给 Runtime 更多机会在内容暴露给 UI 前发现问题。最终是否更可靠,还取决于核验规则、引用结构以及核验器本身是否可信。
索引更新为什么需要版本
如果修改了文档清洗或切片规则,直接覆盖线上索引会带来风险:
- 构建过程中数据可能不完整;
- 新旧切片可能混在一起;
- 新规则没有经过评测;
- 出现问题时难以快速回退。
更可靠的流程是:
v1 继续在线服务→ 独立构建 v2→ 用固定问题验证 v2→ 将 activeVersion 从 v1 切换到 v2→ 观察稳定→ 清理 v1
这就是今天重新建立连接的另一个术语:增量更新。
知道版本构建和切换,却没有记住这个专业名称。问题不在知识缺失,而在术语没有和实践连起来。
最后
今天最重要的收获,不是又记住了几个 RAG 术语,而是学会把一次错误回答拆回完整链路:
硬条件是否正确→ 目标片段是否被召回→ 正确片段是否进入最终 Top-K→ LLM 是否依据片段回答→ 回答是否经过必要核验→ UI 最终展示了什么
只有知道问题发生在哪一层,改动才有意义。
过滤和权限控制负责排除不应该出现的数据;混合检索扩大候选;重排决定最终上下文;Eval 比较改进前后;回答核验则处理生成内容是否有依据。
现在更愿意把 RAG 优化理解为一组有取舍的工程决策,而不是寻找一个永远正确的“最高标准答案”。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
CAD零基础入门教程:坐标输入、图层管理与基础绘图命令
本文面向CAD零基础学习者,系统讲解坐标输入、图层管理与基础绘图命令的核心用法。通过分步实操与常见问题排查,帮助新手建立精确绘图习惯,掌握规范出图的基础能力。
CAD从入门到项目交付:绘图、标注、图块与实战工作流
掌握CAD的核心在于建立“画得准、标得清、复用快、交付稳”的工作流。本文提供从环境设置、高频命令组合、标注规范、图块标准化到项目分阶段交付的完整路径,帮助初学者避免常见返工陷阱,独立完成可检查、可复用、可打印的工程图纸。
Claude Code 登录指南:个人、Teams 与企业账号区分与授权步骤
本文详细解析 Claude Code 登录前的账号类型区分方法,涵盖个人订阅、Teams 席位与企业 Enterprise 席位的授权路径差异。提供终端登录命令、环境变量排查及常见异常处理步骤,帮助用户快速完成正确授权并避免登录路径混淆。
Claude Code 文件修改前的权限模式配置与命令审批指南
本文详细介绍Claude Code在修改文件前的权限模式配置方法,包括defaultMode可选值、permissions allow与deny规则设置、多层级配置文件管理以及 status验证技巧,帮助开发者安全高效地使用AI编程助手。
Claude Code接入VS Code后先测扩展和终端命令
在VS Code中接入Claude Code后,建议优先验证扩展面板与集成终端两条入口。本文提供标准检查顺序、关键命令与常见故障排查路径,帮助你快速确认环境就绪,避免后续开发受阻。
- 热门数据榜
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
1
2
3
4
5
6
7
8
9
10
相关攻略
2026-09-01 16:53
2026-09-01 16:52
2026-09-01 14:27
2026-09-01 14:12
2026-09-01 14:10
2026-09-01 14:07
2026-09-01 13:55
2026-09-01 13:47
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程

