sensenova 免费模型的隐藏坑:token plan limit exhausted vs RPM RateLimitError
本文最后更新于 2026年8月16日 凌晨
sensenova 免费模型的隐藏坑:token plan limit exhausted vs RPM RateLimitError
凌晨 03:00,Hermes 的 cron 心跳脚本突然连续抛出 RateLimitError。我看了一眼异常栈,第一反应是加个 time.sleep(60) 重试——过去遇到这种报错都是这么解决的。
结果重试了 200 次全部失败。
问题不在频率,在配额:免费套餐的月 token 额度已经用光,retry-after 头根本没返回。两种 RateLimitError 共用同一个异常类型,但应对策略截然相反。这篇写清楚如何区分,以及为什么要区分。
一、同一个异常,两种死法
openai 兼容 SDK(Python / JS / LangChain)把”频率超限”和”配额耗尽”都归到 RateLimitError 类,HTTP 状态码也同样是 429 Too Many Requests。区别藏在 error.message 和响应头里:
| 维度 | RPM / RPD 频率超限 | Token plan limit exhausted |
|---|---|---|
| HTTP 状态码 | 429 | 429(偶尔 503) |
error.type |
rate_limit_exceeded |
rate_limit_exceeded(同名) |
error.message 关键词 |
Rate limit / RPM / RPD / x-rpm-limit |
plan limit exhausted / quota exceeded / monthly |
retry-after header |
有(明确秒数) | 无,或为 3600+ 大值 |
x-rpm-limit / x-rpd-limit 头 |
有,当前值接近上限 | 无或为 0 |
| 重试有效? | 是,睡 retry-after 秒 |
否,月底重置或换 key |
| 典型触发 | 并发请求 / 高频 cron | 免费套餐月额度用光 |
二、为什么容易判错
Agent 框架的默认 retry 逻辑几乎总是”见 RateLimitError 就退避重试”,因为绝大部分场景确实只是频率问题。但当你的 free tier 月额度清零时,SDK 依然抛出同名异常,retry 层毫无察觉,开始疯狂重试——每一次重试都是 0 token 成功、几百毫秒的耗时、一次无效 HTTP 请求。
我的 cron 那次,200 次重试 ≈ 13 分钟空转,直到触发外层超时才停下来。如果换成付费套餐的 key 也会是同一个异常,只是月额度够大而已。问题不是”会不会遇到”,是”什么时候遇到”。
三、判定逻辑:先查 message,再决定重试策略
核心思路:429 + message 含配额关键词 = 硬失败;429 + 有 retry-after = 软失败重试。
1 | |
三段分支覆盖了三种典型场景:
- 429 + 配额关键词 → 硬失败,立即抛出,交给上层决定是否切 key。
- 429 + 有 retry-after → 软失败,按秒退避。
- 503 → 上游过载,指数退避重试。
四、sensenova 的几个响应头,值得存进日志
sensenova(及 new-api 后端)会回传以下响应头,监控时建议都抓下来:
1 | |
x-rpm-remaining 和 x-tokens-per-minute-remaining 是提前预警的最佳指标——剩 0 之前 30 秒就降频,比踩了 429 再退避要好得多。
五、落地:在 Agent 里加一层配额守门
单纯靠 SDK retry 不够,建议在 Agent 框架外面再包一层配额守门:
1 | |
- 主 key 使用率 > 80% 时,切换 fallback key(DeepSeek / Qwen)。
- 连续 3 次硬失败 → 发飞书/钉钉通知,不要死循环。
- 每 1 小时聚合一次用量,记录到 metrics,月底自动出报表。
六、踩坑清单
| 坑 | 表象 | 根因 |
|---|---|---|
| 见 429 就重试 | 重试 200 次全失败 | 未区分配额耗尽 vs 频率超限 |
只捕获 RateLimitError |
503 直接 crash | SDK 把 503 归为 APIUnavailableError |
忽略 retry-after 头 |
固定 sleep 60 秒 | 实际 30 秒就够了,浪费 30 秒 |
| 免费套餐无预警 | 半夜突然 429 | 没用 x-xxx-remaining 头做监控 |
| 多 key 轮询不记账 | 每个 key 都被打穿 | 未聚合 usage 到总账本 |
七、系列文章位置
这是”RateLimit 判定”系列第 4 篇,串联起来看:
ND-20260814-001Token 配额高峰时段:夜间 cron 的 RateLimit 实战ND-20260731-001GitHub 五连坑实录(token 失效)ND-20260805-002Gateway Token 锁冲突- 本文:从”重试就行”到”先判定再决定”
一句话总结:永远不要把 RateLimitError 当同一个异常处理。