RAG 评估:CICD 中单元测试 RAG 的权威指南

2025-08-12   出处: Confident AI  作/译者:Jeffrey Ip/Yilia

检索增强生成(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——全球首个面向大语言模型的开源评测基础设施。


声明:本文为本站编辑转载,文章版权归原作者所有。文章内容为作者个人观点,本站只提供转载参考(依行业惯例严格标明出处和作译者),目的在于传递更多专业信息,普惠测试相关从业者,开源分享,推动行业交流和进步。 如涉及作品内容、版权和其它问题,请原作者及时与本站联系(QQ:1017718740),我们将第一时间进行处理。本站拥有对此声明的最终解释权!欢迎大家通过新浪微博(@测试窝)或微信公众号(测试窝)关注我们,与我们的编辑和其他窝友交流。
/98 人阅读/0 条评论 发表评论

登录 后发表评论
最新文章