本文最后更新于 2024-02-08,文章内容可能已经过时。

一个免费的使用 ChatGPT 3.5 API 的方案

自从 Pandora-Next 的作者 pengzhile (始皇)宣布 Pandora-Next 停止运营,愉快的享受免费不限量高并发的 ChatGPT 3.5 API 的日子也就结束了。虽然现在很多第三方 API 提供商也提供很多免费额度使用,甚至多的不一定用得完,但是这究其根本在中国大陆的法律上应该是违法行为,也不一定能保证这些第三方提供商的服务就一直稳定,就像始皇停止运营来的那么突然一样。靠谁都不如靠自己,自己部署一个 server 才更靠谱。

经过我的一段时间探索和使用,目前总算是找到了一套差不多算是可以平替的方案,使用到了三个开源仓库:

ninja

ninja 用于和 OpenAI 进行中转,把 chat 的接口转换 api 的接口,不过由于 ninja 作者开发 ninja 的理念是服务端不存储用户数据,所以 ninja 的 ChatGPT-to-API 接口需要使用 access_Token 做 API Keys ,受限于 access_Token 的有效时间太短以及 access_Token 过长的问题,并不能很愉快的使用。但是 ninja 有使用 refresh_token 获取 access_Token 的接口,因为 refresh_token 的长度很短,且永久有效,所以我在 ninja 的 issue 中提议,使用 refresh_token 当做 API Keys 进行请求,但是被作者因为与其不存储用户数据的理念相悖而被否决。

refresh-gpt-chat

refresh-gpt-chat 用于把 ninja 的 api 接口转换成使用 refresh_token 作为 API Keys 进行请求。实际上是 refresh-gpt-chat 先使用 refresh_token 请求 access_Token ,再使用获取到的 access_Token 请求 API ,并把这个 access_Token 存储下来,反复使用。当 access_Token 过期时,再重新使用 refresh_token 请求 access_Token 。

one-api

996C83CF-7363-4CF3-BAA2-9DE59B18B06B.webpone-api 用于存储多个 refresh_token ,提高并发量。因为 ninja 提供的 API 功能还是基于 OpenAI 的 chat 的 web ,而 web 又无法并发,必须等一句话说完,才能响应下一次请求,所以对于某些需要高并发的使用场景来讲, ninja 配合 refresh-gpt-chat 是不够的。但是在 one-api 内新建多个渠道,每个都存放一个 refresh_token ,用于请求 refresh-gpt-chat ,再使用 one-api 提供的接口,就能解决高并发的问题,愉快使用。而且 refresh_token 如果泄露也不好, one-api 二次分发就能避免 refresh_token 泄露的问题,让我无后顾之忧的把自己的 API Keys 提供给他人使用。

部署

部署上述服务,需要准备:
1、一台在 OpenAI 封锁之外的 VPS ;
2、一定的智慧和解决问题的能力。

部署步骤也并不复杂,特别是 ninja 和 one-api ,几乎是开箱即用,refresh-gpt-chat只要部署好了 Java 环境,运行也很简单。当然如果 VPS 能够支持 Docker ,甚至只需要 docker-compose.yaml 直接跑就行了。

Docker compose

32513628-8E84-44CD-AD16-B14B72900D39.webp以下是我写的,把上述三个仓库的服务一口气跑起来的 docker-compose.yaml,只需要修改最末 refresh-gpt-chat 部分的 getAccessTokenUrlchatUrl 两个参数配置即可直接 docker compose up -d 使用。

version: '3.4'

services:
  ninja:
    image: gngpp/ninja:latest
    container_name: ninja
    restart: unless-stopped
    environment:
      - TZ=Asia/Shanghai
    command: run
    ports:
      - "8080:7999"
  
  watchtower:
    container_name: watchtower
    image: containrrr/watchtower
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: --interval 3600 --cleanup
    restart: unless-stopped

  one-api:
    image: justsong/one-api:latest
    container_name: one-api
    restart: always
    command: --log-dir /app/logs
    ports:
      - "3000:3000"
    volumes:
      - /project/sandbox/data/oneapi:/data
      - /project/sandbox/logs:/app/logs
    environment:
      - SQL_DSN=oneapi:123456@tcp(db:3306)/one-api  # 修改此行,或注释掉以使用 SQLite 作为数据库
      - REDIS_CONN_STRING=redis://redis
      - SESSION_SECRET=random_string  # 修改为随机字符串
      - TZ=Asia/Shanghai
#      - NODE_TYPE=slave  # 多机部署时从节点取消注释该行
#      - SYNC_FREQUENCY=60  # 需要定期从数据库加载数据时取消注释该行
#      - FRONTEND_BASE_URL=https://openai.justsong.cn  # 多机部署时从节点取消注释该行
    depends_on:
      - redis
      - db
    healthcheck:
      test: [ "CMD-SHELL", "wget -q -O - http://localhost:3000/api/status | grep -o '\"success\":\\s*true' | awk -F: '{print $2}'" ]
      interval: 30s
      timeout: 10s
      retries: 3

  redis:
    image: redis:latest
    container_name: redis
    restart: always

  db:
    image: mysql:8.2.0
    restart: always
    container_name: mysql
    volumes:
      - /project/sandbox/data/mysql:/var/lib/mysql  # 挂载目录,持久化存储
    ports:
      - '3306:3306'
    environment:
      TZ: Asia/Shanghai   # 设置时区
      MYSQL_ROOT_PASSWORD: 'OneAPI@justsong' # 设置 root 用户的密码
      MYSQL_USER: oneapi   # 创建专用用户
      MYSQL_PASSWORD: '123456'    # 设置专用用户密码
      MYSQL_DATABASE: one-api   # 自动创建数据库

  refresh-gpt-chat:  
    # 该服务使用的 Docker 镜像
    image: yangclivia/refresh-gpt-chat:latest  
    # Java 的环境变量 (可适当调节,用copilot可以适当调大点,具体可问gpt了解)
    environment:  
      - JAVA_OPTS=-XX:+UseParallelGC -Xms64m -Xmx64m -XX:MaxMetaspaceSize=64m  
    # 为该服务创建的容器的名称
    container_name: refresh-gpt-chat
    # 容器总是重新启动
    restart: always  
    # 容器运行的用户
    user: root  
    # 容器的网络模式
    ports:
      - "8082:8082"
    command:  
      - --log=info
      #自定义端口号 
      - --server.port=8082
      #自定义前缀
#       - --server.servlet.context-path=/tokensTool
      # ninja获取accessToken的Url
      - --getAccessTokenUrl=http(s)://ninja的外部访问地址/auth/refresh_token
      # ninja或者chat2api API接口
      - --chatUrl=http(s)://ninja的外部访问地址/v1/chat/completions

手动部署

ninja 和 one-api 都可以直接下载可执行文件直接启动,无需配置其他环境,这里不做过多介绍,仅仅记录一下启动的命令。

ninja:

在仓库中直接下载对应自己的 VPS 系统的可执行二进制文件即可。

# 添加可执行权限
chmod +x ninja
# 启动ninja
./ninja run
# 后台启动ninja
./ninja start
# 停止后台ninja
./ninja stop
# pm2管理启动ninja
pm2 start ./ninja --name ninja -- run

one-api

在仓库中直接下载对应自己的 VPS 系统的可执行二进制文件即可。

# 添加可执行权限
chmod u+x one-api
# 设置缓存路径为当前路径(适合无root用户)
export TIKTOKEN_CACHE_DIR="$PWD" 
# 启动one-api(把PORT改成自己想用的端口)
./one-api --port PORT --logs-dir ./logs
# pm2管理启动one-api
pm2 start ./one-api --name one-api -- --port PORT --logs-dir ./logs

refresh-gpt-chat

需要配置 OpenJDK , 前文在 hax 的免费主机上已经安装过 OpenJDK ,这里不再赘述。在仓库中下载 .jar 结尾的 fat jar 包即可直接启动:

# 直接启动(把 PORT 替换成自己希望的端口,并填写ninja的访问地址, jar 包文件名替换成自己下载的文件名)
java -jar refresh-gpt-chat-0.0.1-SNAPSHOT.jar --server.port=PORT --server.servlet.context-path=/ --getAccessTokenUrl=http(s)://ninja的访问地址/url/auth/refresh_token --chatUrl=http(s)://ninja的访问地址/v1/chat/completions
# 使用pm2管理启动
pm2 start java --name refresh-gpt-chat -- -jar refresh-gpt-chat-0.0.1-SNAPSHOT.jar --server.port=PORT --server.servlet.context-path=/ --getAccessTokenUrl=http(s)://ninja的访问地址/url/auth/refresh_token --chatUrl=http(s)://ninja的访问地址/v1/chat/completions

使用

进入 one-api 渠道选项卡中,添加新的渠道,类型选择自定义渠道, Base URL 填写 refresh-gpt-chat 的地址,模型选择 gpt-3.5 相关的全部模型。模型重定向中填入以下内容:

{
  "gpt-3.5-turbo-0301": "gpt-3.5-turbo",
  "gpt-3.5-turbo-0613": "gpt-3.5-turbo",
  "gpt-3.5-turbo-16k": "gpt-3.5-turbo",
  "gpt-3.5-turbo-16k-0613": "gpt-3.5-turbo",
  "gpt-3.5-turbo-1106": "gpt-3.5-turbo",
  "gpt-3.5-turbo-instruct": "gpt-3.5-turbo"
}

勾选中秘钥下方的批量创建,然后在秘钥中填入 refresh_token ,一行一个,最后点击提交。再去令牌选项卡中添加新的令牌,按照自己的喜好填写并提交即可。

再使用 one-api 生成的令牌作为 API Keys , one-api 的地址作为端点即可。

有多高并发取决于有多少 refresh_token 被存储在 one-api 中,如果需要使用沉浸式翻译这样的高并发应用,建议存入 20-100 个 refresh_token 进行请求。

获取 refresh_token 的网址:xyhelperfreegpts(这两个都是第三方服务,不能保证账号数据安全,也不能保证其服务一直有效,酌情使用,后果自负)

亦或者可以使用 Apple 设备抓包获取 refresh_token ,具体可以自己搜索。不过这种方式容易导致 Apple 设备被 OpenAI 封禁,请酌情使用。