AI Agent 一跑就 429?真正耗尽的可能是并发连接,不是额度

15 阅读9分钟

同一个 API Key,普通聊天连续问几十次都没事,一换成 Codex、Claude Code 或多 Agent 工作流,就开始出现:

429 Too Many Requests
529 Overloaded
503 Service Unavailable
stream disconnected before completion

很多人的第一反应是“余额没了”或者“RPM 超了”,于是不断充值、换 Key、重启客户端。

但在长时间流式 Agent 场景中,真正被耗尽的可能不是每分钟请求数,而是并发连接槽位

最近有 AI Gateway 开始为整个组织增加“并发中的请求数”限制:请求从发出开始占用槽位,直到完整响应结束或连接关闭才释放。长流式请求会一直占着位置,这正是传统 RPM 指标容易漏掉的部分。

这篇文章会说明:

  1. RPM、TPM 和并发请求到底有什么区别;
  2. 429、529、503 应该如何判断;
  3. 为什么多 Agent 比普通聊天更容易触发限流;
  4. 客户端怎样正确重试、限制并发和设计 Fallback。

说明:本文包含作者使用的 OpenAI 兼容 API 配置示例。统一 Base URL 可以减少客户端配置差异,但是否提供自动故障转移、并发扩容或模型路由,应以具体服务说明和实测结果为准。

[TOC]


一、先看结论:429 不一定代表余额不足

不同服务可能使用同一个 429 表达不同限制:

  • RPM 超限:一分钟内请求次数过多;
  • TPM 超限:一分钟内输入和输出 Token 过多;
  • 并发超限:仍在运行的请求数量过多;
  • 账号或项目额度限制;
  • 月度预算或硬性消费上限;
  • 上游账号池暂时没有可用通道。

因此,看到 429 后不能只看状态码,还要同时检查:

  • 响应体中的 messagetype 和 code
  • Retry-After 响应头;
  • 限流相关响应头;
  • 错误发生在请求开始、流式中途还是工具调用之后;
  • 同一时间运行了多少 Agent 和子 Agent;
  • 单个流式请求持续了多长时间。

529 则通常表示服务端或网关处于瞬时过载状态。它不是所有平台都会使用的通用状态码,但部分模型服务和 AI Gateway 会用它明确区分“你的账号触发限制”和“服务端当前太忙”。

二、RPM 看起来没超,为什么还是会被限流

假设一个系统允许每分钟 600 个请求。

如果每个请求 1 秒结束,平均并发量大约是:

10 requests/sec × 1 sec = 10 concurrent requests

但如果 Agent 的流式响应平均持续 120 秒:

10 requests/sec × 120 sec = 1200 concurrent requests

RPM 完全相同,并发连接数却相差 120 倍。

可以使用一个简化关系理解:

avg concurrency ≈ arrival rate (req/sec) × avg request duration

这就是长流式 Agent 的特殊之处:请求不是发出去就结束,而是会保持 SSE 或其他流式连接,等待模型持续输出、调用工具或完成长推理。

需要注意的是,一个 Agent 会话不一定始终占用同一个 HTTP 请求。标准工具调用流程通常是:

model request
-> returns tool call
-> client runs tool
-> next model request

每次模型请求都会单独占用槽位。真正迅速放大并发的,通常是多个会话、子 Agent 和并行任务同时运行。

三、多 Agent 为什么特别容易触发 429

假设你同时运行 4 个主 Agent,每个主 Agent 又创建 3 个子 Agent。

理论上的活跃执行单元可能达到:

4 main agents + 12 sub-agents = 16 active units

如果每个执行单元同时发起模型请求,就可能瞬间占用 16 个并发槽位。再叠加以下行为,并发量还会继续增加:

  • 自动重试没有退避,失败后立刻再次请求;
  • 工具调用结束后多个子 Agent 同时恢复;
  • 长上下文让单次首 Token 等待时间变长;
  • 上游模型变慢,旧请求迟迟不释放;
  • 客户端断开了,但网关或上游没有及时取消;
  • 多个开发者共用一个组织、项目或 API Key 池。

所以会出现一种看似矛盾的现象:

normal chat works
single agent works
many 429 when running agents in parallel

这通常应该优先检查并发,而不是继续增加重试次数。

四、429、529、503 应该怎么区分

状态码常见含义首要动作
429账号、Key、项目的速率、Token、并发或预算限制解析错误体,降低并发并遵守 Retry-After
529网关或模型服务瞬时过载短暂退避后重试,必要时切换健康上游
503服务暂不可用、没有健康实例或上游池耗尽检查服务状态,退避并限制重试次数

状态码只能提供第一层判断,错误体更重要。

下面这种 429 明确指向并发:

{
  "error": {
    "message": "Too many concurrent requests. Retry shortly or reduce request concurrency.",
    "type": "rate_limit_error",
    "code": "rate_limit_exceeded"
  }
}

而下面这种错误更像服务端过载:

{
  "error": {
    "message": "Gateway overloaded, please retry",
    "type": "overloaded",
    "code": "overloaded"
  }
}

如果错误体写着 insufficient_quotabilling 或 spend limit,才应该重点检查余额和预算,而不是单纯调整并发。

五、先用最小请求检查接口和响应头

设置测试环境变量:

export API_BASE="https://genvis.xyz/v1"
export API_KEY="YOUR_API_KEY"
export MODEL="YOUR_MODEL_ID"

发送一个非流式最小请求:

curl -i --max-time 60 \
  -X POST "$API_BASE/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  --data "{\"model\":\"$MODEL\",\"input\":\"Reply with exactly OK\"}"

检查以下信息:

  • HTTP 状态码;
  • Retry-After
  • 是否有剩余额度或限流响应头;
  • Content-Type 是否为 JSON;
  • 错误体中的 message 和 code
  • 是否返回请求 ID,便于联系服务方排查。

再测试流式响应:

curl -N -i --max-time 120 \
  -X POST "$API_BASE/responses" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  --data "{\"model\":\"$MODEL\",\"input\":\"Explain what an API gateway does in 200 words\",\"stream\":true}"

不要为了测试并发,直接在生产 Key 上一次启动几十或几百个请求。先用 1、2、4 的阶梯逐步增加,并观察响应时间、错误比例和连接释放情况。

六、正确重试:指数退避、抖动和 Retry-After

最危险的重试方式是:

request fails
-> retry immediately
-> fails again
-> all agents retry immediately at once

这会形成“重试风暴”,让已经过载的服务更难恢复。

下面是一个简化的 Python 示例:

import os
import random
import time

import requests

API_URL = "https://genvis.xyz/v1/responses"
API_KEY = os.environ["GENVIS_API_KEY"]
MODEL = os.environ.get("MODEL", "YOUR_MODEL_ID")

retryable_statuses = {429, 500, 502, 503, 504, 529}

for attempt in range(5):
    response = requests.post(
        API_URL,
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "model": MODEL,
            "input": "Reply with exactly OK",
        },
        timeout=60,
    )

    if response.status_code < 400:
        print(response.json())
        break

    if response.status_code not in retryable_statuses:
        raise RuntimeError(
            f"Non-retryable error: {response.status_code} {response.text}"
        )

    retry_after = response.headers.get("Retry-After")
    if retry_after and retry_after.isdigit():
        delay = float(retry_after)
    else:
        delay = min(30, 2 ** attempt) + random.uniform(0, 1)

    print(
        f"Retryable error: {response.status_code}; "
        f"retrying in {delay:.1f}s"
    )
    time.sleep(delay)
else:
    raise RuntimeError("Request failed after 5 attempts")

这个示例做了三件事:

  1. 只重试明确的临时错误;
  2. 优先遵守服务端返回的 Retry-After
  3. 没有响应头时使用指数退避和随机抖动。

生产环境还要注意:如果流已经输出了一部分,盲目重新发送整个请求可能产生重复内容、重复工具调用或重复计费。涉及写数据库、发消息、下单等副作用时,必须配合幂等键或业务去重。

七、限制并发比增加重试更有效

Python 异步任务可以使用信号量限制同时运行的模型请求:

import asyncio

MAX_CONCURRENT_REQUESTS = 4
semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)

async def call_model(client, payload):
    async with semaphore:
        return await client.responses.create(**payload)

并发值不要直接照抄。合理值取决于:

  • API 服务允许的组织级并发;
  • 每个请求的平均持续时间;
  • 是否启用流式响应;
  • 主 Agent 和子 Agent 数量;
  • 上游模型延迟;
  • 业务可以接受的排队时间。

一个实用做法是从 2 或 4 开始,记录以下指标后再提高:

success rate
429/529 ratio
time to first token
total response time
in-flight requests
retry count
cost per task

如果并发从 4 提高到 8 后吞吐没有明显增加,429 和延迟却大幅上升,就说明系统已经接近有效容量上限。

八、什么时候应该切换模型或上游

Fallback 不应该是“任何错误都换模型”。

适合触发临时切换的情况包括:

  • 多次收到 529 或 503;
  • 429 明确表示当前上游并发已满;
  • 服务状态异常且短时间无法恢复;
  • 模型首 Token 延迟持续超过阈值;
  • 当前模型不可用,但备用模型满足相同协议和工具能力。

下面这些错误通常不应该直接切换:

  • 400 请求结构错误;
  • 401 Key 无效;
  • 403 权限不足;
  • 模型不支持当前工具或输入模态;
  • 业务参数本身不合法。

因为配置错误不会通过换上游自动消失,还可能把同一个错误扩散到更多通道。

设计 Fallback 时至少要核对:

API protocol matches
model ID correctly mapped
context window is enough
streaming and tool calls supported
structured output compatible
image/audio modalities match
billing and data policy allow switching

统一 Base URL 的价值,是把客户端改动控制在配置层,让模型接入、日志和鉴权更集中。但“一个入口”并不自动等于“任何错误都会智能切换”,具体路由规则仍然需要验证。

九、排查 Agent 429 的推荐顺序

遇到问题时,可以按下面的顺序检查:

read full error body and response headers
-> identify RPM, TPM, concurrency, or budget limit
-> count in-flight requests and sub-agents
-> drop concurrency to 1 to verify single request
-> raise concurrency step by step: 1, 2, 4
-> honor Retry-After and add exponential backoff
-> check whether stream connections close in time
-> only then consider switching model or upstream

如果单请求稳定、并行就失败,优先检查并发。

如果所有请求都立即 401 或 403,优先检查鉴权和权限。

如果请求运行固定时长后断开,优先检查网关总超时、SSE 空闲超时和反向代理配置。

如果只有特定模型失败,优先检查模型权限、能力和上游状态。

十、总结

Agent 时代的 API 限流,已经不能只看 RPM。

长时间流式响应、多个子 Agent、慢上游和同步重试,会共同推高“仍在运行的请求数”。即使一分钟请求总量没有变化,也可能耗尽并发槽位。

看到 429、529 或 503 时,正确处理方式是:

parse the error first
-> then reduce concurrency
-> honor Retry-After
-> use jittered exponential backoff
-> log in-flight requests and stream duration
-> only then do model or upstream Fallback

稳定的 Agent 调用链,靠的不是无限重试,而是可观测的限流、明确的并发边界和经过验证的降级路径。

参考资料

更新记录

  • 2026-08-25:根据近 72 小时 AI Gateway 并发限制与 529 过载保护动态整理;补充 Agent 流式请求、并发估算、重试、限流和 Fallback 示例。