善于观察的朋友一定会敏锐地发现ChatGPT网页端是逐句给出问题答案的,同样,ChatGPT后台Api接口请求中,如果将Stream参数设置为True后,Api接口也可以实现和ChatGPT网页端一样的流式返回,进而更快地给到前端用户反馈,同时也可以缓解连接超时的问题。
SSE是一种协议,用于实现单向服务器到客户端的通信。使用SSE,服务器可以向客户端推送实时数据,而无需客户端发出请求。
在HTTP协议的基础上,SSE采用文本格式进行通信(通常是JSON)。通过实例化一个EventSource对象,客户端与服务器建立连接后即可监听来自服务器的事件。通过事件监听,客户端可以随时接收服务器端推送的数据。
首先打开ChatGPT网页端,随便问一个问题,然后进入网络选单,清空历史请求记录后,进行网络抓包监听:
可以看到,在触发了回答按钮之后,页面会往后端的backend-api/conversation对话接口发起请求,但这个接口的通信方式并非传统的http接口或者Websocket持久化链接协议,而是基于EventSteam的事件流一段一段地返回ChatGPT后端模型的返回数据。
为什么ChatGPT会选择这种方式和后端Server进行通信?ChatGPT网页端使用Server-sent events通信是因为这种通信方式可以实现服务器向客户端推送数据,而无需客户端不断地向服务器发送请求。这种推送模式可以增强应用程序的性能和响应速度,并减少不必要的网络流量。
与其他实时通信协议(如WebSocket)相比,Server-sent events通信是一种轻量级协议,易于实现和部署。它还有着广泛的浏览器兼容性,并且无需任何特殊的网络配置即可使用。
新的聊天消息将被推送到网页端,以实时显示聊天内容,这是ChatGPT服务器的工作。使用Server-sent events通信,可以轻松地实现这种实时更新功能,并确保网页端与服务器之间的通信效率和稳定性。
说白了,降低成本,提高效率,ChatGPT是一个基于深度学习的大型语言模型,处理自然语言文本需要大量的计算资源和时间。因此,返回响应的速度肯定比普通的读数据库要慢的多,Http接口显然并不合适,因为Http是一次性返回,等待时间过长,而Websocket又过重,因为全双工通信并不适合这种单项对话场景,所谓单项对话场景,就是对话双方并不会并发对话,而是串行的一问一答逻辑,同时持久化链接也会占用服务器资源,要知道ChatGPT几乎可以算是日均活跃用户数全球最高的Web应用了。
效率层面,大型语言模型没办法一下子返回所有计算数据,但是可以通过Server-sent events将前面计算出的数据先“推送”到前端,这样用户也不会因为等待时间过长而关闭页面,所以ChatGPT的前端观感就是像打字机一样,一段一段的返回答案,这种“边计算边返回”的生成器模式也提高了ChatGPT的回答效率。
这里我们使用基于Python3.10的Tornado异步非阻塞框架来实现Server-sent events通信。
首先安装Tornado框架
pip3 install tornado==6.1
随后编写sse_server.py:
import tornado.ioloop import tornado.web push_flag = True from asyncio import sleep class ServerSentEvent(tornado.web.RequestHandler): def __init__(self, *args, **kwargs): super(ServerSentEvent, self).__init__(*args, **kwargs) self.set_header('Content-Type', 'text/event-stream') self.set_header('Access-Control-Allow-Origin', "*") self.set_header("Access-Control-Allow-Headers","*") # 请求方式 self.set_header("Access-Control-Allow-Methods","*") # 断开连接 def on_finish(self): print("断开连接") return super().on_finish() async def get(self): print("建立链接") while True: if push_flag: print("开始") self.write("event: message\n"); self.write("data:" + "push data" + "\n\n"); self.flush() await sleep(2)
建立好推送路由类ServerSentEvent,它继承Tornado内置的视图类tornado.web.RequestHandler,首先利用super方法调用父类的初始化方法,设置跨域,如果不使用super,会将父类同名方法重写,随后建立异步的get方法用来链接和推送消息,这里使用Python原生异步的写法,每隔两秒往前端推送一个事件message,内容为push data。
注意,这里只是简单的推送演示,真实场景下如果涉及IO操作,比如数据库读写或者网络请求之类,还需要单独封装异步方法。
再假定前端onmessage处理程序的事件名称为message。如果想使用其他事件名称,可以使用前端addEventListener来订阅事件,最后消息后必须以两个换行为结尾。
随后编写路由和服务实例:
def make_app(): return tornado.web.Application([ (r"/sse/data/", ServerSentEvent), ]) if __name__ == "__main__": app = make_app() app.listen(8000) print("sse服务启动") tornado.ioloop.IOLoop.current().start()
随后在后台运行命令:
python3 sse_server.py
程序返回:
PS C:\Users\liuyue\www\videosite> python .\sse_server.py sse服务启动
至此,基于Tornado的Server-sent events服务就搭建好了。
客户端我们使用目前最流行的Vue.js3框架:
sse_init:function(){ var push_data = new EventSource("http://localhost:8000/sse/data/") push_data.onopen = function (event) { // open事件 console.log("EventSource连接成功"); }; push_data.onmessage = function (event) { try { console.log(event); } catch (error) { console.log('EventSource结束消息异常', error); } }; push_data.onerror = function (error) { console.log('EventSource连接异常', error); }; }
这里在前端的初始化方法内建立EventSource实例,通过onmessage方法来监听后端的主动推送:
Anda boleh melihat bahawa anda boleh melanggan mesej yang ditolak oleh acara mesej hujung belakang setiap dua saat Pada masa yang sama, SSE menyokong pemotongan dan penyambungan semula secara lalai, dan penuh-. protokol WebSocket dupleks Anda perlu melaksanakannya sendiri di bahagian hadapan dan membuat keputusan.
Atas ialah kandungan terperinci Bagaimana untuk mengakses ChatGPT dalam Python 3.10 untuk melaksanakan penstriman tindak balas ayat demi ayat. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!