1. 429 不是“再试一次”就完事先看清限速维度调用 AI 大模型 API 时突然收到429 Too Many Requests很多人的第一反应是加个sleep再重试或者干脆多申请几个 Key 轮着用。我试过在批量任务里无脑重试结果请求量翻倍失败率反而更高——因为 429 背后限制的往往不是“每秒请求数”这一个维度。大模型 API 的限速通常同时受这几个指标约束RPM每分钟请求数、TPM每分钟 Token 数、并发连接数、模型级额度、账户或组织级配额。你请求次数不多但每次塞进去几万 Token 的上下文照样会被卡在 TPM 上流式输出开了一堆长连接不释放并发数也会先爆。所以排查 429 的第一步不是改代码而是先判断到底是本地请求发太快还是通道侧配额已经打满。这篇按“识别错误码 → 自查频率与并发 → 用 TaoToken 统一通道调整配置 → 复现验证 → 排错”的顺序走配置骨架可以直接复制。适合正在做聊天机器人、RAG 问答、Agent 自动化或批量生成的开发者尤其是多实例部署、本地看起来没超限但线上频繁 429 的情况。2. 用 TaoToken 统一通道收拢 Key 与限速观测TaoToken 在这里的角色是一个统一的 API 接入通道你用它生成一个 Key通过https://taotoken.net/api这个兼容接口去调用不同的大模型而不是在每个业务里散落一堆不同厂商的 Key 和 Base URL。这样做的好处是限速排查时只需要盯一个入口——请求从哪来、打了多少 Token、哪个模型在报 429日志是收敛的。官网入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台生成 API Key。注意 API 地址是https://taotoken.net/api这个不带 UTM 参数配置里直接写它就行。为什么统一通道对排查 429 有帮助因为限速可能发生在两层一层是你本地代码的并发和重试逻辑另一层是通道侧对 Key 或模型的配额限制。如果 Key 分散在多个地方你根本分不清是哪个业务把额度吃掉了。收拢到一个 Key 之后配合控制台的调用记录就能对照“我本地发了多少请求”和“通道侧记了多少请求”快速定位是本地配置问题还是通道侧限速。需要先拿 Key 的话直接去控制台的 API Keys 页面https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有各语言 SDK 的 Base URL 写法。3. 可复制的配置骨架settings.json 与 config.toml下面给两份配置骨架一份给 Node/前端工具链常用的settings.json一份给 Python 或 CLI 工具常用的config.toml。核心思路是把 Base URL、Key、超时、重试、并发上限都显式写出来而不是靠默认值。3.1 settings.json 骨架Node / CLI 工具{ api: { baseUrl: https://taotoken.net/api, apiKey: sk-你的TaoToken密钥, timeoutMs: 60000, maxRetries: 3, retryBaseDelayMs: 1000, retryMaxDelayMs: 16000, jitterRatio: 0.3 }, rateLimit: { maxConcurrency: 4, requestsPerMinute: 60, tokensPerMinute: 90000, queueMaxLength: 200 }, model: { default: claude-sonnet, fallback: claude-haiku, maxTokens: 1024 } }几个参数说明maxConcurrency控制同时进行中的请求数流式连接也算在内requestsPerMinute和tokensPerMinute是本地自限速设得比通道侧配额低一点留余量jitterRatio给重试等待加随机抖动避免多实例同时重试。fallback是主模型被限速时的降级模型日志里一定要记录实际用了哪个。3.2 config.toml 骨架Python / CLI[api] base_url https://taotoken.net/api api_key sk-你的TaoToken密钥 timeout 60 max_retries 3 retry_base_delay 1.0 retry_max_delay 16.0 jitter 0.3 [rate_limit] max_concurrency 4 rpm 60 tpm 90000 queue_max_length 200 [model] default claude-sonnet fallback claude-haiku max_tokens 1024Python 侧读取后把base_url和api_key传给 SDK 的 client 初始化即可。关键是max_retries和retry_max_delay必须有上限否则一个限流问题会被放大成重试风暴。3.3 指数退避加抖动的重试逻辑import random import time def backoff_delay(attempt, base1.0, max_delay16.0, jitter0.3): delay min(base * (2 ** attempt), max_delay) return delay * (1 random.uniform(-jitter, jitter)) def call_with_retry(client, payload, max_retries3): for attempt in range(max_retries 1): try: return client.chat(payload) except RateLimitError as e: if attempt max_retries: raise retry_after getattr(e, retry_after, None) wait retry_after if retry_after else backoff_delay(attempt) time.sleep(wait)如果响应头里带了Retry-After优先按它给的时间等不要自己拍脑袋定固定值。没有这个字段再用指数退避。4. 复现限速并验证配置是否生效配置写完不能只看代码要实际复现一次 429 再验证退避逻辑有没有生效。下面用一个最小脚本故意把并发拉高到超过本地maxConcurrency观察是否触发限速以及重试是否按预期等待。import concurrent.futures import time def burst_call(i): start time.time() try: resp call_with_retry(client, {model: claude-sonnet, messages: [{role: user, content: f测试 {i}}]}) return {i: i, ok: True, elapsed: round(time.time() - start, 2)} except Exception as e: return {i: i, ok: False, error: str(e), elapsed: round(time.time() - start, 2)} with concurrent.futures.ThreadPoolExecutor(max_workers16) as pool: results list(pool.map(burst_call, range(32))) ok sum(1 for r in results if r[ok]) print(f成功 {ok}/32) for r in results[:5]: print(r)把max_workers设成 16而配置里maxConcurrency是 4如果限速逻辑生效你应该看到请求被排队而不是全部同时打出去。成功结果里elapsed会呈现阶梯式增长说明退避在起作用。如果全部瞬间失败说明重试逻辑没接住 429或者maxRetries设成了 0。验证通过后再去 TaoToken 控制台看调用记录对照本地日志的请求数。如果本地记了 32 次但通道侧只记了 20 次说明有请求在本地就被限速拦下了这是正常的如果通道侧记的比本地多那可能是重试把请求放大了需要回头检查maxRetries。想直接对话验证模型是否正常响应可以用模型对话页面https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 手动发一条消息看返回排除是 Key 或通道本身的问题。5. 本篇常见错排查错误一429 后无限重试没有上限。表现是日志里同一个请求反复出现延迟越来越高。检查maxRetries和retryMaxDelay是否设了值队列是否有queueMaxLength。没有上限的重试等于把限流放大成故障。错误二只限了 QPS没管 TPM。请求次数明明不多还是 429大概率是单次 Token 太大。检查maxTokens是否设得过高历史对话轮数是否没裁剪RAG 召回片段是否塞太多。把tokensPerMinute本地限速加上比只限 RPM 更管用。错误三多实例各自限速加起来超了。每个实例看自己都没超requestsPerMinute但 4 个实例加起来就是 4 倍。这种情况要么上分布式限速要么把单实例配额调低到总配额除以实例数。错误四流式连接没释放并发数爆了。流式输出如果异常中断没关连接闲置连接会占着并发额度。检查代码里是否有finally块关闭流或者设置连接空闲超时。错误五把insufficient_quota当成 429 处理。余额或套餐问题返回的错误信息里带quota、balance、billing字样这种重试没用要去控制台看余额和套餐状态。区分清楚再决定是等待还是充值。错误六降级模型后没记日志。主模型被限速切到备用模型回答质量变了但日志里没记后面排查质量问题会很痛苦。每次调用都记录实际使用的模型名。6. 按场景选下一步偶发 429 用指数退避加抖动就够了配置里的retryBaseDelayMs和jitterRatio调好即可。持续 429 说明本地限速和队列没做到位重点检查maxConcurrency和queueMaxLength。请求不多但仍被限优先怀疑 TPM去调maxTokens和上下文裁剪。如果你在长期做编码类任务或 Agent 自动化需要更稳定的调用配额和通道可以看 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。接入和 Key 管理的问题直接对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 和 API Keys 页面 https://taotoken.net/console/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 操作。最后留一个实用习惯每次改完限速配置先用第 4 节那个并发脚本跑一遍看成功率和elapsed分布再去控制台对调用记录。配置改没改对跑一次比看十遍代码都清楚。