文章目录
- 上下文精确率与召回率:别只盯着答案,先看看检索喂了什么
- 一、为什么需要这两个指标
- 1.1 端到端指标的盲区
- 1.2 RAG 的特殊性:没有"相关/不相关"标注
- 二、上下文精确率(Context Precision)
- 2.1 直觉含义
- 2.2 RAGAS 的计算方式
- 2.3 代码
- 2.4 低分意味着什么
- 三、上下文召回率(Context Recall)
- 3.1 直觉含义
- 3.2 RAGAS 的计算方式
- 3.3 代码
- 3.4 低分意味着什么
- 四、两个指标的组合解读
- 五、和传统 IR 指标的区别
- 5.1 什么时候该用传统 IR 指标
- 5.2 RAGAS 指标的噪声问题
- 六、怎么建立一套可用的评测
- 6.1 最小可用评测集
- 6.2 评测脚本
- 6.3 建立基线,然后逐项优化
- 七、常见误区
- 小结
上下文精确率与召回率:别只盯着答案,先看看检索喂了什么
很多人评估 RAG 只看最终答案对不对。答案错了就调 prompt、换模型,折腾半天没效果——其实问题出在检索环节:真正相关的文档根本没被召回来,或者召回来一堆垃圾。
**上下文精确率(Context Precision)和上下文召回率(Context Recall)**就是专门用来评估检索环节的指标。它们把"喂给模型的东西好不好"这件事单独量化出来,让你不用靠猜。
这一篇讲清楚这两个指标怎么算、怎么用、以及它们和传统 IR 指标有什么不同。
一、为什么需要这两个指标
1.1 端到端指标的盲区
只看答案正确率,你会遇到这种情况:
- 检索召回了 10 篇,其中 1 篇是对的,模型恰好用了它 → 答案对 → 指标好看
- 但检索质量其实很差(精确率 0.1),只是运气好
下一次同样的问题换个问法,那 1 篇没召到,答案就错了。你没有修好系统,只是碰巧躲过了一次失败。
反过来也有:
- 检索召回了完全正确的材料,但模型没照着说(忠实度低)→ 答案错
- 你以为是检索的问题,其实完全是生成的问题
不把检索和生成分开评估,就永远在瞎调。
1.2 RAG 的特殊性:没有"相关/不相关"标注
传统信息检索(IR)的精确率、召回率需要人工标注"这篇文档是否相关"。在 RAG 的评测集里,我们通常只有:
question:问题ground_truth:标准答案(或参考答案)contexts:系统实际召回的上下文
没有逐篇的相关性标注。所以 RAG 社区(主要是 RAGAS)重新定义了这两个指标,让它们能基于"标准答案"来计算,而不需要人工标文档。
二、上下文精确率(Context Precision)
2.1 直觉含义
在召回的 N 篇文档里,有多少篇是真正有用的?有用的排在前面吗?
它回答两个问题:
- 召回的噪声多不多(精确性)
- 排序好不好(是否把相关的排在前面)
2.2 RAGAS 的计算方式
RAGAS 用的是Precision@k 的加权平均,核心是"排名越靠前的相关文档,权重越大"。
判定步骤:对每个问题,让 LLM 判断每篇上下文是否与标准答案相关(即:这篇文档是否包含了得出标准答案所需的信息)。
contexts = [c1, c2, c3, c4, c5] 判定结果 = [0, 1, 0, 1, 0] # 1 = 相关计算 Precision@k:
| k | 前 k 篇中相关数 | Precision@k | 是否计入(仅当第 k 篇相关时计入分子) |
|---|---|---|---|
| 1 | 0 | 0/1 = 0 | 否(c1 不相关) |
| 2 | 1 | 1/2 = 0.5 | 是(c2 相关) |
| 3 | 1 | 1/3 = 0.33 | 否 |
| 4 | 2 | 2/4 = 0.5 | 是(c4 相关) |
| 5 | 2 | 2/5 = 0.4 | 否 |
加权公式:
Context Precision = Σ(Precision@k × relevant_k) / 总相关文档数 = (0.5 × 1 + 0.5 × 1) / 2 = 0.5这个设计有个巧妙之处:它惩罚"相关文档排在后面"。
对比两组排序(都有 2 篇相关):
| 排序 | 计算 | 得分 |
|---|---|---|
| [1, 1, 0, 0, 0] | (1/1 + 2/2) / 2 | 1.0 |
| [0, 0, 0, 1, 1] | (1/4 + 2/5) / 2 | 0.325 |
同样召回了 2 篇相关文档,排前面的得 1.0,排后面的只有 0.325。这正是我们想要的——排序质量被纳入了指标。
2.3 代码
fromragasimportevaluatefromragas.metricsimportcontext_precisionfromdatasetsimportDataset data=Dataset.from_dict({"question":["..."],"contexts":[[c1,c2,c3,c4,c5]],"ground_truth":["标准答案文本"],})result=evaluate(data,metrics=[context_precision])print(result["context_precision"])2.4 低分意味着什么
| 分数 | 诊断 | 对策 |
|---|---|---|
| < 0.3 | 检索基本没用,召回的大部分是噪声 | 换 embedding、检查切分、加混合检索 |
| 0.3~0.6 | 能召到,但噪声多或排序差 | 加重排(最有效)、调 top_k |
| 0.6~0.8 | 还行,可以优化 | 减小 top_k、重排、过滤低分 |
| > 0.8 | 检索质量不错 | 问题多半在生成侧 |
精确率低最常见的修法是加重排。因为粗召回(向量检索)本来就追求覆盖,牺牲精确是设计使然;重排就是用来补精确率的。
三、上下文召回率(Context Recall)
3.1 直觉含义
标准答案里的信息,有多少能在召回的上下文中找到?
它衡量的是检索的覆盖能力——有没有把必要的材料都找齐。
注意方向和精确率相反:
- 精确率:召回的里面有多少是有用的(看召回集合)
- 召回率:需要的信息里有多少被召回了(看标准答案)
3.2 RAGAS 的计算方式
步骤:把标准答案拆成若干原子陈述,判断每条陈述能否从召回的上下文中推出。
ground_truth: "一线城市住宿上限 500 元/晚,超标需提前申请特批。" 拆成: G1: 一线城市住宿上限 500 元/晚 G2: 超标需要提前申请特批 判断(能否从 contexts 推出): G1: ✓ G2: ✗ Context Recall = 1 / 2 = 0.5公式:
Context Recall = 能从上下文推出的陈述数 / 标准答案的陈述总数3.3 代码
fromragas.metricsimportcontext_recall result=evaluate(data,metrics=[context_recall])print(result["context_recall"])3.4 低分意味着什么
召回率低 =信息根本没进上下文。这时候不管生成侧怎么优化都没用,因为巧妇难为无米之炊。
常见原因与对策:
| 原因 | 表现 | 对策 |
|---|---|---|
| 切分把答案切断了 | 同一条数据明明在库里 | 调 chunk_size、加 overlap、父子块 |
| embedding 不匹配领域 | 语义相近但向量远 | 换/微调 embedding |
| top_k 太小 | 答案排在 top_k 之外 | 加大 top_k(配合重排) |
| 纯向量检索漏掉关键词 | 专有名词、缩写匹配不上 | 加 BM25 混合检索 |
| 文档根本没入库 | 完全搜不到 | 检查入库流程、数据覆盖 |
| 查询表述与文档差异大 | 人工能搜到系统搜不到 | 查询改写、多查询 |
四、两个指标的组合解读
单独看一个不够,组合起来才能定位问题。
| 精确率 | 召回率 | 诊断 | 优先对策 |
|---|---|---|---|
| 高 | 高 | ✅ 检索健康 | 若答案仍错,查生成侧(忠实度) |
| 低 | 高 | 召得多但杂,噪声多 | 加重排、减小 top_k |
| 高 | 低 | 召得准但漏东西 | 加大 top_k、混合检索、多查询 |
| 低 | 低 | 检索整体有问题 | 换 embedding、检查切分与入库 |
第二行(低-高)是最常见的状态,也是最好修的:加大粗召回保证覆盖(召回率高),然后靠重排把噪声滤掉(精确率上来)。这是"粗召回 + 重排"两阶段架构的核心价值。
**第三行(高-低)**说明检索很"挑剔",只召它最有把握的。解法是扩召回手段(混合检索、多查询、查询分解),而不是继续调 embedding。
五、和传统 IR 指标的区别
值得单独说清楚,避免混淆。
| 维度 | 传统 IR(P@K、Recall@K) | RAGAS 上下文指标 |
|---|---|---|
| 相关性判定 | 人工标注每篇文档 | LLM 基于标准答案判定 |
| 需要什么标注 | 文档级相关性标签 | 标准答案文本 |
| 排序敏感性 | P@K 只在固定 K 处评估 | 加权平均,全程敏感 |
| 粒度 | 文档级 | 陈述级(召回率) |
| 成本 | 人工标注贵 | LLM 调用贵但可自动化 |
关键区别:RAGAS 的指标是为"没有文档级标注"的现实情况设计的。代价是需要调 LLM,且有判定噪声。
5.1 什么时候该用传统 IR 指标
如果你有文档级相关性标注(比如从点击日志、人工标注得到了 relevance label),那传统指标其实更可靠:
defprecision_at_k(retrieved,relevant_set,k):returnlen(set(retrieved[:k])&relevant_set)/kdefrecall_at_k(retrieved,relevant_set,k):returnlen(set(retrieved[:k])&relevant_set)/len(relevant_set)defmrr(retrieved,relevant_set):fori,docinenumerate(retrieved):ifdocinrelevant_set:return1/(i+1)return0有标注就用传统指标(无 LLM 噪声、可复现、便宜);没标注才用 RAGAS。
5.2 RAGAS 指标的噪声问题
LLM 判定带来的问题:
- 不可复现:同样的输入,两次跑可能差 0.05~0.1
- 评判模型影响大:用弱模型评判,分数不可信
- 对标准答案质量敏感:ground_truth 写得含糊,召回率判定就乱
缓解办法:
- 评判模型固定、temperature=0
- 关注相对变化(改之前 vs 改之后)而非绝对值
- 评测集足够大(100+ 条),单次噪声被平均掉
- 关键结论做人工抽检复核
六、怎么建立一套可用的评测
6.1 最小可用评测集
不需要很大规模,50~200 条就能工作:
eval_set=[{"question":"一线城市住宿标准是多少?","ground_truth":"500 元/晚",# 简洁、事实化"relevant_doc_ids":["doc_123"],# 可选,有的话更好},...]ground_truth 的写法很重要:
- ❌ 写得像文章:“根据公司差旅管理制度第三章第二节的规定,一线城市的住宿标准是……”
- ✅ 写得像事实点:“一线城市住宿上限 500 元/晚”
因为召回率是按陈述拆的,ground_truth 越啰嗦,拆分越不稳定。
6.2 评测脚本
fromragasimportevaluatefromragas.metricsimport(context_precision,context_recall,faithfulness,answer_relevancy)fromdatasetsimportDatasetdefrun_eval(eval_set,rag_pipeline):records={"question":[],"answer":[],"contexts":[],"ground_truth":[]}foritemineval_set:ans,ctxs=rag_pipeline(item["question"])records["question"].append(item["question"])records["answer"].append(ans)records["contexts"].append(ctxs)records["ground_truth"].append(item["ground_truth"])ds=Dataset.from_dict(records)returnevaluate(ds,metrics=[context_precision,context_recall,faithfulness,answer_relevancy,])四个指标一起跑:前两个看检索,后两个看生成。
6.3 建立基线,然后逐项优化
不要一次改一堆东西。正确姿势:
- 跑一次基线,记录四个指标
- 定位最弱的那个(通常是上下文精确率或召回率)
- 只针对它改一处(比如加重排)
- 再跑一次,对比
- 重复
这样你能准确知道每个改动带来了多少收益。同时改五处,最后不知道是哪处起了作用(或者哪处起了负作用)。
七、常见误区
误区一:把精确率低当成"要减小 top_k"。
减小 top_k 确实能提精确率,但会伤召回率。正确做法是加大粗召回 + 重排,两个指标一起改善。
误区二:认为指标越高越好,追求 0.95+。
精确率 0.9 意味着召回的文档 90% 有用,这在真实场景下很难且不必要。0.6~0.8 的精确率配合重排,通常已经是健康状态。追求极致会牺牲召回。
误区三:评测集只放简单问题。
简单问题的检索通常都没问题,评测集里必须要有难例:需要多跳的、表述差异大的、涉及专有名词的。否则指标虚高,上线就翻车。
误区四:跑一次就下结论。
LLM 判定有噪声,单次结果波动 ±0.05 很正常。关键改动要跑两三次取平均,或者评测集足够大。
小结
- 上下文精确率:召回的文档里有多少有用,且有用的在不在前面。低分 → 加重排、减 top_k。
- 上下文召回率:标准答案的信息有多少能在上下文中找到。低分 → 扩召回手段(混合检索、多查询、调切分)。
- 组合看才有意义:低精确 + 高召回是"粗召回 + 重排"架构的典型中间态,也是最好修的状态。
- RAGAS 版本不需要文档级标注(靠 LLM 判定),代价是有噪声;有标注时优先用传统 IR 指标。
- ground_truth 要写得简洁事实化,评测集要含难例,改动要一次一处并对比基线。
- 这四个指标(精确率、召回率、忠实度、答案相关性)构成一套完整的 RAG 评估闭环,检索侧和生成侧分开看,才知道该改哪里。
一句话总结:答案错了别急着调 prompt,先看看上下文精确率和召回率——大部分 RAG 问题,根子在检索。