> For the complete documentation index, see [llms.txt](https://walkman42.gitbook.io/best-practice-in-7days/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://walkman42.gitbook.io/best-practice-in-7days/mainbody/day5/5.1-openai-limits.md).

# 5.1 应对OpenAI接口请求限制

#### 5.1.1 OpenAI限速场景

OpenAI的速率限制是 API 对用户或客户端在指定时间段内访问服务次数施加的限制。 官方称这些限制“有助于预防 API 滥用和保证公平访问”，但是实际上更重要的原因是计算能力在用户数量面前太有限了。

其限制的方式包括：

* 每分钟请求数（RPM）
* 每分钟令牌数（TPM）
* 每天请求数（RPD）
* 每分钟图像数（IPM）

速率限制是在组织级别实施的，而不是用户级别，并且会根据所使用的模型而有所不同。使用者还会受到每月在 API 上花费的总额的限制，这也称为“使用限制”。

随着使用者对 OpenAI API 的使用增加以及在 API 上的支出增加，OpenAI 会自动将使用者升级到下一个使用层。这通常会增加大多数模型的速率限制。更高层次的使用者还可以访问更低延迟的模型。

我们不考虑达芬奇和其他模型，主要考虑gpt-3.5-turbo和gpt-4这两个常用模型，一个便宜，另一个功能强大，能够覆盖绝大多数场景。 简单的讲一下前期的限制：

| MODEL                  | RPM | RPD | TPM     |
| ---------------------- | --- | --- | ------- |
| gpt-4                  | 3   | 200 | 10,000  |
| gpt-3.5-turbo          | 3   | 200 | 20,000  |
| text-embedding-ada-002 | 3   | 200 | 150,000 |
| whisper-1              | 3   | 200 | -       |
| tts-1                  | 3   | 200 | -       |
| dall-e-2               | 5   | -   | -       |
| dall-e-3               | 1   | -   | -       |

这个阶段比较麻烦，需要多个初级账号做成集群，系统轮流使用这些账号访问接口。 但是好在产生5美金消费以后，就会得到提升。

| MODEL                  | RPM        | RPD   | TPM     |
| ---------------------- | ---------- | ----- | ------- |
| gpt-4                  | 500        | 10000 | 10000   |
| gpt-3.5-turbo          | 3500       | 10000 | 40000   |
| text-embedding-ada-002 | 500        | 10000 | 1000000 |
| whisper-1              | 50         | -     | -       |
| tts-1                  | 50         | -     | -       |
| dall-e-2               | 50 img/min | -     | -       |
| dall-e-3               | 5 img/min  | -     | -       |

至于怎么到下一个等级，可以看这里：

| 等级   | 资格                          | 金额限制         |
| ---- | --------------------------- | ------------ |
| 未消费  | 用户必须位于允许的地区                 | 每月 100 美元    |
| 等级 1 | 支付 5 美元                     | 每月 100 美元    |
| 等级 2 | 支付 50 美元且距离首次成功付款至少 7 天     | 每月 500 美元    |
| 等级 3 | 支付 100 美元且距离首次成功付款至少 7 天    | 每月 1,000 美元  |
| 等级 4 | 支付 250 美元且距离首次成功付款至少 14 天   | 每月 5,000 美元  |
| 等级 5 | 支付 1,000 美元且距离首次成功付款至少 30 天 | 每月 10,000 美元 |

这意味着，其实消费不是重点，主要是受时间限制。 7天，14天是2个不同的档位。 7天消费达到$50以后，就不再有每天请求数（RPD）的限制。 然而到达等级4以后，其实限制只有金额的区别，下面是等级4的限制，等级5的RPM和TPM与其一致。

|          MODEL         |     RPM     | RPD |    TPM    |
| :--------------------: | :---------: | :-: | :-------: |
|          gpt-4         |    10,000   |  -  |  300,000  |
|      gpt-3.5-turbo     |    10,000   |  -  | 1,000,000 |
| text-embedding-ada-002 |    10,000   |  -  | 5,000,000 |
|        whisper-1       |     100     |  -  |     -     |
|          tts-1         |     100     |  -  |     -     |
|        dall-e-2        | 100 img/min |  -  |     -     |
|        dall-e-3        |  15 img/min |  -  |     -     |

个人使用肯定是够用，但生产环境平台级应用显然是无法靠单个账号满足的，同时我们还要规避异常带来的拥堵和用户负面体验。

* **如果我遇到了速率限制错误会怎样？** 速率限制错误看起来像这样： 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 拒绝履行进一步申请直至经过指定时间。

所以通过一套轮询机制和锁机制来调控就显得非常有必要。

#### 5.1.2 Goruntine经典方案

在 Go 中，协程操作效率远高于进程和线程，为了测试阶段的成本考虑，我们先假设同一时间只允许30个调用OpenAI接口的请求。

这时可以使用一个有缓冲的通道来控制同时进行的请求数量，通道可以当作信号量（semaphore），以限制可以同时运行的 goroutine 的数量。

我们看一段简单的实现代码：

```go
package main

import (
    "sync"
    "time"
)

// 请求函数，模拟发送请求
func sendRequest(requestNum int, semaphore chan struct{}, wg *sync.WaitGroup) {
    defer wg.Done()

    // 获取信号量的一个位置
    semaphore <- struct{}{}
    
    // 在这里发送 API 请求
    // ...

    println("Request", requestNum, "is being processed.")

    // 模拟请求处理时间
    time.Sleep(10 * time.Second)

    // 请求完成后，从信号量释放位置
    <-semaphore
}

func main() {
    var wg sync.WaitGroup
    semaphore := make(chan struct{}, 30) // 限制为30个并发

    // 模拟大量请求
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go sendRequest(i, semaphore, &wg)
    }

    wg.Wait()
}
```

在这个代码中：

* `sendRequest`的函数来模拟发送请求。
* 我们创建了一个缓冲为30的通道`semaphore`，这意味着最多可以有30个空结构体在通道中。
* 每个 goroutine 在开始处理之前，都会向`semaphore`发送一个空结构体。如果通道已满，goroutine 会阻塞，直到通道中有空位。
* 当请求处理完成后，goroutine 会从通道中接收一个值，这会释放一个位置，允许另一个 goroutine 开始执行。

这种方式是利用通道的阻塞特性来实现并发控制，即实现信号量的作用。

这些锁操作只是为了防止触发rate limit，而真要解决问题，还需要靠多账号轮询。

#### 5.1.3 使用账号池轮询来调取API
