问题重现
在调用 ChatGPT API 并使用流式输出时,我们经常会遇到网络问题导致的超时情况。有趣的是,笔者发现在本地调试遇到的超时,会在 10 分钟后自动恢复(为什么是 10 分钟?我们留到后面解释),但是在服务器上等待一会儿却会失败,报出超时异常(错误代码 502)。
笔者认为,本地能恢复的原因可能是自动重试,只是重试的时间有点久(ChatGPT API 没有重试功能,这是项目加入的)。服务器返回「502」是因为内容从后台返回到前端需要经过网关层,而网关层超时校验的时间比自动重试的时间(10 分钟)更短,所以撑不到重试就会报超时异常。
基于以上场景,本文着手解决 ChatGPT API 调用超时问题。
优化诉求
不向用户展示超时的报错信息。
缩短超时后重试的时间间隔。
解决思路
笔者考虑了两种方案。
一是彻底解决网络问题,但难度有点大。这属于 OpenAI 服务器问题,即使是部署在国外的服务器也会出现超时的情况。
二是利用自动重试解决问题。通过调整超时的时间,提升响应速度,方案可行。
实施解决方案
解决过程中,笔者分两步由浅至深地调整了超时时间;如果想直接了解最终方案,请移步「解决方案二」
运行环境>
Python: 3.10.7,openai: 0.27.6,调用方法:
openai.api_resources.chat_completion.ChatCompletion.acreate,( 这是异步调用 ChatGPT 的方法。),方法调用链路:
超时参数 ClientTimeout,一共有 4 个属性 total、connect、sock_read 和 sock_connect。
解决方案一
openai.api_requestor.APIRequestor.arequest_raw 方法中的 request_timeout 参数可以传递 connect 和 total 参数,因此可以在调用openai.api_resources.chat_completion.ChatCompletion.acreate时,设置 request_time(10, 300)。
该方案有效,但没有完全生效:它可以控制连接时间和请求的全部时间,但没有彻底解决超时异常,因为「请求连接时间」和「第一个字符读取时间」是两码事。「请求连接时间」基于 total 时间重试(300s),而网关时间并没有设置这么久。
于是,笔者继续提出「解决方案二」。
解决方案二
使用 monkey_patch 方式重写openai.api_requestor.APIRequestor.arequest_raw 方法,重点在于重写 request_timeout 参数,让其支持原生的 aiohttp.client.ClientTimeout 参数。
1. 新建 api_requestor_mp.py 文件,并写入以下代码。
2. 在初始化 ChatGPT API 的文件头部补充:
设置参数 request_timeout=(10, 300, 15, 10) 后,再调试就没什么问题了。
交付测试,通过。
经验总结
直接看代码、看方法调用链路会有点困难,可以通过异常堆栈来找调用链路,这样更方便。
ChatGPT API 暴露的 request_timeout 参数不够用,需要重写;搜索了一下重写方案,了解到 monkey_patch,非常实用。
项目过程中,笔者发现改代码本身不难,难的是知道「改哪里」「怎么改」以及「为什么」。