Chapter 01

为什么需要 Evals:从"能跑"到"够好"

LLM 应用最贵的失败不是崩溃,而是"看起来能工作但在真实场景里悄悄失效"。本章回答一个看似朴素的问题:为什么我们必须把评估从第一天就写进工程流程。

一个熟悉的故事

假设你做了一个问答机器人。你在本地试了 10 个问题,回答都像模像样。你兴奋地发布,一周后,用户反馈堆积如山:

这不是技术不够好,这是你在盲飞。你没有仪表盘,也没有刻度——这正是 Evals 要解决的问题。

什么是 Evals

Evaluation(评估)
对 AI 系统输出进行系统化、可重复的质量度量。它不是"感觉还行",而是"在 500 条测试集上,正确率从 82% 提升到 87%,置信区间 ±1.2%"。
Eval Set(评估集)
精心挑选的输入-期望输出对。是你的回归测试基线,每次改 prompt、换模型、调参数都要跑一遍。
Scorer(打分器)
把一次输出变成一个可比较的数字。可以是精确匹配(是/否)、相似度(0-1)、LLM-as-Judge(模型打分),或业务规则(JSON 合法性)。
Experiment(实验)
一次完整的"跑评估集 → 得到分数"的过程。你对比的是实验 vs 实验,不是"感觉 A 比 B 好"。

Vibe Check 为什么不够

Vibe check(凭感觉测)是每个 LLM 项目的起点,也是很多团队的终点。它之所以致命:

❌ Vibe Check 的问题

  • 样本量小:试 5 条 ≠ 代表 5 万条
  • 记忆欺骗:你会忘记昨天试过什么
  • 没法对比:改完 prompt 之后怎么知道"真的"变好了
  • 无法回归:旧问题偷偷复发
  • 无法共享:你的"感觉"传不给团队

✅ Evals 解决什么

  • 覆盖度:一次跑 500 条,覆盖长尾
  • 可重复:今天跑和明天跑是同一把尺子
  • 可对比:A 实验 82%,B 实验 87%,硬数据
  • 可回归:CI 上自动跑,退步立刻报警
  • 可沟通:给老板看曲线,不是看"感觉"

Evals 和传统软件测试的本质差异

传统测试是确定性的:assert add(2, 3) == 5。LLM 是概率性的:同一个输入可能生成 5 种合理的回答,也可能生成 1 种看似合理实则错误的回答。这带来 3 个根本区别:

维度传统软件测试AI Evals
断言方式== 精确匹配语义相似 / LLM 评判 / 多维度评分
结果类型通过 / 失败(二元)分数 / 分布 / 置信区间(连续)
失败含义Bug 确定存在可能是坏案例,也可能是评估集偏差
回归目标100% 通过关键指标不下降(pass@k、p95)
更新频率每次 PR每次 prompt / 模型 / 参数变更
数据成本写几行 assert构建和维护 golden set(贵)
核心心智 不要把 Evals 当成测试的"加强版",而要当成一种全新的工程范式。你测的不是"这段代码对不对",而是"这个模型在这个场景的分布表现如何"。

评估金字塔:从单元到生产

完整的 Evals 体系是分层的,每一层解决不同的问题。最底层快、便宜、覆盖广;最顶层慢、贵、最接近真实。

┌──────────────────┐ │ 生产监控 │ 真实用户流量 + 隐式/显式反馈 │ (online) │ 延迟/成本/用户满意度 └──────────────────┘ ┌────────────────────────┐ │ A/B 实验 │ 线上分流对比两个版本 │ (shadow / canary) │ 统计显著性 └────────────────────────┘ ┌──────────────────────────────┐ │ 场景端到端评估 │ 完整工作流 │ (E2E evals) │ 100-1000 条 golden set └──────────────────────────────┘ ┌────────────────────────────────────┐ │ 组件级评估 │ 单个 prompt/工具/retriever │ (component evals) │ 几十到几百条,CI 跑 └────────────────────────────────────┘ ┌──────────────────────────────────────────┐ │ 单元级评估 │ 格式校验、JSON schema │ (unit evals, 秒级) │ 边界输入、注入攻击 └──────────────────────────────────────────┘
单元级 Evals(毫秒~秒)
最便宜的检查。JSON 是否合法、函数调用参数是否完整、是否泄露了系统 prompt、是否触发了禁词。每次 prompt 改动都跑,CI 强制通过。
组件级 Evals(秒~分钟)
测单个子系统。单独测 retriever 的召回率、单独测 query 改写、单独测某个 tool 的 schema 合规性。发现问题能精准定位。
端到端 Evals(分钟~小时)
测完整用户流程。"用户问 X,系统应返回 Y"。贵、慢,但最接近真实。发布前必跑。
在线评估(持续)
生产流量的采样评估 + 用户反馈信号聚合。离线分高 ≠ 线上真的好,只有在线数据能告诉你真相。

什么时候开始写 Evals

一个流传甚广的错误观念:"等我做完 MVP 再做 Evals"。事实是:

经验法则 写第一个 prompt 之前就写第一条 eval。哪怕只有 5 条,也比 0 条强一个数量级。
原因:没有 evals,你无法知道自己的 prompt 改动到底改好了还是改坏了。凭感觉迭代 = 随机游走。

更务实的推进节奏:

  1. Day 1: 写 5-10 条手工 eval,覆盖你最在乎的 happy path 和 1-2 个边界。用 Python assert 或 pytest 就够。
  2. 第 1 周: 扩到 30-50 条,引入一个 LLM-as-Judge 作为辅助指标。开始用 Promptfoo / Braintrust。
  3. 第 1 月: 从真实日志采样构建 golden set(100+ 条),分层(easy/medium/hard)。CI 集成,PR 自动跑回归。
  4. 上线后: 接入生产 trace,做采样评估;用 thumbs up/regenerate 作为隐式信号;跑 A/B 实验。
  5. 长期: 评估集和模型一起进化。新 bug 先进 eval set,再修代码(TDD 思维)。

一个最小可行的 Evals 例子

先感受一下"最朴素的 eval"长什么样。下面是一个情感分类任务的 10 行代码评估:

# eval_sentiment.py — 最小可行 eval
from openai import OpenAI
client = OpenAI()

# 评估集:输入 + 期望标签
cases = [
    ("这部电影太棒了,值回票价", "positive"),
    ("浪费了两个小时,剧本稀烂", "negative"),
    ("还行吧,没什么惊喜也没什么雷点", "neutral"),
    # ... 假设共 50 条
]

def classify(text):
    rsp = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {"role": "system", "content": "只返回一个词:positive/negative/neutral"},
            {"role": "user", "content": text},
        ],
    )
    return rsp.choices[0].message.content.strip().lower()

correct = sum(classify(t) == label for t, label in cases)
print(f"accuracy = {correct}/{len(cases)} = {correct/len(cases):.1%}")

这 20 行代码已经战胜了 vibe check。你可以:

进阶预告 这个朴素版本有 3 个大问题:① 只能测精确匹配,测不了"生成式"任务;② 没有统计显著性;③ 结果没沉淀,跑完就没。后续章节会逐个解决。

本章小结

下一章我们进入指标设计:准确率不够用时,该怎么测"生成式"任务的好坏?