Python3.10接入ChatGPT实现逐句回答流式返回
作者:刘悦的技术博客 发布时间:2022-03-04 04:45:30
引言
善于观察的朋友一定会敏锐地发现ChatGPT网页端是逐句给出问题答案的,同样,ChatGPT后台Api接口请求中,如果将Stream参数设置为True后,Api接口也可以实现和ChatGPT网页端一样的流式返回,进而更快地给到前端用户反馈,同时也可以缓解连接超时的问题。
Server-sent events(SSE)是一种用于实现服务器到客户端的单向通信的协议。使用SSE,服务器可以向客户端推送实时数据,而无需客户端发出请求。
SSE建立在HTTP协议上,使用基于文本的数据格式(通常是JSON)进行通信。客户端通过创建一个EventSource对象来与服务器建立连接,然后可以监听服务器发送的事件。服务器端可以随时将事件推送给客户端,客户端通过监听事件来接收这些数据。
ChatGPT的Server-sent events应用
首先打开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实现Server-sent events应用
这里我们使用基于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链接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方法来监听后端的主动推送:
可以看到,每隔两秒钟就可以订阅到后端的message事件推送的消息,同时,SSE默认支持断线重连,而全双工的WebSocket协议则需要自己在前端实现,高下立判。
来源:https://juejin.cn/post/7207798726382665785
猜你喜欢
- 新建图像文件后选Channels面板,新建Alpha1通道; 做压
- 前言一首歌热门了,参与评论的人也很多,这时无论好坏评论都来了,没有人控评得话,指不定乱七八糟但是自己有喜欢看评论,不想影响好心情,想看看精彩
- 每次写博客都是源于纳闷,python解析pcap这么常用的例子网上竟然没有,全是一堆命令行执行的python,能用吗?玩呢?pip安装sca
- ASP木马防御: 代码如下:const adTypeBinary=1 dim jpg(1):jpg(0)=CB
- 概述 -------------------------------------------------------------------
- 在开发一些需要网络通信的应用中,经常会用到各种网络协议进行通信,博主在开发实验室的机器人的时候就遇到了需要把机器人上采集到的图片传回服务器进
- 手残更新Pycharm 2020.1 版时将配置文件都删除了😂;在此重新记录下配置!安装教程参考:idea2020.1最新版永久破解/pyc
- 关于跨域这个话题,很早就答应过要分享,但是因为懒,一直拖着,直到D2上有人谈起了“完美跨域”。“跨域”应该已经算不上什么难题了,只是提起“完
- 数组新的shape属性应该要与原来的配套,如果等于-1的话,那么Numpy会根据剩下的维度计算出数组的另外一个shape属性值。举个例子:x
- 本文实例讲述了Python实现base64编码的图片保存到本地功能。分享给大家供大家参考,具体如下:# -*- coding:utf-8 -
- 1、get方法请求接口url:显而易见,就是接口的地址url啦headers:请求头,例如:content-type = applicati
- 环境搭建1.安装uwsgi、nginx和djangoapt install nginxpip install uwsgipip instal
- 抽象方法和抽象类 在OOP 语言中,一个类可以有一个或多个子类,而每个类都有至少一个公有方法做为 外部代码访问其的接口。而抽象方法就是为了方
- Python3进制之间的转换在Python里面实现进制之间的转换是非常方便的,有专门的函数来进行这个操作:下面直接上代码:# 进制之间的转换
- 大概五年前吧,我那时还在为一家约会网站做开发工作。他们是早期创业公司,但他们也开始拥有了一些稳定用户量。不像其他约会网站,这家公司向来以洁身
- 节日用心准备的礼物,使用python画玫瑰和爱心,供大家参考,具体内容如下#!/usr/bin/env python#coding=utf-
- 本文实例讲述了Python判断Abundant Number的方法。分享给大家供大家参考。具体如下:Abundant Number,中文译成
- 一、python pip的安装与使用1、pip 是 Python 包管理工具,该工具提供了对Python 包的查找、下载、安装、卸载的功能。
- 起步这是许多开发者在项目初期要面临的一个普遍问题。要怎样来处理多用户类型。本文讲介绍对于不同场景和业务需求如何设计用户模型。为项目提供指导设
- SQL Server四类数据仓库建模的方法主要分为以下四类。第一类是关系数据库的三范式建模,通常我们将三范式建模方法用于建立各种操作型数据库