评测分诊队列:为什么 FIFO 会错过那些至关重要的失败
一个健康的评估集(eval set)本应是成熟的标志。但在任何一个周一,它也可能意味着排队等待的 1,000 个失败案例,而人工审核员只有 8 小时,且每个案例的处理效率大约是 50 个。这笔账很残酷:大约每 20 个失败案例中只有一个会被阅读。剩下的 19 个只能等待。至于哪 19 个在等,哪一个能被选中,完全取决于文件加载的顺序。
大多数团队将其称为“审核失败案例”。但它更像是一场按字母顺序加权的抽奖。一个影响 2% 生产流量且位于文件顶部的失败案例会得到关注。而一个影响 40% 生产流量但位于文件底部的失败案例,即便能被看上一眼,也要等到周五下午。团队在周二发布了针对小问题的修复方案,并在周四写了一份回顾(retro),纳闷为什么仪表盘上的指标毫无变化。
仪表盘没变化的原因并不是团队选错了修复方案。对于摆在他们面前的案例,团队选择了正确的修复方法。问题在于把案例摆在他们面前的那个系统。评估失败是一系列包含大量元数据的 Bug 报告,这些元数据足以支撑一个优先级队列,但审核 员默认情况下永远看不到它们。而按 FIFO 处理的队列,在工程上的荒谬程度相当于在急诊室按病人进门的先后顺序进行分诊。
默认队列是 FIFO,而默认就是错的
任何值得使用的评估框架,其默认行为都是将失败案例丢进一个列表中。列表的排序取决于生成它的因素:Trace ID、时间戳、批处理索引或测试运行器的输出。审核员打开列表,从顶部向下滚动,开始阅读。
当失败数量足够少,审核员可以全部读完时,这没问题。但评估集的初衷就是要增长到超出这个范围。投入评估的全部意义在于发现比凭直觉和临时 Bug 报告更多的失败。一旦评估集开始发挥作用,队列就会溢出。这时,决定哪些失败案例会被审核,就成了整个环节中最关键的决策——甚至比你下一步要对 Prompt 做什么修改更重要。因为 Prompt 的修改取决于你从哪个案例中学习到了该修改什么。
FIFO 对这个问题的回答是:先加载哪个就看哪个。这不叫分类策略。这是策略的缺失。
失败案例的审核价值并不相等
排队等待的案例在审核员无法从列表视图中看到的维度上存在差异:
- 生产流量的代表性。 有些失败案例反映了每天 10% 的用 户都会遇到的查询模式。而另一些则反映了上季度只出现过两次的查询模式。修复前者能产生实质性影响;修复后者则更像是在雕琢手艺。
- 对用户的严重程度。 错误的退款金额和略显啰嗦的回答在评估中都会被记为“失败”。但它们并不是同一种失败。一个是监管事故,一个是风格琐事。
- 去重后的聚类大小。 队列中的 300 个失败案例可能实际上只是 12 种不同的失败模式在不断重复。逐一阅读的审核员会将大部分精力浪费在重复发现这 12 个模式上。
- 对抗性分片的覆盖率。 有些失败是大宗流量中的疏漏。有些则是越狱尝试、极端情况 Prompt 或合规性探测。由于大宗流量之所以被称为大宗是因为其数量庞大,导致对抗性分片在队列中的采样比例往往不足。让队列按频率自动排序会淹没这些对抗性案例。
- 距上次审核该聚类的时间。 你在两天前审核并修复过的失败模式今天不需要再次审核。而一个你从未打开过的失败模式,无论多么罕见,都是团队尚未掌握的信息。
FIFO 队列将所有这些情况视为同一个列表中的同一行。审核员的专业知识是整个环节中最昂贵的资源,却被浪费在碰巧排在最前面的案例上。
分类评分层是显而易见的解决办法,但没人付诸实践
正确的形态应该是具有明确评 分函数的优先级队列。评分不需要太复杂。一个远超 FIFO 的初版方案大概如下:
priority = traffic_share × severity × recency_decay
× adversarial_slice_quota_multiplier
/ cluster_size_already_reviewed
每一项都是团队可以根据现有数据计算出的一个列。流量份额是失败案例与生产日志的关联——上周的流量中有多少比例与此输入类似?严重程度是审核员为每个聚类填写的 1 到 5 的字段,评分会自动继承该值。新近度衰减从 1 开始,随着聚类上次审核以来的天数增加而下降。对抗性乘数是政策杠杆——提升罕见但重要分片的权重,使其不至于被大宗流量淹没。分母则降低了团队已经处理过的聚类的权重。
这是一个分类评分层的问题,而不是研究课题。它与每次值班轮换处理事故严重程度的优先级队列、每个产品团队进行 Bug 分类的逻辑、以及每个客服工具开箱即用的功能如出一辙。它在评估工作流中缺失的原因是,评估框架假设管道末端的人工会处理好这一切。而管道末端的人工只有 50 个案例的处理能力,根本没时间排序。
当有了评分机制,呈现出的形态会非常清晰。队列顶部将由代表真实生产权重、新出现或从未处理过的聚类占据,并点缀一些由政策杠杆强 推上来的对抗性探测案例。审核员的工作将用于修复那些真正能推动生产指标的失败案例。仪表盘终于开始动了。
分批处理:一次判断,全簇通用
分拣评分解决了接下来该看哪一个簇的问题。分批处理(Batching)则解决了审核员从单次阅读中能提取多少价值的问题。
原始的审核循环是打开一个案例,决定怎么处理,再打开下一个,再次决定。如果这两个案例在结构上是相同的失败——根因相同,只是表层文本不同——那么审核员就为一个决定支付了两次成本。在每名审核员每天处理 50 个案例的情况下,支付两次成本意味着浪费了一半的预算。
分批处理的原则是在案例到达审核员之前按形状对其进行聚类,然后每次展示一个簇,附带一个代表性案例和其背后的相似案例数量。审核员做出一次判断(“这个簇是长尾实体的检索缺失,转发给检索团队”),该判断就适用于簇中的每个案例。吞吐量从每天 50 个案例变成每天 50 个“簇”,按照典型的簇规模,这相当于将有效覆盖范围扩大了 2 到 10 倍。
聚类并非魔法。标准方法是对失败 Trace 的输入和输出进行嵌入(Embedding),运行聚类算法,并让审核员在审核期间为簇命名——贴上标签,该簇就变成了一个具名的桶,下次出现类似案例时就会加入现有的桶。这借鉴了定性研究中的开放式编码(Open coding)和轴心编码(Axial coding)。理论饱和(Theoretical saturation)——即 20 个新案例也不会揭示新的失败模式的时刻——是簇集目前已趋于稳定的信号。
经常这样做的团队会告诉你同样的事情:当他们第一次将评估集通过聚类处理时,表面上的 500 个失败会塌缩成大约 30 个真实的失败模式,工作量突然变得与团队规模相匹配,而不是受制于无穷无尽的队列。
无人辩护的对抗性切片配额
将生产流量占比作为主要输入的评分函数有一个已知的失败模式:它会对那些看起来不像大宗流量的案例采样不足。越狱尝试、受监管领域的边缘案例、来自安全研究人员测试系统的提示词——根据定义,这些都是罕见的。将它们的频率乘以严重程度,它们依然会被每个人都会遇到的案例淹没。
解决办法不是让数学去处理它,而是设定配额(Quota)。为对抗性和高严重性切片保留审核预算中明确的一部分——15%、20%,无论政策如何规定。配额是一种“队列顶部”策略:这些簇在达到配额预算之前会绕过正常的优先级计算,只有在当期配额满足后才会回落到通用队列中。
这是设计中经常遭到反对的部分。指标团队的论点是,按配额优先处理罕见案例在数学上是低效的——当有影响 30% 流量的案例在等待时,我们却在把审核时间花在影响 0.1% 流量的案例上。这个论点在平均意义上是正确的,但在长尾情况下是危险的。配额保护的案例是那些会提交给监管机构的案例,是那些在 Twitter 讨论串中浮现的案例,是那些在指标团队的仪表盘显示绿色后的第二天让系统宕机的案例。配额是团队的保险,防止出现“平均正确,长尾崩溃 ”的局面。
学会这一点的团队通常都经历过同样的教训:发布了一个在体积队列中得分很高的模型,结果却因为一个只有 3 个失败案例且从未被打开过的切片而遭到了合规升级。
当队列有了评分层,会发生什么变化
从外部看,这种转变微乎其微。审核员仍然在打开案例。评估框架仍然在发出失败信号。仪表盘仍然显示失败率。
但在底层,工作单位从“一个失败案例”变成了“一个失败簇”,工作顺序从“谁排在最前面”变成了“谁能用最少的审核时间撬动最多的流量,加上你承诺保护的切片”。由于团队选择修复的是那些有分量的案例,每季度实际改变生产指标的修复数量增加了。由于对抗性配额强制审核了那些会被体积队列淹没的案例,合规方面的意外减少了。
更难的转变是文化上的。团队必须接受“我们审核了本周所有失败案例”不再是目标——这本来就不是目标;目标是最大限度地了解系统在哪里崩溃,并发布那些重要的修复。审核所有案例是 FIFO(先进先出)时代的产物,是评估集小到可以进行穷尽式审核时的遗留物。评估集起作用了,它产出了太多内容。正确的反应是制定队列策略,而不是对那些没被阅读的案例产生负罪感。
架构上的觉悟
更深层次的感悟是,评估失败是一系列结构化的 Bug 报告,而 Bug 报告一直需要优先级排序。每个成熟的工程组织都会运行 Bug 分拣(Triage)流程——有上下文背景的人决定本周处理哪些 Bug,哪些滚动到下季度,哪些关闭为“不予修复”。评估失败看起来不同的原因是它们一次成千上万地到达,在提交时没有人工附加优先级,因此分拣步骤必须自动化,否则就不会发生。
一个以 FIFO 方式运行评估队列的团队,正在犯一个与按 Issue 提交顺序运行 Bug 追踪器的团队性质相同的结构性错误。当容量较小时,这种错误是隐形的。一旦容量超过了审核员的吞吐量,它就会成为浪费工程能力的头号原因。
修复方法不是新的基础设施。评分函数只有 20 行代码。聚类只是一个嵌入处理和一个 K-means 调用。对抗性配额只是一个配置字段。难点在于决定队列策略是否值得投入——“我们先看哪些失败”是一个有书面答案的产品问题,而不是一个由文件系统排序决定的默认项。
那些发布最可靠 AI 产品的团队,并不是拥有最多评估案例的团队。他们是让审核员把时间花在能带来最高回报的案例上的团队。其他人都在为简单的案例发布修复,而有影响力的失败案例正在排队等待。
- https://hamel.dev/blog/posts/evals-faq/
- https://langfuse.com/blog/2025-08-29-error-analysis-to-evaluate-llm-applications
- https://futureagi.com/blog/what-is-error-analysis-llm-2026
- https://www.braintrust.dev/articles/llm-as-a-judge-vs-human-in-the-loop-evals
- https://www.braintrust.dev/articles/human-in-the-loop-evals-for-llm-apps
- https://newsletter.pragmaticengineer.com/p/evals
- https://alexstrick.com/posts/2025-05-23-error-analysis-to-find-failure-modes.html
- https://earezki.com/ai-news/2026-03-21-llm-evals-on-real-traffic-not-just-test-suites/
