如何处理模型API速率限制

引言

当我们访问大模型相关的API服务时,通常会遇到速率限制(即限流),它用于防止用户向某个API发送大量请求,防止请求过载,确保每个人都能公平地访问API。

速率限制的方式

速率限制通常有以下几种形式:

  1. RPM(requests per minute) 每分钟请求数
  2. PRD(requests per day) 每天请求数
  3. TPM(tokens per minute) 每分钟token数
  4. TPD(tokens per day) 每天token数
  5. IPM(images per minute) 每分钟图像数:比如针对图像生成模型
  6. IPD(images per day) 每天图像数

速率限制可能会因为其中任一选项中达到峰值而触发。比如RPM限制为20,TPM限制为100k,假设一分钟内发送了20个请求,每个请求只有100(0.1k)个token,那么RPM的限制会触发,即使这20个请求内没有发满100k个token。

如何处理速率限制

当大量调用OpenAI API时,可能会遇到429: Too Many ReuqestsRateLimitError的错误消息,表示超过速率限制。

⚠️ 持续重试不能解决该问题。这里说的处理,是指尽可能不要超过这个速率限制,如果想从根源上解决一种方法是自己部署。

image-20240915175922450

以硅基流动提供的免费嵌入模型为例,它的RPM是2000,即一分钟内只能发送2000个请求,其实不算低;但是TPM只有500k,假设文本块平均为500个token,实际上每分钟只能发送1000个文本块。

错误示范

from langchain_core.embeddings import Embeddings
from itertools import islice
batch_size = 32 # 硅基流动嵌入模型最大批大小为32
def batch_list(it, size=batch_size):it = iter(it)return iter(lambda: list(islice(it, size)), [])class SiliconflowEmbeddings(Embeddings):def __init__(self, model_name: str):self.model_name = model_nameself.api_url = "https://api.siliconflow.cn/v1/embeddings"def embed_documents(self, texts: List[str]) -> List[List[float]]:payload = {"model": self.model_name, "input": texts, "encoding_format": "float"}headers = {"accept": "application/json","content-type": "application/json","authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",}embeds = []for batch in tqdm(batch_list(texts), total=len(texts) // batch_size):payload["input"] = batchresponse = requests.post(self.api_url, json=payload, headers=headers)embeds.extend([d["embedding"] for d in response.json()["data"]])return embedsdef embed_query(self, text: str) -> List[float]:return self.embed_documents([text])[0]

如果我们以512 token为一个文本块,假设一篇文档被拆分为2562个文本块,那么直接运行上面的代码会报错:

Get 2562 chunks
Storing...15%|███████████████████████████▉                                                                                                                                                              | 12/80 [00:06<00:35,  1.94it/s] 
2024-09-16 17:27:42.706 Uncaught app exceptionembeds.extend([d["embedding"] for d in response.json()["data"]])^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not iterable

这是因为它触发了速率限制,如果查看response.status_code会发现它为429

image-20240916172947991

查看硅基流动文档中关于429状态码的描述,正好就是RateLimit。

使用指数退避重试

指数退避(exponential backoff)通过反馈成倍地降低某个过程的速率,以逐渐找到合适的速率。

即在遇到速率限制错误时先进行短暂的睡眠,然后重试失败的请求。如果该请求仍然失败,则增加睡眠的时间并重复该过程,直到请求成功或达到最大重试次数。注意在实现的时候,可能不是固定的成倍数(比如睡眠时间成2),而是会增加一定的随机性。这里增加随机性防止多个设备执行退避重试时产生的同步化。

根据参考1中的方案。要使用指数退避重试,下面先介绍两个库,最后介绍自己实现。

使用tenacity库

要开始,执行pip install tenacity安装依赖。

from langchain_core.embeddings import Embeddings
from itertools import islicefrom tenacity import (retry,stop_after_attempt,wait_random_exponential,
) code_success = 200class SiliconflowEmbeddings(Embeddings):def __init__(self, model_name: str):self.model_name = model_nameself.api_url = "https://api.siliconflow.cn/v1/embeddings"# 最多重试12次@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(12))def _post(self, payload, headers):response = requests.post(self.api_url, json=payload, headers=headers)if response.status_code != code_success:raise Exception(f"Error code: {response.status_code}")return responsedef embed_documents(self, texts: List[str]) -> List[List[float]]:payload = {"model": self.model_name, "input": texts, "encoding_format": "float"}headers = {"accept": "application/json","content-type": "application/json","authorization": f"Bearer {os.environ['OPENAI_API_KEY']}",}embeds = []for batch in tqdm(batch_list(texts), total=len(texts) // batch_size):payload["input"] = batchresponse = self._post(payload, headers)embeds.extend([d["embedding"] for d in response.json()["data"]])return embedsdef embed_query(self, text: str) -> List[float]:return self.embed_documents([text])[0]

tenacity提供一个retry注解,我们可以加到真实调用API处,比如我们封装一下requests.post的代码,检查它返回的状态码,如果是429我们就抛出异常,触发retry里面的重试。这里为了简单,只要不是200就抛出异常。

这样可以处理速率限制,注意如果没有速率限制,正常情况下只需要30秒即可完成嵌入操作:

 19%|██████████████████████████████████▉                                                                                                                                                       | 15/80 [00:07<00:30,  2.15it/s] 

当触发速率限制后,在重试中会进行一些休眠,因此最终的完成的时间会大大超过这个30秒,但至少成功嵌入了:

81it [04:25,  3.27s/it]

使用backoff库

要开始,执行pip install backoff安装依赖。

import backoff @backoff.on_exception(backoff.expo, Exception, max_time=60)
def _post(self, payload, headers):response = requests.post(self.api_url, json=payload, headers=headers)if response.status_code != code_success:raise Exception(f"Error code: {response.status_code}")return response

使用backofftenacity差不多,它也提供了一个注解backoff.on_exception。它会会使用一个叫做jitter的函数来增加随机性避免同步化带来的冲突:

 jitter: A function of the value yielded by wait_gen returningthe actual time to wait. This distributes wait timesstochastically in order to avoid timing collisions acrossconcurrent clients. Wait times are jittered by defaultusing the full_jitter function. Jittering may be disabledaltogether by passing jitter=None.

自己实现指数退避重试

我们可以看看如何手动实现指数退避重试,以认识到它的实现原理非常简单:

import random
import timecode_success = 200
code_rate_limit = 429# 自定义异常
class ConflictError(Exception):status_code: int = 429def retry_with_exponential_backoff(func,initial_delay: float = 1,exponential_base: float = 2,jitter: bool = True,max_retries: int = 12,errors: tuple = (ConflictError,),
):def wrapper(*args, **kwargs):num_retries = 0delay = initial_delay# 循环直到一次成功的响应或达到max_retries次数while True:try:return func(*args, **kwargs)# 在指定的错误上重是except errors as e:# 增加重试次数num_retries += 1# 检查是否达到最大重试次if num_retries > max_retries:raise Exception(f"Maximum number of retries ({max_retries}) exceeded.")# 增加延迟 random.random() 生成 0到1之间的随机数,这里是为了增加随机性delay *= exponential_base * (1 + jitter * random.random())# 休眠time.sleep(delay)# 如果不是指定的异常则抛出except Exception as e:raise ereturn wrapper

我们判断状态码,如果是429则抛出ConflictError,否则直接返回response

@retry_with_exponential_backoff
def _post(self, payload, headers):response = requests.post(self.api_url, json=payload, headers=headers)if response.status_code == code_rate_limit:raise ConflictError()return response

参考

  1. https://platform.openai.com/docs/guides/rate-limits
  2. https://cloud.siliconflow.cn/rate-limits

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/142068.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

详解HTTP/HTTPS协议

HTTP HTTP协议全名为超文本传输协议。HTTP协议是应用层协议&#xff0c;其传输层协议采用TCP协议。 请求—响应模型 HTTP协议采用请求-响应模型&#xff0c;通常由客户端发起请求由服务端完成响应。资源存储在服务端&#xff0c;客户端通过请求服务端获取资源。 认识URL 当…

Linux 系统盘空间不足,想要将 Docker 镜像和容器数据迁移到数据盘

摘要&#xff1a;大家在Linux上用Docker部署项目的时候&#xff0c;有时候会部署多个项目&#xff0c;系统盘空间不足&#xff0c;数据盘又挂载有很多空间&#xff0c;这时候就会想要将 Docker 镜像和容器数据迁移到数据盘&#xff0c;本文主要讲解迁移步骤和迁移过程中遇到的一…

vue2的diff算法

Vue2 的虚拟 DOM diff 算法是一种高效的算法&#xff0c;用于比较新旧两个虚拟 DOM 树&#xff0c;找出差异并更新到真实 DOM 上。这个算法的核心在于尽量减少不必要的 DOM 操作&#xff0c;提高性能。 虚拟dom&#xff1a;把DOM数据化&#xff0c;先通过不断地操作数据&#…

数据集 CULane 车道线检测 >> DataBall

数据集 CULane 车道线检测 自动驾驶 无人驾驶目标检测 CULane是用于行车道检测学术研究的大规模具有挑战性的数据集。它由安装在六辆由北京不同驾驶员驾驶的不同车辆上的摄像机收集。收集了超过55小时的视频&#xff0c;并提取了133,235帧。数据示例如上所示。我们将数据集分为…

【C++算法】前缀和

前缀和 题目链接 前缀和https://www.nowcoder.com/practice/acead2f4c28c401889915da98ecdc6bf?tpId230&tqId2021480&ru/exam/oj&qru/ta/dynamic-programming/question-ranking&sourceUrl%2Fexam%2Foj%3Fpage%3D1%26tab%3D%25E7%25AE%2597%25E6%25B3%2595%2…

传神论文中心|第25期人工智能领域论文推荐

在人工智能领域的快速发展中&#xff0c;我们不断看到令人振奋的技术进步和创新。近期&#xff0c;开放传神&#xff08;OpenCSG&#xff09;传神社区发现了一些值得关注的成就。传神社区本周也为对AI和大模型感兴趣的读者们提供了一些值得一读的研究工作的简要概述以及它们各自…

Java-idea小锤子图标

这一版的idea小锤子图标其实就在这里 点进去就找到了~

mtk7628 网口灯问题

板子上电插入网线到网口&#xff0c;只有wan口灯会亮&#xff0c;插入lan口灯不会亮。对比了ok的代码&#xff0c;先对比设备树&#xff0c;未看到网口相关的GPIO。 mt7628an_WMD-7688A-12816.dts mt7628an_hilink_hlk-7628n.dts 继续查看网口相关代码&#xff0c;加打印&…

在实际LabVIEW开发中,哪些算法是常用的?

在LabVIEW的实际开发中&#xff0c;常用的算法主要集中在数据处理、控制系统、信号处理、图像处理等领域。以下是一些常用算法的介绍&#xff1a; 1. PID控制算法 PID&#xff08;比例-积分-微分&#xff09;控制是LabVIEW中常用的算法之一&#xff0c;广泛应用于工业自动化和…

Leetcode—1184. 公交站间的距离【简单】

2024每日刷题&#xff08;161&#xff09; Leetcode—1184. 公交站间的距离 实现代码 class Solution { public:int distanceBetweenBusStops(vector<int>& distance, int start, int destination) {int clockwise 0;int counterclockwise 0;if(start > desti…

华为防火墙智能选路篇之链路权重(带宽)负载分担

基于链路的权重负载分担&#xff08;真机演示&#xff09; 这里博主采用真机演示&#xff0c;模拟器只能配置没办法模拟出效果&#xff0c;真机能够真实的体验出效果&#xff0c;更好的去理解&#xff0c;所以这边采用真机配置了。环境简化了&#xff0c;防火墙内网接了一台测试…

Zookeeper工作机制和特点

1. Zookeeper工作机制 Zookeeper从设计模式角度来理解&#xff1a; 是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的数据&#xff0c;然后接受观察者的 注册&#xff0c;一旦这些数据的状态发生变化&#xff0c;Zookeeper就将负责通知…

2-3.Android 存储之存储空间(私有空间、公共空间)

一、内部存储与外部存储 内部存储指位于设备的内部存储空间 外部存储指位于设备的外部存储介质&#xff0c;例如&#xff0c;SD 卡 简单理解&#xff0c;内部存储就是存储在手机自身&#xff0c;外部存储就是存储在手机可以外接的东西&#xff0c;好比电脑的硬盘和 U 盘 二、…

Scratch教学案例 —— 制作生日蛋糕

小虎鲸Scratch资源站-免费少儿编程Scratch作品源码,素材,教程分享网站! 简介 在这个教学案例中&#xff0c;我们将使用Scratch制作一个简单而有趣的生日蛋糕动画。通过这个项目&#xff0c;学生可以学习到如何使用Scratch中的基本编程块进行角色控制、造型切换、舞台背景设置以…

CefSharp_Vue交互(Element UI)_WinFormWeb应用---设置应用透明度(含示例代码)

一、界面预览 1.1 设置透明(整个页面透明80%示例) 限制输入值:10-100(数字太小会不好看见) 1.2 vue标题栏 //注册类与js调用 (async function(

如何从 GitHub 上克隆项目

GitHub 是一个广泛使用的代码托管平台&#xff0c;开发者们可以在上面共享和管理他们的代码。如果你想要快速获取一个项目的代码&#xff0c;可以通过克隆&#xff08;clone&#xff09;仓库来实现。本文将详细介绍如何从 GitHub 上克隆项目的步骤。更多内容&#xff0c;请查询…

中介者模式:提升代码可维护性的利器

一&#xff0c;介绍 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为设计模式&#xff0c;它定义了一个中介者对象来封装一系列对象之间的交互。中介者使各对象不需要显式地相互引用&#xff0c;从而使其耦合松散&#xff0c;可以独立地改变它们之间的交互。 …

CefSharp_Vue交互(Element UI)_WinFormWeb应用(2)---置顶和取消置顶(含示例代码)

一、预览 获取winform的置顶参数,和设置置顶参数 1.1 置顶(默认不置顶) 1.2 示例代码

进程间关系和守护进程

序言 当我们使用指令 ps 查看进程的相关信息时&#xff0c;在以前我们只是关注该进程的 PID(该进程的标识符) &#xff0c; PPID(其父进程的标识符) 以及 STAT(该进程的状态)。  那 PGID 和 SID 又是什么&#xff1f;有什么作用呢&#xff1f; 1. 进程组 1.1 什么是进程组&am…

视频工具EasyDarwin将本地视频生成RTSP给WVP拉流列表

效果 ffmpeg生成rtsp流 EasyDarwin的rtsp端口默认的是10054, 使用ffmpeg将本地视频转到EasyDarwin的rtsp。 F:\rtsp\ffmpeg-7.0.2-essentials_build\bin>ffmpeg -re -i F:\rtsp\123.mp4 -rtsp_transport tcp -vcodec h264 -f rtsp rtsp://127.0.0.1:10054/video11 它的直播…