唠唠闲话
由于 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 /prefix_real { proxy_pass https://api.openai.com; # 端口号与 listen 保持一致 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 $server_addr; proxy_set_header X-Forwarded-Proto https; # 或者用 $scheme 保持协议 } }
将第一处的 {your_domain_name}
替换为你的域名,如果没有域名,这行可以注释掉。这里的 prefix_real
根据需要修改,起类似密码的作用。
参数说明:
proxy_pass
:转发的上游服务器地址
proxy_ssl_server_name on
和 SSL/TLS 握手相关(待了解)
proxy_set_header Host
,默认将上游地址作为 Host,这里手动设置为 api.openai.com
proxy_set_header X-Forwarded-For
,建议填写代理所在的 IP 地址(可以通过 curl ifconfig.me
获取),这里通常有两个可用变量:
$server_addr
为 Nginx 的地址,但可能为内网 IP 而非实际 IP
$remote_addr
为客户端地址,不建议使用,否则可能被 OpenAI 限制
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 server { listen 443 ssl; server_name {your_domain_name}; ssl_certificate {your_cert_path}; ssl_certificate_key {your_cert_key_path}; 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 $server_addr; proxy_set_header X-Forwarded-Proto https; # 或者用 $scheme 保持协议 } }
监测数据并转发
比起直接用 nginx 转发,我们通过 Python 脚本转发请求,这样可以监测和处理交互的数据。
使用 Python 的 flask 包编写脚本文件,将请求转发到 OpenAI
使用 Nginx 配置域名
flask 转发请求
编写 main.py
文件,下载链接:main.py 。
下边是对文件内容的解释。
加载头部
导入必要的 Python 包
1 2 3 4 5 from flask import Flask, request, Responseimport requests, randomapp = Flask(__name__)
设置 API 请求日志的保存路径
1 2 3 4 5 6 7 8 9 home = os.path.expanduser("~" ) api_path = os.path.join(home, '.chatlog/apilist.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 API_LIST = [ 'sk-real-1' , 'sk-real-2' , 'sk-real-3' ] 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 @app.route('/v1/chat/completions' , methods=['POST' ] ) def forward_request (): try : 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 api_key in fake_keys: api_key = random.choice(API_LIST) headers = { "Content-Type" : "application/json" , "Authorization" : f"Bearer {api_key} " } response = requests.post(url, json=data, params=params, headers=headers) if not api_key in API_LIST: with open (api_path, 'a' ) as f: f.write(f'{api_key} \n\n' ) 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 Response(response.content, status=response.status_code) except Exception as e: 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 @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} " } 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) 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
启动服务。
开机自启动(可选)
编写启动脚本 chatapi.service
,其中 WorkingDirectory
填写为 main.py
所在的目录。
1 2 3 4 5 6 7 8 9 10 11 12 13 [Unit] Description=chatapi service After=network.target syslog.target Wants=network.target [Service] User=azureuser WorkingDirectory=/path/to/chatapi/directory ExecStart=/usr/bin/python3 main.py Restart=always [Install] WantedBy=multi-user.target
将脚本文件复制到 /etc/systemd/system
目录下,并启动
1 2 3 sudo cp chatapi.service /etc/systemd/system sudo systemctl enable chatapi sudo systemctl start chatapi
检查服务状态
1 sudo systemctl status chatapi
也可以检查端口是否正在使用
修改 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 代理,接口示例参看:
https://github.com/linweiyuan/go-chatgpt-api/blob/main/example/vscode