这是我参加以下比赛的提交作品Kaggle 基准测试挑战赛

读过我文章的人都知道,其中很多实际上是对某些东西的基准测试。大多与 .NET 相关,但仍然可以说,这个挑战赛应该跟我平时做的事情相当接近。

事实恰恰相反。

构建代码基准测试和给 AI 模型做基准测试是两个不同的世界,所以当我第一次看到这个挑战赛时,我完全不知道该对什么做基准测试。在编码任务上比较模型感觉太泛泛了,而我也不想仅仅为了有个基准测试而创建一个基准测试。

然后我看了看我最近几篇文章的主题。很多都是关于 API、弹性、故障,以及系统在出问题时如何表现。这给了我一个实验想法:如果我用一个非常简单的问题来给 AI 模型做基准测试会怎样:重试还是不重试?

一个503 Service Unavailable并不自动意味着重试是安全的。一个POST请求可能已经被处理了。一个幂等键可以完全改变答案。超时可能发生在服务器收到任何东西之前,也可能发生在它已经改变了某些状态之后。

所以仅凭 HTTP 状态码往往是不够的。模型必须理解整个情况。

我基准测试了什么

想法很简单。我准备了几个 API 故障场景,包含关于请求、响应以及一些额外上下文的信息。模型必须返回两样东西:

  • 一个决策:YES,NO,或YES_AFTER_DELAY
  • 一句话解释原因

例如:

POST /payments
503 Service Unavailable

An idempotency key was supplied and the API guarantees
duplicate requests with the same key are not processed twice.

Retry?

开发者可能会立刻说:

YES_AFTER_DELAY

重要的部分不只是503。上下文告诉我们提供了一个幂等键,使用相同键的重复请求不会处理两次支付。但 AI 模型会注意到同样的事情吗?更重要的是,当场景不那么明显时会发生什么?

对于这个小实验,我准备了答案取决于 HTTP 方法、状态码、幂等性、速率限制等细节的场景。

每个模型都得到相同的响应格式:

Decision: YES | NO | YES_AFTER_DELAY
Reason: <one sentence>

评分时,我只使用Decision。这使基准测试保持简单且确定。这个Reason不影响分数。我收集它是因为它能显示模型是真的理解了场景,还是只是出于错误的原因得出了正确答案。这也让基准测试易于在不同模型之间进行比较。每个场景都有一个预期决策,因此最终结果可以简单地按模型答对的 retry 决策百分比来计算。

但为什么要对它做基准测试呢?

想象一下,你正在集成一个外部服务,并希望让调用更具弹性。在如今 AI 辅助编程的世界里,很有可能会由一个编程智能体来完成这项工作。AI 可以轻松生成一个重试策略。更有意思的问题是:它会重试正确的请求吗?因为重试本不该重试的东西,可能比完全不重试糟糕得多。

这正是我想测量的。现在让我们来看看这些场景。

场景

我准备了 14 个场景,每个都基于 Kaggle 基准测试中实际使用的一项任务。最初,它们全都包含在文章里,但那让文章对于一个小实验来说太长了。所以我决定把完整的场景移到一个独立应用中,在那里你可以浏览每一项任务及其预期答案和解释。如果你想先自己试一试,那里还有一个面向人类的测试。毕竟,为什么不也给人做做基准测试呢?😁

试试人类基准测试或浏览所有场景:重试还是不重试?

以下是实验中使用的场景:

  • 安全支付重试- 一个支付请求失败并返回503 Service Unavailable,但幂等键保证重试不会产生重复支付。
  • 不安全支付重试- 一个支付请求返回503 Service Unavailable并带有Retry-After,但没有幂等键,因此重试可能会产生重复扣款。
  • 幂等 PUT- 一个PUT请求成功发送,但在响应到达之前连接丢失。
  • 限流订单- 一个订单请求在处理前被限流,并返回429 Too Many Requests并带有Retry-After。
  • 支付详情冲突- 一个多步骤支付流程到达/payments/details,它返回409 Conflict并带有transient-error: false。
  • 非幂等 PATCH- 一个PATCH请求会增加库存,但在客户端得知更改是否已应用之前连接丢失。
  • 幂等 DELETE- 发送了会话删除请求,但在响应到达之前连接就断开了。
  • Service Unavailable- 一个GET请求收到503 Service Unavailable连同Retry-After响应头。
  • Stale Resource Version- 一个资源被另一个客户端更新,因此一个PATCH使用旧 ETag 的请求失败,返回412 Precondition Failed。
  • Rate Limit Without Retry-After- 一个安全的GET请求反复收到429 Too Many Requests,但服务器没有提供Retry-After响应头。
  • I'm a Teapot- 一个咖啡请求收到了传说中的418 I'm a Teapot响应。
  • Eventual Consistency- 一个新创建的资源在另一个服务尝试使用它时立即返回404 Not Found。
  • Expired Filter Workflow- 一个临时过滤器超时数次后最终返回404 Not Found,因此原始过滤器无法再使用。
  • Cached Cart Options- 一个购物车选项请求失败,返回504 Gateway Timeout,但一个最近过期的缓存响应仍然可以通过stale-if-error获取。

测试的模型

挑选模型相当直接。首先,我选了我工作中经常使用的模型:GPT-5.6 Luna和GPT-5.6 Sol。根据我的经验,Luna 非常高效。它比 Sol 犯更多错误,但使用的 token 也明显更少。

然后我加入了Gemini 3.7 Flash和Gemini 3.1 Pro Preview。我在文本生成和 CSS 样式方面都大量使用过这两者。

我还加入了Claude Sonnet 5和Claude Haiku 4.5。我以前大量使用 Claude 进行编码,但如今我大多转向了 GPT-5.6,因为它在我的工作流程中感觉更高效。

最后,我加入了GLM-5和DeepSeek-R1,主要是出于好奇,想看看它们表现如何。

我也尝试加入 Grok,但我立刻遇到了404 Not Found错误,无论是Grok 4.5还是Grok 4.6,所以它们没有包含在结果中。

目标不是测试所有可用的模型,而是比较我已经在使用的模型组合与几个我好奇的模型。

发现

好的,首先让我们看看排行榜和第一轮运行的结果。

Kaggle leaderboard

我们可以看到,最好的模型,得分为1.00,是GPT-5.6 Sol,紧随其后的是Gemini 3.7 Flash,只有一个错误。然后是四个各有两个错误的模型,DeepSeek-R1有三个,Claude Haiku 4.5以五个错误排在最后。

当我们仔细观察时,可以看到大多数模型在Unsafe Payment Retry任务中犯了错误。这个任务本身因为Retry-After响应头而很棘手,但重复该请求可能会导致重复扣款。只有GPT-5.6 Sol和Gemini 3.1 Pro Preview回答正确。其余模型返回了类似这样的内容:

Decision: YES_AFTER_DELAY
Reason: The server explicitly requests waiting 10 seconds before attempting the request again.

所以大多数模型都掉进了跟随明显 HTTP 信号的陷阱,即使它与更广泛的上下文相冲突。Retry-After说“重试”,但一个没有幂等性的支付说“也许别重试”。说实话,我很高兴大多数模型都失败了。这对作者来说是个小小的胜利,他有时还是能骗过 AI。😄

我也很惊讶,有些模型在具有幂等 HTTP 方法的任务上失败了:幂等 PUT和幂等 DELETE。但它们并没有完全失败。大多数模型回答YES_AFTER_DELAY因为它们假设这是一个临时网络问题,延迟后可以解决。

这些YES对比YES_AFTER_DELAY案例表明,模型可以理解重试是安全的,但在何时重试上存在分歧。这与误解场景不同。因此对于版本 2,我可以允许某些场景有多个有效的Decision值。所以在这些情况下,AI 实际上也训练了我一点。

三次运行,而非一次

那只是第一次运行,但我不想把整个实验建立在单次运行上,所以我又运行了同样的基准测试两次,以检查模型稳定性并收集更多数据。

模型 第 1 次运行 第 2 次运行 第 3 次运行 平均
GPT-5.6 Sol 14/14 14/14 13/14 13.67
Gemini 3.7 Flash 13/14 13/14 13/14 13.00
Gemini 3.1 Pro Preview 12/14 12/14 13/14 12.33
GLM-5 12/14 13/14 12/14 12.33
Claude Sonnet 5 12/14 12/14 12/14 12.00
GPT-5.6 Luna 12/14 12/14 12/14 12.00
DeepSeek-R1 11/14 10/14 10/14 10.33
Claude Haiku 4.5 9/14 9/14 9/14 9.00

总体而言,结果实际上相当稳定。在 112 个模型-场景组合(8 个模型 × 14 个场景)中,有 102 个在三次运行中具有相同的正确/错误结果,约为91%。但有趣的是,相同的分数并不总是意味着相同的行为。有些模型在正确决策的数量上保持稳定,却改变了哪些场景出错。三次运行中都是12/14的分数并不一定意味着模型每次都做出相同的决策。

这在过期过滤器工作流中尤为明显,有四个模型在运行之间改变了结果:GPT-5.6 Sol、Gemini 3.1 Pro Preview、GLM-5,以及DeepSeek-R1。甚至GPT-5.6 Sol,它在前两次运行中得分为14/14,在第三次运行中也将答案从YES改为YES_AFTER_DELAY。它仍然决定该请求应该重试,但在何时。这正是让我质疑精确YES与YES_AFTER_DELAY评分是否总是正确方法的那类场景。

额外的运行也让不安全支付重试这一发现更有说服力。它只被正确回答24 次尝试中仅 6 次,仅25%。更有趣的是,结果在各次运行中完全一致。GPT-5.6 Sol和Gemini 3.1 Pro Preview三次全部答对,而其他六个模型每次运行都答错。所以这看起来不太像随机的模型差异,而更像是模型在解读该场景时的一个系统性陷阱。

另一方面,14 个场景中有 7 个在全部 24 次尝试中都被正确回答。因此,简单的重试案例并不是真正区分这些模型的因素。当重试时机、更宽的上下文或多步状态变得重要时,差异才开始显现。

模型效率

现在让我们看看效率。我将使用第三次运行的图表,因为模型在三次运行中最终都落在图表中大致相同的区域。

To Retry or Not to Retry?: Score vs. Total Cost Pareto

GPT-5.6 Luna似乎是最高效的模型,我并不感到意外。我在工作中主要使用它,因为它便宜,而且通常能让我接近最终解决方案。

Claude Haiku 4.5也位于高效区域,但它的结果在所有测试模型中是最差的,而且仍然比GPT-5.6 Luna更贵。

其余模型都位于右上角,我真的很惊讶DeepSeek-R1竟然成为第二贵的模型。于是我查看了它的输出,发现DeepSeek-R1的回答长度在 4,000 到 9,000 个字符之间,尽管它本应只用Decision和Reason来回答。所有其他模型都遵循了这一指令,所以在这种情况下,成本差异不仅与 token 定价有关,还与模型对所需输出格式的遵循程度有关。

我学到了什么

当我设计这个实验时,我其实担心这些场景对当今的 AI 模型来说会太简单。结果表明,情况并非总是如此。是的,我肯定可以改进这个基准测试,尤其是YES和YES_AFTER_DELAY但即便是最强的模型,一旦决策取决于不止是显而易见的 HTTP 信号,仍然会犯错。真实系统中的重试决策并不总是那么简单。AI 确实能帮助我们推理那些困难的情况,但更广泛的上下文仍然很重要,盲目遵循模型的答案可能是有风险的。

我的基准测试

重试还是不重试?基准测试

Kaggle 对我来说完全陌生,说实话,我很高兴是通过 DEV.to 挑战赛发现了它。我本以为搭建过程会复杂得多,但直接在 UI 中创建这个小型的实验性基准测试竟然出奇地简单。我肯定会更深入地探索 Kaggle,尤其是在看到除了传统 ML 竞赛之外还有那么多可探索的内容之后。