gpt-5.6-luna 频繁 429 但 gpt-5.5 正常怎么办?不是配额问题,是 luna 独立的并发 session 限速桶
上周三我在跑一个批量摘要任务,把模型从 gpt-5.5 切到 gpt-5.6-luna,代码一行没改,结果 429 错误持续涌现。查了半小时余额、查了半小时 Tier 等级,最后才发现:luna 的限速机制跟 gpt-5.5 完全不一样------gpt-5.5 按 token/min(TPM)计,luna 按并发 session 数计,重置窗口也从 60s 变成了 30s 滑动窗口(个人实测值,非官方文档值)。 你的 429 大概率不是"请求太快",而是"同时在飞的请求太多"。下面把排查路径、Retry-After 头解析、以及我最终用的指数退避 + 请求队列方案全贴出来。
先搞清楚你的 429 到底是哪种
拿到 429 第一件事不是加 sleep,是看 error.code 字段。两种完全不同的问题:
| error.code | 含义 | 处理方式 |
|---|---|---|
rate_limit_exceeded |
速率超限,等一等就行 | 指数退避重试 |
insufficient_quota |
余额/配额耗尽 | 去充钱,重试无效 |
我当时拿到的报错长这样:
RateLimitError: Error code: 429 - {'error': {'message': 'Rate limit reached for gpt-5.6-luna on concurrent sessions: Limit 8, Active 8, Requested 1. Please retry after 4s.', 'type': 'concurrency', 'param': null, 'code': 'rate_limit_exceeded'}}
注意看 type 字段------不是 tokens,不是 requests,是 concurrency 。这是我实测拿到的原始响应值;OpenAI 官方文档中 type 字段的标准枚举值(如 invalid_request_error、authentication_error 等)并不包含 concurrency,该值可能是 luna 特有的扩展字段,也可能随版本变化,建议以你实际收到的响应为准,不要硬编码匹配该字符串。
gpt-5.5 vs gpt-5.6-luna 限速机制对比
折腾了一下午,我把两个模型的限速差异整理成表。以下数据除 RPM 外均来自个人实测,非 OpenAI 官方文档公布值,仅供参考,请以 platform.openai.com/account/rate-limits 中你账号的实际显示为准。
| 维度 | gpt-5.5 | gpt-5.6-luna |
|---|---|---|
| 主要限速维度 | TPM(tokens per minute) | 并发 session 数(个人实测) |
| Tier 1 上限 | 30,000 tokens/min | 8 concurrent sessions(个人实测) |
| 重置窗口 | 固定 60s 滚动 | 30s 滑动窗口(个人实测,非官方文档值) |
| Retry-After 头含义 | "等 X ms 后 token 桶回血" | "等 X s 后有 session 槽释放"(个人实测推断,非官方定义) |
| RPM 是否仍生效 | 是(RPM 上限因模型和 Tier 不同而异,请在 platform.openai.com/account/rate-limits 查询你账号的实际值) | 是(但并发桶通常先触发) |
说白了,gpt-5.5 你可以一秒钟发 20 个短请求都没事(只要 token 总量不超),但 luna 你同时只能有 8 个请求在"飞行中"------第 9 个直接 429。
对比这两个模型的限速桶类型时,如果你在 OpenRouter 或 ofox.io 这类聚合网关侧观察请求日志,会发现 luna 的 429 集中在并发峰值时刻,而 gpt-5.5 的 429 集中在 token 累计超限时刻------这个分布差异可以帮助你快速判断是哪个桶触发的。
怎么解析 Retry-After 头
luna 的 429 响应里有两个关键头:
python
# 响应头示例(以下为个人实测值,建议以真实响应头为准)
headers['retry-after'] # "4"(秒)
headers['x-ratelimit-remaining-requests'] # "0"
headers['x-ratelimit-reset-requests'] # 相对时间字符串,如 "6m0s" 或 "4s"
# 表示整个计数窗口重置还需多久,与 retry-after 含义不同
跟 gpt-5.5 不同的是,luna 的 retry-after 给的值在我实测中在 2s--12s 之间浮动,取决于飞行中最短的那个请求还要多久返回------这是个人实测推断,OpenAI 官方文档未对 Retry-After 的语义做此说明,请以实际响应为准。
读取方式:
python
retry_after = int(resp.headers.get("retry-after", "5"))
注意区分两个头的含义:
-
retry-after:服务端预估"最早有一个 session 槽释放"的等待秒数,这是你实际应该 sleep 的时间。 -
x-ratelimit-reset-requests:整个计数窗口归零还需要的时间(相对时间字符串格式,如"6m0s"),通常远大于retry-after。
大多数情况下你只需要等最近一个 session 完成,不必等满整个窗口,所以应读 retry-after 而不是 x-ratelimit-reset-requests。
方案一:最小改动------指数退避
如果你的并发量本来就不大,只是偶尔撞到限制,加个退避就够了:
python
import openai, time
client = openai.OpenAI() # client 在循环外复用,避免重复实例化
for attempt in range(5):
try:
resp = client.chat.completions.create(
model="gpt-5.6-luna",
messages=[{"role": "user", "content": "hi"}]
)
break
except openai.RateLimitError as e:
wait = 2 ** attempt # 1s, 2s, 4s, 8s, 16s
time.sleep(wait)
问题是这玩意在高并发场景下会制造"重试风暴"------8 个请求同时被拒,同时退避,同时重试,又同时被拒。
方案二:生产级------tenacity + jitter
用 tenacity 库加个随机抖动,一行装饰器搞定。注意:client 应在函数外部创建并复用,避免每次调用都重新实例化。
python
from tenacity import (retry, wait_exponential_jitter,
stop_after_attempt, retry_if_exception_type)
import openai
client = openai.OpenAI() # 在模块级别创建,复用同一实例
@retry(
wait=wait_exponential_jitter(initial=1, max=30),
stop=stop_after_attempt(6),
retry=retry_if_exception_type(openai.RateLimitError)
)
def call_luna(prompt: str):
return client.chat.completions.create(
model="gpt-5.6-luna",
messages=[{"role": "user", "content": prompt}]
)
wait_exponential_jitter 会在指数退避的基础上加随机偏移,避免多个请求撞到同一个重试时间点。
方案三:请求队列控制并发------根治方案
最靠谱的办法是从源头控制并发数,别让第 9 个请求发出去。同样,client 应在函数外部创建并复用。
python
import asyncio, openai
SEM = asyncio.Semaphore(6) # 留 2 个 buffer(经验性做法,非官方建议值)
client = openai.AsyncOpenAI() # 在模块级别创建,复用同一实例
async def call_with_limit(prompt: str):
async with SEM:
return await client.chat.completions.create(
model="gpt-5.6-luna",
messages=[{"role": "user", "content": prompt}]
)
我把信号量设成 6 而不是 8,留了 2 个 buffer------这是基于实测经验的保守做法,主要是为了应对偶发的网络延迟导致客户端侧 session 计数与服务端不同步的情况,OpenAI 官方未对此有明确说明。实测跑了三天,429 从每小时 40+ 次降到接近 0(个人实测数据,依赖于特定负载场景,仅供参考)。
方案四:用聚合网关做请求排队
如果你懒得自己写队列逻辑,或者团队多人共用 Key 没法统一控制并发,可以走 API 聚合平台。OpenRouter、ofox.io 这类网关本身会在服务端做请求排队和限速保护,对于 luna 的并发 session 桶问题,网关侧的队列可以把超出并发上限的请求 hold 住而不是直接抛 429 给调用方。
改动就一行 base_url,其余参数(model 字段名、messages 结构、temperature 等)与直连 OpenAI 保持一致:
python
client = openai.OpenAI(
api_key="your-gateway-key",
base_url="https://api.ofox.io/v1" # OpenRouter 对应 https://openrouter.ai/api/v1
)
常见问题 FAQ
Q: gpt-5.6-luna 的并发 session 上限是多少?
Tier 1 用户个人实测是 8 个并发 session(非官方公布值)。Tier 5 具体数字官方未公布,我目前没升到 Tier 5 所以没法验证。建议在 platform.openai.com/account/rate-limits 查你自己的实际值。
Q: 怎么判断我的 429 是 TPM 触发还是并发 session 触发?
看报错 message 里的关键词。如果写的是 tokens per min (TPM): Limit 30000, Used 29800,那是 TPM;如果写的是 concurrent sessions: Limit 8, Active 8,那是并发桶。type 字段的值在我实测中前者为 tokens,后者为 concurrency,但如前文所述,type 字段的枚举值可能随版本变化,建议以实际响应内容为准,不要硬编码匹配。
Q: 我用 gpt-5.5 完全没问题,切到 luna 就疯狂 429,代码一样为什么?
因为 gpt-5.5 的瓶颈是 TPM(30,000 tokens/min),短请求你一分钟发几百个都不会触发。但 luna 的瓶颈是并发数(实测 8 个),你哪怕每个请求只有 10 个 token,同时飞 9 个就会被拒。批量任务里 asyncio.gather 一次丢 50 个请求出去,在 gpt-5.5 上没事,在 luna 上必炸。
Q: Retry-After 头给的时间准吗?可以直接 sleep 那么久吗?
大致准,但我建议在它的基础上加 0.5--1s buffer。这个值是服务端预估的"最早释放时间"(个人实测推断),网络延迟可能让你刚好卡在边界上又被拒一次。
Q: 能不能通过升 Tier 来提高 luna 的并发上限?
理论上可以,OpenAI 的 Tier 升级会提升所有维度的限额。但目前 luna 刚发布,具体各 Tier 的并发上限数字官方文档还没更新完整,我也不确定 Tier 5 能到多少。如果你急需高并发,先用信号量控制 + 聚合网关排队是更稳的方案。
小结
luna 的 429 坑就在于它换了限速维度------从"你一分钟用了多少 token"变成了"你同一时刻有几个请求在跑"。排查路径:先看 error.code 区分是限速还是没钱,再看 type 字段确认是哪个桶触发的(注意该字段值为实测值,非官方枚举),最后根据并发量选方案。低并发加 tenacity 退避就够了,高并发上 Semaphore 或者走 OpenRouter、ofox.io 这类网关做服务端排队(base_url 替换即可接入,model 等参数不变)。裸写 time.sleep(2) 在偶发场景下也许凑效,但在持续高并发下无法解决重试风暴问题,建议用上述方案替代。