逐句回答,流式返回,ChatGPT采用的Server-sent events后端實時推送協(xié)議Python3.10實現(xiàn),基于Tornado6.1

善于觀察的朋友一定會敏銳地發(fā)現(xiàn)ChatGPT網(wǎng)頁端是逐句給出問題答案的,同樣评矩,ChatGPT后臺Api接口請求中更卒,如果將Stream參數(shù)設置為True后,Api接口也可以實現(xiàn)和ChatGPT網(wǎng)頁端一樣的流式返回稚照,進而更快地給到前端用戶反饋蹂空,同時也可以緩解連接超時的問題。

Server-sent events(SSE)是一種用于實現(xiàn)服務器到客戶端的單向通信的協(xié)議果录。使用SSE上枕,服務器可以向客戶端推送實時數(shù)據(jù),而無需客戶端發(fā)出請求弱恒。

SSE建立在HTTP協(xié)議上辨萍,使用基于文本的數(shù)據(jù)格式(通常是JSON)進行通信》档客戶端通過創(chuàng)建一個EventSource對象來與服務器建立連接锈玉,然后可以監(jiān)聽服務器發(fā)送的事件。服務器端可以隨時將事件推送給客戶端义起,客戶端通過監(jiān)聽事件來接收這些數(shù)據(jù)拉背。

ChatGPT的Server-sent events應用

首先打開ChatGPT網(wǎng)頁端,隨便問一個問題默终,然后進入網(wǎng)絡選單椅棺,清空歷史請求記錄后犁罩,進行網(wǎng)絡抓包監(jiān)聽:

[圖片上傳失敗...(image-348dd-1678231464780)]

可以看到,在觸發(fā)了回答按鈕之后两疚,頁面會往后端的backend-api/conversation對話接口發(fā)起請求床估,但這個接口的通信方式并非傳統(tǒng)的http接口或者Websocket持久化鏈接協(xié)議,而是基于EventSteam的事件流一段一段地返回ChatGPT后端模型的返回數(shù)據(jù)诱渤。

為什么ChatGPT會選擇這種方式和后端Server進行通信丐巫?ChatGPT網(wǎng)頁端使用Server-sent events通信是因為這種通信方式可以實現(xiàn)服務器向客戶端推送數(shù)據(jù),而無需客戶端不斷地向服務器發(fā)送請求勺美。這種推送模式可以提高應用程序的性能和響應速度递胧,減少了不必要的網(wǎng)絡流量。

與其他實時通信協(xié)議(如WebSocket)相比励烦,Server-sent events通信是一種輕量級協(xié)議谓着,易于實現(xiàn)和部署泼诱。此外坛掠,它也具有廣泛的瀏覽器兼容性,并且可以在不需要特殊網(wǎng)絡配置的情況下使用治筒。

在ChatGPT中屉栓,服務器會將新的聊天消息推送到網(wǎng)頁端,以便實時顯示新的聊天內(nèi)容耸袜。使用Server-sent events通信友多,可以輕松地實現(xiàn)這種實時更新功能,并確保網(wǎng)頁端與服務器之間的通信效率和穩(wěn)定性堤框。

[圖片上傳失敗...(image-4d1c6c-1678231464780)]

說白了域滥,降低成本,提高效率蜈抓,ChatGPT是一個基于深度學習的大型語言模型启绰,處理自然語言文本需要大量的計算資源和時間。因此沟使,返回響應的速度肯定比普通的讀數(shù)據(jù)庫要慢的多委可,Http接口顯然并不合適,因為Http是一次性返回腊嗡,等待時間過長着倾,而Websocket又過重,因為全雙工通信并不適合這種單項對話場景燕少,所謂單項對話場景卡者,就是對話雙方并不會并發(fā)對話,而是串行的一問一答邏輯客们,同時持久化鏈接也會占用服務器資源虎眨,要知道ChatGPT幾乎可以算是日均活躍用戶數(shù)全球最高的Web應用了蟋软。

效率層面,大型語言模型沒辦法一下子返回所有計算數(shù)據(jù)嗽桩,但是可以通過Server-sent events將前面計算出的數(shù)據(jù)先“推送”到前端岳守,這樣用戶也不會因為等待時間過長而關閉頁面,所以ChatGPT的前端觀感就是像打字機一樣碌冶,一段一段的返回答案湿痢,這種“邊計算邊返回”的生成器模式也提高了ChatGPT的回答效率。

Python3.10實現(xiàn)Server-sent events應用

這里我們使用基于Python3.10的Tornado異步非阻塞框架來實現(xiàn)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內(nèi)置的視圖類tornado.web.RequestHandler,首先利用super方法調(diào)用父類的初始化方法罐氨,設置跨域臀规,如果不使用super,會將父類同名方法重寫栅隐,隨后建立異步的get方法用來鏈接和推送消息塔嬉,這里使用Python原生異步的寫法,每隔兩秒往前端推送一個事件message,內(nèi)容為push data租悄。

注意谨究,這里只是簡單的推送演示,真實場景下如果涉及IO操作泣棋,比如數(shù)據(jù)庫讀寫或者網(wǎng)絡請求之類胶哲,還需要單獨封裝異步方法。

另外這里假定前端onmessage處理程序的事件名稱為message潭辈。如果想使用其他事件名稱鸯屿,可以使用前端addEventListener來訂閱事件,最后消息后必須以兩個換行為結(jié)尾把敢。

隨后編寫路由和服務實例:

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結(jié)束消息異常', error);  
    }  
};  
  
  
       push_data.onerror = function (error) {  
    console.log('EventSource連接異常', error);  
};  
  
  
      }

這里在前端的初始化方法內(nèi)建立EventSource實例技竟,通過onmessage方法來監(jiān)聽后端的主動推送:

[圖片上傳失敗...(image-8fc5af-1678231464780)]

可以看到冰肴,每隔兩秒鐘就可以訂閱到后端的message事件推送的消息,同時榔组,SSE默認支持斷線重連熙尉,而全雙工的WebSocket協(xié)議則需要自己在前端實現(xiàn),高下立判搓扯。

結(jié)語

不僅僅可以實現(xiàn)ChatGPT的流式返回功能检痰,SSE在Web應用程序中的使用場景非常廣泛,例如實時的新聞推送锨推、實時股票報價铅歼、在線游戲等等公壤,比起輪詢和長輪詢,SSE更加高效椎椰,因為只有在有新數(shù)據(jù)到達時才會發(fā)送厦幅;同時SSE支持自定義事件和數(shù)據(jù),具有更高的靈活性和復用性慨飘,為流式數(shù)據(jù)返回保駕護航确憨,ChatGPT的最愛,誰不愛瓤的?最后奉上項目地址休弃,與眾鄉(xiāng)親同饗:github.com/zcxey2911/sse_tornado6_vuejs3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市圈膏,隨后出現(xiàn)的幾起案子塔猾,更是在濱河造成了極大的恐慌,老刑警劉巖稽坤,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丈甸,死亡現(xiàn)場離奇詭異,居然都是意外死亡慎皱,警方通過查閱死者的電腦和手機老虫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門叶骨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茫多,“玉大人,你說我怎么就攤上這事忽刽√煲荆” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵跪帝,是天一觀的道長今膊。 經(jīng)常有香客問我,道長伞剑,這世上最難降的妖魔是什么斑唬? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮黎泣,結(jié)果婚禮上恕刘,老公的妹妹穿的比我還像新娘。我一直安慰自己抒倚,他們只是感情好褐着,可當我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著托呕,像睡著了一般含蓉。 火紅的嫁衣襯著肌膚如雪频敛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天馅扣,我揣著相機與錄音斟赚,去河邊找鬼。 笑死差油,一個胖子當著我的面吹牛汁展,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播厌殉,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼食绿,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了公罕?” 一聲冷哼從身側(cè)響起器紧,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎楼眷,沒想到半個月后铲汪,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡罐柳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年掌腰,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片张吉。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡齿梁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出肮蛹,到底是詐尸還是另有隱情勺择,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布伦忠,位于F島的核電站省核,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏昆码。R本人自食惡果不足惜气忠,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赋咽。 院中可真熱鬧旧噪,春花似錦、人聲如沸冬耿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽亦镶。三九已至日月,卻和暖如春袱瓮,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背爱咬。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工尺借, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人精拟。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓燎斩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蜂绎。 傳聞我的和親對象是個殘疾皇子栅表,可洞房花燭夜當晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容