上下文精确率与召回率:别只盯着答案,先看看检索喂了什么

个人主页: > for_ever_love__ <(欢迎各位大佬莅临😊)
其他栏目: > 大模型开发从0到1 <
其他栏目: > iOS项目总结大全 <
其他栏目: > 我想学python了 <
其他栏目: > iOS UI <

文章目录

  • 上下文精确率与召回率:别只盯着答案,先看看检索喂了什么
    • 一、为什么需要这两个指标
      • [1.1 端到端指标的盲区](#1.1 端到端指标的盲区)
      • [1.2 RAG 的特殊性:没有"相关/不相关"标注](#1.2 RAG 的特殊性:没有"相关/不相关"标注)
    • [二、上下文精确率(Context Precision)](#二、上下文精确率(Context Precision))
      • [2.1 直觉含义](#2.1 直觉含义)
      • [2.2 RAGAS 的计算方式](#2.2 RAGAS 的计算方式)
      • [2.3 代码](#2.3 代码)
      • [2.4 低分意味着什么](#2.4 低分意味着什么)
    • [三、上下文召回率(Context Recall)](#三、上下文召回率(Context Recall))
      • [3.1 直觉含义](#3.1 直觉含义)
      • [3.2 RAGAS 的计算方式](#3.2 RAGAS 的计算方式)
      • [3.3 代码](#3.3 代码)
      • [3.4 低分意味着什么](#3.4 低分意味着什么)
    • 四、两个指标的组合解读
    • [五、和传统 IR 指标的区别](#五、和传统 IR 指标的区别)
      • [5.1 什么时候该用传统 IR 指标](#5.1 什么时候该用传统 IR 指标)
      • [5.2 RAGAS 指标的噪声问题](#5.2 RAGAS 指标的噪声问题)
    • 六、怎么建立一套可用的评测
      • [6.1 最小可用评测集](#6.1 最小可用评测集)
      • [6.2 评测脚本](#6.2 评测脚本)
      • [6.3 建立基线,然后逐项优化](#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 篇文档里,有多少篇是真正有用的?有用的排在前面吗?

它回答两个问题:

  1. 召回的噪声多不多(精确性)
  2. 排序好不好(是否把相关的排在前面)

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 代码

python 复制代码
from ragas import evaluate
from ragas.metrics import context_precision
from datasets import Dataset

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 代码

python 复制代码
from ragas.metrics import context_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),那传统指标其实更可靠:

python 复制代码
def precision_at_k(retrieved, relevant_set, k):
    return len(set(retrieved[:k]) & relevant_set) / k

def recall_at_k(retrieved, relevant_set, k):
    return len(set(retrieved[:k]) & relevant_set) / len(relevant_set)

def mrr(retrieved, relevant_set):
    for i, doc in enumerate(retrieved):
        if doc in relevant_set:
            return 1 / (i + 1)
    return 0

有标注就用传统指标(无 LLM 噪声、可复现、便宜);没标注才用 RAGAS。

5.2 RAGAS 指标的噪声问题

LLM 判定带来的问题:

  1. 不可复现:同样的输入,两次跑可能差 0.05~0.1
  2. 评判模型影响大:用弱模型评判,分数不可信
  3. 对标准答案质量敏感:ground_truth 写得含糊,召回率判定就乱

缓解办法:

  • 评判模型固定、temperature=0
  • 关注相对变化(改之前 vs 改之后)而非绝对值
  • 评测集足够大(100+ 条),单次噪声被平均掉
  • 关键结论做人工抽检复核

六、怎么建立一套可用的评测

6.1 最小可用评测集

不需要很大规模,50~200 条就能工作:

python 复制代码
eval_set = [
    {
        "question": "一线城市住宿标准是多少?",
        "ground_truth": "500 元/晚",           # 简洁、事实化
        "relevant_doc_ids": ["doc_123"],        # 可选,有的话更好
    },
    ...
]

ground_truth 的写法很重要:

  • ❌ 写得像文章:"根据公司差旅管理制度第三章第二节的规定,一线城市的住宿标准是......"
  • ✅ 写得像事实点:"一线城市住宿上限 500 元/晚"

因为召回率是按陈述拆的,ground_truth 越啰嗦,拆分越不稳定。

6.2 评测脚本

python 复制代码
from ragas import evaluate
from ragas.metrics import (
    context_precision, context_recall, faithfulness, answer_relevancy
)
from datasets import Dataset

def run_eval(eval_set, rag_pipeline):
    records = {"question": [], "answer": [], "contexts": [], "ground_truth": []}
    for item in eval_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)
    return evaluate(ds, metrics=[
        context_precision, context_recall,
        faithfulness, answer_relevancy,
    ])

四个指标一起跑:前两个看检索,后两个看生成。

6.3 建立基线,然后逐项优化

不要一次改一堆东西。 正确姿势:

  1. 跑一次基线,记录四个指标
  2. 定位最弱的那个(通常是上下文精确率或召回率)
  3. 只针对它改一处(比如加重排)
  4. 再跑一次,对比
  5. 重复

这样你能准确知道每个改动带来了多少收益。同时改五处,最后不知道是哪处起了作用(或者哪处起了负作用)。


七、常见误区

误区一:把精确率低当成"要减小 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 问题,根子在检索。

相关推荐
for_ever_love__3 小时前
RAG 里的拒答:什么时候应该说‘我不知道
人工智能·rag·处理·拒答·不知道·怎么回答·检索不到
RuoyiOffice4 小时前
Spring AI+Vue3 企业知识库设计:让每个答案有依据,让每份资料守住权限
java·spring boot·vue3·rag·企业知识库·spring ai·ruoyi office
余槐i1 天前
Ollama 长文档问答后半段全空:context 默认值与静默截断的代价
性能优化·大模型·api·rag·ollama
像风一样自由20201 天前
55.从零搭建一个企业文档问答系统总体架构与技术选型
人工智能·架构·大模型·rag·智能体
for_ever_love__1 天前
LangChain 效果评测:没有评测,优化就是在瞎调
人工智能·学习·langchain·rag·评估·langsmith·评测指标
做个文艺程序员1 天前
MQ第10篇:RAG与消息队列实战:Java构建AI客服系统的检索增强生成完整架构
人工智能·架构·rocketmq·向量数据库·rag·ai客服·spring ai
东莞市云毅网络有限公司1 天前
用 Python 给官网做字段级变更监控:快照、比对与变更清单
python·数据清洗·rag·企业知识库·文档解析
流浪0011 天前
大模型技术全景(二十四):RAG 发展的四种基础范式与未来趋势
llm·rag·advanced rag·naive rag·graph rag·modular rag
XLYcmy2 天前
AI 赋能后中文与英文数据库对比分析报告 上
数据库·ai·llm·prompt·检索·wos·cnki