外观
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、优化切片粒度、改进嵌入模型或查询重写),以确保所有必要的信息都能被检索出来。
计算原理
- 分解参考答案:将人类提供的标准答案(Ground Truth)分解为独立的陈述句或事实点(Statements)。
- 逐一验证:检查每一个事实点是否能在检索到的上下文(Contexts)中找到依据(通常由 LLM 判断语义是否一致)。
- 统计比例:计算被上下文覆盖的事实点数量占总事实点数量的比例。
示例
假设用户问题是:“爱因斯坦在哪一年获得了诺贝尔奖,是因为什么发现?”
参考答案 (Ground Truth):“爱因斯坦在1921年获得了诺贝尔物理学奖,因为发现了光电效应定律。”
关键事实点:
- 年份:1921年
- 奖项:诺贝尔物理学奖
- 原因:对理论物理的贡献/发现光电效应定律
- 场景 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 之间,分数越高表示回复与用户意图的对齐度越好。
- 关注点:
- 主要关注回复是否直接、恰当地解决了原始问题。
- 不评估事实的准确性(即不判断内容真假,只判断是否跑题)。
- 会惩罚那些不完整或包含不必要细节的回复。
计算原理
该指标的计算基于一个核心理念:如果一个回答正确地解决了问题,那么仅从这个回答出发,应该能够高概率地还原出原始问题。
- 逆向生成问题:利用大语言模型(LLM)根据生成的回复,逆向推导出 NN 个(默认为3个)可能的问题。
- 计算相似度:计算这些“生成的问题”的嵌入向量(Embedding)与“原始用户问题”的嵌入向量之间的余弦相似度(Cosine Similarity)。
- 取平均值:将所有相似度得分取平均,即为最终的回答相关性得分。
示例
用户问题:“法国在哪里?它的首都是什么?”
- 低相关性回复:“法国位于西欧。”
- 原因:虽然信息正确,但遗漏了关于“首都”的关键部分,导致从该回复难以还原出完整原问题。
- 高相关性回复:“法国位于西欧,巴黎是其首都。”
- 原因:完整覆盖了问题的两个部分,能很好地还原原问题意图。
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),即模型是否编造了上下文中不存在的信息。
- 不评估回复是否回答了用户的问题(那是“回答相关性”的任务),也不评估上下文本身是否正确,只评估回复是否严格基于给定的上下文。
计算原理
- 提取主张(Statement Identification):将生成的回复拆解为一个个独立的陈述句或主张(Claims)。
- 验证主张(Verification):检查每一个主张是否可以仅从提供的“检索上下文”中推断出来。
- 如果可以推断,记为支持(Supported)。
- 如果不能推断或与上下文矛盾,记为不支持(Not Supported)。
- 计算得分
示例
用户问题:“爱因斯坦何时何地出生?”
检索上下文:“阿尔伯特·爱因斯坦(生于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