RAG 的四条失败路径
一个"回答错了"的 RAG 请求,错可能错在 4 个地方:
用户问题 ──▶ [1] 检索 ──▶ [2] 过滤/重排 ──▶ [3] 生成 ──▶ 答案
│ │
│ │
没拿到相关文档 拿到了但没用 / 乱用
失败 A: 相关文档压根没索引
失败 B: 索引了但检索打分没上去
失败 C: 检索上去了但被 reranker 丢掉
失败 D: 都拿对了,生成阶段幻觉/忽略上下文
一个好的 RAG 评估体系要让你能回答:"这次错了,错在 A/B/C/D 哪个?"而不是只能说"错了"。
RAGAS 的四象限
RAGAS(Retrieval-Augmented Generation Assessment)提出 4 个正交指标,构成 2×2 矩阵:
| 看检索质量 | 看生成质量 | |
|---|---|---|
| 面向上下文 | Context Precision / Recall | Faithfulness |
| 面向问题 | Context Relevancy | Answer Relevancy |
指标 1:Faithfulness(忠实度)
定义
答案中的每个事实陈述是否都能在检索到的上下文里找到支持。衡量模型有没有胡编。
计算
用 LLM 把答案拆成原子陈述 → 对每个陈述判断"上下文是否支持"(yes/no) → 支持比例。
信号
Faithfulness 低 = 模型幻觉 = 生成阶段问题。
FAITHFULNESS_PROMPT = """下面是一个问题、检索到的上下文、以及模型给出的答案。 问题: {question} 上下文: {context} 答案: {answer} 第一步:把答案拆成独立的事实陈述(每行一个)。 第二步:对每个陈述,判断上下文是否支持它(YES/NO)。 返回 JSON: {{ "claims": [ {{"claim": "...", "supported": true}}, {{"claim": "...", "supported": false}} ] }}""" def faithfulness(question, context, answer): result = judge_llm(FAITHFULNESS_PROMPT.format(...)) claims = result["claims"] if not claims: return 1.0 return sum(c["supported"] for c in claims) / len(claims)
指标 2:Answer Relevancy(答案相关性)
定义
答案是否真正回应了问题,还是跑题/答非所问。
计算(RAGAS 实现)
用 LLM 根据答案反推可能的问题(生成 n 个候选) → 每个候选问题与原问题计算嵌入余弦相似度 → 均值。
信号
Answer Relevancy 低 = 答非所问 / 过度模糊 / 前言不搭后语。
指标 3:Context Precision(上下文精度)
定义
检索回来的 top-k 上下文里,真正与问题相关的占比——且相关的排在前面。
计算
用 LLM 判定每个 chunk 是否相关,按位置计算 MAP(Mean Average Precision)变体。
信号
Precision 低 = 检索召回了噪声 / 重排没工作好。
指标 4:Context Recall(上下文召回)
定义
生成正确答案所需的信息,是否已经全在检索上下文里。需要参考答案。
计算
把参考答案拆成事实陈述 → 每个陈述判断能否从上下文推出 → 命中比例。
信号
Recall 低 = 该拿的文档没拿到 = 索引/检索阶段问题。
对比 Precision vs Recall
Precision:我拿回来的,有多少是对的?
Recall:对的文档里,我拿回来了多少?
检索系统的常见失败模式是 Recall 低(关键文档没进 top-k)而 Precision 看起来还行(因为 top-k 里大多相关但都不是关键的)。
Recall:对的文档里,我拿回来了多少?
检索系统的常见失败模式是 Recall 低(关键文档没进 top-k)而 Precision 看起来还行(因为 top-k 里大多相关但都不是关键的)。
四个指标如何组合诊断
| Faithfulness | Answer Relevancy | Context Precision | Context Recall | 诊断 |
|---|---|---|---|---|
| 高 | 高 | 高 | 高 | ✅ 系统健康 |
| 低 | 高 | 高 | 高 | ⚠️ 生成阶段幻觉,prompt 没约束好 |
| 高 | 低 | 高 | 高 | ⚠️ 模型跑题,可能 prompt 不聚焦 |
| 高 | 高 | 低 | 高 | ⚠️ 召回进来但噪声大,缺少 reranker |
| 高 | 高 | 高 | 低 | ⚠️ 检索没拿到关键文档,需改 embedding 或补索引 |
| 高 | 低 | 低 | 低 | 🚨 检索根本坏了,模型拿没用信息强答 |
| 低 | 低 | 低 | 低 | 💥 系统多处故障,从零排查 |
RAGAS 上手
from ragas import evaluate from ragas.metrics import faithfulness, answer_relevancy, context_precision, context_recall from datasets import Dataset data = { "question": ["公司的退款政策是什么?"], "answer": ["支持 7 天无理由退款,发起后 3-5 个工作日到账。"], "contexts": [["本公司支持 7 天无理由退款...", "另有会员等级说明..."]], "ground_truth": ["支持 7 天无理由退,3-5 天到账"], } ds = Dataset.from_dict(data) result = evaluate( ds, metrics=[faithfulness, answer_relevancy, context_precision, context_recall], ) print(result) # {'faithfulness': 1.0, 'answer_relevancy': 0.95, # 'context_precision': 0.85, 'context_recall': 1.0}
超越 RAGAS:更细粒度的检索指标
如果你能标注"哪些文档是黄金正确文档",可以用传统 IR 指标,比 RAGAS 更精确:
Hit@k / Recall@k
Top-k 结果里有没有黄金文档(任何一个命中就算)。
MRR(Mean Reciprocal Rank)
第一个命中文档的位置倒数的均值。越靠前越好。
nDCG@k
考虑相关性分级(不只是相关/不相关),且按位置折扣。最严格的排名指标。
def hit_at_k(retrieved_ids, gold_ids, k=5): return int(any(doc_id in gold_ids for doc_id in retrieved_ids[:k])) def mrr(retrieved_ids, gold_ids): for i, doc_id in enumerate(retrieved_ids, 1): if doc_id in gold_ids: return 1.0 / i return 0.0 import numpy as np def ndcg_at_k(retrieved_ids, gold_relevance, k=5): # gold_relevance: dict[doc_id -> 相关度 0/1/2/3] dcg = sum( gold_relevance.get(d, 0) / np.log2(i + 2) for i, d in enumerate(retrieved_ids[:k]) ) ideal = sorted(gold_relevance.values(), reverse=True)[:k] idcg = sum(r / np.log2(i + 2) for i, r in enumerate(ideal)) return dcg / idcg if idcg else 0.0
生成阶段专项:Citation Accuracy
如果你的 RAG 要求带引用,应该额外评估:
- Citation presence:每个事实陈述是否有引用标注
- Citation pointing correctness:引用指向的 chunk 是否真的支持该陈述
- Citation coverage:引用覆盖的陈述占比
# 简化版 citation 校验 def check_citations(answer_with_citations, contexts): # 答案格式: "xxx[1] yyy[2]", 每个 [n] 对应 contexts[n-1] claims_with_refs = parse_claims(answer_with_citations) # 自行实现 n_supported = 0 for claim, ref_idx in claims_with_refs: if llm_entails(claim, contexts[ref_idx]): n_supported += 1 return n_supported / len(claims_with_refs)
评估集的特殊要求
RAG 评估集除了常规字段,还要包含:
question— 用户问题ground_truth_answer— 标准答案ground_truth_docs— 回答此问题需要的黄金文档 ID 列表question_type— 事实型 / 总结型 / 多跳推理 / 无答案difficulty— 简单 / 中等 / 难
常被忽视的类别:无答案问题
RAG 评估集必须包含"知识库里没有答案"的问题。模型应该说"不知道",而不是胡编。15% 左右的评估集建议是此类。
本章小结
- RAG 失败有 4 条路径,单一指标无法定位
- RAGAS 四指标:Faithfulness / Answer Relevancy / Context Precision / Context Recall
- 通过四指标组合可快速判断问题在检索还是生成
- 有标注黄金文档时,用 Hit@k / MRR / nDCG 更精确
- 带引用的 RAG 要加 Citation Accuracy
- 评估集必须包含"无答案"类别,以测试模型的诚实度