检索增强生成(RAG)已成为为LLMs提供额外上下文以生成定制输出的最流行方法。这对于聊天机器人或 AI 代理等LLM应用来说非常有用,因为 RAG 能为用户提供比 GPT-4 等LLMs训练数据更为情境化的体验。
不出所料,LLM 从业者在开发过程中很快就遇到了评测 RAG 应用的问题。但得益于 RAGAs 的研究,在 2024 年评测 RAG 系统的通用检索器-生成器性能已是一个基本解决的问题。别误会,构建 RAG 应用仍具挑战性——你可能使用了错误的嵌入模型、糟糕的分块策略,或输出了错误格式的响应,而这正是 LlamaIndex 等框架试图解决的。
但随着 RAG 架构日益复杂,以及LLM从业者在这些项目上的协作增多,破坏性变更的发生频率比以往任何时候都要高。
数学表示
本教程将引导你如何建立一个全自动的评测/测试套件,用于在 CI/CD 流水线中对 RAG 应用进行单元测试。准备好学习如何设置理想的 RAG 开发工作流程了吗?
简而言之,RAG 评测
我解释过典型的 RAG 架构包含一个检索器——该组件通过向量搜索从知识库中获取检索上下文,以及一个生成器——该组件利用检索器提供的上下文构建提示并生成一个自定义LLM响应作为最终输出。
典型的 RAG 架构
一个高性能的 RAG 系统源于高性能的检索器和生成器。因此,RAG 评测指标现在通常聚焦于评测这两个组件。其基本假设是,只有当检索器能成功检索到正确且相关的上下文,且生成器能有效利用这些上下文产生理想输出(即事实正确且相关的输出)时,RAG 应用才能表现出色。
常见 RAG 指标
基于上述原因,RAG 评测指标通常分别聚焦于检索器和生成器。值得注意的是,RAGAs 是评测通用 RAG 性能的流行方法,并提供以下检索指标(针对检索器的指标)。让我们来看看常见的 RAG 评测指标:
上下文召回率
上下文召回率衡量的是检索上下文对预期输出中信息的覆盖程度。上下文召回率关注的是检索器,并要求将预期输出作为目标标签。这对某些人来说可能有些困惑,但之所以需要预期输出,是因为使用实际输出作为真实标准是没有意义的。试想一下,如果不知道理想的输出应该是什么,又怎能判断检索上下文的质量呢?
上下文精确度
上下文精确度是一种衡量 RAG 检索器根据相关性对检索上下文进行排序效果的指标。这一点很重要,因为LLMs往往会更多地考虑靠近提示模板末端的节点。这意味着一个糟糕的重新排序器会导致你的LLM聚焦于“错误”的检索节点,进而可能引发幻觉或产生不相关的答案。
答案相关性
答案相关性衡量的是你的 RAG 生成器(通常仅指LLM及提示本身)生成答案的相关性程度。需注意,答案相关性直接关系到检索器的质量,因为在 RAG 流程中生成输出依赖于检索上下文的信息。若检索上下文存在误导或不相关,则必然导致输出的答案相关性降低。
忠实度
忠实度衡量的是你的 RAG 生成器以检索上下文为基准所输出的幻觉程度。与答案相关性类似,忠实度的高低取决于检索上下文的相关性。
RAG 指标,终究并非完美
它们最大的优势,除了高效性之外,在于其与使用场景无关的特性。无论你是为财务顾问构建聊天机器人,还是开发数据提取应用,这些指标都能如预期般发挥作用。讽刺的是,尽管与使用场景无关,你很快会发现这些指标终究过于通用。面向财务顾问的聊天机器人可能需要额外指标,如处理客户数据时的偏见考量,而数据提取应用则可能需要确保输出符合 JSON 格式的指标。
除了通用的 RAG 评测指标外,以下是如何通过 DeepEval 将额外的LLM评测指标整合到你的 RAG 评测流程中。首先,安装 DeepEval:
pip install deepeval
然后,导入并定义你的 RAGAs 指标:
from deepeval.metrics.ragas import (
RAGASContextualPrecisionMetric,
RAGASFaithfulnessMetric,
RAGASContextualRecallMetric,
RAGASAnswerRelevancyMetric,
)
contextual_precision = RAGASContextualPrecisionMetric()
contextual_recall = RAGASContextualRecallMetric()
answer_relevancy = RAGASAnswerRelevancyMetric()
faithfulness = RAGASFaithfulnessMetric()
除了 RAG 指标外,还包括使用 G-Eval 的任何其他指标
from deepeval.metrics import GEval
from deepeval.test_case import LLMTestCaseParams
bias = GEval(
name="Bias",
criteria="Coherence - determine if the actual output has an inherent bias against Asian culture.",
evaluation_params=[LLMTestCaseParams.ACTUAL_OUTPUT],
)
最后,根据这些指标定义一个测试用例来评测你的 RAG 应用:
from deepeval import evaluate
from deepeval.test_case import LLMTestCase
test_case = LLMTestCase(
input="",
actual_output="",
expected_output="",
retrieval_context=[""]
)
evaluate(
test_cases=[test_case],
metrics=[
contextual_precision,
contextual_recall,
answer_relevancy,
faithfulness,
bias
]
)
现在这一切对于快速原型设计来说都很棒,但如果你想设置你的LLM游戏,并实际将评测作为开发工作流程的一部分呢?
使用 DeepEval 对 RAG 应用进行单元测试
DeepEval 是一个开源的评测框架,专为LLMs设计(常被称为LLMs的单元测试/Pytest,或LLMs的完整测试套件)。在上一节中,我们探讨了如何使用 RAG 评测指标及特定用例的附加指标来评测 RAG 应用。本节将通过一个完整的单元测试示例,带你深入了解 DeepEval 的使用。
先决条件
与其他工程/数据科学家的工作流程不同,CI/CD 管道中评测的主要目标是防止针对特定 git 提交/PR 对 RAG 应用程序所做的破坏性更改。因此,不反映 RAG 应用程序更改的静态评测数据集并不适用。
因此,请不要事先准备包含你 RAG 应用实际输出和检索上下文的评测数据集。相反,准备一组输入及你希望测试 RAG 应用对应实际输出的预期结果,因为我们将在评测时运行你的 RAG 应用。
安装 DeepEval,并可选登录(我保证你不会失望):
pip install deepeval
deepeval login
创建一个测试文件
虽然我们在前一节展示了如何使用 DeepEval 的 evaluate 函数,但这里我们将摒弃那种方法,转而利用 DeepEval 的 Pytest 集成功能。
首先,创建一个测试文件:
touch test_rag.py
初始化评测指标
与之前的示例类似,在新创建的测试文件中初始化你的评测指标:
from deepeval.metrics.ragas import (
RAGASContextualPrecisionMetric,
RAGASFaithfulnessMetric,
RAGASContextualRecallMetric,
RAGASAnswerRelevancyMetric,
)
from deepeval.metrics import BiasMetric
bias = BiasMetric(threshold=0.5)
contextual_precision = RAGASContextualPrecisionMetric(threshold=0.5)
contextual_recall = RAGASContextualRecallMetric(threshold=0.5)
answer_relevancy = RAGASAnswerRelevancyMetric(threshold=0.5)
faithfulness = RAGASFaithfulnessMetric(threshold=0.5)
注意,你可以选择为每个指标定义阈值。DeepEval 中的每个指标输出一个 0 到 1 之间的分数,只有当分数等于或高于阈值时,该指标才算通过。另一方面,正如我们稍后将看到的,一个测试用例只有在所有指标都通过时才算通过。
定义输入和预期输出
在这里,你将定义希望在评测时运行 RAG 应用程序的输入集。
...
# Replace this with your own data
input_output_pairs = [
{
"input": "...",
"expected_output": "...",
},
{
"input": "...",
"expected_output": "...",
}
]
(注:你也可以从 CSV 或 JSON 文件导入这些数据)
完整示例
将所有内容整合起来,这里是一个完整的示例,展示如何使用 DeepEval 对 RAG 应用进行单元测试:
import pytest
from deepeval import assert_test
from deepeval.metrics.ragas import (
RAGASContextualPrecisionMetric,
RAGASFaithfulnessMetric,
RAGASContextualRecallMetric,
RAGASAnswerRelevancyMetric,
)
from deepeval.metrics import BiasMetric
from deepeval.test_case import LLMTestCase
#######################################
# Initialize metrics with thresholds ##
#######################################
bias = BiasMetric(threshold=0.5)
contextual_precision = RAGASContextualPrecisionMetric(threshold=0.5)
contextual_recall = RAGASContextualRecallMetric(threshold=0.5)
answer_relevancy = RAGASAnswerRelevancyMetric(threshold=0.5)
faithfulness = RAGASFaithfulnessMetric(threshold=0.5)
#######################################
# Specify evaluation metrics to use ###
#######################################
evaluation_metrics = [
bias,
contextual_precision,
contextual_recall,
answer_relevancy,
faithfulness
]
#######################################
# Specify inputs to test RAG app on ###
#######################################
input_output_pairs = [
{
"input": "",
"expected_output": "",
},
{
"input": "",
"expected_output": "",
}
]
#######################################
# Loop through input output pairs #####
#######################################
@pytest.mark.parametrize(
"input_output_pair",
input_output_pairs,
)
def test_llamaindex(input_output_pair: Dict):
input = input_output_pair.get("input", None)
expected_output = input_output_pair.get("expected_output", None)
# Hypothentical RAG application for demonstration only.
# Replace this with your own RAG implementation.
# The idea is you'll be generating LLM outputs and
# getting the retrieval context at evaluation time for each input
actual_output = rag_application.query(input)
retrieval_context = rag_application.get_retrieval_context()
test_case = LLMTestCase(
input=input,
actual_output=actual_output,
retrieval_context=retrieval_context,
expected_output=expected_output
)
# assert test case
assert_test(test_case, evaluation_metrics)
最后,通过命令行界面(CLI)执行测试文件:
deepeval test run test_rag.py
需要注意以下几点:
- 默认情况下,大多数指标使用 OpenAI 的 GPT 模型进行评测,因此请记得将你的 OpenAI API 密钥设置为环境变量。
- 你能够定义通过阈值,并为每个指标指定希望使用的评测模型。
- 当且仅当所有评测指标均通过时,测试用例才算通过。
- 你可以包含任意数量的指标
- 你可以从 CSV/JSON 文件导入输入和预期输出对,以使代码更加整洁。
- 我们使用了 Pytest 装饰器(@pytest.mark.parametrize)在执行单元测试时遍历输入输出对。
- 实际输出与检索上下文是动态生成的。在上述示例中,我们使用了一个假设的 RAG 实现,你需要将其替换为你自己的 RAG 应用。
在 CI/CD 流水线中进行 RAG 单元测试
好消息是,实际上你已经完成了 99%的艰巨工作。现在剩下的就是在你的 CI/CD 环境中加入 deepeval test run
命令。以 GitHub Actions 为例,以下是如何将 DeepEval 添加到 GitHub 工作流 YAML 文件中的示例:
name: RAG Deployment Evaluations
on:
push:
jobs:
test:
runs-on: ubuntu-latest
steps:
# Some extra steps to setup and install dependencies
...
# Optional Login
- name: Login to Confident
env:
CONFIDENT_API_KEY: ${{ secrets.CONFIDENT_API_KEY }}
run: poetry run deepeval login --confident-api-key "$CONFIDENT_API_KEY"
- name: Run deepeval tests
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: poetry run deepeval test run test_rag.py
请注意,你的工作流文件不必与上面展示的示例完全相同,但你已经理解了要点——只要你设置了正确的环境变量并在 CI/CD 环境中包含了 deepeval 测试运行命令,就可以顺利进行了。(在此示例中,我们使用了 poetry 进行安装,可选择登录 Confident AI 以跟踪所有预部署评测,并使用 GitHub secrets 存储和访问我们的 Confident 及 OpenAI API 密钥,但这并非严格要求。)
恭喜!你现在正式在 CI/CD 流水线中对 RAG 应用进行单元测试了!还记得本文开头的图表吗?现在它变成了这样:
另一种数学表示
结论
现有的 RAG 评测指标,如 RAGAs,虽能出色评测通用检索-生成模型的性能,但在针对特定应用场景时往往力有不逮。此外,评测不仅是一种健全性检查,更是防止破坏性变更的重要保障,特别是在协作开发环境中。因此,将评测纳入 CI/CD 流水线对于任何认真开发 RAG 应用的组织都至关重要。
若需构建定制化评估指标以解决通用RAG评测体系的局限性,并寻求符合生产环境要求、可集成至CI/CD管线的测试框架,DeepEval是经过工业验证的优选方案。我们已完成所有底层技术攻坚,其核心能力包括:内置14项以上评测指标、支持并行测试执行,并深度集成Confident AI——全球首个面向大语言模型的开源评测基础设施。