[控场AI]
· 8 分钟阅读· 4,145 字

无法单元测试的AI功能,如何用Evals保障质量

无法单元测试的AI功能,如何用Evals保障质量

AI功能无法单元测试,但可用评估集加三层打分和上线门槛系统化衡量质量。

文章指出传统单元测试对AI功能失效,因为同一输入输出不确定,团队常退化为手动试提示词的演示式测试。替代方案是评估集:由真实输入集合、每个输入好答案的书面定义、每次改动记录的分数三部分组成,分数趋势比绝对值更重要。评估样本应来自真实工作负载并刻意覆盖模糊、越界、坏输入、对抗性和高风险案例。打分需要代码检查、按具体量规的人工评审、模型评审三层结合,模型评审不能作为高风险场景的唯一裁判。上线门槛必须提前约定,包括典型案例分数线与任何比例都不可接受的mustNotFail失败项。上线后提示词修改、模型更新、数据与成本变化都会影响质量,评估集应持续从生产失败案例中扩充。

传统的单元测试在AI功能面前失效了——同一个摘要请求跑两次,结果不同但都合理。团队往往退回到手动试几个提示词就上线,这其实是演示,不是测试。本文介绍用评估集(evals)系统化衡量AI输出质量的方法:用真实输入构建测试集、写下“好答案”的定义、用代码检查+人工评分+模型评分三层机制打分,并在上线前约定不可妥协的底线。

rss source: You Cannot Unit Test a Summary. You Can Still Test It.

为什么单元测试对AI功能失效

每个开发者都有肌肉记忆:功能完成,写个测试喂输入、断言输出。保存成功、日期解析正确,这些都能用 expect(output).toBe(expected) 锁死。

但AI功能打破了这个反射。让模型总结同一段讲座,两次得到不同摘要,却都说得通;让模型分类客服消息,99次正确,第100次自信地错。断言没有可抓的东西。

危险的不是测试失败,而是团队干脆不测试了。有人手动挑几个提示词跑一遍,读着答案觉得“还行”,就认为通过了。这是演示,不是测试——因为挑输入的人自己决定了什么算通过。

替代单元测试的东西叫评估集(eval set)。它不是平台,就是三样东西:真实输入的集合、每个输入“好答案”的书面定义、每次改动后记录的分数。分数本身不如趋势重要:你不需要证明功能完美,只需要知道周四改的提示词让事情变好还是变坏。

用真实输入,而不是你会挑的输入

评估样本必须来自实际工作负载:真实客服消息、真实文档、真实通话录音。虚构的例子总是比现实干净,而在干净样本上调出来的功能,一到脏数据上就露馅。

没有生产数据时,用最接近的东西:旧工单、早期客户的样本文档、试点录音——任何人在不知道模型会读的情况下产出的内容。

然后刻意去找不舒服的案例:

  • 模糊请求:正确行为是追问澄清,而不是猜测。
  • 越界请求:比如订票助手被问法律建议,正确回答是礼貌拒绝或转人工。
  • 坏输入:模糊扫描件、填了一半的表单、背景噪音、混合语言。
  • 对抗性输入:试图让系统忽略指令的文本,系统能执行动作时尤其关键。
  • 高风险案例:答错要赔钱、丢信任或出安全问题的场景。

这类精心挑选的几十个样本,比几千个没人看的合成数据更能暴露真问题。评估集会随着生产数据逐步扩充。

写下“好”的定义才是真正的工作

对每个样本,由懂业务的人写下好回答长什么样。有时是精确的:发票总额是这个数、来电者要周二下午三点。更多时候是一份短清单:提到截止日期、不承诺退款、不超过一段、客户情绪激动时转人工。

价值主要在这里,而且和代码无关。写定义强迫团队对功能目标达成一致。如果两个人对同一个助手想要相反的行为,最好在填表格第12行时发现——第二周吵架是免费的,上线后吵架很贵。

三种打分方式,缺一不可

代码检查。 函数能验证的就让函数验证:抽取字段对不对、日期是否合法、语音助手是否用正确的时段调用了预订工具、回复是否超长、有没有泄露不该分享的链接。这些又快又便宜,每次改动都跑,也是最接近旧单元测试的部分,适合作为起点。

按量规人工评审。 有些质量只有人能判断:摘要是否准确、语气是否合适。诀窍是量规要具体到两个评审者对同一输出打出一致的分。“质量打1到5分”产生噪音;“是否陈述了源文中不存在的事实”产生信号。

模型给模型打分。 用第二个模型按量规给输出打分,能扩展到人力覆盖不了的地方,确实有用。但它也有自己一贯的偏差。我把它当作人工评审的助手:定期用人工样本校验它的评分,高风险场景绝不让它当唯一裁判。

模型给模型打分(LLM-as-a-judge)在实践中通常的做法是:把原始输入、待评输出以及量规(rubric)一起放进评审模型的提示词中,要求它输出结构化评分或通过/不通过判断。这种方式成本远低于人工,且可批量运行,但需要注意几个已知偏差:评审模型倾向于给更长的回答打高分(长度偏差)、偏好与自己生成风格相似的输出(自我偏好偏差)、以及在评分标准模糊时产生位置偏差(同一对输出调换顺序后结论翻转)。缓解手段包括:在量规中明确列出可勾选的检查项而非使用模糊的1-5分、定期抽样由人工复核并计算评审模型与人工评分的一致率、对高风险场景保留人工终审。

测试框架可以小得不好意思

听到“evals”人们就想到平台。我推荐的第一版是一个表格(输入+预期检查),加一个跑完打印分数并与上次对比的脚本。示意代码(非客户仓库内容):

type EvalCase = {
  id: string;
  input: string;
  checks: Array<(output: string) => boolean>;
  mustNotFail?: boolean; // e.g. never quote a price
};

async function runEvals(cases: EvalCase[], run: (input: string) => Promise<string>) {
  let passed = 0;
  const hardFailures: string[] = [];

  for (const c of cases) {
    const output = await run(c.input);
    const ok = c.checks.every((check) => check(output));
    if (ok) passed++;
    else if (c.mustNotFail) hardFailures.push(c.id);
  }

  const score = passed / cases.length;
  console.log(`score ${(score * 100).toFixed(1)}%  hard failures: ${hardFailures.length}`);
  return { score, hardFailures };
}

有意思的不是循环,而是 mustNotFail 标志,它引出了我见过最多的错误。

在看到分数之前先定上线门槛

团队看到分数后,很容易论证“这个分数可以接受”。87% 听起来不错——尤其当你已经做了三周。

所以要提前和结果负责人约定:

  • 典型案例达到多少分才能上线。
  • 哪些失败在任何比例下都不可接受:医疗或法律建议、错误价格、其他客户的数据。这些是 mustNotFail 案例,一个就能否决发布,不管平均分多高。
  • 系统不确定时怎么办。通常正确的设计是转人工,把潜在失败变成安全结果。

mustNotFail 案例与普通评估案例的区别在于语义:普通案例贡献的是通过率,而 mustNotFail 案例是硬约束——任何一个失败都会否决发布,无论平均分多高。这种设计对应的是工程中的“安全不变量”思路:平均质量可以权衡,但某些失败模式(泄露他人数据、给出错误价格、提供医疗或法律建议)在任何比例下都不可接受。提前约定门槛的意义在于避免“分数锚定效应”——团队看到87%后容易论证这个数字可以接受,但如果门槛是上线前就写下的95%,87%就是一个明确的未达标信号,而不是一个需要辩护的数字。

上线后它才真正回本

AI功能的变化方式和普通代码不同,多数变化不会出现在diff评审里:

  • 提示词修改:为修一个问题改的措辞,可能悄悄破坏另外三个。
  • 模型更新:供应商会更新和下线模型,同一个模型名逐月行为都可能不同,最终可能被迫切换。
  • 数据变化:如果功能基于文档回答,重新组织文档就会改变答案。
  • 成本变化:换更便宜的模型控制开销是合理的,但前提是你能证明质量没掉。

每次这类变化都跑一遍评估集,一次紧张的主观判断就变成了对比。把旧分数和新分数并排放在团队能看到的地方,让回归像失败的测试一样阻止变更。

模型更新带来的评估需求常被低估。供应商对模型的更新通常不会改变模型名称,行为却可能逐月漂移;而模型下线(deprecation)则往往给出有限的迁移窗口,迫使团队在时间压力下切换。这两种情况都意味着评估集必须能在不修改代码的前提下快速重跑,并保留历史分数用于对比。把每次模型版本变更、提示词修改、数据源调整都当作一次需要跑评估的“变更事件”,是让评估集真正发挥作用的组织习惯,而不只是技术上的准备。

生产才是评估集的真正来源

上线前的评估是对流量的educated guess(有根据的猜测),生产才是真的,评估集应持续从中学习:

  • 记录足够调查的信息(在隐私规则内):输入、输出、系统决策。
  • 盯住失败信号:用户重复问同一个问题、要求转人工、员工纠正结果、点踩。
  • 把每个真实失败变成新案例,写下正确答案,让同样的错误下次被自动抓住。

语音场景最明显。真实通话有打断、改主意、业务不处理的请求,回放这些是检查改动的一部分。电话很快且没有撤销键,这也是我另一篇相关文章叫“电话无法复现”的原因。

语音之外也一样。对于把讲座录音转成摘要和提纲的应用,有用的评估问题很具体:摘要是否漏了关键要点,或加了讲师没说过的话?这两个检查比任何泛泛的“质量分”都更能抓问题。

诚实的成本

第一版评估集不是大项目,是构建中的几天工作,大部分精力花在懂业务的人写下“好”的定义上。真正花钱的是注意力:必须有人负责“好”的定义并定期审阅失败案例。如果这个人是“没有人”,评估集就会过期,你又回到凭印象发版。

你的评估设置现在长什么样?我特别好奇有没有人在高风险场景信任模型评审,以及你怎么校验它。

#AI功能测试#evals评估集#大模型测试#AI质量保障#LLM评估#模型评审#AI上线门槛
分享:

相关推荐