如果说普通 Agent 是“会聊天的同事”,那么 RAG Agent 就是“会查资料的同事”。
理想状态是:
先找到正确资料,再根据资料回答,最后把证据摆出来。
现实中却经常变成:
检索:我找到了一堆。
模型:很好,但我决定自由发挥。
用户:你依据呢?
模型:依据我的常识。
所以,RAG 测试不能只看“回答像不像正确答案”,必须把问题拆成两部分:
- 检索是否找对了?
- 生成是否用对了?
一、先理解 RAG 的基本链路
一个典型的 RAG 流程如下:
用户问题 ↓ 问题改写 / 意图识别 ↓ 召回相关文档 ↓ 过滤无权限或过期内容 ↓ Rerank 重排序 ↓ 拼接上下文 ↓ LLM 生成回答 ↓ 引用来源与答案校验可以把它类比成考试:
- 检索:去图书馆找资料;
- Rerank:把最相关的资料放到最前面;
- 生成:根据资料组织答案;
- 引用:在答案后面标注“这句话来自哪一页”。
因此:
找错资料,答案大概率错;
找对资料但不会用,答案仍然可能错;
没找到资料却硬答,通常就是幻觉。
二、RAG 测试的核心分层
建议将测试拆为五层:
| 测试层级 | 核心问题 |
|---|---|
| 数据层 | 文档是否完整、准确、最新 |
| 切分层 | Chunk 是否合理 |
| 检索层 | 能否召回正确文档 |
| 生成层 | 回答是否忠于文档 |
| 安全层 | 是否越权访问知识 |
很多团队只测最后一层:
用户提问 → 看最终答案这会导致一个问题:
答案错了,但你不知道究竟是“没找着”,还是“找着了但模型胡说”。
正确做法是记录完整链路:
{"query":"退款多久到账?","retrieved_chunks":[],"reranked_chunks":[],"prompt_context":[],"answer":"通常 1-3 个工作日到账","citations":[]}三、数据集测试:垃圾进,智能出不了
RAG 的第一道质量关不是模型,而是知识库。
如果知识库里存在:
- 过期政策;
- 重复文档;
- 互相冲突的版本;
- 标题与内容不一致;
- PDF 解析错乱;
- 表格内容丢失;
- 图片文字未识别;
- 权限标签缺失;
那么后面的检索和生成都会被拖下水。
1. 文档完整性测试
检查文档是否被完整导入:
- 标题是否存在;
- 正文是否完整;
- 表格是否丢列;
- 列表编号是否错乱;
- 页眉页脚是否污染正文;
- 代码块是否被打散;
- 图片中的关键文字是否丢失;
- 文档页数与切分结果是否匹配。
示例
原文:
退款金额将在审核通过后的 3 个工作日内原路退回。解析后却变成:
退款金额将在审核通过后的 3 个 工作日内原路退回。单看这段似乎没问题,但如果数字、单位或否定词被解析丢失,后果就严重了:
原文:不支持 7 天无理由退货 解析后:支持 7 天无理由退货这已经不是检索问题,而是知识库投毒。
2. 文档版本测试
建立带版本信息的测试数据:
《退款政策》v1:退款到账时间为 7 个工作日 《退款政策》v2:退款到账时间为 3 个工作日需要验证:
- 当前生效版本能被召回;
- 旧版本不会排在新版本之前;
- 文档更新时间可被识别;
- 新文档发布后索引是否及时更新;
- 删除或下线的文档不会继续命中。
关键断言
assert"3个工作日"inanswerassert"7个工作日"notinanswerassertretrieved_docs[0].version=="v2"四、Chunk 测试:切得太碎,像知识碎尸;切得太大,像整本字典
文档切分是 RAG 中非常容易被低估的环节。
1. Chunk 太小
例如把下面内容切成几段:
退款条件: 1. 商品未使用; 2. 保留完整包装; 3. 申请时间不超过 7 天。如果每个 Chunk 只保留一条规则,检索可能只召回:
申请时间不超过 7 天。模型可能不知道这三个条件必须同时满足。
2. Chunk 太大
如果一个 Chunk 包含整篇几十页的产品手册:
- 相关内容容易被噪声淹没;
- Token 消耗增加;
- 模型注意力分散;
- 引用定位变得困难。
3. Chunk 测试维度
| 测试项 | 关注点 |
|---|---|
| Chunk 大小 | 是否包含完整语义 |
| Overlap | 上下文衔接是否足够 |
| 切分边界 | 是否切断句子、表格、条款 |
| 元数据 | 标题、章节、页码是否保留 |
| 父子文档 | 子块命中后能否回溯完整上下文 |
| 特殊结构 | 表格、代码、FAQ 是否正确切分 |
推荐测试语料
故意准备以下内容:
- 关键条件分布在相邻段落;
- 否定词位于 Chunk 边界;
- 标题与正文被分开;
- 表格跨页;
- 一个概念在多个章节中出现;
- 同一个词在不同业务语境中含义不同。
重点断言
assertchunk_contains_complete_rule(chunk)assertchunk.metadata["title"]isnotNoneassertchunk.metadata["page"]isnotNoneassert"不"notinlost_boundary_text一句话:
Chunk 测试的目标不是“平均切得漂亮”,而是“关键知识不能被切没”。
五、检索方式测试:关键词、向量,还是混合检索?
1. 关键词检索
适合:
- 产品编号;
- 订单号;
- API 名称;
- 错误码;
- 专有名词;
- 精确条款。
例如:
ERR_CONNECTION_RESET这种问题使用关键词检索通常很有效。
2. 向量检索
适合:
- 同义表达;
- 自然语言问题;
- 口语化描述;
- 用户不知道标准术语的场景。
例如:
钱什么时候能退回来?可能对应文档中的:
退款到账时效3. 混合检索
混合检索同时结合:
- 关键词匹配;
- 向量语义相似度;
- 元数据过滤;
- 时间、权限和业务条件。
测试用例
| 用户问题 | 期望检索方式 |
|---|---|
| 错误码 ERR-1001 是什么 | 关键词优先 |
| 钱什么时候退回来 | 向量语义优先 |
| 查询华东区 2025 年退款政策 | 关键词 + 元数据过滤 |
| “那个接口为什么超时” | 语义检索 + 上下文理解 |
| 产品型号 X100 的保修条款 | 精确匹配 + 语义补充 |
测试时不要只验证“有没有召回”,还要验证:
- 正确文档是否进入 Top-K;
- 无关文档是否过多;
- 结果顺序是否合理;
- 同义问题是否稳定召回;
- 拼写错误是否有一定容错;
- 专有名词是否不会被错误改写。
六、Recall@K、Precision@K:检索到底找得全不全、准不准
1. Recall@K
Recall@K 衡量:
所有应该被召回的相关文档中,Top-K 找到了多少。
公式:
Recall@K = Top-K 中召回的相关文档数 / 全部相关文档数例如,某问题有 4 个相关 Chunk:
Top-5 召回了其中 3 个那么:
Recall@5 = 3 / 4 = 75%Recall 低,说明:
- 关键文档没被找到;
- Chunk 切分不合理;
- Query 改写失败;
- Embedding 不适配;
- 相似度阈值过高;
- Top-K 设置过小。
2. Precision@K
Precision@K 衡量:
Top-K 结果中,有多少是真正相关的。
公式:
Precision@K = Top-K 中相关文档数 / K例如:
Top-5 中有 3 个相关 Chunk那么:
Precision@5 = 3 / 5 = 60%Precision 低,说明:
- 召回噪声过多;
- Top-K 过大;
- 相似度阈值过低;
- 文档内容重复;
- Query 过于模糊;
- Rerank 效果不好。
面试中如何区分
Recall 关注“该找的有没有找全”;
Precision 关注“找出来的是否大部分有用”。
七、MRR 与 Hit Rate:正确答案排第几、到底有没有命中
1. MRR
MRR 关注第一个正确结果排在第几位。
公式:
MRR = 1 / 第一个相关结果的排名例如:
| 第一个相关结果位置 | Reciprocal Rank |
|---|---|
| 第 1 位 | 1 |
| 第 2 位 | 0.5 |
| 第 3 位 | 0.333 |
| 第 5 位 | 0.2 |
如果正确文档总是在第 8 位,即使 Top-10 最终召回了它,模型也可能先看到大量噪声。
2. Hit Rate
Hit Rate 关注:
Top-K 中是否至少出现一个相关结果。
公式:
Hit Rate@K = 命中至少一个相关文档的问题数 / 总问题数例如 100 个问题中,有 92 个问题的 Top-5 至少命中一条相关文档:
Hit Rate@5 = 92%指标不能单独看
可能出现:
Hit Rate 很高,但 MRR 很低说明正确文档虽然找到了,但排名靠后。
也可能出现:
Recall 很高,但 Precision 很低说明“什么都找来了一点”,但噪声多得像搜索引擎失控。
八、Top-K 与相似度阈值测试
1. Top-K 太小
可能导致:
- 关键证据未被召回;
- 多条件问题缺少完整上下文;
- 多文档问题只拿到一半答案。
2. Top-K 太大
可能导致:
- 无关内容进入 Prompt;
- Token 消耗上涨;
- 模型被冲突信息干扰;
- 错误引用概率增加。
3. 相似度阈值太高
结果可能为空:
用户问法稍微变化,系统就认为没有相关内容。4. 相似度阈值太低
结果可能变成:
标题沾边的文档全部被召回。参数对比测试
K = 3、5、10 Threshold = 0.60、0.70、0.80分别记录:
- Recall@K;
- Precision@K;
- MRR;
- Hit Rate;
- 空检索率;
- 平均 Token;
- 最终答案准确率;
- 幻觉率。
不要迷信“Top-K 越大越好”。
给模型 100 条资料,不代表它就比拿到 5 条资料更聪明;有时候只是让它拥有了 95 个分心的理由。
九、Rerank 测试:把“相关”排到前面
初始召回通常追求覆盖率,结果未必精准。Rerank 会进一步根据 Query 与文档的关系重新排序。
例如初始召回:
1. 退款申请流程 2. 退款到账时效 3. 订单取消规则 4. 售后联系方式问题是:
退款多久到账?经过 Rerank 后,理想顺序应为:
1. 退款到账时效 2. 退款申请流程 3. 订单取消规则 4. 售后联系方式Rerank 测试重点
- 是否把真正回答问题的 Chunk 排到前面;
- 是否优先考虑完整上下文;
- 是否识别时间、地区、产品等限定条件;
- 是否过滤标题相似但内容无关的文档;
- 多文档冲突时能否优先最新有效版本;
- Rerank 是否过度偏向关键词重合。
对比指标
重点观察:
Rerank 前后的 MRR Rerank 前后的 Precision@K Rerank 前后的最终回答准确率如果 Rerank 后 MRR 下降,说明它不是在“重新排序”,而是在“重新添乱”。
十、生成质量测试:有资料,不等于会答题
检索正确后,还要验证模型是否忠实使用证据。
1. 回答是否有文档依据
问题:
公司支持哪些退款方式?文档只写:
支持原路退款。模型却回答:
支持原路退款、银行卡退款和现金退款。其中后两项没有依据,就属于无证据扩展。
2. 引用是否支持结论
引用存在,不代表引用有效。
文档内容:
普通用户退款到账时间为 3 个工作日。模型回答:
所有用户退款到账时间均为 3 个工作日。虽然引用了原文,但结论扩大了适用范围。
这属于:
引用存在,但引用不支持结论。
3. 是否断章取义
文档:
会员订单支持 7 天无理由退货,但定制商品除外。模型:
会员订单支持 7 天无理由退货。如果用户咨询的是定制商品,这个回答就忽略了关键例外。
4. 检索不到时是否拒答
当知识库没有相关内容时,正确回答应类似:
我没有在当前知识库中找到足够依据,无法确认该规则。错误回答:
根据平台惯例,应该是 7 天。RAG Agent 最重要的能力之一,就是:
没查到时,敢说“我不知道”。
十一、必须区分的四类问题
这是 RAG 面试中非常容易考察的分析能力。
1. 检索正确,生成错误
检索结果:明确写着退款需要 3 个工作日 最终回答:退款需要 7 个工作日结论:
- 检索模块正常;
- 生成模块没有忠实遵循上下文;
- 需要检查 Prompt、模型理解、冲突信息和引用逻辑。
2. 检索错误,生成看似正确
问题:查询产品 A 的保修期 检索结果:召回了产品 B 的保修政策 最终回答:答案碰巧是 2 年这类问题很危险。
因为最终答案可能暂时正确,但系统其实没有证据保障。产品版本、地区或政策一变化,就会暴露问题。
3. 检索为空,模型凭空回答
检索结果:无 最终回答:根据相关规定,通常是 30 天结论:
- 空检索处理失败;
- 模型产生幻觉;
- 应增加“无证据不得作答”的约束。
4. 引用存在,但不支持结论
文档:部分商品支持退货 回答:所有商品都支持退货需要重点测试:
- 全称与部分;
- 必须与可以;
- 不得与可以;
- 仅限某地区;
- 仅限某版本;
- 时间和数量限制;
- 例外条件。
十二、多文档冲突测试:资料打架时,Agent 不能装没看见
知识库中经常存在冲突:
文档 A:退款到账时间为 7 个工作日 文档 B:退款到账时间调整为 3 个工作日正确行为不是随机选一句,而是:
- 识别存在冲突;
- 判断版本和生效时间;
- 优先使用当前生效文档;
- 必要时向用户说明差异;
- 无法判断时明确说无法确认。
测试数据至少覆盖
- 新旧版本冲突;
- 总部与地区政策冲突;
- 普通用户与会员政策冲突;
- 产品 A 与产品 B 规则冲突;
- 正文与 FAQ 冲突;
- 已下线文档与现行文档冲突。
自动化断言
assertanswer.uses_latest_effective_policy()assertnotanswer.mix_conflicting_versions()assertanswer.explains_conflict_when_ambiguous()十三、过期文档测试:知识库也会“拿旧闻当新闻”
为文档增加时间信息:
{"title":"配送政策","effective_from":"2024-01-01","effective_to":"2024-12-31","status":"expired"}测试问题:
当前配送时效是多少?需要验证:
- 已过期文档是否被过滤;
- 当前文档是否优先;
- 没有当前文档时是否提示无法确认;
- 文档发布时间和生效时间是否混淆;
- 定时更新后索引是否刷新。
特别注意:
发布时间 ≠ 生效时间一篇今天上传的公告,可能写的是下个月才生效的政策。
十四、权限测试:RAG 不能成为“知识库版越权查询”
RAG 检索必须结合权限过滤。
权限条件可能包括:
- 用户;
- 角色;
- 部门;
- 租户;
- 地区;
- 产品线;
- 文档密级;
- 生效范围。
高危测试场景
| 场景 | 预期 |
|---|---|
| 普通用户查询内部文档 | 不应召回 |
| T1 用户查询 T2 文档 | 不应召回 |
| 无权限文档与有权限文档内容相同 | 只能引用有权限版本 |
| 修改请求中的 user_id | 服务端不能信任 |
| 修改 tenant_id | 不能绕过权限过滤 |
| 直接猜测文档 ID | 拒绝访问 |
| 无权限文档已进入缓存 | 其他用户不能命中 |
不仅要检查最终回答
即使模型最终没有引用敏感文档,也要检查:
- 敏感 Chunk 是否进入检索结果;
- 是否进入 Prompt;
- 是否进入日志;
- 是否写入缓存;
- 是否被摘要或长期记忆保存。
没有显示在答案里,不等于没有泄露。
十五、Query 改写测试:问题被改坏了,检索再努力也没用
很多 RAG 系统会先改写用户问题。
用户:那会员呢?如果前文是:
普通用户退款到账时间是 3 个工作日。合理改写应接近:
会员用户退款到账时间是多少?错误改写可能是:
会员权益有哪些?测试重点
- 是否保留前文实体;
- 是否保留时间、地区、产品条件;
- 是否正确解析代词;
- 是否错误补充用户没有说过的信息;
- 改写后是否改变原问题意图;
- 多轮上下文过长时是否仍能正确改写。
对比测试
同时记录:
原始问题 改写问题 原始问题的检索结果 改写问题的检索结果如果原始问题能召回,改写后反而找错了,就要重点排查 Query Rewrite。
十六、RAG 测试数据集怎么设计
建议建立多类型评测集。
1. 标准问题集
问:退款多久到账? 标准答案:审核通过后 3 个工作日内 标准文档:refund_policy_v22. 同义问题集
退款多久到账? 钱什么时候能退? 退的钱几天能回来? 原路退款要等多久?3. 干扰问题集
退款多久到账? 文档中有大量“退款申请流程”内容,但没有到账时间。验证是否会把“申请流程”误当成“到账时效”。
4. 无答案问题集
公司是否支持火星配送?预期:
知识库未找到相关依据5. 冲突问题集
准备多个版本或不同地区的冲突规则,验证 Agent 是否识别差异。
6. 权限问题集
为不同用户准备不同可见范围的文档,验证召回结果与回答均不越权。
7. 长文本问题集
把关键答案放在:
- 文档开头;
- 文档中间;
- 文档结尾;
- 表格中;
- 附录中;
- 跨两个 Chunk 的位置。
十七、RAG 自动化测试断言
1. 检索层断言
retrieved=retrieve(query,user_id,tenant_id)assertexpected_doc_idinretrieved.doc_idsassertretrieved.top_k<=5assertall(chunk.has_required_metadata()forchunkinretrieved.chunks)assertall(chunk.is_authorized(user_id,tenant_id)forchunkinretrieved.chunks)2. 生成层断言
answer=generate(query,retrieved)assertanswer.is_grounded_in(retrieved.chunks)assertanswer.citations_support_claims()assertanswer.does_not_introduce_unsupported_facts()3. 空检索断言
retrieved=retrieve("知识库没有的问题")assertlen(retrieved.chunks)==0answer=generate(query,retrieved)assertanswer.explicitly_states_insufficient_evidence()assertnotanswer.fabricates_answer()4. 版本断言
assertretrieved.top_doc.version==latest_effective_versionassertnotanswer.uses_expired_document()5. 隔离断言
assertnotretrieved.contains_unauthorized_chunks()assertnotanswer.contains_cross_tenant_information()十八、一个完整的 RAG 测试用例
测试目标
验证 Agent 能否根据最新退款政策回答问题。
测试数据
v1:退款到账时间为 7 个工作日 v2:自 2025 年 1 月 1 日起,退款到账时间调整为 3 个工作日测试步骤
1. 用户提问:退款多久到账? 2. 查看 Top-K 检索结果 3. 查看 Rerank 顺序 4. 查看传入模型的上下文 5. 查看最终答案 6. 查看引用来源预期结果
检索:召回 v2 排序:v2 排在 v1 前 生成:回答 3 个工作日 引用:引用 v2 安全:无无权限文档失败定位
| 现象 | 可能原因 |
|---|---|
| v2 没召回 | 索引、切分、Embedding 或过滤问题 |
| v2 在第 5 位 | Rerank 或相似度问题 |
| 上下文有 v2,答案说 7 天 | 生成忠实性问题 |
| 答案正确但引用 v1 | 引用绑定问题 |
| 无结果仍回答 3 天 | 幻觉控制问题 |
| 普通用户召回内部政策 | 权限过滤问题 |
十九、面试高频题
1. 如何判断 RAG 是检索错了,还是生成错了?
建议回答:
我会分别记录原始 Query、改写 Query、Top-K 召回结果、Rerank 结果、最终 Prompt、模型回答和引用信息。如果正确证据没有进入上下文,属于检索链路问题;如果正确证据已经进入上下文,但模型仍然给出错误结论,则属于生成或提示约束问题。如果检索为空但模型仍然回答,则属于无证据生成或幻觉控制问题。
2. Recall 和 Precision 有什么区别?
Recall 衡量相关文档找得全不全,Precision 衡量召回结果准不准。Recall 低说明可能漏掉关键证据,Precision 低说明噪声较多。实际需要根据业务在两者之间平衡,不能只追求其中一个指标。
3. Top-K 越大越好吗?
不是。Top-K 太小可能漏掉关键文档,太大则会引入噪声、增加 Token 消耗,并可能让模型受到冲突信息干扰。我会通过 Recall@K、Precision@K、MRR、最终答案准确率、Token 消耗和幻觉率进行综合评估。
4. 如何测试引用是否可信?
不能只判断答案是否带引用,还要验证引用内容是否真的支持对应结论。重点检查否定词、时间范围、适用对象、例外条件、数量限制和版本信息,避免出现“引用存在但结论被扩大”的问题。
5. 如果知识库没有答案,Agent 应该怎么做?
Agent 应明确说明当前知识库没有找到足够依据,必要时引导用户补充条件或转人工,而不是根据常识猜测。自动化测试中需要验证空检索时不会生成无依据结论。
6. 如何测试 RAG 的权限隔离?
我会准备不同用户、角色和租户的文档,分别验证检索结果、Prompt 上下文、最终回答、引用、缓存和日志,确保无权限文档不会进入任何一环。同时尝试篡改 user_id、tenant_id 和 document_id,验证服务端不会仅信任客户端参数。
二十、面试综合回答模板
我会从数据质量、文档切分、检索质量、生成忠实性、版本管理和权限隔离几个方面测试 RAG Agent。
在检索侧,我会评估 Recall@K、Precision@K、MRR 和 Hit Rate,并对比关键词检索、向量检索、混合检索以及 Rerank 的效果。同时测试 Chunk 大小、Overlap、Top-K 和相似度阈值对结果的影响。
在生成侧,我会验证答案是否有文档依据、引用是否支持结论、是否存在断章取义,以及检索为空时是否拒答。对于多文档冲突,我会检查系统是否能识别版本、地区和适用范围差异。
在安全侧,我会验证过期文档过滤、用户隔离、租户隔离和文档权限,确保无权限内容不会进入检索结果、Prompt、缓存和日志。
最后,通过完整链路日志区分“检索错误、生成错误、空检索幻觉和引用不支持结论”,从而准确定位问题,而不是只根据最终回答猜原因。
二十一、RAG 测试速记口诀
文档先验货,Chunk 别切错;
召回看 Recall,准确看 Precision;
正确排前面,MRR 才漂亮;
有据再生成,没据别硬编;
引用要对得上,冲突要说清;
版本不能旧,权限不能漏。
最后记住一句:
RAG 的目标不是让 Agent “看起来懂很多”,而是让它只根据找得到、看得懂、用得上的证据回答。