API 代理转发 | ChatGPT 应用实战

闪电发卡1年前ChatGPT1226

image.png

由于 OpenAI 的 API 会限制访问 IP,除了在本地访问时开启代理外,还可以使用代理链接的方式。

教程需要一台国外服务器,个人主要用 Azure,内容一览:

  • 将用户所有请求转发给 OpenAI,不做任何处理

  • 将用户请求发送给 OpenAI,在这过程中记录请求日志(包括请求密钥和请求内容)

  • 越过 ChatGPT 的 Cloudflare 限制

注:本篇配合 openai_api_call 工具使用更佳。

直接转发请求

参考知乎:Nginx 反向代理 OpenAI API

设置 http 转发

配置 Nginx 信息如下,vim /etc/nginx/sites-enabled/default

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
   listen 80;
   server_name {your_domain_name};
   location / {
       proxy_pass  https://api.openai.com/;
       proxy_ssl_server_name on;
       proxy_set_header Host api.openai.com;
       proxy_set_header Connection '';
       proxy_http_version 1.1;
       chunked_transfer_encoding off;
       proxy_buffering off;
       proxy_cache off;
       ; proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header X-Forwarded-Proto $scheme;
   }
}

将第一处的 {your_domain_name} 替换为你的域名,保存退出。

参数说明:

  • proxy_pass:转发的上游服务器地址

  • proxy_ssl_server_name on 和 SSL/TLS 握手相关(待了解)

  • proxy_set_header Host,默认将上游地址作为 Host,这里手动设置为 api.openai.com

  • proxy_set_header X-Forwarded-For,默认将客户端 IP 作为 X-Forwarded-For,这里注释掉,避免暴露实际 IP

  • proxy_set_header X-Forwarded-Proto,默认将客户端协议作为 X-Forwarded-Proto

设置 ssl 证书

申请 ssl 证书后,将其放在 your_cert_path 和 your_cert_key_path 处,并修改 Nginx 配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
   listen 443 ssl;
   server_name {your_domain_name};
   ssl_certificate {your_cert_path};
   ssl_certificate_key {your_cert_key_path};
   ssl_session_cache shared:le_nginx_SSL:1m;
   ssl_session_timeout 1440m;
   ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
   ssl_prefer_server_ciphers on;
   ssl_ciphers TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
   location / {
       proxy_pass  https://api.openai.com/;
       proxy_ssl_server_name on;
       proxy_set_header Host api.openai.com;
       proxy_set_header Connection '';
       proxy_http_version 1.1;
       chunked_transfer_encoding off;
       proxy_buffering off;
       proxy_cache off;
       # proxy_set_header X-Forwarded-For $remote_addr;
       proxy_set_header X-Forwarded-Proto $scheme;
   }
}

监测数据并转发

比起直接用 nginx 转发,我们通过 Python 脚本转发请求,这样可以监测和处理交互的数据。

  • 使用 Python 的 flask 包编写脚本文件,将请求转发到 OpenAI

  • 使用 Nginx 配置域名

flask 转发请求

编写 main.py 文件,下载链接:main.py

下边是对文件内容的解释。

加载头部

导入必要的 Python 包

1
2
3
4
5
#!/usr/bin/env python
from flask import Flask, request, Response
import requests, random

app = Flask(__name__)

设置 API 请求日志的保存路径

1
2
3
4
5
6
7
8
9
# file for saving API requests
home = os.path.expanduser("~")
# api path
api_path = os.path.join(home, '.chatlog/apilist.log')
# request log
logfile = 'chatdata-' + datetime.datetime.now().strftime("%Y-%m-%d") + '.log'
log_path = os.path.join(home, '.chatlog', logfile)
os.makedirs(os.path.dirname(log_path), exist_ok=True)
os.makedirs(os.path.dirname(api_path), exist_ok=True)

替换密钥

在 API_LIST 中填写实际的 API 密钥,当请求密钥为 fake_keys 中的值时,会随机返回 API_LIST 中的一个密钥。

1
2
3
4
# real API keys
API_LIST = [ 'sk-real-1', 'sk-real-2', 'sk-real-3']
# fake API keys
fake_keys = [ 'sk-fake-1', 'sk-fake-2', 'sk-fake-3']

转发 chat 请求

将请求转发到 OpenAI,将返回结果保存到日志文件中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# wrap chat requests
@app.route('/v1/chat/completions', methods=['POST'])
def forward_request():
   try:
       # get request headers, body and query parameters
       api_key = request.headers.get('Authorization').strip("Bearer ").strip()
       url = 'https://api.openai.com/v1/chat/completions'
       data = request.get_json()
       params = request.args
       # If the API key starts with fake-, randomly select one key
       if api_key in fake_keys:
           api_key = random.choice(API_LIST)

       # Forward the data to the target API and get the response
       headers = {
           "Content-Type": "application/json",
           "Authorization": f"Bearer {api_key}"
       }
       response = requests.post(url, json=data, params=params, headers=headers)

       # Save API request log
       if not api_key in API_LIST:
           with open(api_path, 'a') as f:
               f.write(f'{api_key}\n\n')
       
       # Save request data
       with open(log_path, 'a', encoding="utf-8") as f:
           f.write(f'#Request\n{data}\n#Request\n')
           f.write(f'#Response\n{response.content}\n#Response\n\n')

       # return the response
       return Response(response.content, status=response.status_code)
   except Exception as e: # hide the error message
       return Response(f"invalid request!\n", status=400)

转发其他请求

这部分用于获取 API 的使用信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# wrap dashboard requests
@app.route('/<path:path>', methods=['GET'])
def handle_all(path):
   try:
       api_key = request.headers.get('Authorization').strip("Bearer ").strip()
       if api_key in fake_keys:
           api_key = random.choice(API_LIST)
       headers = {
               "Content-Type": "application/json",
               "Authorization": f"Bearer {api_key}"}
       # get recent usage
       if path == 'v1/dashboard/billing/usage':
           start_date = request.args.get('start_date')
           end_date = request.args.get('end_date')
           url = f'https://api.openai.com/v1/dashboard/billing/usage?start_date={start_date}&end_date={end_date}'
           response = requests.get(url, headers=headers)
           return Response(response.content, status=response.status_code)
       # get subscription info
       elif path == 'v1/dashboard/billing/subscription':
           url = 'https://api.openai.com/v1/dashboard/billing/subscription'
           response = requests.get(url, headers=headers)
           return Response(response.content, status=response.status_code)
       else:
           return Response(f"Unsupported url!\n", status=400)
   except Exception as e:
       return Response(f"invalid request!\n", status=400)

启动服务

最后,加上启动服务的代码:

1
2
if __name__ == '__main__':
   app.run(host='0.0.0.0', debug=False, port=5000)

执行 python main.py 启动服务。

修改 Nginx 配置文件

这一步需要域名,根据监听的端口号编写 nginx 配置文件,当访问该域名时,自动转发到相应的端口。

在 /etc/nginx/sites-available/default 文件中,添加如下内容:

1
2
3
4
5
6
7
8
9
10
server {
   listen 80;
   server_name <域名地址>;

   location / {
       proxy_pass http://localhost:5000;
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
   }
}

设置 ssl 证书

  1. 在域名所在服务商申请证书后,下载 nginx 证书文件,如 example.pem 和 example.key

  2. 将证书文件上传到服务器,比如到 /etc/nginx/cert 目录下

  3. 修改 nginx 配置文件,追加下边内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    server {
       listen 443 ssl;
       server_name <域名地址>;

       ssl_certificate /etc/nginx/cert/example.pem;
       ssl_certificate_key /etc/nginx/cert/example.key;

       location / {
           proxy_pass http://localhost:5000;
           proxy_set_header Host $host;
           proxy_set_header X-Real-IP $remote_addr;
       }
    }

    注意服务器需要开启 443 入站端口。

越过 Cloudflare 限制

借助 go-chatgpt-api 项目,只需要编写 docker-compose 文件:

1
2
3
4
5
6
7
8
9
10
version: "3"
services:
 go-chatgpt-api:
   container_name: go-chatgpt-api
   image: linweiyuan/go-chatgpt-api:latest # 最新
   ports:
     - 8080:8080 # 宿主机8080端口可按需改为其它端口
   environment:
     - GIN_MODE=release
   restart: unless-stopped

然后编写 nginx 配置文件,设置反向代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
  listen 80;
  server_name <域名地址>;
  location / {
      proxy_pass  http://127.0.0.1:8080;
      proxy_ssl_server_name on;
      proxy_set_header Host api.openai.com;
      proxy_set_header Connection '';
      proxy_http_version 1.1;
      chunked_transfer_encoding off;
      proxy_buffering off;
      proxy_cache off;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Forwarded-Proto $scheme;
  }
}

443 端口类似配置。为避免代理被他人使用,这里也可以修改参数,比如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
  listen 80;
  server_name <域名地址>;
  location /password {
      proxy_pass  http://127.0.0.1:8080;
      proxy_ssl_server_name on;
      proxy_set_header Host api.openai.com;
      proxy_set_header Connection '';
      proxy_http_version 1.1;
      chunked_transfer_encoding off;
      proxy_buffering off;
      proxy_cache off;
      proxy_set_header X-Forwarded-For $remote_addr;
      proxy_set_header X-Forwarded-Proto $scheme;
  }
}

另外,该项目不仅支持 API 代理,也支持 Access Token 代理,接口示例参看:

https://github.com/linweiyuan/go-chatgpt-api/blob/main/example/vscode


相关文章

对接ChatGPT时如何保证同一个用户会话的连惯性

要保证对接ChatGPT API时是同一个用户的连续问题,可以使用API返回的completion对象中的conversation_id属性来实现。conversation_id是一个字符串,可以唯一...

ChatGPT API接口编程基础与使用技巧

ChatGPT API接口编程基础与使用技巧

一、OpenAi Api调用库OpenAi开放了一系列模型接口API,包括ChatGPT、图像生成、音频、文件、敏感数据拦截等。若要集成这些模型接口调用到我们开发的系统里,可以通过多种编程语言的HTT...

大模型的生态圈:AI初创公司如何快速崛起

近年来,随着人工智能(AI)技术的快速发展,大模型的生态圈逐渐成形,为众多AI初创公司提供了广阔的发展空间。那么,AI初创公司是如何在这片热土上快速崛起的呢?今天我们就来聊聊这个话题。 首先,我们要明...

为什么选择ChatGPT Plus共享账号?详细分析及优势介绍

闪电发卡ChatGPT产品推荐:ChatGPT独享账号:https://www.chatgptzh.com/post/86.htmlChatGPT Plus独享共享账号购买代充:https://www...

编码器-解码器注意力层- Transformer教程

闪电发卡ChatGPT产品推荐: ChatGPT独享账号:https://www.chatgptzh.com/post/86.html ChatGPT Plus独享共享账号购买代充:https://w...

解密Prompt系列:升级Instruction Tuning:Flan/T0/InstructGPT/TKInstruct

解密Prompt系列:升级Instruction Tuning:Flan/T0/InstructGPT/TKInstruct

这一章我们聊聊指令微调,哈哈只要你细品,你就会发现大家对prompt和instruction的定义存在些出入,部分认为instruction是prompt的子集,部分认为instruction是句子类...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。