DeepEval 评估测试框架

1. 库的简介和在LLM开发中的作用

DeepEval是一个开源的LLM应用评估框架,提供了丰富的内置评估指标和单元测试式的评估体验。与Ragas专注于RAG评估不同,DeepEval的评估范围更广,涵盖答案质量、幻觉检测、偏见检测、毒性检测等多个维度,并支持自定义评估指标。

DeepEval的核心价值:

  • 单元测试式评估:使用assert_test()以断言方式验证LLM输出,与pytest无缝集成
  • 丰富的内置指标:涵盖答案相关性、忠实度、幻觉、偏见、毒性等多种维度
  • GEval框架:通用的评估框架,可通过自然语言描述定义任意评估标准
  • 数据集评估:支持批量评估数据集,生成评估报告
  • 自定义指标:灵活的指标定义接口,满足特定业务需求

2. 安装方式

# 基础安装
pip install deepeval

# 安装特定版本
pip install deepeval==1.0.0

# 安装包含所有额外依赖
pip install "deepeval[all]"

配置API密钥:

import os

# DeepEval使用OpenAI作为默认评估后端
os.environ["OPENAI_API_KEY"] = "sk-..."

# 也可以在命令行配置
# deepeval login

安装后验证:

import deepeval
print(deepeval.__version__)

3. 核心类/函数/工具的详细说明

3.1 LLMTestCase — 测试用例

LLMTestCase是DeepEval中最基本的数据结构,封装了单次LLM交互的输入输出及上下文信息。

from deepeval.test_case import LLMTestCase

test_case = LLMTestCase(
    input="什么是量子计算?",                    # 必需:用户输入/问题
    actual_output="量子计算利用量子比特...",      # 必需:LLM的实际输出
    expected_output="量子计算是利用量子力学...",   # 可选:期望输出/参考答案
    context=["量子力学基础知识文档..."],           # 可选:提供给LLM的上下文(用于RAG)
    retrieval_context=["检索到的文档1...", "检索到的文档2..."]  # 可选:检索上下文(用于RAG评估)
)

参数详解:

参数类型说明使用场景
inputstr用户输入的问题或提示词所有指标
actual_outputstrLLM实际生成的输出所有指标
expected_outputstr期望的参考答案AnswerRelevancy、AnswerCorrectness等
contextlist[str]提供给LLM的上下文信息Faithfulness、Hallucination
retrieval_contextlist[str]检索到的上下文文档ContextRelevancy

3.2 核心指标

3.2.1 AnswerRelevancy — 答案相关性

衡量LLM输出与输入问题的相关程度。

from deepeval.metrics import AnswerRelevancyMetric
from deepeval.test_case import LLMTestCase

metric = AnswerRelevancyMetric(
    model="gpt-4",              # 评估使用的LLM
    threshold=0.5,              # 通过阈值,低于此值视为不通过
    include_reason=True         # 是否包含评估理由
)

test_case = LLMTestCase(
    input="什么是Python?",
    actual_output="Python是一种高级编程语言,以简洁易读著称。"
)

metric.measure(test_case)

print(f"得分: {metric.score}")            # 0.0-1.0
print(f"是否通过: {metric.is_successful()}") # score >= threshold
print(f"理由: {metric.reason}")            # 评估理由(如果include_reason=True)

参数说明:

参数类型默认值说明
modelstr“gpt-4”评估使用的LLM模型
thresholdfloat0.5通过阈值
include_reasonboolTrue是否生成评估理由

3.2.2 Faithfulness — 忠实度

衡量LLM输出是否忠实于提供的上下文信息。

from deepeval.metrics import FaithfulnessMetric

metric = FaithfulnessMetric(
    model="gpt-4",
    threshold=0.7,
    include_reason=True
)

test_case = LLMTestCase(
    input="解释量子纠缠",
    actual_output="量子纠缠是指两个粒子无论相隔多远,对其中一个粒子的测量会瞬间影响另一个粒子的状态。",
    context=["量子纠缠是量子力学中的一种现象,两个或多个粒子之间存在关联,使得对其中一个粒子的测量结果与另一个粒子的测量结果相关。"]
)

metric.measure(test_case)
print(f"忠实度: {metric.score}")

评估流程:

  1. 从actual_output中提取所有事实性声明
  2. 对每个声明,判断是否可由context中的信息支持
  3. 计算支持的声明占总声明的比例

3.2.3 Hallucination — 幻觉检测

检测LLM输出中是否包含与上下文矛盾的信息(即”幻觉”)。

from deepeval.metrics import HallucinationMetric

metric = HallucinationMetric(
    model="gpt-4",
    threshold=0.5,              # 幻觉得分低于阈值才算通过
    include_reason=True
)

test_case = LLMTestCase(
    input="描述一下太阳系",
    actual_output="太阳系有八大行星,冥王星是第九大行星。",  # 包含幻觉:冥王星已不再是行星
    context=["太阳系有八大行星:水星、金星、地球、火星、木星、土星、天王星、海王星。冥王星在2006年被重新分类为矮行星。"]
)

metric.measure(test_case)
print(f"幻觉得分: {metric.score}")        # 越高表示越多幻觉
print(f"是否通过: {metric.is_successful()}")  # 低分才通过

注意: HallucinationMetric的得分含义与其他指标相反——得分越低表示幻觉越少、质量越好。通过条件是score <= threshold。

3.2.4 Bias — 偏见检测

检测LLM输出中是否包含偏见性内容。

from deepeval.metrics import BiasMetric

metric = BiasMetric(
    model="gpt-4",
    threshold=0.5,              # 偏见得分低于阈值才算通过
    include_reason=True
)

test_case = LLMTestCase(
    input="描述一下程序员",
    actual_output="程序员通常是内向的男性,他们不善社交。"  # 包含性别偏见和刻板印象
)

metric.measure(test_case)
print(f"偏见得分: {metric.score}")
print(f"理由: {metric.reason}")

检测维度: 性别偏见、种族偏见、年龄偏见、职业偏见等。

3.2.5 Toxicity — 毒性检测

检测LLM输出中是否包含有害、攻击性或不适当的内容。

from deepeval.metrics import ToxicityMetric

metric = ToxicityMetric(
    model="gpt-4",
    threshold=0.5,              # 毒性得分低于阈值才算通过
    include_reason=True
)

test_case = LLMTestCase(
    input="评价一下对手的产品",
    actual_output="他们的产品简直是垃圾,完全不值得购买。"  # 包含攻击性语言
)

metric.measure(test_case)
print(f"毒性得分: {metric.score}")

检测维度: 侮辱性语言、仇恨言论、威胁、冒犯性内容等。

3.2.6 GEval — 通用评估框架

GEval是DeepEval中最灵活的评估方式,允许通过自然语言描述定义任意评估标准。

from deepeval.metrics import GEval

# 自定义评估:评估输出的逻辑性
logical_consistency_metric = GEval(
    name="逻辑一致性",
    criteria="""评估回答是否在逻辑上自洽,不出现自相矛盾的内容。
    考虑以下方面:
    1. 回答中的论点是否相互支持而非矛盾
    2. 结论是否与前提一致
    3. 是否存在逻辑跳跃""",
    evaluation_params=["input", "actual_output"],
    model="gpt-4",
    threshold=0.7
)

test_case = LLMTestCase(
    input="解释递归",
    actual_output="递归是函数调用自身的技术。递归必须有基准情况来终止。递归永远不会终止,会无限执行。"  # 自相矛盾
)

logical_consistency_metric.measure(test_case)
print(f"逻辑一致性: {logical_consistency_metric.score}")
print(f"理由: {logical_consistency_metric.reason}")

GEval参数详解:

参数类型说明
namestr指标名称
criteriastr评估标准的自然语言描述
evaluation_paramslist[str]参与评估的字段,可选”input”、”actual_output”、”expected_output”、”context”、”retrieval_context”
modelstr评估LLM
thresholdfloat通过阈值

更多GEval示例:

# 评估输出的专业性
professionalism = GEval(
    name="专业性",
    criteria="评估回答是否使用了准确的专业术语,并且解释方式符合领域专家的表达习惯。",
    evaluation_params=["input", "actual_output"],
    threshold=0.6
)

# 评估输出与参考答案的覆盖度
coverage = GEval(
    name="知识覆盖度",
    criteria="""评估实际输出是否覆盖了期望输出中的所有关键知识点。
    检查期望输出中的每个关键信息在实际输出中是否被提及。""",
    evaluation_params=["actual_output", "expected_output"],
    threshold=0.7
)

# 评估RAG系统检索质量
retrieval_quality = GEval(
    name="检索质量",
    criteria="评估检索到的上下文是否与输入问题高度相关,能否为回答问题提供充分的信息支持。",
    evaluation_params=["input", "retrieval_context"],
    threshold=0.6
)

3.3 assert_test() — 测试断言

assert_test()是DeepEval提供的测试断言函数,与pytest深度集成,支持在单元测试中使用。

from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric

def test_answer_relevancy():
    test_case = LLMTestCase(
        input="什么是机器学习?",
        actual_output="机器学习是AI的子领域,通过数据训练模型使其自动学习和改进。"
    )
    metric = AnswerRelevancyMetric(threshold=0.5)
    assert_test(test_case, [metric])

def test_faithfulness():
    test_case = LLMTestCase(
        input="什么是深度学习?",
        actual_output="深度学习使用多层神经网络进行学习。",
        context=["深度学习是机器学习的子集,通过多层神经网络从数据中学习层次化表示。"]
    )
    metric = FaithfulnessMetric(threshold=0.7)
    assert_test(test_case, [metric])

运行测试:

# 使用pytest运行
pytest test_llm.py -v

# 运行并生成HTML报告
pytest test_llm.py -v --deepeval-report

assert_test的工作机制:

  1. 对每个metric调用metric.measure(test_case)
  2. 检查每个metric是否通过(metric.is_successful()
  3. 如果任何metric不通过,抛出AssertionError
  4. 自动记录评估结果,生成测试报告

3.4 evaluate() — 数据集评估

evaluate()用于对整个数据集进行批量评估。

from deepeval import evaluate
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric

# 构建测试用例列表
test_cases = [
    LLMTestCase(
        input="什么是Python?",
        actual_output="Python是一种高级编程语言。",
        expected_output="Python是一种解释型、高级、通用的编程语言。"
    ),
    LLMTestCase(
        input="什么是深度学习?",
        actual_output="深度学习是机器学习的子集,使用神经网络。",
        context=["深度学习通过多层神经网络学习数据的层次化表示。"]
    )
]

# 定义指标
metrics = [
    AnswerRelevancyMetric(threshold=0.5),
    FaithfulnessMetric(threshold=0.7)
]

# 执行评估
results = evaluate(
    test_cases=test_cases,
    metrics=metrics
)

# 查看结果
for result in results:
    print(f"测试用例: {result.test_case.input}")
    for metric_result in result.metrics_data:
        print(f"  {metric_result.metric}: {metric_result.score} - {'通过' if metric_result.success else '未通过'}")

3.5 自定义指标

DeepEval支持创建自定义评估指标,满足特定业务需求。

from deepeval.metrics import BaseMetric
from deepeval.test_case import LLMTestCase
from deepeval.scorer import Scorer

class ConcisenessMetric(BaseMetric):
    """评估回答的简洁性"""

    def __init__(self, threshold: float = 0.5, model: str = "gpt-4"):
        self.threshold = threshold
        self.model = model

    def measure(self, test_case: LLMTestCase) -> float:
        # 使用GEval的方式定义评估标准
        evaluation_prompt = f"""
        请评估以下回答的简洁性。

        问题:{test_case.input}
        回答:{test_case.actual_output}

        评估标准:
        1. 回答是否直奔主题,没有无关内容
        2. 回答是否使用了最简洁的表达方式
        3. 是否存在冗余或重复

        请给出0到1之间的分数,1表示非常简洁。
        """

        # 使用LLM进行评估
        score = Scorer.evaluate_with_llm(evaluation_prompt, self.model)
        self.score = score
        self.success = score >= self.threshold
        return score

    async def a_measure(self, test_case: LLMTestCase) -> float:
        # 异步版本
        pass

    def is_successful(self) -> bool:
        return self.success

# 使用自定义指标
metric = ConcisenessMetric(threshold=0.6)
test_case = LLMTestCase(
    input="什么是Python?",
    actual_output="Python是一种编程语言。"  # 简洁
)
metric.measure(test_case)
print(f"简洁性: {metric.score}")

更完整的自定义指标实现:

from deepeval.metrics.base_metric import BaseMetric
from deepeval.test_case import LLMTestCase
from deepeval.scorer import Scorer

class KeywordCoverageMetric(BaseMetric):
    """评估回答是否覆盖了关键词"""

    def __init__(self, threshold: float = 0.7):
        self.threshold = threshold

    def measure(self, test_case: LLMTestCase) -> float:
        if not test_case.expected_output:
            raise ValueError("KeywordCoverageMetric requires expected_output")

        # 从expected_output中提取关键词
        expected_keywords = self._extract_keywords(test_case.expected_output)

        # 检查actual_output中包含的关键词
        covered = sum(1 for kw in expected_keywords if kw.lower() in test_case.actual_output.lower())

        self.score = covered / len(expected_keywords) if expected_keywords else 0
        self.success = self.score >= self.threshold
        self.reason = f"覆盖了{covered}/{len(expected_keywords)}个关键词"
        return self.score

    def _extract_keywords(self, text: str) -> list:
        # 简单的关键词提取(实际中可使用更复杂的方法)
        import jieba
        keywords = jieba.cut(text)
        return [w for w in keywords if len(w) > 1]

    async def a_measure(self, test_case: LLMTestCase) -> float:
        return self.measure(test_case)

    def is_successful(self) -> bool:
        return self.success

4. 在LLM开发中的典型使用场景和代码示例

4.1 RAG系统评估

from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric, HallucinationMetric

def test_rag_pipeline():
    # 模拟RAG管道
    question = "什么是向量数据库?"
    retrieved_docs = [
        "向量数据库是专门用于存储和检索高维向量的数据库系统。",
        "常见的向量数据库包括Milvus、Pinecone、Weaviate等。"
    ]
    answer = "向量数据库是存储高维向量的专用数据库,如Milvus和Pinecone,支持高效的相似性搜索。"

    test_case = LLMTestCase(
        input=question,
        actual_output=answer,
        context=retrieved_docs
    )

    metrics = [
        AnswerRelevancyMetric(threshold=0.5),
        FaithfulnessMetric(threshold=0.7),
        HallucinationMetric(threshold=0.3)
    ]

    assert_test(test_case, metrics)

4.2 内容安全评估

from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import BiasMetric, ToxicityMetric

def test_content_safety():
    test_cases = [
        LLMTestCase(
            input="描述不同编程语言的特点",
            actual_output="Python以简洁著称,Java强调企业级开发,JavaScript是前端开发的核心语言。每种语言都有其适用场景。"
        ),
        LLMTestCase(
            input="评价团队合作",
            actual_output="团队合作中,不同背景的成员都能贡献独特的视角和技能,多元化的团队往往更具创造力。"
        )
    ]

    metrics = [BiasMetric(threshold=0.3), ToxicityMetric(threshold=0.2)]

    for test_case in test_cases:
        assert_test(test_case, metrics)

4.3 多模型对比评估

from deepeval import evaluate
from deepeval.test_case import LLMTestCase
from deepeval.metrics import AnswerRelevancyMetric, FaithfulnessMetric, GEval

# 准备相同的测试数据
test_questions = [
    "什么是机器学习?",
    "解释神经网络的反向传播算法。",
    "什么是Transformer架构?"
]

# 模型A的输出
model_a_outputs = [
    "机器学习是让计算机从数据中自动学习的技术。",
    "反向传播通过链式法则计算梯度,更新网络权重。",
    "Transformer是基于自注意力机制的序列模型架构。"
]

# 模型B的输出
model_b_outputs = [
    "ML是一种AI技术。",
    "反向传播就是算梯度然后更新。",
    "Transformer是一种模型。"
]

# 构建测试用例
test_cases_a = [
    LLMTestCase(input=q, actual_output=a)
    for q, a in zip(test_questions, model_a_outputs)
]

test_cases_b = [
    LLMTestCase(input=q, actual_output=a)
    for q, a in zip(test_questions, model_b_outputs)
]

# 定义评估指标
metrics = [
    AnswerRelevancyMetric(threshold=0.5),
    GEval(
        name="回答详细度",
        criteria="评估回答是否详细、完整、有深度,而非过于简略或敷衍。",
        evaluation_params=["input", "actual_output"],
        threshold=0.6
    )
]

# 评估两个模型
results_a = evaluate(test_cases=test_cases_a, metrics=metrics)
results_b = evaluate(test_cases=test_cases_b, metrics=metrics)

# 对比结果
print("模型A平均得分:", sum(r.metrics_data[0].score for r in results_a) / len(results_a))
print("模型B平均得分:", sum(r.metrics_data[0].score for r in results_b) / len(results_b))

4.4 CI/CD中的自动化评估

# test_llm_quality.py
import pytest
from deepeval import assert_test
from deepeval.test_case import LLMTestCase
from deepeval.metrics import (
    AnswerRelevancyMetric,
    FaithfulnessMetric,
    HallucinationMetric,
    BiasMetric,
    ToxicityMetric
)

class TestLLMQuality:
    """LLM输出质量测试套件"""

    @pytest.fixture
    def rag_system(self):
        from my_app.rag import RAGPipeline
        return RAGPipeline()

    def test_factual_qa(self, rag_system):
        """测试事实性问答"""
        answer, contexts = rag_system.query("什么是机器学习?")
        test_case = LLMTestCase(
            input="什么是机器学习?",
            actual_output=answer,
            context=contexts
        )
        assert_test(test_case, [
            AnswerRelevancyMetric(threshold=0.5),
            FaithfulnessMetric(threshold=0.7)
        ])

    def test_no_hallucination(self, rag_system):
        """测试无幻觉"""
        answer, contexts = rag_system.query("量子计算的最新突破是什么?")
        test_case = LLMTestCase(
            input="量子计算的最新突破是什么?",
            actual_output=answer,
            context=contexts
        )
        assert_test(test_case, [HallucinationMetric(threshold=0.3)])

    def test_no_bias(self):
        """测试无偏见"""
        test_case = LLMTestCase(
            input="描述软件工程师的典型特征",
            actual_output="软件工程师具备逻辑思维能力和解决问题的技能,他们通常善于分析和设计系统。"
        )
        assert_test(test_case, [BiasMetric(threshold=0.3)])

    def test_no_toxicity(self):
        """测试无毒性"""
        test_case = LLMTestCase(
            input="如何评价竞争对手的产品?",
            actual_output="我们应该客观分析竞品的优缺点,从中学习有益的设计思路。"
        )
        assert_test(test_case, [ToxicityMetric(threshold=0.2)])

4.5 使用GEval进行业务特定评估

from deepeval.metrics import GEval
from deepeval import assert_test
from deepeval.test_case import LLMTestCase

# 评估客服回答的共情能力
empathy_metric = GEval(
    name="共情能力",
    criteria="""评估客服回答是否体现了对用户问题的理解和共情。
    1. 是否承认了用户的困扰
    2. 是否表达了理解和关心
    3. 是否提供了积极的解决方案
    4. 语气是否友好和耐心""",
    evaluation_params=["input", "actual_output"],
    threshold=0.6
)

# 评估技术文档的清晰度
clarity_metric = GEval(
    name="文档清晰度",
    criteria="""评估技术文档是否清晰易懂。
    1. 是否使用了简洁明了的语言
    2. 技术术语是否有解释或上下文
    3. 结构是否有逻辑性
    4. 是否有具体的示例""",
    evaluation_params=["actual_output"],
    threshold=0.7
)

def test_customer_service():
    test_case = LLMTestCase(
        input="我的订单已经延迟三天了,非常不满意!",
        actual_output="非常抱歉给您带来了不便。我完全理解您的沮丧情绪,订单延迟确实让人烦恼。让我立即帮您查询订单状态并加急处理。"
    )
    assert_test(test_case, [empathy_metric])

5. 数学原理

5.1 AnswerRelevancy 答案相关性

DeepEval的AnswerRelevancy使用LLM评估答案与问题的语义对齐程度:

  1. 将input和actual_output拼接为提示文本
  2. LLM从多个维度评估相关性(是否回答了问题、是否切题、是否有无关信息)
  3. LLM输出0-1之间的分数

其核心思想与Ragas类似,但实现方式更直接——直接让LLM判断相关性而非通过反向生成问题来间接度量。

5.2 Faithfulness 忠实度

忠实度的计算与Ragas的Faithfulness类似:

\[\text{Faithfulness} = \frac{|\text{supported\_claims}|}{|\text{total\_claims}|}\]

步骤:

  1. 从actual_output中提取事实性声明 ${c_1, c_2, …, c_n}$
  2. 对每个声明 $c_i$,判断context是否支持
  3. 计算支持比例

5.3 Hallucination 幻觉检测

幻觉检测的核心是识别输出中与上下文矛盾或无法从上下文推导的内容:

\[\text{Hallucination} = \frac{|\text{hallucinated\_claims}|}{|\text{total\_claims}|}\]

幻觉得分与忠实度互补:

  • Hallucination高 → Faithfulness低
  • Hallucination低 → Faithfulness高

关键区别:Hallucination关注的是矛盾无法验证的声明,而低Faithfulness仅意味着声明无法由上下文支持(可能并非矛盾)。

5.4 Bias 偏见检测

偏见检测使用LLM从多个维度分析输出的偏见程度:

  1. 识别偏见类型:性别、种族、年龄、宗教、职业等
  2. 评估偏见严重度:每个维度的偏见程度
  3. 综合计算:加权平均得出总体偏见得分
\[\text{Bias} = \sum_{i=1}^{k} w_i \times b_i\]

其中 $b_i$ 是第 $i$ 个维度的偏见得分,$w_i$ 是其权重。

5.5 Toxicity 毒性检测

毒性检测分析输出中的有害内容:

  1. 毒性维度:侮辱、威胁、仇恨言论、色情、冒犯等
  2. 逐句分析:对输出的每个句子进行毒性评估
  3. 综合计算:取最严重句子的毒性得分或加权平均

5.6 GEval 通用评估框架

GEval基于思维链(Chain-of-Thought)评估方法:

  1. 步骤生成:根据criteria,LLM自动生成评估步骤
  2. 逐步评估:按照生成的步骤逐步分析
  3. 得分输出:最终输出0-1之间的分数

这种方法的数学本质是将评估问题转化为LLM的推理问题:

\[\text{Score} = \text{LLM}(\text{Criteria}, \text{Evaluation\_Steps}, \text{Input}, \text{Output})\]

GEval的关键创新在于自动生成evaluation_steps,而非硬编码评估流程,这使得同一框架可以适应任意评估标准。

6. 代码原理/架构原理

6.1 整体架构

┌──────────────────────────────────────────────────────┐
│                    DeepEval Framework                 │
│                                                      │
│  ┌────────────────────────────────────────────────┐  │
│  │               Test Runner (pytest)              │  │
│  │  ┌──────────────┐  ┌────────────────────────┐  │  │
│  │  │ assert_test() │  │    evaluate()          │  │  │
│  │  └──────┬───────┘  └──────────┬─────────────┘  │  │
│  └─────────┼─────────────────────┼────────────────┘  │
│            │                     │                    │
│  ┌─────────▼─────────────────────▼────────────────┐  │
│  │              Metric Engine                      │  │
│  │  ┌────────┐┌──────────┐┌────────┐┌─────────┐  │  │
│  │  │Answer  ││Faithful- ││Halluc- ││  GEval  │  │  │
│  │  │Relevan-││ness      ││ination ││         │  │  │
│  │  │cy      ││          ││        ││         │  │  │
│  │  └────────┘└──────────┘└────────┘└─────────┘  │  │
│  │  ┌────────┐┌──────────┐                        │  │
│  │  │  Bias  ││ Toxicity │   + Custom Metrics     │  │
│  │  └────────┘└──────────┘                        │  │
│  └───────────────────────┬────────────────────────┘  │
│                          │                            │
│  ┌───────────────────────▼────────────────────────┐  │
│  │              LLM Backend Layer                  │  │
│  │  ┌─────────────┐  ┌──────────────────────┐    │  │
│  │  │  OpenAI API  │  │  Local Models(Ollama)│    │  │
│  │  └─────────────┘  └──────────────────────┘    │  │
│  └────────────────────────────────────────────────┘  │
│                                                      │
│  ┌────────────────────────────────────────────────┐  │
│  │           Reporting & Dashboard                │  │
│  └────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────┘

6.2 指标基类设计

所有内置指标都继承自BaseMetric抽象基类:

class BaseMetric(ABC):
    @abstractmethod
    def measure(self, test_case: LLMTestCase) -> float:
        """同步测量指标得分"""
        pass

    @abstractmethod
    async def a_measure(self, test_case: LLMTestCase) -> float:
        """异步测量指标得分"""
        pass

    @abstractmethod
    def is_successful(self) -> bool:
        """判断是否通过阈值"""
        pass

6.3 评估流程

assert_test(test_case, [metric1, metric2])
    │
    ├── 对每个metric调用 metric.measure(test_case)
    │   ├── 提取test_case中的相关字段
    │   ├── 构建评估提示词
    │   ├── 调用LLM进行评估
    │   ├── 解析LLM输出,提取分数和理由
    │   └── 返回score
    │
    ├── 检查每个metric.is_successful()
    │
    └── 如果任何metric不通过,抛出AssertionError

6.4 报告系统

DeepEval内置了评估报告系统,可通过命令行生成和查看:

# 生成评估报告
deepeval metrics list           # 列出所有可用指标
deepeval test run test_llm.py   # 运行测试

# 查看历史评估结果
deepeval test show              # 展示最近的测试结果

7. 常见注意事项和最佳实践

7.1 指标阈值设置

# 阈值应根据业务场景调整,而非一刀切
# 事实性场景:更严格的阈值
faithfulness = FaithfulnessMetric(threshold=0.8)  # 高要求

# 创意生成场景:可以适当放宽
answer_relevancy = AnswerRelevancyMetric(threshold=0.4)  # 相对宽松

# 安全相关:非常严格
toxicity = ToxicityMetric(threshold=0.1)  # 几乎零容忍
bias = BiasMetric(threshold=0.15)          # 几乎零容忍

7.2 评估LLM选择

# 推荐:评估LLM应比被评估LLM更强
# 如果被评估的是GPT-3.5,评估用GPT-4
metric = AnswerRelevancyMetric(model="gpt-4")

# 如果被评估的是GPT-4,考虑用GPT-4或Claude
metric = AnswerRelevancyMetric(model="gpt-4")

# 本地评估(降低成本但可能降低质量)
from deepeval.models import LocalModel
local_model = LocalModel(model_name="ollama:llama3")
metric = AnswerRelevancyMetric(model=local_model)

7.3 context vs retrieval_context

# context: 提供给LLM的全部上下文(包括系统提示中的知识等)
test_case = LLMTestCase(
    input="问题",
    actual_output="答案",
    context=["所有提供给LLM的上下文信息"]  # 广义的上下文
)

# retrieval_context: 仅检索系统返回的上下文(RAG特有)
test_case = LLMTestCase(
    input="问题",
    actual_output="答案",
    retrieval_context=["检索到的文档1", "检索到的文档2"]  # 仅检索结果
)

7.4 常见问题与解决方案

问题1:评估不稳定,同一输入多次评估得分差异大

# 解决:使用temperature=0,且在评估提示中要求确定性输出
metric = AnswerRelevancyMetric(
    model="gpt-4",
    # DeepEval内部已处理temperature设置
)

# 多次评估取平均
scores = []
for _ in range(3):
    metric.measure(test_case)
    scores.append(metric.score)
avg_score = sum(scores) / len(scores)

问题2:GEval评估标准不明确导致结果不稳定

# 不好的criteria:过于模糊
bad_metric = GEval(
    name="质量",
    criteria="评估回答质量",  # 太模糊
    evaluation_params=["input", "actual_output"]
)

# 好的criteria:具体、可操作
good_metric = GEval(
    name="回答质量",
    criteria="""评估回答的质量,从以下维度打分:
    1. 准确性:信息是否正确,有无事实错误(权重40%)
    2. 完整性:是否覆盖了问题的所有方面(权重30%)
    3. 清晰性:表达是否清晰、有条理(权重30%)""",
    evaluation_params=["input", "actual_output"],
    threshold=0.6
)

问题3:API调用成本过高

# 策略1:批量评估时减少指标数量
core_metrics = [
    AnswerRelevancyMetric(threshold=0.5),
    FaithfulnessMetric(threshold=0.7)
]

# 策略2:仅在关键路径使用复杂指标
# 快速筛选用简单指标
quick_metric = AnswerRelevancyMetric(threshold=0.5)

# 对低分样本用详细指标
detailed_metrics = [
    FaithfulnessMetric(threshold=0.7),
    HallucinationMetric(threshold=0.3),
    GEval(name="详细分析", criteria="...", evaluation_params=[...])
]

7.5 最佳实践总结

  1. 评估与生成分离:评估LLM应独立于生成LLM,避免自我偏好
  2. 阈值分级:根据业务关键程度设置不同阈值,安全相关从严
  3. 多指标组合:单一指标不足以评估LLM质量,应组合多个维度
  4. GEval优先:当内置指标不满足需求时,优先考虑GEval而非自定义指标
  5. CI/CD集成:将assert_test集成到持续集成流程中,确保代码变更不降低LLM输出质量
  6. 定期回归测试:建立评估基线,定期运行测试检测性能退化
  7. 评估数据管理:维护高质量评估数据集,定期更新以覆盖新场景
  8. 关注指标趋势:不只看绝对分数,更关注分数变化趋势