速率限制
概述
什么是速率限制?
速率限制是 API 对用户或客户端在指定时间内访问服务器的次数施加的限制。
为什么我们有速率限制?
速率限制是 API 的常见实践,它们出于几个不同的原因而被设置:
- 它们有助于防止滥用或误用 API。例如,恶意行为者可能会通过请求来淹没 API,试图使其超载或导致服务中断。通过设置速率限制,OpenAI 可以防止这种活动发生。
- 速率限制有助于确保每个人都能公平地访问 API。如果一个人或组织进行过多的请求,可能会拖累其他所有人使用 API。通过调节单个用户可以进行的请求数量,OpenAI 确保最多数量的人有机会使用 API 而不经历减缓。
- 速率限制可以帮助 OpenAI 管理其基础设施上的总负载。如果对 API 的请求急剧增加,则可能会给服务器带来压力并导致性能问题。通过设置速率限制,OpenAI 可以帮助所有用户维护平稳一致体验。
请完整阅读本文档以更好地了解 OpenAI 的 速度极值系统如何工作。我们提供代码示例和处理常见问题所需解决方案,请在填写“极值增长申请表”之前遵循此指南,并详细说明如何在最后一部分填写该表格。
我们 API 的极值是什么?
我们根据使用特定端点以及您拥有哪种类型账户,在组织级别而非用户级别强化极值控管 。 极值按两种方式测量:RPM(每分钟请求数)和 TPM(每分钟 tokens 数)。下表突出显示了我们 API 的默认极值 ,但这些极值可根据您的用例在填写“Rate Limit Increase Request” 表格后进行增加 。
账户类型 | Text & Embedding(文本和嵌入) | Chat(聊天对话) | Edit(编辑) | Image(图像) | Audio(音频) |
---|---|---|---|---|---|
Free trial users(免费试用用户) | 3 RPM ,150,000 TPM | 3 RPM, 40,000 TPM | 3 RPM, 150,000 TPM | 5 images / min | 3 RPM |
按需付费用户(前 48 小时) | 60 RPM ,250,000 TPM | 60 RPM , 60,000 TPM | 20 RPM ,150,000 TPM | 50 images / min | 50 RPM |
按量付费用户(48 小时后 | 3,500 RPM ,350,000 TPM | 3,500 RPM ,90,000 TPM | 20 RPM ,150,000 TPM | 50 images / min | 50 RPM |
对于 gpt-3.5-turbo-16k
,即用即付用户的 TPM 限制是上面列出的值的 2 倍,分别为 120K TPM 和 180K TPM。
TPM(每分钟标记数)单位因模型而异:
类型 | 1 TPM 等价于 |
---|---|
davinci | 1 token/分钟 |
curie | 25 tokens/分钟 |
babbage | 100 tokens/分钟 |
ada | 200 tokens/分钟 |
从实际角度来看,这意味着您可以每分钟向 ada 模型发送大约 200 倍的令牌,而相对于 davinci 模型则更多。
需要注意的是,速率限制可以由任一选项触发,取决于哪个先发生。例如,您可能会向 Codex 端点发送 20 个请求,并仅使用 100 个 tokens 来填充您的限制,即使在这些 20 个请求中没有发送 40k tokens。
速率限制如何工作?
如果您的速率限制为每分钟 60 个请求和每分钟 150k davinci tokens,则将受到两者之一的限制,无论哪种情况先发生。例如,如果您的最大请求数/分为 60,则应能够每秒发送 1 个请求。如果您每 800 毫秒发送 1 次请求,在达到速率限制后,只需让程序休眠 200 毫秒即可再次发送一个请求;否则后续请求将失败。对于默认值 3,000 requests/min,默认值下客户可以有效地每 20ms 或 0.02 秒发送 1 次请求。
如果我遇到了速率限制错误会怎样?
速率限制错误看起来像这样: Rate limit reached for default-text-davinci-002 in organization org-{id} on requests per min. Limit: 20.000000 / min. Current: 24.000000 / min. 如果你遇到了速度上线问题,则意味着你在短时间内进行了过多的申请,并且 API 拒绝履行进一步申请直至经过指定时间。
速度上限与 max_tokens
我们提供的每种模型都有一个固定数量的 token 可以作为输入传递给它们进行处理。不能增加模型接收标记数目上界。例如: 如果使用 text-ada-001,则可以向该模型发送最多 2048 tokens 每个请求。
错误处置
我可以采取哪些措施来减轻此类问题?
OpenAI Cookbook 有一个Python 笔记本详细说明如何避免出现频率极高(rate limit)错误。
当提供编程式访问、批量处理功能和自动化社交媒体发布时,请谨慎考虑 - 可以考虑仅针对信任客户启用这些功能。
为防止自动化和高容量滥用,在指定时间范围内(日、周或月),设置单个用户使用量上界 。 对于超出此上界用户,请考虑实施硬性规定或手动审核流程。
通过指数回退重试
避免频繁调用 API 方法导致频繁报错也很简单——随机等待并重新尝试调用 API 方法就好了!具体做法是:当 API 返回“429 Too Many Requests”状态码时暂停执行代码片段,并根据当前已经重试过几次计算出等待时间 t ,然后再重新尝试调用 API 方法;若依旧返回“429 Too Many Requests”状态码则再暂停 t 秒钟之后重复以上操作…… 直至成功获取数据!
指数回退意味着在命中第一个 rate limit 错误时执行短暂休眠并重试不成功的 request 。 如果 request 仍未成功,则增加 sleep 长度并重复该过程。 这将持续到 request 成功或达到最大重试次数为止。 此方法具有许多优点:
- 自动重试意味着您可以从 speed limit errors 中恢复而不会崩溃或丢失数据
- 指数回退意味着首先尝试快捷方式 retry ,同时仍然从较长延迟中获益 retry ,因此前几次 retry 失败。
- 添加随机抖动以延迟 retries 不同时刻击中所有 retries 的效果。
请注意,不成功的请求会影响您每分钟的限制,因此持续重新发送请求是行不通的。 以下是一些使用指数退避的 Python 示例解决方案。
示例 1:使用 Tenacit 库
Tenacity 是一个 Apache 2.0 许可的通用重试库,使用 Python 编写,旨在简化将重试行为添加到几乎任何内容的任务。要向您的请求添加指数退避,请使用 tenacity.retry 装饰器。下面的示例使用 tenacity.wait_random_exponential 函数向请求添加随机指数退避。
import openai
from tenacity import (
retry,
stop_after_attempt,
wait_random_exponential,
) # for exponential backoff
@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6))
def completion_with_backoff(**kwargs):
return openai.Completion.create(**kwargs)
completion_with_backoff(model="text-davinci-003", prompt="Once upon a time,")
请注意,Tenacity 库是第三方工具,OpenAI 不对其可靠性或安全性做出任何保证。
示例 2:使用 backoff 库
另一个提供退避和重试功能修饰符的 Python 库是 backoff:
import backoff
import openai
@backoff.on_exception(backoff.expo, openai.error.RateLimitError)
def completions_with_backoff(**kwargs):
return openai.Completion.create(**kwargs)
completions_with_backoff(model="text-davinci-003", prompt="Once upon a time,")
示例 3:手动实现指数回退
如果您不想使用第三方库,可以按照此示例实现自己的退避逻辑:
# imports
import random
import time
import openai
# define a retry decorator
def retry_with_exponential_backoff(
func,
initial_delay: float = 1,
exponential_base: float = 2,
jitter: bool = True,
max_retries: int = 10,
errors: tuple = (openai.error.RateLimitError,),
):
"""Retry a function with exponential backoff."""
def wrapper(*args, **kwargs):
# Initialize variables
num_retries = 0
delay = initial_delay
# Loop until a successful response or max_retries is hit or an exception is raised
while True:
try:
return func(*args, **kwargs)
# Retry on specific errors
except errors as e:
# Increment retries
num_retries += 1
# Check if max retries has been reached
if num_retries > max_retries:
raise Exception(
f"Maximum number of retries ({max_retries}) exceeded."
)
# Increment the delay
delay *= exponential_base * (1 + jitter * random.random())
# Sleep for the delay
time.sleep(delay)
# Raise exceptions for any errors not specified
except Exception as e:
raise e
return wrapper
@retry_with_exponential_backoff
def completions_with_backoff(**kwargs):
return openai.Completion.create(**kwargs)
再次声明,OpenAI 对此解决方案的安全性或效率不作任何保证,但它可以成为您自己解决方案的良好起点。
批量请求
OpenAI API 对每分钟的请求数和令牌数有单独的限制。
如果您达到了每分钟请求次数的限制,但是在每分钟令牌方面有可用容量,则可以将多个任务分批处理到每个请求中,以增加吞吐量。这将允许您处理更多的令牌,特别是对于我们较小的模型。
发送一批提示与正常 API 调用完全相同,只需将字符串列表传递给 prompt 参数即可。
不使用批处理的例子:
import openai
num_stories = 10
prompt = "Once upon a time,"
# serial example, with one story completion per request
for _ in range(num_stories):
response = openai.Completion.create(
model="curie",
prompt=prompt,
max_tokens=20,
)
# print story
print(prompt + response.choices[0].text)
使用批处理的例子
import openai # for making OpenAI API requests
num_stories = 10
prompts = ["Once upon a time,"] * num_stories
# batched example, with 10 story completions per request
response = openai.Completion.create(
model="curie",
prompt=prompts,
max_tokens=20,
)
# match completions to prompts by index
stories = [""] * len(prompts)
for choice in response.choices:
stories[choice.index] = prompts[choice.index] + choice.text
# print stories
for story in stories:
print(story)
警告:响应对象可能不会按提示的顺序返回完成情况,因此请始终记住使用索引字段将响应与提示匹配。
请求增加
我应该在什么时候考虑申请速率限制增加?
我们的默认速率限制有助于最大化稳定性并防止滥用我们的 API。我们会增加限制以启用高流量应用程序,因此申请速率限制增加的最佳时间是当您认为您拥有必要的流量数据来支持提高速率限制的强有力理由时。没有支持数据的大幅度速率限制增加请求不太可能被批准。如果您正在准备产品发布,请通过 10 天分阶段发布获得相关数据。
请记住,速率限制增加有时需要 7-10 天,因此如果存在支持当前增长数字将达到您的速率限制所需数据,则尽早计划并提交是明智之举。
我的速率限制增加请求会被拒绝吗?
一个常见原因是缺乏证明其合理性所需数据而导致拒绝。下面提供了数值示例,展示如何最好地支持一个速率上升请求,并尽力批准所有符合安全策略和显示支持数据要求的请求。我们致力于使开发人员能够使用我们的 API 进行规模化和成功。
我已经为我的文本/代码 API 实现了指数退避算法,但仍然出现错误。如何提高我的频次上线?
目前,我们不支持提高免费测试端点(例如编辑端点)等功能。 我们也不会提高 ChatGPT 频次上线,但你可以参与 ChatGPT 专业版访问列表。
我们知道受到频次上线约束可能带来多大挫败感,并且很想为每个人都提高默认值。 但是由于共享容量约束,在 Rate Limit Increase Request 表单中只能批准付费客户证明需要通过审查后方可进行频次上线调整 。 为了帮助评估您真正需要哪些内容,请在“分享需求证据”部分中提供关于当前使用情况或基于历史用户活动预测 的统计信息 。 如果没有这些信息,则建议采取逐步释放方法:首先以当前比例释放服务给一小部分用户,在 10 个工作日内收集使用情况数据 ,然后根据该数据提交正式频次上线调整请求以供审核和批准。
如果您提交了申请并获得批准,则在 7-10 个工作日内通知您审批结果。 以下是填写此表格的一些示例:
DALL-E API 示例
模型 | 预估 tokens 数/分钟 | 预估请求数 | 用户数 | 需要的证据 | 1 小时最大吞吐成本 |
---|---|---|---|---|---|
DALL-E API | N/A | 50 | 1000 | 我们的应用目前正在生产中,根据过去的流量情况,我们每分钟大约发出 10 个请求。 | $60 |
DALL-E API | N/A | 150 | 10,000 | 我们的应用在 App Store 中越来越受欢迎,我们开始遇到速率限制。我们能否获得默认限制的三倍,即每分钟 50 个图像?如果需要更多,我们将提交新表格。谢谢! | $180 |
语言模型示例
模型 | 预估 tokens 数/分钟 | ESTIMATE 预估请求数 REQUESTS/MINUTE | 用户数 | 需要的证据 | 1 小时最大吞吐成本 |
---|---|---|---|---|---|
text-davinci-003 | 325,000 | 4,0000 | 50 | 我们将向一组初始的 Alpha 测试人员发布,并需要更高的限制来适应他们的初始使用。 我们在这里提供了一个链接到我们的 Google Drive,显示分析和 API 使用情况。 | $390 |
text-davinci-002 | 750,000 | 10,000 | 10,000 | 我们的应用程序受到了很多关注,我们有 50,000 人在等待列表上。 我们希望每天向 1000 人的小组推出,直到达到 50,000 个用户。 请查看此链接,以了解过去 30 天内我们当前令牌/分钟流量情况。 这是针对 500 个用户的,并且根据他们的使用情况,我们认为 750,000 个令牌/分钟和 10,000 个请求/分钟将作为一个良好的起点。 | $900 |
Code 模型示例
模型 | 预估 tokens 数/分钟 | ESTIMATE 预估请求数 REQUESTS/MINUTE | 用户数 | 需要的证据 | 1 小时最大吞吐成本 |
---|---|---|---|---|---|
code-davinci-002 | 150,000 | 1,000 | 15 | 我们是一组正在撰写论文的研究人员。我们估计,在本月底之前完成研究,需要在 code-davinci-002 上获得更高的速率限制。这些估计基于以下计算[...] | Codex 模型目前处于免费测试阶段,因此我们可能无法立即为这些模型提供增量。 |
请注意,这些示例仅为一般用例场景,实际使用率将根据具体的实现和使用情况而有所不同。