这是我参加以下比赛的提交作品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,所以它们没有包含在结果中。
目标不是测试所有可用的模型,而是比较我已经在使用的模型组合与几个我好奇的模型。
发现
好的,首先让我们看看排行榜和第一轮运行的结果。
我们可以看到,最好的模型,得分为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 次尝试中都被正确回答。因此,简单的重试案例并不是真正区分这些模型的因素。当重试时机、更宽的上下文或多步状态变得重要时,差异才开始显现。
模型效率
现在让我们看看效率。我将使用第三次运行的图表,因为模型在三次运行中最终都落在图表中大致相同的区域。
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 竞赛之外还有那么多可探索的内容之后。

