跳到正文
Hacker News· gghootch·· 10 天前AI 评分62

LLM 评测在 CI 中有多嘈杂?

How noisy are LLM evals in CI?

AI 导读

作者统计了公开仓库 GitHub Actions 默认分支上连续两次运行的评测结果,发现通过的评测在下一次运行中失败的比例约为 2.5%,五个仓库合计 2690 条评测中有 68 条出现这种情况。

正文 · AI 翻译

你修改了一个提示词,提交了一个拉取请求,然后你的评估套件从 48 个通过变成了 46 个。是你的改动破坏了两个评估,还是它们本来就会失败?我们测量了在公共仓库的 CI 中,通过的评估在下一次运行时失败的概率,以及一个套件能透过这种噪声看到多大的破坏。

通过的评估大约每 40 次会有 1 次偶然失败

我们提取了每个仓库从 GitHub Actions 在其默认分支上上传的评估结果,将连续的运行对齐,并统计了在一次运行中通过、在下一次运行中失败的评估。从未到达模型的失败(缺少 API 密钥、速率限制、超时)被排除在外。

仓库 格式 每次运行的评估数 运行次数 通过的评估在下一次运行中失败
JRichlen/agent-plugins promptfoo 约 12 33 389 个中的 2 个(0.5%)
PrefectHQ/prefect-mcp-server pytest、JUnit XML 21 27 508 个中的 0 个(0%)
abpai/skills JUnit XML 28 12 272 个中的 2 个(0.7%)
launchdarkly/ai-tooling promptfoo 59 13 651 个中的 18 个(2.8%)
evloghq/evlog JUnit XML 约 17 53 870 个中的 46 个(5.3%)

在这五个仓库中,2,690 个通过的评估中有 68 个在下一次运行中失败:2.5%。提交在运行之间落到了默认分支上,因此其中一些失败可能是真实的更改。这些比率是仅由偶然因素导致的上限。

更大的套件本身就显示出偶然性。onyx-dot-app/agent-wiki 每晚运行约 2,268 个评估。在 56 个夜晚中,中位数为 73 个评估在某一晚通过后下一晚失败,且计数从未达到零。在代码完全没有变化的 22 个夜晚中,中位数为 73.5。在整个期间,2,268 个评估中有 541 个至少通过和失败各一次。

在 2.5% 到 3% 的情况下,一个包含 50 个评估的套件在任意两次运行之间会丢失一两个,而一个包含 300 个评估的套件会丢失八九个。一个没有改变任何内容的拉取请求经常会显示“回归”。

我们排除的两个套件,以及原因

另外两个仓库被测量但被排除在表格之外,因为它们的数字测量的不是模型。

  • 0% 但什么都没测量到。 TimMoyence/Innov-mind-museum 的结果中唯一的评估在每次运行中都通过。它调用的端点返回 HTTP 401“需要令牌”,而评分器将错误消息接受为答案。
  • 45%。 chmonitor/chmonitor 的评估调用其位于 Cloudflare 之后的生产端点。其许多输出是 Cloudflare 质询页面(“稍等片刻……”)和“Worker 超出资源限制”错误,而不是答案。

这很常见。在 PrefectHQ/prefect-mcp-server#200 上,20 个评估中有 14 个在第一次推送时失败。这 14 个都是同一个 404:评判模型 claude-opus-4-1-20250805 已被弃用。该拉取请求没有破坏任何东西,下一次推送更换了评判模型,全部 20 个都通过了。(案例研究)

在你从通过率中读出任何含义之前,请检查每个结果,无论通过还是失败,都来自模型,而不是来自错误页面。

你的套件能看到多大的破坏?

假设套件中每个通过的评估在任意一次运行中偶然失败的概率为 p。那么一次没有真实更改的运行会丢失大约 n × p 个评估。一个拉取请求的失败只有在数量超过 20 个拉取请求中 19 个由偶然因素产生的失败时才会凸显出来。以下是更改必须破坏多少个评估才能达到这一点:

套件 每次运行偶然失败 超出偶然的阈值 一半情况下能看到的破坏 80% 情况下能看到的破坏 看到 2 个评估破坏的概率
20 个评估,2% 噪声 0.4 3 个失败 3 个评估 3 个评估 30%
50 个评估,3% 噪声 1.5 5 个失败 4 个评估 5 个评估 17%
100 个评估,3% 噪声 3 7 个失败 4 次评估 6 次评估 17%
300 次评估,5% 噪声 15 22 次失败 8 次评估 11 次评估 11%
2,268 次评估,3.2% 噪声 73 88 次失败 16 次评估 23 次评估 6%

当你在整个套件中统计失败次数时,更大的套件并不能更好地发现小的破坏。它的偶然失败会随之增长,而一些真正的失败会淹没其中。上面那个 2,268 次评估的套件可能因一次真实回归而丢失 15 次评估,而且它往往看起来和任何一个平常的夜晚没什么两样。

它能发现的是某个从不失败的评估出现了破坏。在 HolmesGPT/holmesgpt#1452 中,一次重构停止向 agent 发送其 runbook 目录,一个 8 次评估套件中的某个评估失败了:162_get_runbooks,而它自 1 月初以来的全部 230 次运行都通过了。该仓库自己的评估检查仍然显示绿色。像这样的评估出现单次失败并非偶然。(案例研究)

什么有帮助

测量你自己的噪声。在未更改的默认分支上重新运行你的评估,或者保留每次默认分支运行的结果,并统计通过的评估失败的频率。其他每一个决策都取决于这个数字,而且不同套件之间差异很大,如表所示。

将每个评估与其自身历史进行比较。不要将通过率与固定阈值进行比较。将拉取请求的结果与基础分支上相同的评估配对,并根据该评估此前失败的频率来判断一次失败。一个从不失败的评估出现失败,比每周都失败的评估出现三次失败更有意义。

将错误与答案分开。缺少密钥、速率限制、已退役的模型或错误页面都不是结果。将其计为“无法运行”,而不是通过或失败。

以稳定的评估作为门禁,其余则跟踪观察。与特定行为绑定、每次运行都通过的评估,可以在单次失败时阻止合并。广泛的质量扫描更适合作为趋势来观察,而不是用作门禁。

重复重要的评估。将一次评估运行 3 到 10 次并判断通过的比例,可以降低其噪声。在 HumanEval 上,Meta 的研究人员发现,配对并取平均可将达到显著性的最小差异从 12 个百分点降低到 2 到 4 个百分点。如果你随后单独测试每个评估,请针对你测试的数量进行校正,否则有些评估会因偶然看起来像是坏了。

在相信单次失败之前先重新运行。一个偶然失败率为 3% 的评估连续失败两次的概率约为千分之一。在 launchdarkly/ai-tooling#189 中,该仓库自己的评估报告显示 configs-create 相对于 5 月记录的基线下降了 25 个百分点,并且其门禁在 onboarding(4 个中的 2 个)上失败。导致下降的那个测试在此拉取请求之前,也曾在 main 分支的 12 个夜晚中有 7 个夜晚失败,而重新运行同一提交则通过了门禁。(案例研究)

evalship 如何做到这一点

evalship 读取你的 GitHub Actions 已经上传的评估结果,并使用上述方法将每个拉取请求与你的默认分支进行比较。它会在默认分支上最近 30 次运行中测量每个套件的噪声,将偶然可以解释的失败标记为“在偶然范围内”,将无法运行的结果搁置一旁,并在拉取请求上评论剩下的内容。PR 分析如何工作 中有详细信息。它对公共仓库免费,设置只需将一段提示粘贴到你的编码 agent 中:开始使用。

延伸阅读

来源:Hacker News · evalship.com