API 代理转发 | ChatGPT 应用实战
由于 OpenAI 的 API 会限制访问 IP,除了在本地访问时开启代理外,还可以使用代理链接的方式。
教程需要一台国外服务器,个人主要用 Azure,内容一览:
将用户所有请求转发给 OpenAI,不做任何处理
将用户请求发送给 OpenAI,在这过程中记录请求日志(包括请求密钥和请求内容)
越过 ChatGPT 的 Cloudflare 限制
注:本篇配合 工具使用更佳。
直接转发请求
参考知乎:
设置 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
文件,下载链接:。
下边是对文件内容的解释。
加载头部
导入必要的 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 证书
在域名所在服务商申请证书后,下载 nginx 证书文件,如 example.pem
和 example.key
。
将证书文件上传到服务器,比如到 /etc/nginx/cert
目录下
修改 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 代理,接口示例参看: