
改了一个 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 应用的测试盲区,不是工程师懒,是测试范式本身还没有迁移过来。
为什么传统单元测试在这里失效
传统单元测试的核心假设是:给定输入,输出是确定的。
python
# 传统测试:这样不行
def test_refund_answer():
result = chatbot.respond("退款需要多久?")
assert result == "退款通常需要 3-5 个工作日" # ❌ LLM 每次输出都不一样
LLM 的输出本质上是概率分布的采样。即使是同一个输入,温度不为零时每次输出都有细微差异。更重要的是,"正确"本身就是一个模糊概念:
- "退款需要 3-5 个工作日" 和 "通常 3 到 5 个工作日内处理完成" 语义等价,但字符串匹配会失败
- "退款大约需要一周" 在某些上下文里可以接受,在某些里不行
所以,LLM 应用的测试需要引入两个新概念:评分而不是断言 ,以及 LLM-as-judge。
评分 vs 断言的根本差异
传统单元测试基于断言(是/否):
python
# 断言:二元判断
assert result == "退款通常需要 3-5 个工作日" # 通过或失败,没有中间态
LLM Eval 基于评分(连续值):
python
# 评分: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,对工程师友好。
安装
bash
pip install deepeval
构建 Golden Dataset
Golden dataset 是你手工标注的"标准答案集"。每条数据包括:
input:用户输入actual_output:你的应用实际输出(运行时生成)expected_output:期望的正确回答retrieval_context:RAG 场景下,检索到的上下文
对于一个客服机器人,可以这样构建:
python
# 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 测试
python
# 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])
运行:
bash
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)定义自己的评分标准:
python
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(适合数据合规要求或网络受限场景):
python
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 参数多次采样取平均可以显著缓解:
python
# 提高 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、换了模型版本、调整了检索策略,你需要知道:这次变更让哪些指标变好了?哪些变差了?
bash
# 设置实验名称,追踪这次变更
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 都跑测试,而不是等有问题了才手动跑。
yaml
# .github/workflows/llm-eval.yml
name: LLM Evaluation Gate
on:
pull_request:
paths:
- 'prompts/**'
- 'app/llm/**'
- 'tests/eval/**'
jobs:
eval:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@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/ --fail-threshold 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/upload-artifact@v4
with:
name: eval-results-${{ github.sha }}
path: eval-results/
关键参数:--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%) → 异步评分 → 指标聚合 → 告警
在线监控的典型设置:
python
# 使用 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 日移动平均持续下降
python
# 每日告警检查(配合 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------这是在线监控最大的价值之一:
python
@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 文件就完全够用。
python
# 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 的四个指标:
python
from deepeval.metrics import (
ContextualPrecisionMetric, # 检索精度:检索结果中有多少是有用的
ContextualRecallMetric, # 检索召回:期望答案所需信息有多少被检索到
ContextualRelevancyMetric, # 检索相关性:检索结果与问题的相关程度
FaithfulnessMetric # 忠实度:回答中的事实有多少能在检索结果中找到依据
)
诊断思路:
| 症状 | 可能原因 | 关注指标 |
|---|---|---|
| 回答听起来合理但事实错 | LLM 幻觉 | Faithfulness ↓ |
| 回答与问题不相关 | 检索失败或 prompt 问题 | ContextualRelevancy ↓ |
| 只回答了问题的一部分 | 检索召回不足 | ContextualRecall ↓ |
| 回答冗长但质量低 | 检索精度低,噪音太多 | ContextualPrecision ↓ |
使用这套指标组合做一次诊断性 eval,通常能快速定位 RAG 系统的瓶颈在哪一层:
python
# 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 评估指标:
python
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):
python
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 脚本存储结果,建立你的第一份基线
两周内完成 :
-
配置 GitHub Actions,把 eval 接入 PR 流程
-
设置合理的 fail threshold(建议从 0.65 开始)
-
跑一次"变更前 vs 变更后"的对比实验,感受回归追踪的价值
一个月目标 :
-
引入 Langfuse(自托管)或 Braintrust(云端),开始可视化版本间指标趋势
-
对 5% 的生产流量做异步在线评分
-
建立低分 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