如何处理模型API速率限制

news2024/12/29 10:51:19

引言

当我们访问大模型相关的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_name
        self.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"] = batch
            response = requests.post(self.api_url, json=payload, headers=headers)
            embeds.extend([d["embedding"] for d in response.json()["data"]])

        return embeds

    def 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 exception
    embeds.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 islice

from tenacity import (
    retry,
    stop_after_attempt,
    wait_random_exponential,
) 

code_success = 200

class SiliconflowEmbeddings(Embeddings):
    def __init__(self, model_name: str):
        self.model_name = model_name
        self.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 response
    
    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"] = batch
            response = self._post(payload, headers)
            embeds.extend([d["embedding"] for d in response.json()["data"]])

        return embeds

    def 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 returning
            the actual time to wait. This distributes wait times
            stochastically in order to avoid timing collisions across
            concurrent clients. Wait times are jittered by default
            using the full_jitter function. Jittering may be disabled
            altogether by passing jitter=None.

自己实现指数退避重试

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

import random
import time

code_success = 200
code_rate_limit = 429


# 自定义异常
class ConflictError(Exception):
    status_code: int = 429

   

def 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 = 0
        delay = 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 e

    return 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.coloradmin.cn/o/2140663.html

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

相关文章

连续时间,离散频率 傅里叶

时域周期——不是把一个信号周期化&#xff0c;而是周期信号取一个周期是x(t),对其周期化不会发生时域的重叠。故当接收到信号&#xff0c;在DFT时&#xff0c;以整个接收到的时间信号为周期进行延拓 推导公式时思路&#xff1a;时域卷积周期冲击&#xff0c;用傅里叶变换推导出…

一键智能改写文章,快速提升内容的吸引力

在这个信息如潮水般涌来的时代&#xff0c;优质的内容创作成为了吸引眼球、传递价值的关键。而有时候&#xff0c;我们可能会面临着已有文章需要优化、旧内容需要焕发新活力的情况。此时&#xff0c;一键智能改写文章的工具就如同一位神奇的魔法师&#xff0c;它能帮助我们将文…

基于深度学习的图像分类或识别系统(含全套项目+PyQt5界面)

目录 一、项目界面 二、代码实现 1、网络代码 2、训练代码 3、评估代码 4、结果显示 三、项目代码 一、项目界面 二、代码实现 1、网络代码 该网络基于残差模型修改 import torch import torch.nn as nn import torchvision.models as modelsclass resnet18(nn.Modul…

【C语言】(指针系列2)指针运算+指针与数组的关系+二级指针+指针数组+《剑指offer面试题》

前言&#xff1a;开始之前先感谢一位大佬&#xff0c;清风~徐~来-CSDN博客&#xff0c;由于是时间久远&#xff0c;博主指针的系列忘的差不多了&#xff0c;所以有些部分借鉴了该播主的&#xff0c;有些地方如果解释的不到位&#xff0c;请翻看这位大佬的&#xff0c;感谢大家&…

C++ char*和char[] 可能指向的内存区域详解(附实验)

C char* 指向的内存区域详解 写在前面c内存结构简介指针常量和常量指针简介情况一&#xff1a;char* 指向栈区内容情况二&#xff1a;char* 指向堆区内容情况三&#xff1a;char* 指向常量区内容情况四&#xff1a;char* 指向静态区内容情况五&#xff1a;char* 指向全局区内容…

Scratch游戏-史诗忍者7免费下载

小虎鲸Scratch资源站-免费少儿编程Scratch作品源码,素材,教程分享网站! 作品描述&#xff1a; 在Scratch版本的《史诗忍者7》中&#xff0c;你需要穿越关卡&#xff0c;击败敌人并收集33个水果。通过灵活的操作和精准的攻击&#xff0c;逐步闯过重重难关。游戏中提供了丰富的技…

【GESP】C++一级练习BCQM3005,基本输出语句printf

一道基础练习题&#xff0c;练习基本输出语句printf。 BCQM3005 题目要求 描述 输出表达式1234∗5678的结果。 输入 无 输出 1234∗56787006652 输入样例 无 输出样例 1234 * 5678 7006652 全文详见个人独立博客&#xff1a;https://www.coderli.com/gesp-1-bcqm3005/ 【…

使用 SuperCraft AI 设计书橱模型的指南

在现代家居设计中&#xff0c;书橱不仅是存放书籍的地方&#xff0c;更是展示个人品味和风格的重要家具。借助 SuperCraft AI&#xff0c;你可以轻松设计出独一无二的书橱。以下是详细的步骤指南&#xff0c;帮助你从零开始设计一个理想的书橱。 1. 创建项目 首先&#xff0c…

【探索数据结构与算法】插入排序:原理、实现与分析(图文详解)

目录 一、插入排序 算法思想 二、插入排序 算法步骤 四、复杂度分析 时间复杂度&#xff1a;O(n^2) 空间复杂度&#xff1a;O(1) 稳定性&#xff1a;稳定算法 五、应用场景 &#x1f493; 博客主页&#xff1a;C-SDN花园GGbond ⏩ 文章专栏&#xff1a;探索数据结构…

node卸载流程

步骤&#xff1a; 1.开始中搜素”命令提示符“&#xff0c;并将其以”管理员身份运行“ 在弹出的框中输入cmd&#xff0c;并确认进入”命令提示符“ 2.在里面通过npm config list查看node相关文件路径&#xff0c; 并找到config from与prefix &#xff0c;后面对应的路径&…

element-plus的菜单组件el-menu

菜单是几乎是每个管理系统的软件系统中不可或缺的&#xff0c;element-plus提供的菜单组件可以快速完成大部分的菜单的需求开发&#xff0c; 该组件内置和vue-router的集成&#xff0c;使用起来很方便。 主要组件如下 el-menu 顶级菜单组件 主要属性 mode:决定菜单的展示模式…

visual studio给项目增加eigen库 手把手教程

Eigen是一个开源的C库&#xff0c;主要用来支持线性代数&#xff0c;矩阵和矢量运算&#xff0c;数值分析及其相关的算法。Eigen 除了C标准库以外&#xff0c;不需要任何其他的依赖包。Eigen库3.4.0版本的下载地址为&#xff1a; https://gitlab.com/libeigen/eigen/-/archive/…

qt-creator-10.0.2之后版本的jom.exe编译速度慢下来了

1、Qt的IDE一直在升级&#xff0c;qt-creator的新版本下载地址 https://download.qt.io/official_releases/qtcreator/ 2、本人一直用的是qt-creator-10.0.2版本&#xff0c;官网历史仓库可以下载安装包qt-creator-opensource-windows-x86_64-10.0.2.exe https://download.qt…

清华大佬自曝:接到了省烟草局的offer,我就拒掉了华为!结果华为立马给我申请了特殊涨薪,总包70w是烟草的2倍,这可如何是好?

《网安面试指南》http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247484339&idx1&sn356300f169de74e7a778b04bfbbbd0ab&chksmc0e47aeff793f3f9a5f7abcfa57695e8944e52bca2de2c7a3eb1aecb3c1e6b9cb6abe509d51f&scene21#wechat_redirect 《Java代码审…

Java设计模式—面向对象设计原则(四) ----->接口隔离原则(ISP) (完整详解,附有代码+案例)

文章目录 3.4 接口隔离原则(ISP)3.4.1 概述3.4.2 案列 3.4 接口隔离原则(ISP) 接口隔离原则&#xff1a;Interface Segregation Principle&#xff0c;简称ISP 3.4.1 概述 客户端测试类不应该被迫依赖于它不使用的方法&#xff1b;一个类对另一个类的依赖应该建立在最小的接…

我国常见电压等级有哪些?来试试电压版2048让你轻松牢记

作为电气工程专业的小姜同学&#xff0c;平时喜欢敲点代码&#xff0c;前一阵做了一个电气特色的2048小游戏。既能缓解学习压力&#xff0c;又能让大家在玩之中把我国的电压等级牢记于心。 项目预览 功能描述 游戏方法与2048相同&#xff0c;根据我国标准的电压等级&#xff…

基于SpringBoot的人事管理系统【附源码】

基于SpringBoot的人事管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2系统功能结构设计 4.3数据库设计 4.3.1数据库E-R图设计 4.3.2 数据库表结构设计 5 系统实现 5.1管理员功能介绍 5.1.1管理员登…

【Leetcode:1184. 公交站间的距离 + 模拟】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

PDF转JPG,奋斗汪的必备技能,你掌握了吗?

现在大家都用电脑手机处理文件&#xff0c;PDF和JPG是最常见的两种。PDF文件方便打印和分享&#xff0c;而JPG图片小巧清晰&#xff0c;适合在手机上看和发给别人。有时候&#xff0c;我们需要把PDF文件变成JPG图片&#xff0c;比如想把教材或报告变成图片&#xff0c;方便在手…

F28335 时钟及控制系统

1 F28335 系统时钟来源 1.1 振荡器OSC与锁相环PLL 时钟信号对于DSP来说是非常重要的,它为DSP工作提供一个稳定的机器周期从而使系统能够正常运行。时钟系统犹如人的心脏,一旦有问题整个系统就崩溃。DSP 属于数字信号处理器, 它正常工作也必须为其提供时钟信号。那么这个时钟…