跳转到主要内容

配额窗口机制重写后,这批夜间脚本是如何拖垮你的交互流量的

阅读需 1 分钟Tian PanTian Pan

一个稳定运行了十个月的 cron 任务是你系统中最危险的任务,因为其中的代码没变,你的代码也没变,唯一改变的是别人发布的、你们团队没人读的发行说明(release notes)中的一句话。那个每晚在 00:05 UTC 启动、在十分钟内清空工作队列并重新进入休眠状态的每晚 embedding 刷新任务,曾是教科书般的范例。它通过在用户醒来前占用几分钟刚重置的每分钟配额,并在当天的剩余时间内保持在每日配额之内,从而与白天的交互式流量和谐共存。接着,服务商重写了每日窗口的核算方式,保持了每分钟窗口不变,并保留了你客户端测试的所有签名。批处理任务依然稳定运行。但每晚 00:13 UTC,交互界面开始返回 429 错误。团队一直在追查一个根本不存在的、本应在一周后才开始的上游维护窗口。

Bug 从不在你的代码里。Bug 在于“每日限制”不再是前一天的意思,而你的调度程序固定在了一个与旧定义对齐的时钟边界上。这篇文章讨论的是:速率限制核算(rate-limit accounting)作为一种服务商可以在不破坏任何签名的情况下修改的契约;两个独立正确的调度是如何组合成拒绝服务模式的;以及如何通过架构手段让 cron 任务不再是一个连接到别人时钟上的定时炸弹。

每日配额不是一个名词,而是一种核算策略

当服务商告诉你每日限制是一百万 token 时,他们并没有告诉你“每日”是什么意思。他们只是给了你一个数字。这个数字是一个预算;而决定预算何时补充的策略是一个独立的对象,文档通常会对后者含糊其辞。两个服务商都可以说“每天一百万 token”,其中一个在 00:00 UTC 重置计数器,而另一个则跟踪一个滚动二十四小时桶(rolling twenty-four-hour bucket),你昨天 03:00 消耗的每个 token 都会在今天 03:00 释放。你的应用程序看到的是同样的标题数字。你的批处理任务看到的则是两个完全不同的世界。

在 2026 年,两家服务商都发布了关于分钟级 RPM 和 TPM 上限的滚动窗口文档——这是六十秒持续滑动的核算,随着时间的推移,每个请求留下的足迹都会变小。而策略模糊性存在于每日上限、每组织支出上限和突发允许量中。有些是按日历对齐的,有些是滚动的,有些曾经是前者并悄悄变成了后者。少数是根据信用池扣费的,信用池本身按日历补充,比如 Anthropic 在 2026 年 6 月转向独立的每月额度,用于自动化工作流,其扣减独立于交互式用户看到的对话窗口配额。

请将该单位视为一种策略,而不是一个名词。契约不是那个每日数字。契约是每日数字加上决定其何时补充的规则,而你只能控制其中之一。

为什么两个正确的时钟会组成一个错误的系统

你的 cron 任务和服务商的配额计数器各自运行起来都很合理。你的任务在 00:05 UTC 启动,因为那时上游数据分区保证已经结清,更早启动会导致刷新不完整。服务商的分钟窗口在每分钟的开始时重置,因为对齐挂钟分钟是运行全球速率限制计量层成本最低的方式。每个时钟独立地产生了一种有用的行为。但它们的设计初衷并非为了协同工作。

这种组合之所以能奏效,仅仅是因为两个时钟对“每日”的定义相同。当服务商将每日核算收紧为滚动二十四小时桶,同时保持分钟窗口与日历对齐时,这两个时钟就不再对称了。批处理任务慷慨地消耗了分钟配额,并在运行的前八分钟内耗尽了滚动的每日分配量,因为所有这些 token 现在都是针对接下来的二十四小时扣除的,而不是针对今天。从 00:13 开始,共享该密钥的所有其他工作负载的每日桶都是空的。八分钟的批处理任务并没有消耗八分钟的容量;它消耗了二十四小时的容量。

429 错误也没有告诉你任何有用的信息。它们指向同步界面,因为那是仍在尝试发出请求的界面。批处理任务报告运行成功,因为它在桶排空之前已经完成了工作。两个了解真相的系统在“这是谁的问题”上产生了分歧:批处理任务说“我没问题”,交互界面说“上游坏了”,没有任何一个仪表板能将这两者视为同一个事件的两个侧面。

隐式默认故障模式及其变体

这个模式有一系列变体。你的 cron 任务与供应商的重置时间之间的冲突只是更广泛 bug 类别的一个实例:其行为取决于供应商有权更改的值,而你的客户端从未明确声明该值,你的测试也从未验证过它。隐式的 max_tokens 默认值、静默的分词器(tokenizer)升级、自动调整复杂性分类器的自动路由——这些都具有相同的特征。你签署了一份以 JSON 请求和响应为表现形式的合同,但该合同的行为取决于隐藏在背后的规则,而供应商会按照自己的节奏修订这些规则。

速率限制计费是该家族中最危险的成员,因为它与时间紧密相关。响应字段的变化会在下一个请求到达时立即显现。而配额窗口计算方式的变化,只有当利用旧语义的工作负载模式再次运行时才会显现。如果你的批处理任务每晚运行,而供应商在周二发布了更改,那么在 UTC 时间周三 00:05 之前你都可能没意识到。第一晚看起来像随机噪声。第二晚看起来像是个规律。到第四晚,轮值运维(on-call)开始排查上游维护窗口,因为症状看起来就是那样,而实际的根本原因可能是发布日志(release-notes)文件中的一句话,将其归类为“改进的每日使用平滑处理”。

会员专享

余下内容仅对会员开放。

会员可读完整内容 —— 每一个公开发布的观点背后,那些框架、决策与推理的全貌。

  • 完整文章,包含未公开存档的部分
  • 可落地的工作框架,附带权衡与决策依据
  • 新文章抢先看,先于公开发布

随时取消 · 一次订阅,畅读全部

参考资料

保持联系,关注我获取更多内容

阅读需 8 分钟

供应商配额在你的全球流量从未选中的时区重置

供应商配额按照供应商的时间重置,而不是客户的时间。当周期的临界结束点与你的流量峰值时区重叠时,429 错误看起来就像是随机噪声 —— 而 UTC 仪表板掩盖了背后的真相。

insider
llm-ops
阅读需 9 分钟

供应商 SLA 差距:为什么你的 LLM 提供商的运行时间忽略了导致产品崩溃的故障模式

你的 LLM 供应商 99.95% 的运行时间指标并不能覆盖拒绝率飙升、静默模型更新或配额驱动的降级。以下是能够涵盖这些情况的功能可用性检测方法。

insider
llm-ops
阅读需 9 分钟

隐形的交接:为什么生产环境中的 AI 故障集中在组件边界上

大多数生产环境中的 AI 故障并不是发生在模型内部,而是发生在不可见的缝隙中 —— 即一个组件的输出变成另一个组件输入的交界处。本文将探讨如何发现并强化这些边界。

insider
ai-engineering
阅读需 9 分钟

静默成功:当你的 Agent 宣告完成但实际上什么也没发生

Agent 往往因为喋喋不休而失败。自信的文字掩盖了工具错误,而写入操作从未真正提交。解决方案是:将模型的声明降级为假设,将工具响应和操作后探测提升为权威信号,并衡量效果落地而非单次对话的成功。

insider
ai-agents
阅读需 10 分钟

你的评测套件也是生产负载:当每晚测试耗尽线上流量配额时

当每晚运行的评测套件与线上产品共享同一个供应商组织账号时,一场由“嘈杂邻居”引发的生产事故就不可避免。本文将介绍如何隔离配额、根据 Token 影响对 PR 进行限制,并像对待真正的生产负载一样对待你的评测任务。

llm-ops
evaluation