Jev 限流与并发:官方数字和落地清单

先说结论

  • 官方 Models 页给 Jev 1.13 的公开限流是 250,000 tokens/s1,200 requests/min,任一超过返回 429;官方明确这些数字会动态调整,不能当成固定 SLA。
  • 官方写明:SDK 会默认退避重试并尊重 retry-after 头;直接调 HTTP API 的人要自行查看 Handling rate limits 章节。这是官方数据,不是我的推断。
  • 官方 cookbook 的两个公开端点示例里,实体对齐用 MAX_WORKERS = 6,技能推荐用 WORKERS = 8,注释分别说公开端点大约 8 并发以上就会被限流、当前配置对限流“gentle”。这说明公开端点并发经验值大约在个位数。
  • 官方定价是 $42/Btok,且输出 token 免费。按 250k tokens/s 上限推算,打满令牌速率一小时理论输入成本约 $37.8。这是我自己推算,不是官方承诺的账单。
  • 落地建议:本地队列 + 可配并发上限 + 指数退避和抖动 + 幂等键,超时降级到规则;429/5xx/网络错误可重试,400/401/422 不要盲目重试。

证据一:Models 页的官方限流数字,以及“动态调整”这句关键提醒

官方文档的 Models 页 在 Current models 表里直接列出了 Jev 1.13 的限流:

Rate limits 250,000 tokens per second / 1,200 requests per minute

这句话是官方数据。它的含义不能只看一个数字。官方在下方解释里写的是“Measured in tokens per second and requests per minute”,也就是说,请求是否触发限流,取决于两个维度中的任何一个:token 速率超了,或请求数超了,都会触发限制。官方原句是:

A request over either limit returns 429 Too Many Requests.

注意这里的 “either limit”。所以不是把 token 控制在 250k/s 以内就没事,RPM 也是独立限制。如果业务请求非常小、数量却很高,很可能先撞到 1,200 RPM;如果请求非常大、数量很少,又可能先撞到 250k tokens/s。

紧接着官方写了一句对调用方式很重要的话:

Our client SDKs retry with backoff by default and honor the retry-after header when the response carries one. If you call the HTTP API directly, see Handling rate limits.

这句话包含三层信息:第一,官方 SDK 自带重试;第二,重试默认带退避;第三,如果响应里有 retry-after 头,SDK 会遵守。这些都是官方说的,不是我的推测。对直接调 HTTP API 的人来说,官方指向 API 参考里的 Handling rate limits 章节,而不是让你盲目自己写重试。

但更重要的提醒是官方对限流稳定性的表述。Models 页原文写:

Rate limits are adjusting dynamically. We are serving a very large volume of demand, and the limits above can change without notice while we do, as upcoming large GPU deals land and we let in more users.

这句必须放大来看。上线前你可能按 250k tokens/s 和 1,200 RPM 做容量规划,但官方明确说“可以变化而不另行通知”。所以如果你把公开端点限流当成 SLA,扩峰时不监控 429、不做降级,就是给自己埋雷。官方也提到等基础设施稳定后会有更稳定限制,高限制可在 custom/enterprise plans 获取。这不是当前公开版的承诺。

证据二:官方 API 页与 429 处理

官方在 Models 页说,如果你直接调 HTTP API,要去看 Handling rate limits;相关内容属于 API reference 的错误处理范围。此次我拿到的 API 参考页片段刚好到 Score 示例就截断了,没有完整覆盖到 429 处理段落,因此我不能在这里假装引用该章节的完整英文原句。这部分我会写进“这次没核实的”。

但可以确证的是:官方 Models 页已经给出了 SDK 默认退避、尊重 retry-after 这两个事实;这两句不依赖 API 页也能成立。对于直接 HTTP 调用的人,最稳妥的做法是不要把 429 当普通错误,而要把它当作可恢复的限流信号。下面“重试骨架”一节给的指数退避 + 抖动 + 幂等键,是我自己的实现建议,不是官方 API 页的原句。

证据三:两个官方 cookbook 的并发实践值

这是本文最值得看的增量:官方限流数字是全局上限,但工程上开多少并发才不容易把自己打挂?官方 cookbook 里给了两个参考值。

Knowledge graph entity alignment 中,代码顶部有这么一行:

MAX_WORKERS = 6 # small pool; the public endpoint rate-limits above roughly eight

Skill suggestion 中,也有类似配置:

WORKERS = 8 # small pool: enough to keep a live run to minutes, gentle on rate limits

这两段是官方 cookbook 里一线作者所写的工程实践注释,不是 Models 页的官方限流定义。它们不能替代官方限流数字,但透露了一个非常有用的经验值:公开端点在示例请求形状下,并发大约 6~8 是稳的,高过大约 8 就可能触发限流。这正好和 Models 页的 RPM 限制呼应:如果你的每个请求处理时间不是极小,那么几百个并发很容易把 RPM 打穿。

需要注意:这两个 cookbook 使用的是旧版 jev-1.12,时间戳分别是 2026-08-11 和 2026-07-31;官方 Models 页当前列的是 jev-1.13.0。并发经验值不一定完全等价,但两个独立示例都选了小个位数池子,这个信号是明确的。我们应把它视作“官方发布示例中的工程实践”,而不是官方对新版本做出的明确推荐。

成本与限流一起算:打满一小时理论上多少钱

官方价格在 Models 页有直接数据:Price (per Btok / per Mtok) $42 / $0.042,且原文写明“Charged per input token. Output tokens are free.”。这是官方数据。结合官方限流 250,000 tokens/s,可以做一个理论推算:

tokens_per_sec = 250_000  # 官方限流,Token 维度
seconds_per_hour = 3600
price_per_btok_usd = 42   # 官方价格,每十亿 token

hourly_tokens = tokens_per_sec * seconds_per_hour
hourly_cost_usd = hourly_tokens / 1e9 * price_per_btok_usd

print(f"{hourly_tokens:,} tokens/h, ${hourly_cost_usd:.2f}/h")
# 900,000,000 tokens/h, $37.80/h

这个 $37.8/h 是我自己推算出来的理论上限:如果你的输入 token 速率一直顶满官方 TPS 限制,一小时就是 9 亿 input tokens,按 $42/Btok 计费就是约 $37.8。它不代表真实账单会正好落在这个数字,因为真实业务还会受到 RPM 限制、请求包大小、重试次数、限流降级等因素影响。例如 1,200 RPM 限制意味着最多 72,000 个请求/小时;如果你每个请求都很小,可能 RPM 限制先触顶,实际 token 消耗远到不了 250k/s。反之,如果请求很大,也可能直接被 64k 上下文限制卡住,而不是 TPS。所以这个数适合用来做“最坏情况下每小时要烧多少钱”的预算上限,而不是精确成本预估。

重试骨架:哪些错误该重试,哪些不该

官方 SDK 虽然会默认重试,但它管不了你的幂等语义。假如你的业务动作是“判断后写入标签”、触发下游通知、扣费,或者带有副作用,盲目重试可能造成重复动作。所以我的建议是:即使使用官方 SDK,也要把请求包装成带幂等键的本地队列任务;如果直接 HTTP 调用,自己实现指数退避 + 抖动。

下面是一个可用的重试骨架。它是我自己的实现建议,不代表官方 SDK 的行为,也不代表官方推荐。代码中重试 429、5xx 和网络/超时错误,不重试 400、401、422 这类客户端错误。

import random
import time

RETRYABLE_HTTP = {429, 500, 502, 503, 504}
SKIP_RETRY_HTTP = {400, 401, 422}

def call_with_retry(make_request, idempotency_key, max_retries=5, base_delay=1.0):
    """
    指数退避 + 抖动 + 幂等键。
    retryable: 429/5xx/网络错误/超时
    no_retry: 400/401/422
    """
    attempt = 0
    while True:
        try:
            resp = make_request(headers={"Idempotency-Key": idempotency_key})
            if resp.status_code in RETRYABLE_HTTP:
                # 可重试状态:抛出异常进入退避
                raise RuntimeError(f"retryable {resp.status_code}")
            if resp.status_code in SKIP_RETRY_HTTP:
                return resp  # 请求错误,重试不会好
            resp.raise_for_status()
            return resp
        except (RuntimeError, ConnectionError, TimeoutError):
            if attempt >= max_retries:
                raise
            sleep = base_delay * (2 ** attempt) + random.uniform(0, 0.3)
            time.sleep(sleep)
            attempt += 1

这里我用 RuntimeError 代表可重试状态;如果你的 HTTP 客户端有自定义异常,替换成对应异常即可。关键的几个判断是:

  • 429:典型的限流,官方 Models 页也把它列为超限返回码。应当重试,并且最好先读 retry-after 头再决定等待时间。
  • 5xx:服务端暂时错误,可以重试。
  • 网络错误和超时:连接失败、读超时等,重试是合理的。
  • 400、401、422:这类错误表示请求本身有问题,例如格式、鉴权、语义校验,重试基本不会改变结果,还可能放大问题。这里把它们列为不重试,是我的实现选择,不是官方 API 页的明确建议。

幂等键放在请求头里时,需要服务端支持该头;如果服务端不支持幂等,至少在自己的队列层做去重标记,避免重试造成重复动作。

落地清单:把服务保护起来

综合上面的官方数字、cookbook 实践和我的工程判断,上线前可以按这个清单来做。下面这部分是我的建议,不是官方文档的规定。

  1. 本地队列先接住请求。业务侧产生请求后,先进入本地队列,再由 worker 以固定并发消费。不要让上游流量直接喷射到 TypeSafe API。
  2. 并发上限做成可配置。初始值可以参考 cookbook 的个位数水平,比如 4~8;上线后按 429 率和延迟调大或调小。不要默认开 32/64。
  3. 超时降级到规则。如果 Jev 在超时后仍未返回,或持续 429,先降级到确定性规则、上一次缓存结果或人工兜底。不要无限期等模型。
  4. 重试必须带幂等键和退避。429/5xx/网络错误可以重试,400/401/422 不要重试。重试等待用指数退避 + 抖动,降低同步冲击。
  5. 记录模型版本和响应头。官方 Models 页提到 alias 会迁移,响应 model 字段会报告实际版本;记录它,方便排查模型变化导致的准确率漂移。
  6. 监控 429 比例,而不是只看 QPS。429 比例突然升高,说明你已逼近动态限流值;此时应收窄并发,而不是简单加机器。

这次没核实的

  • API 参考页 Handling rate limits 章节的具体 429 处理建议原文未能核实,本次拿到的片段只到 Score 示例。
  • 官方 SDK 默认重试的具体最大次数、退避公式、是否所有可重试状态码都重试,未能从给定页面核实。
  • 两个 cookbook 里的 6 和 8 并发,是在特定请求形状、旧版 jev-1.12 下的实践注释,是否在新版或高请求大小下仍然安全,未能核实。它们不是官方给新版 Jev 的明确并发阈值。
  • 官方是否保证每次 429 响应都带 retry-after 头,未能核实;Models 页只说“when the response carries one”。

参考来源

评论区

0 条评论

登录后可评论。

小猫跳舞 12 阅读