你能给一个函数写单测:相同输入、相同输出,绿或红。但你没法用同样的方式给 LLM 写单测——同一个 prompt 可能给出不同答案,"正确"是模糊的,一个修好某个用例的改动会悄悄弄坏另外十个。于是团队靠感觉发版:某人试几个 prompt,"好像变好了",就上线了。这不可扩展,还会藏住回归。评测(Evals)就是用测量取代感觉的那套功夫——一种可重复地给 LLM 应用或 Agent 打分的办法,让你靠证据而不是靠希望去迭代。本文讲怎么搭这套:数据集、打分方法、用模型来评判、评测 Agent、以及把它接进 CI 和线上。

⚡ 快速要点
  • 概率系统没法做单测 —— 评测是统计性的:跑很多用例、打分、看通过率。单个例子说明不了什么。
  • 评测集才是资产。 一份精挑的"黄金集"(真实用例:输入 → 期望/标准),从线上轨迹里长出来,比任何单一指标都值钱。
  • 四种打分法: 启发式/精确、代码断言、LLM-as-judge、人工——对每个标准用最便宜又可信的那种。
  • LLM-as-judge 把人类判断规模化处理模糊标准,但它有偏差(位置、冗长、偏爱自己),必须对齐人工标注做校准
  • Agent 需要轨迹评测,不只是看最终答案——它选对工具了吗、顺序合理吗、有没有空转或烧爆预算?
  • 两个循环: 离线评测把关发版(CI 回归),在线评测(A/B + 护栏)抓住离线集漏掉的。
  • 评测会被刷分。 过拟合到你的集合,你提升的是数字不是产品——留一份保留集,并从真实流量持续刷新。
tldr

评测 harness 就是给"不确定性"写的测试套件:一份用例数据集、一种在每个用例上跑你系统的方式、一个或多个打分器(启发式、代码、LLM-judge、人工)给输出打分,以及把分数聚合成可与基线对比的指标。数据集从真实流量建,模糊的东西用你已对齐人工的 LLM-judge 打,Agent 评轨迹而不只是答案,发版用离线集把关,并保持在线评测在跑——因为线上总会给你惊喜。

评测数据集黄金集:输入 → 期望 被测系统prompt · RAG · agent 打分器启发式·代码·judge·人工 聚合与对比通过率 · 忠实度 · 成本 vs 基线 发布 ✓ / 迭代 ↺有回归?把关发版 线上轨迹+ 用户反馈 迭代 —— 改 prompt / 模型 / 检索 curate 新用例
评测闭环 —— 在数据集上跑系统、给每个输出打分、与基线聚合对比,然后发布或迭代;线上轨迹持续把新用例回灌进数据集

为什么要评测,而不是凭感觉

LLM 应用有三个性质打破了常规测试。它们非确定(同一输入会变,所以跑一遍说明不了什么)。"正确"是开放式的(很少有唯一正确字符串——一段摘要可以好在很多方面)。改动有非局部影响(为一个用例改好 prompt 会悄悄拖垮别的)。后果是你必须统计地评估:跑一群用例、追踪成功,就像追踪一个模型的准确率——而不是盯着单个输出。评测就是团队用一个数字而不是一场争论来回答"这一版到底有没有更好"。

评什么:评测金字塔

别只端到端测整体。像测试金字塔一样混合粒度:大量便宜的组件评测(检索器返回对的文档了吗?路由选对工具了吗?输出能解析成合法 JSON 吗?)、较少的端到端评测(给定用户请求,最终答案好吗?),以及薄薄一层昂贵的人工评审。组件评测能定位失败——端到端掉分时,它们告诉你哪一级坏了——这正是 RAG 或 Agent 这种多步流水线最需要的。

评测数据集

一切都建在数据集上,而好的数据集才是真正的护城河。一个用例 = 一个输入 + 一种判断输出的办法——有时是精确的期望答案,更多时候是一组标准或一个用于对比的参照。用例从哪来?

两条规矩让集合保持诚实:标一份你从不调参的保留集(这样你量的是泛化,不是记忆),以及不断从新流量刷新(静态集会随用法漂移而过时)。目标是覆盖输入分布和失败模式,而不是堆数量——几百个精选用例胜过几千个近似重复。

怎么给输出打分

每个用例都需要一个打分器把输出变成分数。有四类,从最便宜最可靠排起:

打分器何时用
启发式 / 精确有已知答案或模式:精确匹配、正则、含关键词、合法 JSON、分类准确率。便宜、确定——能用就用。
代码断言正确性可由跑代码检验:生成的 SQL 能执行并返回对的行吗?代码过测试了吗?强且客观。
LLM-as-judge标准模糊、偏语言:这段摘要忠实吗?这个回答有用、语气对吗?便宜地规模化类人判断。
人工评审细微之处的金标准,也用来校准其它打分器;每次都跑太慢太贵,所以抽样。

诀窍是把打分器和标准对上:别花 LLM-judge 去查一个正则就能验的东西,也别假装关键词匹配能抓住"这段解释清不清楚"。很多真实评测会组合打分器——代码查格式 + LLM-judge 评质量。

一次评测运行 = 数据集 × 系统 × 打分器
scores = []
for case in dataset:
    out = system.run(case.input)            # 被测的 app/agent
    s = {
        "format":  is_valid_json(out),         # 启发式
        "correct": run_tests(out, case.tests),  # 代码
        "helpful": judge.score(case.input, out, rubric),  # LLM-as-judge
    }
    scores.append(s)
report = aggregate(scores)               # 每个标准的通过率
assert report.correct >= baseline.correct  # CI 门禁:不许回归

认真做 LLM-as-Judge

对那些模糊标准——忠实度、有用性、语气、"有没有遵循指令"——一个强模型能以人工零头的成本和延迟给输出打分。这就是 LLM-as-judge,它让评测开放式生成变得可行。但天真的 judge 不可靠,所以把它当成一个你要部署的模型来对待:先设计,再验证。

查询 / 任务 系统输出 评分细则 / 参照 Judge LLM按细则打分 分数 + 理由如 1–5 · 通过/不通过
LLM-as-judge —— 任务、系统输出和一份明确的评分细则进入 judge 模型,返回分数加理由;信任它之前先对齐人工标注做校准

让 judge 可靠

要点

Judge 是你系统的一部分,所以它也要有自己的评测。"我们用 LLM 来打分"在你能说出"而且它和人工标注 X% 一致"之前,不算质量论断。校准,正是把可信 judge 和"自说自话"区分开的东西。

评 Agent,不只是评答案

Agent(见 Agent 如何工作)把门槛抬高了:一个多步 Agent 可能走着糟糕的路径到达一个好的最终答案——或在中途以"只看最终答案就漏掉"的方式失败。所以要评轨迹,不只是结果:

实操上,你记录完整轨迹(每个 prompt、工具调用、观测)并打分——用启发式查工具调用合法性和步/成本预算,用 LLM-judge 评"这计划合理吗"。这就是 harness 工程 和评测密不可分的原因:你是对着轨迹评测来调 harness。

离线门禁与在线评测

评测在两个循环里跑,两个都要。

离线 —— 把关发版

在每个有意义的改动(改 prompt、换模型、调检索)上,于 CI 里跑评测套件。套件报告每个标准的通过率,并与当前基线对比;掉分就挡住合并,跟单测失败一样。这就是让你能无惧地改 prompt 的东西——集合会抓住那些悄悄的回归。

在线 —— 线上永远不等于你的集合

离线评测只覆盖你想到要放进去的,而真实流量总在漂,所以也要在线上评:用真实结果指标对新旧版做 A/B;在抽样的线上输出上跑便宜的护栏评测(在线 LLM-judge 标记不忠实或不安全的回答);采集用户反馈(点赞、编辑、重生成)作为持续但有噪声的质量信号。线上发现的失败,会变成明天的离线用例——闭合上图那个循环。

坑与取舍

小结

评测把"好像更好"变成"确实更好,好这么多"。从线上建一个真实数据集,每个标准用最便宜又可信的方法打分(启发式 → 代码 → 校准过的 LLM-judge → 人工),评 Agent 看轨迹和成本而不只是最终答案,发版用离线套件把关,并保持在线评测在跑——因为评测集永远不会完全等于现实。能测量质量的团队,比围着它争论的团队迭代得更快。

🎯 面试速答

为什么 LLM 应用没法做单测? 它非确定、"正确"开放式,所以你统计地评——很多用例上的一个率,而不是单个输入/输出。
评测里最重要的是什么? 数据集——从线上流量长出来的精选黄金集,带一份你从不调参的保留集。
怎么给模糊输出打分? 带明确细则的 LLM-as-judge(尽量成对),对齐人工标注、并消位置/冗长偏差。
怎么评一个 Agent? 除了最终成功,还评轨迹:工具调用正确性、步骤顺序合理、不空转,以及步/token/成本预算。
离线 vs 在线评测? 离线套件在 CI 把关发版(抓回归);在线 A/B + 护栏评测 + 用户反馈抓离线集漏掉的,并把新用例回灌。
最大风险? 过拟合评测集或信一个没校准的 judge——你优化的是指标而不是产品。

← 上一篇
Harness 工程