返回博客

Eval 驱动开发:用可量化的验收标准驯服 AI 工程流水线

AI 辅助开发里最容易被忽视的环节,不是写 prompt,不是选模型,而是在执行之前先说清楚「完成」长什么样

传统软件工程里我们有单元测试,出了 bug 至少有一个确定性的地方可以追溯。但 AI Agent 辅助的工作流里,如果没有事先定义好验收标准,「完成」这个词就悬在空气里——Agent 觉得自己做完了,你看着输出也感觉差不多,但下一个改动就把刚做好的东西悄悄破坏了,没人发现。

这是 Eval 驱动开发(EDD)想解决的核心问题。

先写 eval,再动手实现

EDD 最反直觉的一条原则:eval 要在实现之前定义,不是之后补充。

原因是心理上的:实现完了再写 eval,人会不自觉地「迁就」已有输出,把本来不达标的结果定义成「算过了」。先写 eval 是一种强制澄清——它逼着你在动手前回答几个问题:这个功能要满足哪些条件才算做完?哪些已有行为不能被改动?边界 case 是什么?

把这些问题回答完,eval 自然就出来了。而且你会发现,回答不上来的地方往往正是需求本身没想清楚的地方,这时候花 10 分钟补设计,远比让 Agent 跑半小时再返工来得划算。

两类 eval 是基础配置:

  • 能力 eval:验证这次实现是否达成了目标能力,每条 case 有明确的 PASS/FAIL 判断
  • 回归 eval:验证已有功能没有被破坏,特别是关键路径(登录、权限、核心业务流)

评分器的选择决定了 eval 的可靠性

写好 eval 定义只是第一步,怎么判断「过没过」同样重要。

三类评分器各有适用场景:

代码评分器是首选。用 shell 脚本或测试命令做确定性断言——grep 检查代码是否包含某个导出、npm test 跑指定测试套件、npm run build 验证构建通过。确定性高,不会因为「大模型今天心情不好」而给出不一样的结论。

模型评分器用于代码评分器覆盖不到的地方——输出质量、语义正确性、结构是否合理。本质是用另一个 LLM 当裁判,给出 1-5 分和推理过程。这类评分器本身有随机性,不适合放进发布门禁,但作为辅助参考很有价值。

人工评分器留给安全、权限、不可逆操作等场景。有些东西不应该也不能完全自动化,打一个「[HUMAN REVIEW REQUIRED]」标记,说明变更内容和风险等级,让该人看的人去看。

代码评分器优先于模型评分器

能写成确定性断言的,就不要用 LLM 判断。模型评分器的问题不是「会出错」,而是「出错了你不知道」。同一段输出,今天判 PASS,明天换个上下文可能判 FAIL,发布门禁要求的是稳定性,不是「大概率对」。

pass@k:用重复实验量化可靠性

AI 工程里有一个普通测试不需要的概念:同一个任务跑多次,通过率是多少。

这就是 pass@k 指标的来源。pass@1 是首次尝试成功率,pass@3 是三次内至少一次成功的概率,pass^3 是三次连续全部成功——难度依次递增。

推荐的阈值感:能力 eval 要求 pass@3 达到 90% 以上,关键路径的回归 eval 要求 pass^3 = 100%。

这个指标在做以下决策时特别有用:升级了 prompt 版本是否真的更好、换了新模型后原有能力有没有退化、某个功能是「偶发性成功」还是「稳定可交付」。没有 pass@k,这些问题只能凭感觉回答;有了 pass@k,至少有一个可以对话的数字。

eval 是一等公民,不是事后产物

EDD 的最后一条工程原则,也是最容易被工程师忽视的:eval 文件要和代码一起版本控制,一起维护。

.claude/evals/ 目录里的定义文件、运行历史、回归基线——这些不是「跑完就删」的临时产物,而是每次改动的历史见证。六个月后你要判断一个行为变化是有意为之还是意外回归,eval 记录是唯一可靠的依据。

代码在演进,eval 也要跟着演进。功能需求变了,对应的 eval 定义要更新;新增了一个边界 case,eval 里要补上;删掉一个功能,对应的 eval 也要清理——不然 eval 套件会慢慢退化成「总是 PASS 但什么都验不到」的空架子。

eval 套件的健康度,直接决定了整个 AI 辅助工作流能跑多快、能跑多稳。

一句话心法

先定义「完成」,再开始执行——eval 不是事后的检验,而是对任务边界的提前锚定。