配额饥饿:当你的 AI 功能相互消耗速率限制时
凌晨 2 点,一个定时报告生成任务向共享的 API 密钥并行发出五十个 LLM 请求。等到早上 9 点的产品演示开始时,每一个实时对话补全都在悄无声息地超时。错误仪表板一片绿色,日志里没有 429 错误。模型确实在返回响应——只是慢了十秒,而这个功能的 SLA 是两秒。
这就是配额饥饿。它不像故障,它看起来只是"今天 AI 有点慢"。
每个 LLM 提供商都会对 API 访问施加速率限制:每分钟 token 数(TPM)、每分钟请求数(RPM),或两者兼有。当一款产品拥有多个 AI 驱动的功能——聊天助手、搜索相关性评分、报告生成器、邮件分类器——而所有这些功能共享同一个 API 密钥时,它们就在争夺同一个有限的配额池。谁赢得竞争,完全取决于谁先发出请求。没有任何协商、优先级或公平性,纯粹是先到先得的机制。
这里真正的工程痛点不是速率限制本身,而是这种竞争是隐形的。你构建每个功能时都相信它拥有完整的配额访问权。你从来没有明确决定让批处理任务可以饿死聊天助手。优先级是在凌晨两点,由一个定时任务隐式设定的 。
为什么共享配额是一个隐藏的并发问题
主流提供商的速率限制远比单一上限复杂。Anthropic 独立跟踪输入 TPM、输出 TPM 和 RPM——合并的 token 计数可能具有误导性,因为一个生成长补全的功能可能耗尽输出配额,而输入配额依然充裕。OpenAI 的限制随层级和累计消费额扩展。Gemini Flash 在付费层级提供每分钟 400 万 TPM,无最低消费要求。
这些提供商共同的执行模型是:配额是一个密钥上所有请求共享的池,实时消耗,以稳定速率补充。补充是连续的——大致来说,40 万 TPM 的限制每秒补充约 6,700 个 token。当批处理任务保持五十个并发请求,且补全消耗 token 的速度超过补充速率时,桶就会一直空着。交互式请求到来时发现没有可用配额,只能等待。
这是分布式系统中资源饥饿问题在 API 上的具体体现。后台工作负载本身并没有做任何错误的事情——它发出了请求,消耗了配额,获得了结果。问题在于缺少任何为高优先级工作负载预留容量的机制。
不受约束的共享配额会产生三种故障模式:
饥饿(Starvation)——高吞吐量的批处理消费者持续占用配额,而低吞吐量的交互式请求永远无法获得足够的配额来继续执行。批处理任务不需要做任何恶意的事情,它只需要有足够的并行度来让池持续耗尽。
队头阻塞(Head-of-line blocking)——当请求按 FIFO 顺序排队时,一个消耗大量 token 的大型提示会延迟其后 的所有请求。原本期望两秒响应的用户,却要等待某个文档摘要任务消耗完配额窗口。
优先级反转(Priority inversion)——低优先级任务获取了高优先级任务所需的配额,高优先级任务只能等待。在共享配额系统中,这会悄无声息地发生:没有任何机制可以抢占或重排序。这种反转直到你开始排查延迟时才会浮出水面。
最小可行修复:分离 API 密钥
消除跨功能配额饥饿最快的方法,是停止共享密钥。为面向用户的交互式功能分配一个专用 API 密钥,为后台批处理工作负载分配另一个独立密钥。每个密钥拥有自己的配额封装。后台报告任务消耗其全部批处理密钥的配额,根本无法影响交互式密钥的 TPM。
这并非总是可行的——提供商的层级通常将配额上限与消费额挂钩,拆分密钥可能会拖慢层级晋升——但即便是部分分离也很有价值。将功能分成两类(实时 vs. 异步)已经消除了最严重的故障模式:凌晨 2 点的定时任务饿死早上 9 点的演示。
分离密钥还让可观测性变得简单。你可以独立监控每个密钥,无需复杂的归因逻辑就能看到哪个工作负载正在接近其限制。
无法分离密钥时的优先级队列
当密钥分离不可行时——多个团队共享企业账户,或提供商按总消费额而非按密钥分配配额层级——你需要应用层面的配额治理。
令牌桶算法是最适合 LLM 速率限制的方案。应用程序维护一个内部 token 余额,按提供商允许的 TPM 速率补充。在发出任何请求之前,应用程序估算 token 成本并从桶中扣除。会透支桶的请求进入等待队列。这将提供商的执法机制——发生在网络边界,在 API 调用之后——转变为可以应用优先级规则的本地执法。
优先级队列位于令牌桶的前面。进入队列之前,LLM 请求按优先级分类:
- 实时用户交互(对话补全、搜索排名、推荐)
- 时效性后台任务(Webhook、近实时数据丰富)
- 定时批处理任务(报告生成、批量分类)
- 内部工具和调试查询
当令牌桶满时,所有层级可以自由推进。当桶降至阈值以下——比如剩余 20%——队列停止从低优先级层级出队。批处理任务暂停,近实时任务限速,交互式请求继续消耗剩余容量。
需要防范的一种病态:被无限期暂停的批处理任务和从未被优先处理的一样糟糕。实现优先级晋升——如果低优先级请求等待时间超过配置阈值,其优先级自动提升。这可以防止在持续高负载期间批处理工作负载被永久饥饿。
对于多实例部署,通过 Redis 使用滑动窗口计数器同步令牌桶状态。当多个实例并发运行时,每个实例本地维护的桶会积累误差:每个实例都认为自己有配额,但集合起来超出了提供商允许的范围。
让优先级决策变得明确
配额饥饿背后的治理问题是:当资源不够所有人使用时,谁来决定哪些功能获得资源?
没有明确的治理,这个决定就会被偶然做出——由部署时序、哪个团队的定时任务先运行、哪个工程师配置了最激进的重试策略来决定。让决策变得明确意味着将其写下来,并在代码中执行。
一个实际的起点是由工程和产品共同拥有的优先级层级文档。每个 LLM 功能被分配到一个层级,层级决定:
- 最大并发数(功能可以同时保持多少个并发请求)
- 配额受限时的队列位置
- 无法获得配额时是否有降级回退路径
分类需要产品的参与,因为这从根本上是一个业务决策:当我们必须在聊天助手和报告生成器之间做出选择时,哪个对用户更重要?工程师不应该单方面做出这个判断。
分类确定后,自动执行它。优先级队列配置是代码,已提交、已审查。当新功能上线时,它必须在发出 LLM 调用之前声明其优先级层级。未分类请求的默认值应该是低优先级——新功能不应该在未经审查的情况下继承已有功能的配额预算。
在用户之前发现饥饿的可观测性
标准的错误率监控无法捕捉配额饥饿。模型在响应,请求在完成,由于退避逻辑透明地处理了重试,429 错误很少甚至没有出现。在饥饿状态下改变的是延迟。
按功能类型监控 P99 延迟,而不仅仅是聚合 P99。批处理任务可以是慢的(其延迟在优雅降级),而聊 天功能在悄悄超时(其 P99 正在飙升)。聚合 P99 可能看起来稳定,而面向用户的功能实际上已经着火了。
对交互式与批处理配额消耗比率发出告警。在正常情况下,交互式功能应该消耗可预测比例的配额。当批处理消耗相对于交互式消耗上升时——尤其是在预期有用户流量的时间段——这是饥饿风险的前兆信号。
队列深度是一个滞后但清晰的指标。在请求速率稳定的情况下,待处理 LLM 请求队列增长,意味着请求没有被清空。这就是队头阻塞或 token 耗尽。
将配额利用率作为一级指标追踪:当前 TPM 占密钥限制的百分比,持续采样。超过 5 分钟维持在 80% 利用率时发出告警。在该水平下,任何负载峰值都会导致饥饿。告警让你在用户注意到之前有时间调查。
一个微妙的陷阱:在多实例系统中,单个实例报告的配额利用率只反映该实例的本地状态。跨实例聚合指标——所有 Pod 每分钟的总请求数——才能获得与提供商实际执行的限制相匹配的全局视图。
配额耗尽时的优雅降级
对于高优先级功能,构建回退路径的工程投入是值得的。当配额不可用且用户无法等待时:
- 如果请求在语义上接近最近的请求,提供缓存结果(语义缓存可以在高峰负载期间吸收相当大比例的对话查询)
- 返回部分结果——摘要而非完整生成,或无 LLM 处理的纯检索响应
- 切换到有独立配额封装的辅助密钥上更小、更便宜的模型
熔断器防止功能持续锤击耗尽的配额池 。当功能的 LLM 调用持续失败——无论是由于配额耗尽还是提供商问题——熔断器打开,并将后续调用立即路由到回退路径。这在毫秒内快速失败,而不是等待超时。它还防止重试风暴:在配额压力下不受熔断保护的重试会在最糟糕的时刻增加负载。
支撑这一切的组织模式
技术层面的配额治理依赖于一个组织模式:由单一团队拥有 API 密钥和配额,每个功能团队向其申请配额分配,而不是自行配置。
没有集中所有权,常见的故障模式是每个团队自行配置密钥,在没有聚合消费可见性的情况下各自优化延迟,直到财务团队为了达到消费层级而整合两个团队的密钥时,才发现配额竞争。
拥有 LLM 网关的平台团队对每个功能的消费模式拥有全局可见性。他们可以看到新功能上线后立即消耗了过大比例的配额。他们可以将优先级层级分类作为生产访问的前提条件。他们可以在业务优先级变化时重新分配配额。
平台团队还拥有成本回溯基础设施:每月按功能显示每个产品团队 LLM 消费的报告。仅仅是成本回溯——在任何分摊机制之前——就能改变团队行为。团队会发现他们在生产中对昂贵的模型运行调试查询,找到没有并发限制的批处理任务,了解他们的重试配置在退避期间不断锤击 API。
成本信号创造了速率限制执法无法产生的责任感。能在仪表板上看到自己配额消耗的团队,会以那些只看到错误率的团队所不具备的方式考虑优化。
优先构建什么
如果你的多个 AI 功能共享同一个 API 密钥,而你尚未考虑过配额分配,优先顺序是:
- 将批处理工作负载的 API 密钥与交互式功能分离。这是一天的改动,能立即防范最严重的故障模式。
- 添加每个功能的配额利用率日志。在你能治理分配之前,你需要看清谁在消耗什么。
- 在所有重试逻辑中实现带抖动的指数退避。配额压力下同步的重试会造成重试风暴,加剧饥饿——抖动打破了同步。
- 在你对消耗模式有了可见性之后,再构建优先级队列和令牌桶。将配置设计为代码,使优先级层级明确且可审查。
- 在功能数量增长到优先级层级文档变成各方谈判之前,建立平台团队或配额治理流程。
配额饥饿造成的故障模式特别隐蔽,因为它会奖励碰巧最先运行的功能——通常是最廉价、最不面向用户的那些。最重要的面向用户的功能——那些延迟 SLA 最严格的——往往是竞争中的失败者。在规模扩大之前做好治理,是阻止凌晨 2 点的定时任务主宰你产品演示的唯一方法。
- https://portkey.ai/blog/rate-limiting-for-llm-applications/
- https://portkey.ai/blog/tackling-rate-limiting-for-llm-apps/
- https://devtk.ai/en/blog/ai-api-rate-limits-comparison-2026/
- https://arxiv.org/html/2407.00047v1
- https://agentgateway.dev/blog/2025-11-02-rate-limit-quota-llm/
- https://dev.to/pranay_batta/building-hierarchical-budget-controls-for-multi-tenant-llm-gateways-ceo
- https://www.getmaxim.ai/articles/retries-fallbacks-and-circuit-breakers-in-llm-apps-a-production-guide/
- https://handsonarchitects.com/blog/2025/denial-of-wallet-cost-aware-rate-limiting-part-2/
- https://medium.com/@sohaibsohailengineer/the-azure-llm-quota-problem-youll-hit-at-scale-and-how-i-built-around-it-8069be46bc90
- https://www.cloudzero.com/blog/ai-cost-management/
