欢迎光临
我们一直在努力

LLM 应用的Evals 工程实践:从手动测试到自动化回归测试体系

LLM 应用的 Evals 工程实践:从手动测试到自动化回归测试体系

改了一个 prompt,结果三天后才发现客服机器人的退款回答全错了——这是 LLM 应用特有的故障模式。本文讲透 Evals 的底层逻辑,从第一个测试用例写到 CI 集成,附真实可运行代码和 2026 年主流工具横评。


背景:一个价值 27 亿美元的行业教训

2026 年,LLM 可观测性市场规模已经达到 27 亿美元,预计到 2030 年将突破 92 亿美元,年复合增长率 36.2%(数据来源:The Business Research Company)。Gartner 预测,到 2028 年,50% 的 GenAI 生产部署将包含专门的 LLM 可观测性投入——而 2026 年初这一比例只有 15%。

这不是因为大家有钱没地方花。这是因为足够多的团队踩了足够深的坑,才推动了整个市场的诞生。

最核心的坑,叫做软故障(Soft Failure)。


为什么你的 LLM 应用没有测试

上线一个传统 API,出错了马上报 500;上线一个 LLM 应用,出错了……它还是 200,还是把答案返回给用户了,只不过那个答案是错的。

这是 LLM 应用最危险的特性:软故障。模型不会抛异常,它只是悄悄地给出一个自信、流畅、错误的回答。

举两个真实案例(均来自公开的工程博客):

案例一:一个客服机器人团队对系统提示做了一次措辞优化,让回答更友好。三天后才发现,退款时间从"3-5 个工作日"变成了"1-2 个工作日"——模型在更友好的语气里顺手改了事实,没有任何告警触发,因为系统根本没有针对输出内容的测试。

案例二:一个 RAG 问答系统更换了 embedding 模型以降低成本,检索准确率下降了 18%,但延迟和错误率监控全部正常,问题在用户投诉率上升后两周才被发现。

LLM 应用的测试盲区,不是工程师懒,是测试范式本身还没有迁移过来。


为什么传统单元测试在这里失效

传统单元测试的核心假设是:给定输入,输出是确定的。

# 传统测试:这样不行
def test_refund_answer():
result = chatbot.respond("退款需要多久?")
assert result == "退款通常需要 3-5 个工作日" # ❌ LLM 每次输出都不一样

LLM 的输出本质上是概率分布的采样。即使是同一个输入,温度不为零时每次输出都有细微差异。更重要的是,"正确"本身就是一个模糊概念:

  • “退款需要 3-5 个工作日” 和 “通常 3 到 5 个工作日内处理完成” 语义等价,但字符串匹配会失败
  • “退款大约需要一周” 在某些上下文里可以接受,在某些里不行

所以,LLM 应用的测试需要引入两个新概念:评分而不是断言,以及 LLM-as-judge。

评分 vs 断言的根本差异

传统单元测试基于断言(是/否):

# 断言:二元判断
assert result == "退款通常需要 3-5 个工作日" # 通过或失败,没有中间态

LLM Eval 基于评分(连续值):

# 评分:0 到 1 的连续值
score = relevancy_metric.evaluate(actual_output, question) # 0.85
assert score >= 0.75 # 与阈值比较

这个范式转变有深刻含义:你不再追求"完全正确",而是追求"足够好"。你可以接受 85% 的相关性,但不能接受 50%。这与产品质量的真实语义更吻合。

LLM-as-judge 的工作原理与校准

LLM-as-judge 是指用一个(通常更强的)LLM 来评判你的应用输出是否符合某个标准。这个想法听起来很循环——用 AI 评判 AI——但它经过了系统性验证:

  • Zheng et al. 2023 年 MT-Bench 论文中,GPT-4 作为 judge 与人类评分者的一致率达到 80% 以上,相比人类评审者之间的一致率(75%-85%)相当
  • 实践中,对于事实准确性、相关性、一致性等常见维度,LLM judge 的评分稳定性明显高于人工评分(人工评分存在疲劳、情绪、不同评审员标准不一致等干扰)
  • 成本优势:用 GPT-4o-mini 做 judge,评分 1000 条测试用例的成本约 $5-15,相比人工标注便宜 100 倍以上

当然 LLM-as-judge 也有已知局限性:

场景LLM judge 适用性建议替代方案
事实准确性、相关性 ✅ 高准确率 直接用
代码正确性 ⚠️ 中等 搭配代码执行验证
医疗/法律专业判断 ❌ 不可靠 必须领域专家人工校准
语言流畅性、语气 ✅ 高准确率 直接用
数学计算 ❌ 不可靠 程序验证

人工校准:建议每月随机抽取 50-100 条 case,同时做 LLM judge 评分和人工评分,计算 Spearman 相关系数。如果低于 0.75,说明你的 criteria prompt 需要优化或者这个维度不适合 LLM-as-judge。


Evals 的三层架构

一个成熟的 LLM 应用测试体系由三层构成:

┌────────────────────────────────────────────┐
│ 层3:在线监控(Online Evaluation) │
│ 生产流量采样 → 实时评分 → 质量漂移告警 │
├────────────────────────────────────────────┤
│ 层2:回归追踪(Regression Tracking) │
│ 每次变更跑 golden dataset → 版本间对比 │
├────────────────────────────────────────────┤
│ 层1:Golden Dataset + 离线 Eval │
│ 手工标注测试集 → 评分指标 → CI 集成 │
└────────────────────────────────────────────┘

大多数团队只需要先建好层1,就已经比 90% 的 LLM 应用强了。


为什么选 DeepEval 作为起点

在开始写代码之前,先说一下工具选择的逻辑。

目前 Python 生态里主要的 eval 框架选择:DeepEval(pytest 原生)、Promptfoo(YAML 配置,适合多语言栈)、RAGAS(专门针对 RAG)、自己基于 LLM API 手写脚本。

我推荐工程师团队从 DeepEval 开始,原因:

  • pytest 原生集成:你已经知道怎么写 pytest,DeepEval 只是加了新的 metric 断言方式,学习曲线最低
  • 50+ 内置指标开箱即用:覆盖了 RAG 评估、幻觉检测、Agent 工具调用准确性等主要场景,不需要从头实现评分逻辑
  • 完全开源,eval 跑在本地:数据不必上传云端,适合有数据合规要求的团队
  • 平滑升级路径:后面如果需要可视化仪表盘,可以切换到 Confident AI(同一个团队的商业产品)或者 Langfuse,均有官方 adapter
  • 重要提醒:DeepEval 的 judge model 默认调用 OpenAI API。如果你的环境有网络限制或希望使用国产模型做 judge,后面会专门展示如何配置。

    层1 实战:用 DeepEval 写第一个 Eval

    DeepEval 是目前 Python 生态里最成熟的离线 eval 框架,提供 50+ 内置指标,原生支持 pytest,对工程师友好。

    安装

    pip install deepeval

    构建 Golden Dataset

    Golden dataset 是你手工标注的"标准答案集"。每条数据包括:

    • input:用户输入
    • actual_output:你的应用实际输出(运行时生成)
    • expected_output:期望的正确回答
    • retrieval_context:RAG 场景下,检索到的上下文

    对于一个客服机器人,可以这样构建:

    # tests/golden_dataset.py
    GOLDEN_CASES = [
    {
    "input": "退款需要多久?",
    "expected_output": "退款通常需要 3-5 个工作日",
    "retrieval_context": ["退款政策:标准退款处理时间为 3-5 个工作日"]
    },
    {
    "input": "我的订单还没到,怎么查物流?",
    "expected_output": "您可以在订单详情页点击「查看物流」按钮查询实时状态",
    "retrieval_context": ["物流查询:进入【我的订单】→【订单详情】→【查看物流】"]
    },
    {
    "input": "支持哪些支付方式?",
    "expected_output": "支持微信支付、支付宝、银行卡和花呗",
    "retrieval_context": ["支付方式:微信支付、支付宝、银行卡、花呗"]
    },
    # … 至少 50 条,覆盖你的主要场景
    ]

    建议规模:刚起步时 50-100 条足够,能覆盖主要意图类别。超过 500 条才需要专门的数据管理工具。

    写 Eval 测试

    # tests/test_customer_support_eval.py
    import pytest
    from deepeval import assert_test
    from deepeval.metrics import (
    AnswerRelevancyMetric,
    FaithfulnessMetric,
    HallucinationMetric
    )
    from deepeval.test_case import LLMTestCase
    from your_app import chatbot
    from golden_dataset import GOLDEN_CASES

    @pytest.mark.parametrize("case", GOLDEN_CASES)
    def test_answer_relevancy(case):
    """测试回答是否与问题相关"""
    actual = chatbot.respond(case["input"])

    test_case = LLMTestCase(
    input=case["input"],
    actual_output=actual,
    expected_output=case["expected_output"],
    retrieval_context=case["retrieval_context"]
    )

    metric = AnswerRelevancyMetric(threshold=0.7)
    assert_test(test_case, [metric])

    @pytest.mark.parametrize("case", GOLDEN_CASES)
    def test_faithfulness(case):
    """测试回答是否与检索内容一致(不幻觉)"""
    actual = chatbot.respond(case["input"])

    test_case = LLMTestCase(
    input=case["input"],
    actual_output=actual,
    retrieval_context=case["retrieval_context"]
    )

    # Faithfulness: 回答中的事实主张是否都能在 retrieval_context 中找到依据
    metric = FaithfulnessMetric(threshold=0.8)
    assert_test(test_case, [metric])

    运行:

    deepeval test run tests/test_customer_support_eval.py

    输出示例:

    ====================================================
    Running 6 test cases…
    ====================================================

    Test case 1: 退款需要多久?
    ✓ AnswerRelevancyMetric (0.89 >= 0.7) PASSED
    ✓ FaithfulnessMetric (0.95 >= 0.8) PASSED

    Test case 2: 我的订单还没到,怎么查物流?
    ✓ AnswerRelevancyMetric (0.82 >= 0.7) PASSED
    ✗ FaithfulnessMetric (0.62 < 0.8) FAILED
    Reason: 实际输出提到了"客服电话",但检索上下文中未包含此信息

    ====================================================
    Test Results: 5 passed, 1 failed
    Overall Score: 0.817
    ====================================================

    这条 FaithfulnessMetric 失败的信息非常有价值——它明确告诉你应用在这条 case 上产生了幻觉,引用了不在上下文中的信息。你可以针对性地调整 RAG 检索策略或 prompt,而不是靠运气发现问题。

    用 G-Eval 做自定义评分

    当内置指标不够用时,可以用 G-Eval(LLM-as-judge)定义自己的评分标准:

    from deepeval.metrics import GEval
    from deepeval.test_case import LLMTestCaseParams

    # 自定义:检查回答是否包含正确的时间信息
    time_accuracy_metric = GEval(
    name="TimeAccuracy",
    criteria="""
    判断"实际输出"中关于时间的描述是否与"检索上下文"中的信息一致。
    如果实际输出没有提到时间,或者时间范围与上下文矛盾,评分应低于 0.5。
    """
    ,
    evaluation_params=[
    LLMTestCaseParams.INPUT,
    LLMTestCaseParams.ACTUAL_OUTPUT,
    LLMTestCaseParams.RETRIEVAL_CONTEXT
    ],
    threshold=0.8
    )

    G-Eval 的原理是:用一个更强的 LLM(通常是 GPT-4o 或 Claude)来评判你的应用输出,并给出 0-1 的分数。这比字符串匹配灵活,比人工评分省时。

    配置使用国产模型作为 judge(适合数据合规要求或网络受限场景):

    from deepeval.models import DeepEvalBaseLLM
    from openai import OpenAI

    class QwenJudge(DeepEvalBaseLLM):
    """使用通义千问作为 judge model"""

    def __init__(self):
    self.client = OpenAI(
    api_key="your_dashscope_api_key",
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1"
    )

    def load_model(self):
    return self.client

    def generate(self, prompt: str) > str:
    response = self.client.chat.completions.create(
    model="qwen-max",
    messages=[{"role": "user", "content": prompt}],
    temperature=0
    )
    return response.choices[0].message.content

    async def a_generate(self, prompt: str) > str:
    return self.generate(prompt)

    def get_model_name(self):
    return "qwen-max"

    # 注入自定义 judge model
    time_accuracy_metric = GEval(
    name="TimeAccuracy",
    criteria="判断实际输出中关于时间的描述是否与检索上下文中的信息一致",
    evaluation_params=[
    LLMTestCaseParams.ACTUAL_OUTPUT,
    LLMTestCaseParams.RETRIEVAL_CONTEXT
    ],
    model=QwenJudge(), # ← 使用国产模型
    threshold=0.8
    )

    同样的方式可以配置 DeepSeek、Kimi、GLM 等任何支持 OpenAI 兼容接口的模型。


    关键指标解析

    DeepEval 内置的指标覆盖了 LLM 应用最常见的评估维度:

    指标含义适用场景
    AnswerRelevancy 回答与问题的相关程度 所有 Q&A 场景
    Faithfulness 回答是否与检索上下文一致 RAG 应用(防幻觉)
    ContextualPrecision 检索结果中有用内容的比例 RAG 检索优化
    ContextualRecall 期望答案所需信息被检索到的比例 RAG 检索覆盖
    HallucinationMetric 回答中不在上下文中的事实主张 高风险事实性场景
    TaskCompletionMetric Agent 是否完成了用户目标 AI Agent
    ToolCorrectnessMetric Agent 是否调用了正确的工具 Function Calling Agent
    GEval 自定义 LLM-as-judge 评分 自定义业务标准

    深入理解各指标的计算方式

    用好 DeepEval,需要理解各指标在底层是怎么运作的,这样才能判断为什么某个 case 评分低、以及 threshold 设多少合理。

    AnswerRelevancy 的计算

    DeepEval 的 AnswerRelevancy 不是直接让 judge 打一个整体分,而是:

  • 从 actual_output 中提取所有"声明"(statements)
  • 对每个声明,判断是否与 input 相关
  • 相关声明占总声明数的比例就是得分
  • 如果你的回答里有 10 个声明,9 个相关、1 个不相关(比如末尾加了句"如有其他问题请联系客服"),分数就是 0.9。这个设计对"废话比例"非常敏感,在大多数业务场景下这是合理的——用户只想要相关答案。

    Faithfulness 的计算(防幻觉)

  • 从 actual_output 中提取所有"事实性声明"(fact claims)
  • 对每个声明,判断是否能在 retrieval_context 中找到依据
  • 有依据的声明比例就是得分
  • 注意:Faithfulness 只检查事实主张,不检查语气、格式等内容。如果 actual_output 的所有事实都来自 retrieval_context,即使回答结构很混乱,Faithfulness 也会给高分。这也意味着:Faithfulness 高不代表回答好,它只保证没有凭空捏造的事实。

    G-Eval 的计算机制与稳定性优化

    G-Eval(源自 Liu et al. 2023 论文)的计算步骤:

  • 把你的 criteria 文字和 test case 内容拼成一个 meta-prompt
  • 调用 judge model,让它对每个评估维度给出 1-5 分,并提供 chain-of-thought 推理
  • 对多次采样的评分取加权平均
  • 归一化到 0-1
  • G-Eval 的方差通常比其他指标高——同一个 case 多次运行,分数可能在 0.7-0.9 之间波动。这是 LLM 随机性的固有特性。通过 n 参数多次采样取平均可以显著缓解:

    # 提高 G-Eval 评分稳定性:指定多次采样
    metric = GEval(
    name="Correctness",
    criteria="判断回答是否在事实上准确、完整",
    evaluation_params=[
    LLMTestCaseParams.INPUT,
    LLMTestCaseParams.ACTUAL_OUTPUT,
    LLMTestCaseParams.EXPECTED_OUTPUT
    ],
    threshold=0.7,
    n=5 # 运行 5 次取平均,标准差从 ±0.12 降到 ±0.05
    )

    实测数据:n=1 时,同一 case 多次运行的评分标准差约为 ±0.10-0.15;n=5 时降到 ±0.04-0.06。对 CI 阻塞决策来说,这个差异很重要——你不希望因为一次随机低分而阻断 PR 合并。

    层2:回归追踪——发现你的变更破坏了什么

    单次运行 eval 是快照;真正的价值在于跨版本比较。

    当你修改了 prompt、换了模型版本、调整了检索策略,你需要知道:这次变更让哪些指标变好了?哪些变差了?

    # 设置实验名称,追踪这次变更
    deepeval test run tests/ \\
    –experiment-name "prompt-v2-friendlier-tone"

    在 Confident AI(DeepEval 的云端配套)或 Langfuse 中,你可以看到:

    实验对比:
    prompt-v1 prompt-v2
    AnswerRelevancy: 0.82 0.85 ↑ +0.03
    Faithfulness: 0.79 0.71 ↓ -0.08 ⚠️
    TimeAccuracy: 0.91 0.83 ↓ -0.08 ⚠️

    这张表就是文章开头那个翻车案例如果有 eval 体系本可以在上线前看到的信息——Faithfulness 和 TimeAccuracy 都下降了,团队可以在发布前决策是否接受这个 trade-off。


    把 Eval 塞进 CI:GitHub Actions 实例

    Evals 的最大价值在于强制每次 PR 都跑测试,而不是等有问题了才手动跑。

    # .github/workflows/llm-eval.yml
    name: LLM Evaluation Gate

    on:
    pull_request:
    paths:
    'prompts/**'
    'app/llm/**'
    'tests/eval/**'

    jobs:
    eval:
    runs-on: ubuntulatest
    timeout-minutes: 30

    steps:
    uses: actions/checkout@v4

    name: Set up Python
    uses: actions/setuppython@v5
    with:
    python-version: '3.11'
    cache: 'pip'

    name: Install dependencies
    run: pip install deepeval pytest

    name: Run eval suite
    run: deepeval test run tests/eval/ failthreshold 0.75
    env:
    OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
    APP_API_KEY: ${{ secrets.APP_API_KEY }}

    name: Upload eval results
    if: always()
    uses: actions/uploadartifact@v4
    with:
    name: evalresults${{ github.sha }}
    path: evalresults/

    关键参数:–fail-threshold 0.75 表示如果任何指标的平均分低于 0.75,CI 流水线失败,PR 不能合并。

    重要提醒:CI eval 的成本是真实的。50 条测试用例 × 3 个指标 × 每个指标调用 1-2 次 LLM judge,一次完整 eval 大约消耗 $1-5 美元。

    实际成本参考(以 GPT-4o-mini 作为 judge,2026 年 5 月价格):

    测试集规模指标数预估 token 消耗预估成本
    50 条 2 个 ~150K tokens ~$0.15
    50 条 5 个 ~380K tokens ~$0.38
    200 条 3 个 ~600K tokens ~$0.60
    200 条 5 个 ~1M tokens ~$1.00
    1000 条 3 个 ~3M tokens ~$3.00

    注:使用 GPT-4o 作为 judge 成本提高约 10-15 倍。对大多数业务场景,GPT-4o-mini 的判断质量足够(相关性、忠实度等通用维度);只有在复杂推理类任务才建议升级到 GPT-4o。

    可以通过以下方式控制成本:

  • 把完整 eval 套件限制在 prompts/ 或 app/llm/ 目录变更时才触发
  • 区分"冒烟测试集"(10-20 条核心用例,每次 PR)和"完整集"(100+ 条,只在合并到 main 时跑)
  • 使用较便宜的模型(如 GPT-4o-mini)作为 judge,对大多数场景准确率足够

  • 层3:在线监控——生产中的质量漂移检测

    离线 eval 是"你知道会出错的场景",在线监控是"你不知道的"。

    生产请求 → 采样(比如 5%) → 异步评分 → 指标聚合 → 告警

    在线监控的典型设置:

    # 使用 Langfuse 对生产请求进行在线评分
    from langfuse import Langfuse
    from langfuse.decorators import observe, langfuse_context

    langfuse = Langfuse()

    @observe()
    def chatbot_respond(user_input: str) > str:
    response = your_llm_call(user_input)

    # 异步打分(不影响响应延迟)
    langfuse_context.score_current_observation(
    name="answer_relevancy",
    value=score_relevancy(user_input, response), # 你的评分逻辑
    comment="online-eval-v1"
    )

    return response

    在生产中,你需要关注的三个核心信号:

    1. 质量漂移:某个指标的 7 日移动平均持续下降

    # 每日告警检查(配合 cron 或云函数运行)
    from langfuse import Langfuse
    import datetime

    def check_quality_drift(threshold=0.70, window_days=7):
    client = Langfuse()
    end = datetime.datetime.utcnow()
    start = end datetime.timedelta(days=window_days)

    scores = client.get_scores(
    name="answer_relevancy",
    from_timestamp=start,
    to_timestamp=end
    )

    if not scores.data:
    return

    avg = sum(s.value for s in scores.data) / len(scores.data)
    if avg < threshold:
    send_alert(f"⚠️ 质量漂移:answer_relevancy {window_days}日均值 {avg:.3f} < {threshold}")

    2. 分布偏移:出现了 golden dataset 里没有的新意图类型。可以通过对生产请求做意图分类,检测"未知意图"比例是否上升。

    3. 高置信度错误:模型给出高置信度但错误的回答(幻觉风险最高)。当 Faithfulness < 0.5 同时 AnswerRelevancy > 0.8 时,说明模型"自信地说错了",这是最危险的信号。

    从生产失败中扩充 golden dataset——这是在线监控最大的价值之一:

    @observe()
    def chatbot_respond(user_input: str) > str:
    response = your_llm_call(user_input)

    # 异步低分捕获(不阻塞响应)
    relevancy = score_relevancy(user_input, response)
    if relevancy < 0.60:
    save_to_review_queue({
    "input": user_input,
    "output": response,
    "score": relevancy,
    "timestamp": datetime.datetime.utcnow().isoformat()
    })

    return response

    低分生产 case 经过人工审核(确认是真实质量问题而不是 judge 误判),加入 golden dataset,下次离线 eval 就会覆盖这个场景。这是让 eval 体系持续改进的飞轮:生产发现问题 → 加入测试集 → 自动防止回归。


    工具选型:2026 年主流 Eval 平台横评

    过去两年,LLM eval 工具市场从几乎空白发展到十几个主流平台。选工具不难,难的是理解什么时候该用哪个、什么时候不需要用工具。

    下面基于 benchmarkingagents.com(2026 年 4 月)的中立横评 + 实际使用体验整理:

    工具开源免费自托管最适合不适合
    DeepEval ✅ OSS ✅ 本地跑 工程师团队、pytest 集成、CI 需要 PM/非技术人员参与的场景
    Langfuse ✅ OSS ✅ 完整 自托管、团队数据主权、成本控制 需要最佳的 CI 集成 UX
    Braintrust ❌ 云端 ✅ 慷慨 开发者体验、prompt 管理、CI 需要本地数据或避免供应商锁定
    LangSmith ❌ 云端 有限 LangChain 团队,零配置 非 LangChain 栈(集成成本高)
    Arize Phoenix ✅ OSS ✅ 完整 生产监控、multimodal、embedding 只需要离线 eval(过于重量级)
    Confident AI ✅ OSS 完整评测+可观测性闭环 需要精美的非技术仪表盘

    决策指南

    选工具最重要的原则是:不要一开始就选最复杂的。见过太多团队,在 eval 体系还没建立起来之前,就先花两周把 Arize Phoenix 的 Docker 集群配好了,然后因为平台太复杂、维护成本太高,最终放弃了整个 eval 实践。

    按团队规模和阶段来选:

    从零开始,团队 < 5 人,产品还在快速迭代: → DeepEval + CSV 输出,或者直接用本文后面的"最简实现"脚本 → 够用了,别过度工程化,先把 golden dataset 建起来最重要

    需要团队协作和版本追踪,团队 5-20 人: → Langfuse(开源自托管,数据在自己手里)或 Braintrust(云端,开发者体验更好) → 两者各有优势:Langfuse 适合数据合规要求严的场景;Braintrust 的 CI 集成和 prompt 版本管理体验更流畅

    已经在用 LangChain/LangGraph,且不打算换框架: → LangSmith,一个环境变量搞定全量 trace,几乎零配置

    大团队(>30 人),有数据合规/安全要求: → Langfuse 或 Arize Phoenix(完全自托管,Docker 或 Kubernetes) → 有专门的基础设施团队维护,否则自托管平台本身会变成负担

    做的是 AI Agent,需要多步骤 trace 和工具调用评估: → HoneyHive 或 Arize Phoenix,专门针对 Agent 的 trace 可视化更强

    什么时候不需要平台

    如果你的测试集少于 100 条,没有生产流量需要监控,一个 Python 脚本 + CSV 文件就完全够用。

    # eval_simple.py —— 没有框架也能跑的最小实现
    import csv
    import json
    from openai import OpenAI

    client = OpenAI()

    def llm_judge(question, answer, expected):
    """最简单的 LLM-as-judge 实现"""
    prompt = f"""
    问题:
    {question}
    期望答案:
    {expected}
    实际答案:
    {answer}

    请判断实际答案是否与期望答案在语义上一致。
    只回答 JSON: {{"score": 0-1, "reason": "简短理由"}}
    """
    response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": prompt}],
    response_format={"type": "json_object"}
    )
    return json.loads(response.choices[0].message.content)

    # 对每条测试用例评分,写入 CSV
    results = []
    for case in GOLDEN_CASES:
    actual = chatbot.respond(case["input"])
    judgment = llm_judge(case["input"], actual, case["expected_output"])
    results.append({
    "input": case["input"],
    "actual": actual,
    "score": judgment["score"],
    "reason": judgment["reason"]
    })

    with open("eval-results.csv", "w") as f:
    writer = csv.DictWriter(f, fieldnames=["input", "actual", "score", "reason"])
    writer.writeheader()
    writer.writerows(results)

    avg_score = sum(r["score"] for r in results) / len(results)
    print(f"平均分:{avg_score:.3f}")
    if avg_score < 0.75:
    exit(1) # CI 失败

    50 条用例用 gpt-4o-mini 评分,成本约 $1-3,运行时间 2-3 分钟。


    RAG 应用的专项评估维度

    RAG(检索增强生成)应用是目前 LLM 应用最主流的形态之一。在 RAG 应用中,除了通用的相关性和忠实度,还需要评估检索质量本身。很多时候,回答不好不是 LLM 的问题,是检索出来的上下文就不对。

    DeepEval 提供了专门针对 RAG 的四个指标:

    from deepeval.metrics import (
    ContextualPrecisionMetric, # 检索精度:检索结果中有多少是有用的
    ContextualRecallMetric, # 检索召回:期望答案所需信息有多少被检索到
    ContextualRelevancyMetric, # 检索相关性:检索结果与问题的相关程度
    FaithfulnessMetric # 忠实度:回答中的事实有多少能在检索结果中找到依据
    )

    诊断思路:

    症状可能原因关注指标
    回答听起来合理但事实错 LLM 幻觉 Faithfulness ↓
    回答与问题不相关 检索失败或 prompt 问题 ContextualRelevancy ↓
    只回答了问题的一部分 检索召回不足 ContextualRecall ↓
    回答冗长但质量低 检索精度低,噪音太多 ContextualPrecision ↓

    使用这套指标组合做一次诊断性 eval,通常能快速定位 RAG 系统的瓶颈在哪一层:

    # RAG 专项评估
    @pytest.mark.parametrize("case", RAG_TEST_CASES)
    def test_rag_pipeline(case):
    result = rag_pipeline.query(case["question"])

    test_case = LLMTestCase(
    input=case["question"],
    actual_output=result["answer"],
    expected_output=case["expected_answer"],
    retrieval_context=result["retrieved_docs"] # 传入实际检索到的文档
    )

    assert_test(test_case, [
    ContextualPrecisionMetric(threshold=0.7),
    ContextualRecallMetric(threshold=0.8),
    FaithfulnessMetric(threshold=0.85)
    ])

    一个真实的 RAG eval 结果对比,在更换 embedding 模型前后:

    指标比较 (n=100 test cases):
    old embedding new embedding 变化
    ContextualPrecision: 0.71 0.68 ↓ -0.03
    ContextualRecall: 0.79 0.91 ↑ +0.12 ✅
    Faithfulness: 0.88 0.87 → 持平
    AnswerRelevancy: 0.82 0.85 ↑ +0.03 ✅

    这个结果清楚地告诉你:新 embedding 模型的召回率明显提升(更全面),但精度略有下降(多召回了一些不太相关的段落)。根据你的业务场景(是更怕漏答还是更怕噪音),可以做出有数据依据的决策。

    AI Agent 的评估特殊性

    相对于单轮 Q&A,AI Agent 的评估更复杂,因为 Agent 需要进行多步推理、工具调用、状态管理。DeepEval v3.0 提供了专门的 Agent 评估指标:

    from deepeval.metrics import (
    TaskCompletionMetric, # 任务是否最终完成
    ToolCorrectnessMetric, # 工具调用是否正确
    )
    from deepeval.test_case import LLMTestCase

    # Agent 测试案例需要包含完整的推理轨迹
    def test_booking_agent():
    # 模拟运行 Agent
    result = booking_agent.run("帮我预订明天下午 3 点的会议室 A")

    test_case = LLMTestCase(
    input="帮我预订明天下午 3 点的会议室 A",
    actual_output=result.final_answer,
    # 传入 Agent 的完整工具调用轨迹
    tools_called=result.tool_calls # [{"name": "check_room_availability", "input": {…}}, …]
    )

    assert_test(test_case, [
    TaskCompletionMetric(threshold=0.8),
    ToolCorrectnessMetric(threshold=0.9)
    ])

    Agent 评估的难点在于:正确的"过程"可能有很多种,但结果只有一个。ToolCorrectnessMetric 不要求 Agent 必须用你预期的工具调用顺序,而是判断最终调用的工具集合是否满足任务需要。

    构建最小可行 Eval 栈的路线图

    Week 1:建立基础

    • 收集 50 条真实用户问题作为 golden dataset
    • 对每条手工标注期望输出
    • 安装 DeepEval,写 3 个核心测试用例
    • 本地跑通:deepeval test run tests/test_eval.py

    Week 2-3:接入 CI

    • 配置 GitHub Actions workflow
    • 设置合理的失败阈值(建议从 0.65 开始,再调高)
    • 建立"冒烟集"(10-15 条)和"完整集"分层

    Month 2:升级为平台

    • 引入 Langfuse(OSS 自托管)或 Braintrust(云端)
    • 开始追踪版本间指标变化
    • 对生产流量的 5% 做异步在线评分

    Month 3-6:闭环

    • 用失败的生产案例扩充 golden dataset
    • 建立"eval 失败 → 修复 → 重新评估"的闭环流程
    • 对评分本身做校准(人工抽样对比 LLM judge 的准确率)

    常见坑和反模式

    坑1:一开始就建太大的 golden dataset 想着"以后用得着",花两周标注了 2000 条,然后发现 eval 每次跑 40 分钟,开发体验太差,直接放弃。从 50 条开始,够用就行。

    坑2:只看平均分,不看分布 平均 0.82 可能掩盖了某个意图类别全部失败(0.3 分)被其他高分稀释的情况。分类别看指标,设置 per-category 阈值。

    坑3:用同一个模型做应用和 judge 让 GPT-4o 评判自己的输出,它会给自己打高分。Judge 要么用更强的模型,要么用不同厂商的模型,减少自我偏好。

    坑4:threshold 设太高,导致 CI 总是失败 工程师开始绕过 eval CI 或者直接把 threshold 改低。合理的起步 threshold 是 0.65-0.70,随着你对 judge 的校准越来越准再调高。

    坑5:把 eval 当成唯一的质量信号

    Eval 分数高不等于用户满意。Eval 是工程质量门控,不是产品指标。你的 golden dataset 是你能想到的场景,但用户的创造力总是超出你的预期。Eval 体系再完善,也要继续关注:用户侧的满意度评分、Human CSDN(人工审核抽样)、转化率和留存率等真实信号。

    坑6:没有对 judge 本身做校准

    LLM judge 不是万能的。建议每个月随机抽取 50-100 条 case,同时做 LLM judge 评分和人工评分,计算两者的 Spearman 相关系数。如果低于 0.70,说明你的评分标准(criteria prompt)需要优化,或者这个维度不适合 LLM-as-judge。

    常见的 judge 失效场景:

    • 简短回答:judge 倾向于给简短回答打低分,即使简短本身就是正确的(如"不支持"这类 yes/no 回答)
    • 风格偏好:不同 judge model 对"专业语气"的定义不同,同一个回答 GPT-4o 打 0.8,Qwen-max 可能打 0.6
    • 行业术语:通用 judge 对特定行业术语(金融、医疗、法律)的准确性判断不如领域专家

    解决方案:在 G-Eval 的 criteria 里加入具体的打分锚定示例(few-shot):

    time_accuracy_metric = GEval(
    name="TimeAccuracy",
    criteria="""
    判断"实际输出"中关于时间的描述是否与"检索上下文"中的信息一致。

    评分标准(1-5分):
    5分:时间信息完全准确,与上下文一致(如"3-5个工作日"对应上下文中的"3-5个工作日")
    4分:时间信息基本准确,有轻微表述差异但语义等价(如"约一周"对应"5-7个工作日")
    3分:时间信息模糊,没有明确给出时间范围
    2分:时间信息不准确,有明显偏差(如"1-2天"对应"3-5个工作日")
    1分:时间信息完全错误或与上下文矛盾
    """,
    evaluation_params=[
    LLMTestCaseParams.ACTUAL_OUTPUT,
    LLMTestCaseParams.RETRIEVAL_CONTEXT
    ],
    threshold=0.8
    )

    加入具体的评分锚点后,judge 的一致性通常能提高 15-20%。


    总结

    LLM 应用的测试体系比传统软件测试晚了至少两年。大多数团队现在还在靠人工检查和用户投诉发现问题——本质上,这是把质量控制外包给了用户,是对用户时间和信任的消耗。

    本文介绍的三层 eval 体系,核心思想可以用一句话概括:把"不知道有没有变好"变成"能量化地知道变好了多少、变差了什么"。

    具体落地路径:

    最小起步(1-2 天):

  • 从历史日志里挑 50 条真实用户问题,手工标注期望输出
  • 安装 DeepEval,写 AnswerRelevancy + Faithfulness 两个 metric
  • 本地跑通,看哪些 case 在失败,理解失败原因
  • (可选)写一个简单的 CSV 脚本存储结果,建立你的第一份基线
  • 两周内完成: 5. 配置 GitHub Actions,把 eval 接入 PR 流程 6. 设置合理的 fail threshold(建议从 0.65 开始) 7. 跑一次"变更前 vs 变更后"的对比实验,感受回归追踪的价值

    一个月目标: 8. 引入 Langfuse(自托管)或 Braintrust(云端),开始可视化版本间指标趋势 9. 对 5% 的生产流量做异步在线评分 10. 建立低分 case 的人工审核 → 加入 golden dataset 的循环

    这套体系建好之后,你会发现一个有趣的副产品:对模型、prompt、检索策略的每一次改动,都有了可量化的依据。你不再需要靠直觉决定"这次改动到底值不值得上线",你可以直接看数据。

    这就是从"直觉驱动的 AI 开发"到"数据驱动的 AI 开发"的核心跨越。

    LLM observability 市场 2026 年已经达到 27 亿美元,到 2030 年预计超过 92 亿,36.2% 的年复合增长率在软件行业里几乎无可比拟。Gartner 预测到 2028 年,50% 的 GenAI 生产部署将包含 LLM 可观测性投入——从今天的 15% 到未来的 50%,这中间的距离就是还没踩过这个坑的团队。

    你的 LLM 应用有 eval 吗?如果还没有,今天是开始的最好时机。


    参考资料

  • Braintrust Team. What is LLM evaluation? A practical guide to evals, metrics, and regression testing. Braintrust, May 16, 2026. https://www.braintrust.dev/articles/llm-evaluation-guide
  • LLM Eval Tools Compared 2026 — Langfuse, LangSmith, Braintrust, Arize, Humanloop, HoneyHive. benchmarkingagents.com, April 2026. https://benchmarkingagents.com/tools-compared/
  • Braintrust Team. DeepEval alternatives (2026): Best tools for LLM evals, RAG, and agent testing. Braintrust, March 2, 2026. https://www.braintrust.dev/articles/deepeval-alternatives-2026
  • Confident AI. Top 7 LLM Observability Tools in 2026. Confident AI, May 2026. https://www.confident-ai.com/knowledge-base/compare/top-7-llm-observability-tools
  • DeepEval Documentation. Metrics & Test Cases. https://deepeval.com/docs
  • 赞(0)
    未经允许不得转载:171主机测评 » LLM 应用的Evals 工程实践:从手动测试到自动化回归测试体系
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址