评测的尺子:设计 AI 评测的几个原则

做 AI 工具和知识库,每次改动之后都要回答同一个问题:这次到底有没有变好?提示词调了一版,知识库补了一批文档,感觉上变好了——但感觉在这个领域几乎不可信。过去一段时间,我给 Agent 的 Skill 做 with/without 对照评测(Skill 是挂载给 Agent 的领域操作手册,对照评测就是同一批任务在有它和没它两种条件下各跑一遍,比较结果),也给知识库问答建过评测集。这个过程中确立的几个原则,记在这里。

统一成 Q&A:问题、标准答案、评判

被评测的任务有两类:问答型,考知识库能不能答对一个领域内的问题;编码型,考 Agent 能不能正确修复一个缺陷。一开始很容易想给编码型做强校验:检查 diff 是否命中预期文件、改动位置是否一致。我反对了这条路,把两类评测统一归一化为 Markdown 的 Q&A 协议,每个 case 三件套:问题、标准答案(golden)、评判标准(judge)。三者分工明确:问题交给被测方,golden 只给评判环节看,judge 定义两者怎么比对。评测跑起来就是三步——被测方拿到问题、产出回答,judge 对照 golden 打分并给出分析。

反对强校验的理由,我当时的原话是「有正确规划,写对是问题不大的」。模型给出正确规划之后,具体写法存在大量合法变体:文件可以拆分,函数可以换名,等价实现也可以有好几种。强校验 diff 或文件位置,会把这些合法变体统统判错,引入大量噪音,同时丢掉真正想考察的东西——模型有没有理解问题、给出正确的解法意图。文档形式反而更完备地描述了问题和解法:一份好的方案文档,比一个恰好匹配的 diff 携带更多信息。

输入先清洗,答案要隔离

评测输入不能拿原始材料直接用。真实的问题记录是杂乱的,混着无关上下文和当时的讨论过程,直接当被测输入,考察的东西就不纯粹了。所以先把原始材料清洗成规范化的问题描述文件,再进评测。清洗产物按正式文档的标准写:背景、现象、约束条件交代清楚,让被测方拿到的信息量和当时真正处理这个问题的人对齐——多了会泄题,少了又考不出真实水平。

清洗时同时做答案隔离:输入不得泄漏修复答案。原始材料里经常带着结论,比如问题记录的末尾往往跟着当时的处理方式。隔离不干净,评测测的就是「抄答案」,分数再高也说明不了任何能力。

Judge 是尺子,尺子要锁定

judge prompt 决定每个 case 怎么打分,它就是评测的尺子。对尺子我只有一条规则:版本锁定,不许原地修改。发现 judge 写得不好,正确的做法是新建 v2、把评测配置切换过去,然后重跑全部历史 case,之后才采信新分数。

尺子变了,历史分数就全部失效。原地改过 judge 之后,上周的分数和今天的分数出自两把不同的尺子,横向比较失去意义——而评测存在的全部价值就在于比较。锁定版本麻烦,重跑历史 case 也有成本,但这是让分数可信的最低代价。

最常见的破坏方式很隐蔽:复核时看到一个明显的误判,顺手在 judge 里补一句规则。单看这是修复,实际效果是悄悄换了尺子,这次的分数和所有历史分数从此不可比。所以哪怕只改一句,也要走新建版本加重跑的流程。

只评语义,不评形式

judge 的评分标准里写死了几条,针对的都是评测里最常见的假阴性——答案是对的,分数是错的:

  • 不因代码写法、措辞、排版与标准答案不同而扣分。考察的是语义等价,字面差异不计。
  • 标准答案支持多解。一个问题存在多种正确解法时,golden 里并列写出,被测答案与其中任何一解语义一致即命中。
  • 只读评测不因「没改文件」扣分。有些评测场景里,被测 Agent 只允许读仓库、输出分析文档,这时评测是「文档对文档」的协议,judge 拿输出文档和标准答案比对。Agent 没有实际改文件、没有跑命令,都不构成扣分理由,协议本身就没要求这些。

这几条合起来就是:评意图和语义,不评实现路径的字面形态。

评测集本身就是知识

一个 case 由真实问题加验证过的解答组成,这本身就是知识。所以评测集按知识的标准来维护:

  • 对 AI 友好,也对人友好。格式规范到 Agent 能直接解析,人打开也能直接读懂。一份好的评测集读起来应该像一份可靠的 FAQ,翻一遍就了解这个领域常见的问题和正确的处理方式。
  • 跟随仓库一起迭代。代码或知识变了,相关 case 同步修订,评测集不做只增不改的档案。
  • 配几个自检 case。答案显然、必须得满分的用例,用来验证评测链路本身没坏。链路坏了跑出来的分数,比没有分数更危险。

可重放,可审阅

被评测对象固定用 commit 来 pin,不用分支。分支会移动,钉在分支上的评测过一个月就无法复现;钉到 commit,任何时候都能重放当时的组合,结论才经得起复查。代码仓库、Skill、知识库内容都适用这条:凡是会随时间变化的被测对象,评测记录里都要留下一个确切的版本坐标。

结果输出有两条要求。第一,不能只给总分。每个失败的 case 要能定位到:原始问题是什么、参考答案是什么、AI 实际答了什么、judge 的分析和证据在哪里。只有总分的报告没法行动,分数跌了之后,第一个要回答的问题一定是「跌在哪」。

第二,提供两条消费通道。一条是结构化产物,Agent 可以接着处理,比如批量归因失败的 case;另一条是人可以直接审阅的页面。同一份结果服务两类读者,哪一条断了,评测都会退化成只有跑的人自己看的报表。结果放在稳定目录里,保留模型与配置组合、输入、输出和评判依据,任何一次运行可追溯,任意两次运行可横向比较。

复核结论要改回源头

评测跑完、人工复核完,结论不能停在一次报表上。复核发现知识错了就改知识,提示词弱了就改提示词,Skill 缺覆盖就补 Skill。评测报告只是中间产物,最终产出是对系统本身的修正,而下一轮评测的分数,就是这次修正的验证。

评测的全部意义,是让「有没有变好」从感觉变成可以回答的问题。尺子本身不产生进步,但没有尺子,进步和退步都看不见。