Skip to content

Ragas评估指标

指标

定义与作用

  • 定义:指标提供了一个数值基础,用于比较、优化和决策。它帮助评估应用程序及其各个组件相对于测试数据的表现。
  • 作用:
    • 反馈循环:“你无法改进你无法衡量的东西”。指标是迭代的指南针,能区分成功的实验和失败的尝试。
    • 组件选择:帮助在不同的 LLM、检索器或代理配置中选择最佳方案。
    • 错误诊断:定位导致性能不佳的具体组件。
    • 持续监控:追踪随时间变化的性能,检测数据漂移或模型退化。

指标设计原则

  • 单一关注点:一个指标只衡量一个特定方面。
  • 直观可解释:结果应易于团队理解和沟通。
  • 客观性:优先选择客观标准(不同评估者之间的一致性高),而非主观判断。
  • 少而精:选择几个能提供强信号的指标,避免大量弱信号指标干扰决策。
  • 评分范围一致:通常归一化到特定范围(如 0-1)以便比较。

参考资料

上下文精确度 (Context Precision)

介绍

上下文精确度 (Context Precision)

  • 目的:评估检索器将相关文档块(Chunks)排在检索结果列表顶部的能力。
  • 关注点:它不仅关心检索到了多少相关内容(这是召回率的事),更关心排序质量。如果相关信息出现在列表的最前面,得分就高;如果相关信息沉在列表底部,得分就低。
  • 直观理解:如果所有相关文档都排在最前面,得分为 1;如果相关文档排在后面,分数会显著降低。

示例

python
from openai import AsyncOpenAI
from ragas.llms import llm_factory
from ragas.metrics.collections import ContextPrecision

# 配置 LLM
MODEL = "deepseek-v3.2"
API_KEY = "sk-"
BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
client = AsyncOpenAI(api_key=API_KEY, base_url=BASE_URL)
llm = llm_factory(MODEL, client=client)

# 创建指标
scorer = ContextPrecision(llm=llm)

# 计算指标
result_a = scorer.score(
    user_input="Where is the Eiffel Tower located?",
    reference="The Eiffel Tower is located in Paris.",
    retrieved_contexts=[
        "The Eiffel Tower is located in Paris.",
        "The Brandenburg Gate is located in Berlin.",
    ],
)
# 结果:相关信息在第一位,得分接近 1.0。
print(f"Context Precision Score: {result_a.value}")

# 计算指标
result_b = scorer.score(
    user_input="Where is the Eiffel Tower located?",
    reference="The Eiffel Tower is located in Paris.",
    # 注意:这里的 retrieved_contexts 顺序与上面的不一致
    retrieved_contexts=[
        "The Brandenburg Gate is located in Berlin.",
        "The Eiffel Tower is located in Paris.",
    ],
)
# 结果:虽然包含了正确答案,但被排在了第二位(无关信息在前),得分降至约 0.5。
print(f"Context Precision Score: {result_b.value}")

输出结果

bash
Context Precision Score: 0.9999999999
Context Precision Score: 0.49999999995

不同场景下的实现方式

有参考答案时 (With Reference)

  • 指标名称:ContextPrecision (新 API) 或 LLMContextPrecisionWithReference (旧 API)
  • 工作原理:将检索到的每个上下文块与参考答案 (Ground Truth) 进行比较,判断其相关性并计算排序得分。
  • 适用场景:你有标准的正确答案用于评估。

无参考答案时 (Without Reference)

  • 指标名称:ContextUtilization (新 API) 或 LLMContextPrecisionWithoutReference (旧 API)
  • 工作原理:在没有标准答案的情况下,利用 LLM 将检索到的上下文块与模型生成的回答 (Response) 进行比较,判断哪些上下文被实际“利用”来生成回答,进而评估排序。
  • 适用场景:生产环境中通常没有标准答案,只有用户问题和模型回答。

上下文召回率 (Context Recall)

介绍

上下文召回率 (Context Recall)

  • 目的:衡量检索到的上下文(Context)在多大程度上覆盖了**参考答案(Ground Truth)**中包含的所有关键信息。
  • 核心问题:为了能够回答这个问题,检索到的文档中是否包含了所有必需的事实?
  • 取值范围:0 到 1。
    • 1.0:检索到的上下文包含了回答问题所需的全部信息(完美覆盖)。
    • 0.0:检索到的上下文中完全没有包含回答问题所需的信息。

重要性

  • 诊断检索缺失:如果 Context Recall 分数低,说明检索器漏掉了关键文档。即使你的生成模型(LLM)再强大,如果它没看到关键信息,也无法生成正确答案(即“巧妇难为无米之炊”)。
  • 与 Faithfulness(忠实度)的关系:
    • 低 Recall + 高 Faithfulness:模型很诚实,只根据检索到的少量信息回答,但因为信息不全,答案不完整。
    • 高 Recall + 低 Faithfulness:信息都找全了,但模型产生了幻觉,没有利用好这些信息。
    • 理想状态:高 Recall + 高 Faithfulness。

上下文召回率 (Context Recall) 是衡量检索完整性的标尺。

  • 如果你发现 RAG 系统的回答经常遗漏关键信息或不完整,首先应该检查这个指标。
  • 低分意味着你需要优化检索策略(如扩大 Top-K、优化切片粒度、改进嵌入模型或查询重写),以确保所有必要的信息都能被检索出来。

计算原理

  1. 分解参考答案:将人类提供的标准答案(Ground Truth)分解为独立的陈述句或事实点(Statements)。
  2. 逐一验证:检查每一个事实点是否能在检索到的上下文(Contexts)中找到依据(通常由 LLM 判断语义是否一致)。
  3. 统计比例:计算被上下文覆盖的事实点数量占总事实点数量的比例。

示例

假设用户问题是:“爱因斯坦在哪一年获得了诺贝尔奖,是因为什么发现?”

参考答案 (Ground Truth):“爱因斯坦在1921年获得了诺贝尔物理学奖,因为发现了光电效应定律。”

关键事实点:

  1. 年份:1921年
  2. 奖项:诺贝尔物理学奖
  3. 原因:对理论物理的贡献/发现光电效应定律
  • 场景 A(高召回率):
    • 检索到的上下文:“阿尔伯特·爱因斯坦因发现光电效应定律,于1921年被授予诺贝尔物理学奖。”
    • 分析:上下文覆盖了所有3个事实点。
    • 得分:3/3=1.03/3=1.0
  • 场景 B(低召回率):
    • 检索到的上下文:“爱因斯坦是一位著名的物理学家,他在20世纪初提出了相对论。”
    • 分析:上下文中没有提到“1921年”、“诺贝尔奖”或“光电效应”。
    • 得分:0/3=0.00/3=0.0
  • 场景 C(中等召回率):
    • 检索到的上下文:“爱因斯坦因光电效应的发现获得了诺贝尔奖。”
    • 分析:覆盖了“原因”和“奖项”,但缺少“1921年”这个关键事实。
    • 得分:2/3≈0.672/3≈0.67
python
from openai import AsyncOpenAI
from ragas.llms import llm_factory
from ragas.metrics.collections import ContextRecall

# 配置 LLM
MODEL = "deepseek-v3.2"
API_KEY = "sk-"
BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
client = AsyncOpenAI(api_key=API_KEY, base_url=BASE_URL)
llm = llm_factory(MODEL, client=client)

# 创建上下文召回率指标
scorer = ContextRecall(llm=llm)

# 计算上下文召回率
result_a = scorer.score(
    user_input="爱因斯坦在哪一年获得了诺贝尔奖,是因为什么发现?",
    retrieved_contexts=[
        "阿尔伯特·爱因斯坦因发现光电效应定律,于1921年被授予诺贝尔物理学奖。"
    ],
    reference="爱因斯坦在1921年获得了诺贝尔物理学奖,因为发现了光电效应定律。",
)
print(f"Context Recall Score: {result_a.value}")

# 计算上下文召回率
result_b = scorer.score(
    user_input="爱因斯坦在哪一年获得了诺贝尔奖,是因为什么发现?",
    retrieved_contexts=["爱因斯坦是一位著名的物理学家,他在20世纪初提出了相对论。"],
    reference="爱因斯坦在1921年获得了诺贝尔物理学奖,因为发现了光电效应定律。",
)
print(f"Context Recall Score: {result_b.value}")

# 计算上下文召回率
result_c = scorer.score(
    user_input="爱因斯坦在哪一年获得了诺贝尔奖,是因为什么发现?",
    retrieved_contexts=["爱因斯坦因光电效应的发现获得了诺贝尔奖。"],
    reference="爱因斯坦在1921年获得了诺贝尔物理学奖,因为发现了光电效应定律。",
)
print(f"Context Recall Score: {result_c.value}")

输出结果

bash
Context Recall Score: 1.0
Context Recall Score: 0.0
Context Recall Score: 0.5

回答相关性(Answer Relevancy)

介绍

回答相关性(Answer Relevancy)

  • 衡量标准:它衡量生成的回复(Response)与用户输入(User Input/Question)的相关程度。
  • 评分范围:通常在 0 到 1 之间,分数越高表示回复与用户意图的对齐度越好。
  • 关注点:
    • 主要关注回复是否直接、恰当地解决了原始问题。
    • 不评估事实的准确性(即不判断内容真假,只判断是否跑题)。
    • 会惩罚那些不完整或包含不必要细节的回复。

计算原理

该指标的计算基于一个核心理念:如果一个回答正确地解决了问题,那么仅从这个回答出发,应该能够高概率地还原出原始问题。

  1. 逆向生成问题:利用大语言模型(LLM)根据生成的回复,逆向推导出 NN 个(默认为3个)可能的问题。
  2. 计算相似度:计算这些“生成的问题”的嵌入向量(Embedding)与“原始用户问题”的嵌入向量之间的余弦相似度(Cosine Similarity)。
  3. 取平均值:将所有相似度得分取平均,即为最终的回答相关性得分。

示例

用户问题:“法国在哪里?它的首都是什么?”

  • 低相关性回复:“法国位于西欧。”
    • 原因:虽然信息正确,但遗漏了关于“首都”的关键部分,导致从该回复难以还原出完整原问题。
  • 高相关性回复:“法国位于西欧,巴黎是其首都。”
    • 原因:完整覆盖了问题的两个部分,能很好地还原原问题意图。
python
from openai import AsyncOpenAI
from ragas.llms import llm_factory
from ragas.embeddings.base import embedding_factory
from ragas.metrics.collections import AnswerRelevancy

# 配置 LLM:调用阿里提供的deepseek-v3.2
MODEL = "deepseek-v3.2"
API_KEY = "sk-"
BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
client = AsyncOpenAI(api_key=API_KEY, base_url=BASE_URL)
llm = llm_factory(MODEL, client=client)

# 配置 Embeddings:调用ollama的embeddings
ollama_client = AsyncOpenAI(base_url="http://127.0.0.1:11434/v1")
embeddings = embedding_factory("openai", model="bge-m3", client=ollama_client)

# 创建回答相关性指标
scorer = AnswerRelevancy(llm=llm, embeddings=embeddings)

# 计算回答相关性
result_a = scorer.score(
    user_input="法国在哪里?它的首都是什么?",
    response="法国位于西欧。",
)
print(f"Answer Relevancy Score: {result_a.value}")

# 计算回答相关性
result_b = scorer.score(
    user_input="法国在哪里?它的首都是什么?",
    response="法国位于西欧,巴黎是其首都。",
)
print(f"Answer Relevancy Score: {result_b.value}")

输出结果

bash
Answer Relevancy Score: 0.8251151658757543
Answer Relevancy Score: 0.9095701784602982

忠实度(Faithfulness)

介绍

忠实度(Faithfulness)

  • 衡量标准:它衡量生成的回复(Response)与检索到的上下文(Retrieved Context)之间的事实一致性。
  • 评分范围:0 到 1 之间,分数越高表示回复越忠实于提供的上下文。
  • 核心逻辑:如果回复中的每一个主张(Claim)都能从检索到的上下文中推导出来,则该回复被认为是忠实的。
  • 关注点:
    • 主要检测幻觉(Hallucination),即模型是否编造了上下文中不存在的信息。
    • 不评估回复是否回答了用户的问题(那是“回答相关性”的任务),也不评估上下文本身是否正确,只评估回复是否严格基于给定的上下文。

计算原理

  1. 提取主张(Statement Identification):将生成的回复拆解为一个个独立的陈述句或主张(Claims)。
  2. 验证主张(Verification):检查每一个主张是否可以仅从提供的“检索上下文”中推断出来。
    • 如果可以推断,记为支持(Supported)。
    • 如果不能推断或与上下文矛盾,记为不支持(Not Supported)。
  3. 计算得分

示例

用户问题:“爱因斯坦何时何地出生?”

检索上下文:“阿尔伯特·爱因斯坦(生于1879年3月14日)是一位出生于德国的理论物理学家……”

情况 A:高忠实度回复

  • 回复:“爱因斯坦于1879年3月14日出生在德国。”
  • 分析:
    • 主张1:“爱因斯坦出生在德国” -> 上下文支持(是)。
    • 主张2:“爱因斯坦生于1879年3月14日” -> 上下文支持(是)。
  • 得分:2/2=1.02/2=1.0

情况 B:低忠实度回复

  • 回复:“爱因斯坦于1879年3月20日出生在德国。”
  • 分析:
    • 主张1:“爱因斯坦出生在德国” -> 上下文支持(是)。
    • 主张2:“爱因斯坦生于1879年3月20日” -> 上下文不支持(上下文说是14日)。
  • 得分:1/2=0.51/2=0.5
python
from openai import AsyncOpenAI
from ragas.llms import llm_factory
from ragas.metrics.collections import Faithfulness

# 配置 LLM:调用阿里提供的deepseek-v3.2
MODEL = "deepseek-v3.2"
API_KEY = "sk-"
BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1"
client = AsyncOpenAI(api_key=API_KEY, base_url=BASE_URL)
llm = llm_factory(MODEL, client=client)

# 创建忠实度指标
scorer = Faithfulness(llm=llm)

# 计算指标
result_a = scorer.score(
    user_input="爱因斯坦何时何地出生?",
    response="爱因斯坦于1879年3月14日出生在德国。",
    retrieved_contexts=[
        "阿尔伯特·爱因斯坦(生于1879年3月14日)是一位出生于德国的理论物理学家"
    ],
)
print(f"Faithfulness Score: {result_a.value}")

# 计算指标
result_b = scorer.score(
    user_input="爱因斯坦何时何地出生?",
    response="爱因斯坦于1879年3月20日出生在德国。",
    retrieved_contexts=[
        "阿尔伯特·爱因斯坦(生于1879年3月14日)是一位出生于德国的理论物理学家"
    ],
)
print(f"Faithfulness Score: {result_b.value}")

输出结果

bash
Faithfulness Score: 1.0
Faithfulness Score: 0.5