Eval 异味目录:让你的 LLM 评估套件比没有评估还糟糕的反模式
我去年合作过的一个团队拥有一套包含 847 个测试用例的评估套件,仪表盘一片绿色,发布节奏从外部看非常有纪律。然而,他们的旗舰摘要功能开始为大约二十分之一的客户支持线程生成言之凿凿的错误摘要。该能力的评估得分在连续六个月里一直保持在 94%。当我们对这套套件进行审计时,发现问题并不在于评估在撒谎。问题在于这些评估已经悄然腐化,测量了错误的东西,惩罚了正确的模型行为,并与它们正在评估的模型共享盲点。这套套件并不是像传统测试那样以一种响亮的方式崩溃,而是像温度计一样坏掉了——无论你把它放在哪里,它都显示室温。
测试异味(Test smells)在传统软件领域已经被研究了二十年。Van Deursen 的目录、xUnit 模式分类以及更近期的工作都记录了那些看起来正常的测试如何能积极地损害代码库——通过编码错误的规范、使重构变得昂贵、以及制造让真正的 bug 隐藏得更深的虚假信心。LLM 评估是一个非常新的领域,以至于同类的文献几乎不存在,但同样的动态已经发生在我 交流过的每个 AI 团队中。不同之处在于,LLM 评估异味具有传统测试所不具备的机制:训练数据重叠、随机输出、评委模型反馈循环、能力漂移。你不能只是简单地移植旧的分类体系,你需要一个新的。
接下来是我一直在积累的目录——我见过的五种将评估套件变成负担的“异味”,以及每种异味的重构模式。目标不是抛弃你的评估,而是恢复它们的信号,就像测试重构可以恢复生产代码中的信号一样。
异味 1:训练-评估数据泄漏 (Training-Eval Data Leakage)
这种异味表现为:你的评估得分高得可疑,但与用户实际体验到的功能不相关。具体来说,在你的模型可能看过的任务上得分很高,而在它可能没看过的任务上得分很低。
这是被记录最多的 LLM 评估失败案例,它至少有三个团队容易混淆的机制。首先是经典的基准测试污染:模型在包含测试集的语料库上进行了训练,因此评估测量的是记忆力而非能力。其次是微妙的操作性泄漏:你的评估集是使用你正在评估的同一个模型生成的(例如由 GPT-4 生成的“黄金数据集”,然后你用它来测试 GPT-4 变体),分布会悄悄地编码评估者的偏好。第三是 LLM 作为评委(LLM-as-judge)设置中的偏好泄漏,其中评委模型是在被评判模型产生的数据(或与其密切相关的数据)上进行微调的,因此评委系统性地高估了某些风格。
最近的研究表明,即使是缓解策略也可能无法实现其初衷。通过代码重构来掩饰基准测试、合成扰动、改写——在所有设置下,没有一种方法能既有效又忠实于原始评估意图。实际的教训不是污染无法修复,而是任何超过六个月的评估集,特别是从类网页资源中整理或由前沿模型生成的,都应该被视为已污染,直到证明并非如此。
重构方法:按出处拆分你的评估套件。为每个案例标记其来源——生产环境合成、手写、公开基准测试、模型生成。按每个存储桶(bucket)跟踪分数,而不是汇总分数。当不同桶之间的差距很大时(公开基准测试比新鲜的生产衍生案例高出 15 分以上),你就获得了一个可以采取行动的污染信号,而不是一个谜团。添加一个源自过去 30 天生产数据的“新鲜案例”队列,并将其回归的权重设置得比旧的公开桶更高。随着时间的推移,将公开基准测试案例完全从准入(gating)套件中移除,仅保留作为历史参考。
异味 2:脆弱的精确匹配断言 (Brittle Exact-Match Assertions)
这种异味表现为:你的测试结果是红色的,但输出是正确的。“答案是 42” 无法通过预期的 “42” 验证。JSON 键顺序的交换破坏了断言。一个开始将数字包裹在单位中的模型(例如 “42 ms” 而不是 “42”)摧毁了你的通过率,而且没人能分清这种回归是真的还是假的。
这直接移植自传统测试异味文献,其中“断言轮盘赌”(Assertion Roulette)和“脆弱测试”(Fragile Test)已被分类多年。在 LLM 评估中,它呈现出一种特别有害的形式,因为自然语言没有规范的表面形式。模型生成成千上万种有效的改写之一,而你的断言只接受一种。每次模型升级都会洗牌措辞的分布,而你的评估套件会因为模型漂移到另一种有效的形式而惩罚它。
精确匹配仍然有其地位。格式至上的结构化输出(SQL 查询、函数调用、机器可读的枚举)应该使用精确或近乎精确的相等性进行检查——这不是异味,这是正确的工具。这里的异味是将精确匹配应用于你实际上关心语义等价的自由形式输出。
重构方法:将自由形式的断言提升到语义层。对于简短的事实性回答,使用提取并归一化断言(解析数字、去除单位、进行比较)。对于较长的输出,优先选择基于属性的检查(“答案包含正确的实体”、“答案不违背来源”、“答案不超过两句话”),而不是全字符串相等。对于真正的开放式文本,使用具有明确标准的量表评分评委评估(rubric-graded judge evaluation),而不是 BLEU 风格的词汇重叠。当你确实需要根据参考文本进行断言时,使用具有质量控制阈值的语义相似度,而不是精确匹配——并固定用于比较的嵌入模型,以便阈值在不同运行中保持意义。
坏味道 3:评测腐化 (Eval Rot)
现象:你的评测套件通过了,但用户却在抱怨,这两组信号不再重叠。生产环境出现的失败方式是你的套件从未设计去检测的,因为你的套件是针对两代前的模型失败模式设计的。
在传统测试中,这有时被称为“测试腐化”或“测试相关性衰减”,但 LLM 评测腐化的速度更快、更无情。模型能力每几个月就会发生变化。产品界面在扩张。用户行为在适应模型的怪癖,这改变了输入分布。一个在 2024 年初围绕 GPT-4 的拒绝模式编写的评测套件,很大程度上是在测量一个已经不存在的产物——更糟糕的是,它在因为新模型没有重现旧行为而惩罚它们。该套件测量的不是质量,而是与旧模型特有习性的向后兼容性。
这里有一个特定的子坏味道,我称之为“能力锚定”(capability pinning):编写断言是为了要求特定的推理路径(“响应必须提到中间步骤 X”),而不是正确的结果。当一个能力更强的模型通过不同的路径得出正确答案时,测试就会失败,而一名急于求成的工程师会通过提示词(prompting)将模型“修复”回旧路径。能力锚定会将你的评测套件变成一种回归强制机制,积极地抵制模型的改进。
重构:强制执行评测新鲜度 SLO。每个案例都有一个最后评审日期。超过阈值(90 天是一个合理的起点,对于快速演进的功能则更短)的案例会被标记为待评审——不是自动删除,而是要求在它们能够拦截发布之前,根据当前的生产失败模式重新检查。将此与持续摄取管道相结合:采样生产环境追踪记录,标注有趣的失败案例,并将这些失败转化为新的评测案例。那些将评测集视为当前失败模式的活跃索引,而不是过去模式的化石的团队,不会遇到腐化问题。
具体来说,审计你当前的断言是否存在能力锚定。对于每个命名了特定中间步骤的断言,请问:如果模型通过不同的路径得出了正确的最终答案,这个测试会通过吗?如果答案是否定的,且该路径可能更好,请重写断言以检查结果,而不是路径。
坏味道 4:裁判模型勾结 (Judge-Model Collusion)
现象:你的 LLM-as-judge 评测给那些人工评审认为平庸的输出打出了极高的分数。当裁判与生产模型属于同一模型家族时,评分始终较高;而当你换成不同家族的裁判模型时,评分就会下降。你的裁判模型所持的观点,看起来极其可疑地像生产模型训练时所遵循的偏好。
这种机制已被深入研究。裁判模型更倾向于在其自身分布下具有更低困惑度(perplexity)的输出——这意味着由相似模型生成的输出,或针对相似偏好信号进行微调的输出,会系统性地获得更高评分。这就是“自我偏好偏差”,即使对裁判进行精细的提示词工程,这种偏差依然存在。再加上位置偏差(裁判倾向于根据模型家族和提示词结构选择靠前或靠后的选项)、冗长偏差(无论内容如何,更长、更流利的输出都会获得更高评分)以及对齐偏差(裁判倾向于符合其自身政策偏好的输出),你得到的评测衡量的是与裁判的风格一致性,而不是质量。
更深层次的失败模式是反馈循环勾结:裁判模型和生产模型共享盲点。两个模型都会产生的幻觉不会被标记。两个模型共有的事实性缺失变得不可见。你的评测永远无法看到其自身认知底线以下的东西。
重构:将裁判视为一个需要自身验证的组件,而不是事实来源。首先,构建一个人造标注的校准集——几百个具有代表性的生产案例并附带人工评分——并测量裁判与人工 的一致性。如果一个裁判在你的任务上与人工的一致性低于中等水平,那它就不是裁判,而是一个随机信号发生器。其次,利用裁判模型的多样性:在至少两个模型家族之间轮换,并将它们分歧严重的任何案例标记出来进行人工评审。第三,优先使用窄而具体的裁判提示词(例如:“此输出是否包含与源文件矛盾的事实陈述?”按布尔值评分),而不是模糊的提示词(例如:“评分 1-10 分”)。窄提示词在偏差下退化得更优雅。
最后,对于任何你自己的生产模型可能担任裁判或与裁判有共同祖先的基准测试,请在内部仪表盘中披露这一点。当勾结被贴上标签而不是被隐藏时,就更容易被发现。
坏味道 5:虚荣聚合指标 (Vanity Aggregates)
现象:你的评测仪表盘显示了一个巨大的数字。那个数字是绿色的。没人能告诉你它代表什么,而且当被追问时,那个数字会因为没人能解释的原因而波动。
这是传统“无信息测试名称”和“神秘访客”坏味道的 LLM 评测版,由于 LLM 评测套件通常跨越迥然不同的任务类型,这一问题被放大了。将事实性、指令遵循、安全性、语气和延迟混合成一个数字的单一聚合评分,从根本上说是一个公关(PR)指标,而不是工程信号。当数字变动时,你无法分辨是事实性变好了、安全性变差了、语气发生了偏移,还是延迟爆炸了。当数字持平时,你无法分辨是没有任何变化,还是相等的正向和反向变动互相抵消了。
需 要警惕的子坏味道是“跨严重性平均”。一个将“模型在任何情况下都不应回答的问题”案例与一个细微的“语气偏好”案例平均在一起的套件,隐含地声称它们同等重要。事实并非如此。在平均值下,关键案例的回归会被平庸案例的改进所稀释,当实际的安全状况下降时,聚合指标却可能朝着错误的方向移动,看起来像是在进步。
重构:将你的评测套件拆分为信号簇——事实性、指令遵循、安全性、格式合规性、延迟、成本——并独立报告它们。绝不要跨簇合并成一个单一的标量。在每个簇内,按严重程度进行分层:一个“绝不能失败”桶,其中的任何回归都会拦截发布;一个“核心质量”桶,会触发人工评审;一个“打磨”桶,仅供信息参考。一个提升了 3 点打磨分但降低了 1 点安全分的发布不应该上线,任何理智的仪表盘都不应该让它看起来像是进步。分层仪表盘能防止算术造假;而聚合仪表盘则在诱导造假。
核心坏味道:没有错误分析的评估
在所有这五种特定坏味道之下,隐藏着一种结构性的问题:运行评估却不对失败案例进行错误分析。一个只产生通过/失败数字,却没人坐下来阅读实际失败输出的测试套件,会积累上述所有的坏味道,因为这些坏味道只有在查看具体细节时才会显现。数据污染的案例在宏观统计上看起来很正常;你必须亲自核查才能发现模型是在背诵。脆弱的断言看起来像功能退化;你必须阅读差异对比(diff)才能发现输出其实是 正确的。过时的案例会一直通过,直到你发现它测试的行为早已变得无关紧要。错误分析——这种每周手动阅读 30 个失败案例的枯燥实践——是防止上述清单从源头上形成的关键活动。
跳过错误分析的团队往往将评估视为一种 CI 检查。进行错误分析的团队则将评估视为一种透镜,并随着观察对象形态的变化不断打磨这把透镜。第二类团队交付的 AI 系统经得起考验;第一类团队交付的系统看起来一直是绿色的,直到彻底崩溃。
周一早上该做什么
你不需要重写你的评估套件,你需要对其进行完善。按来源对案例进行标记,使数据污染可见。随机审计 20 个断言的脆弱性,并将最严重的问题转换为属性检查(property checks)。为每个案例标注最后审查日期,并停用任何超过新鲜度 SLO 且无人能解释其合理性的案例。将你的评判模型(judge)与人类标签进行对比测量,如果一致性很差,要么修复评判模型,要么停止使用。将你的仪表盘拆分为不同的集群,并拒绝发布任何掩盖了严重程度的汇总统计。并且每周坐下来,阅读 30 个失败的追踪记录(traces)并真实地去理解它们。
那些能够在模型升级、产品转型和能力跨越中幸存下来的套件,是维护者将其视为“活的基础设施”的套件。那些沦为虚荣指标仪表盘的套件,则是三个月都没人打开过失败案例的套件。其中的区别完全在于实践——这意味着解决方案就在你触手可及的地方。
- https://aclanthology.org/2025.acl-long.901/
- https://aclanthology.org/2025.emnlp-main.511/
- https://leak-llm.github.io/
- https://howiehwong.github.io/preference_leakage.pdf
- https://arxiv.org/abs/2410.21819
- https://llm-judge-bias.github.io/
- https://aclanthology.org/2025.ijcnlp-long.18.pdf
- https://testsmells.org/
- https://link.springer.com/article/10.1007/s11219-024-09663-7
- https://arxiv.org/html/2411.13768v3
- https://latitude.so/blog/why-ai-agents-break-in-production
- https://hamel.dev/blog/posts/evals-faq/
- https://eugeneyan.com/writing/eval-process/
- https://newsletter.ruder.io/p/the-evolving-landscape-of-llm-evaluation
- https://langfuse.com/blog/2025-08-29-error-analysis-to-evaluate-llm-applications
