花1塊錢讓你的網(wǎng)站支持 ChatGPT
本文轉(zhuǎn)載自微信公眾號「前端司南」,作者Tusi。轉(zhuǎn)載本文請聯(lián)系前端司南公眾號。
最近 ChatGPT 在技術(shù)圈子可太火了,票圈也被刷屏。我也決定來湊個熱鬧,給自己的博客加一個 ChatGPT 對話功能。
先附上體驗(yàn)鏈接,源碼在底部也可以找到。
感謝大家的支持,我的 Open AI個人賬戶免費(fèi)額度已經(jīng)用盡,非常抱歉,請大家自行按照文章和源碼搭建體驗(yàn)吧,或者自己注冊一個賬號去后臺體驗(yàn)。
體驗(yàn) ChatGPT
ChatGPT 是 Open AI 訓(xùn)練的一個 AI 對話模型,可以支持在多種場景下進(jìn)行智能對話。
想體驗(yàn) ChatGPT,首先要注冊賬戶,但是這個產(chǎn)品在國內(nèi)網(wǎng)絡(luò)并不能直接用,需要自行解決網(wǎng)絡(luò)問題。
搞定網(wǎng)絡(luò)問題后,注冊時會讓你提供郵箱驗(yàn)證,
接著要驗(yàn)證手機(jī)號,但是很遺憾國內(nèi)手機(jī)號用不了。
你也可以選擇用 Google 賬號登錄,但是最終還是要驗(yàn)證手機(jī)號。
所以我們需要先找一個國外的能接收短信驗(yàn)證碼的手機(jī)號,此時可以上SMS-ACTIVATE。
這是一個在這個星球上數(shù)以百萬計的服務(wù)中注冊帳戶的網(wǎng)站。 我們提供世界上大多數(shù)國家的虛擬號碼,以便您可以在線接收帶有確認(rèn)代碼的短信。 在我們的服務(wù)中,還有虛擬號碼的長期租賃,轉(zhuǎn)發(fā)連接,電話驗(yàn)證等等。
SMS-ACTIVATE 上的價格是盧布,我們需要使用手機(jī)號碼做短信驗(yàn)證,經(jīng)過查詢可以發(fā)現(xiàn),最便宜的是印度地區(qū)的手機(jī)號,零售價格是 10.5 盧布。
按照匯率算了一下,大概是1塊多RMB。
SMS-ACTIVATE 支持用某寶充值,我買了一個印度號,就可以收到來自 Open AI 的驗(yàn)證碼了。
注意,這個號碼只是租用,是有期限的,所以我們要抓緊時間把注冊流程搞完,20分鐘過了,這個號碼就不是你的了。
注冊完 Open AI 的賬號后,就可以到 ChatGPT 的 Web工作臺體驗(yàn)一把 AI 對話了。
通過 API 接入 Open AI 能力
體驗(yàn)完 ChatGPT 之后,對于搞技術(shù)的我們來說,可能會想著怎么把這個能力接入到自己的產(chǎn)品中。
快速上手
ChatGPT 是 Open AI 訓(xùn)練出來的模型,Open AI 也提供了 API 給開發(fā)者們調(diào)用,文檔和案例也比較全面。
機(jī)器學(xué)習(xí)很重要的一個步驟就是調(diào)參,但對于前端開發(fā)者來說,大部分人肯定是不知道怎么調(diào)參的,那我們就參考官方提供的最契合我們需求的案例就好了,這個 Chat 的案例就非常符合我們的場景需要。
官方有提供一個 nodejs 的 starter,我們可以基于此快速上手測試一把。
它的核心代碼是這么一部分,其中用到的openai是官方封裝好的 NodeJS Library。
在調(diào)用 API 之前需要先在你的 Open AI 賬戶中生成一個 API Key。
目前官方給到的免費(fèi)額度是 18 刀,超過的部分就需要自己付費(fèi)了。計費(fèi)是根據(jù) Token 來算的,至于什么是 Token,可以參考Key concepts。
我們把上面那個 Chat 案例的參數(shù)拿過來直接用上,基本上也有個七八分 AI 回答問題的樣子了,這個可以自己去試一試效果,并不復(fù)雜。
接著就是研究一下怎么把這個 starter 的關(guān)鍵代碼集成到自己的產(chǎn)品中。
產(chǎn)品分析
我之前有在自己的博客中做過一個簡單的 WebSocket 聊天功能,而在 AI 對話這個需求中,前端 UI 部分基本上可以參考著WebSocket 聊天功能改改,工作量不是很大,主要工作量還是在前后端的邏輯和對接上面。
ChatGPT 的這個產(chǎn)品模式,它不是一個常規(guī)的 WebSocket 全雙工對話,而是像我們平常調(diào)接口一樣,發(fā)生用戶輸入后,客戶端發(fā)送請求到服務(wù)端,等待服務(wù)端響應(yīng),最后反饋給用戶,它僅僅是從界面上看起來像是聊天,實(shí)際上不是一個標(biāo)準(zhǔn)的聊天過程。所以前后端交互主要還是靠 HTTP 接口對接。
核心要素 Prompt
在openai.createCompletion調(diào)用時有一個很重要的參數(shù)prompt,它是對話的上下文信息,只有這個信息足夠完整,AI 才能正確地做出反饋。
舉個例子,假設(shè)在對話過程中有2個回合。
第一個回合中,傳參prompt是愛因斯坦是誰?,機(jī)器人很好理解,馬上能給出符合實(shí)際的回復(fù)。
第二個回合傳參prompt是他做了什么貢獻(xiàn)?,看到機(jī)器人的答復(fù),你可能會覺得有點(diǎn)離譜,因?yàn)檫@根本就是牛頭不對馬嘴。但是仔細(xì)想想,這是因?yàn)闄C(jī)器人不知道上下文信息,所以機(jī)器人不能理解他代表的含義,只能通過他做了什么貢獻(xiàn)?整句話去推測,所以從結(jié)果上看就是符合語言的邏輯,但是不符合我們給出的語境。
如果我們把第二個回合的傳參prompt改成你: 愛因斯坦是誰?\nAI: 愛因斯坦(Albert Einstein)是20世紀(jì)最重要的物理學(xué)家,他被譽(yù)為“時空之父”。他發(fā)現(xiàn)了相對論,并獲得諾貝爾物理學(xué)獎。\n你: 他做了什么貢獻(xiàn)?\nAI:,機(jī)器人就能夠理解上下文信息,給出接下來的符合邏輯的答復(fù)。
所以,我們的初步結(jié)論是:prompt參數(shù)應(yīng)該包含此次對話主題的較完整內(nèi)容,才能保證 AI 給出的下一次回答符合我們的基本認(rèn)知。
前后端交互
對于前端來說,我們通常關(guān)注的是,我給后端發(fā)了什么數(shù)據(jù),后端反饋給我什么數(shù)據(jù)。所以,前端關(guān)注點(diǎn)之一就是用戶的輸入,用上面的例子說,愛因斯坦是誰?和他做了什么貢獻(xiàn)?這兩個內(nèi)容,應(yīng)該分別作為前端兩次請求的參數(shù)。而且,對于前端來說,我們也不需要考慮后端傳給 Open AI 的prompt是不是完整,只要把用戶輸入的內(nèi)容合理地傳給后端就夠了。
對于后端來說,我們要關(guān)注 session 問題,每個用戶應(yīng)該有屬于自己和 AI 的私密對話空間,不能和其他的用戶對話串了數(shù)據(jù),這個可以基于 session 實(shí)現(xiàn)。前端每次傳過來的信息只有簡單的用戶輸入,而后端要關(guān)注與 Open AI 的對接過程,結(jié)合用戶的輸入以及會話中保留的一些信息,合并成一個完整的prompt傳給 Open AI,這樣才能得到正常的對話過程。
所以基本的流程應(yīng)該是這個樣子:
我們根據(jù)這個流程輸出第一版代碼。
后端V1版本代碼
前端V1版本關(guān)鍵代碼
基本的對話能力已經(jīng)有了,但是最明顯的缺點(diǎn)就是一個回合等得太久了,我們希望他速度更快一點(diǎn),至少在交互上看起來快一點(diǎn)。
流式輸出(服務(wù)器推 + EventSource)
還好 Open AI 也支持 stream 流式輸出,在前端可以配合 EventSource 一起用。
You can also set the stream parameter to true for the API to stream back text (as data-only server-sent events).
基本的數(shù)據(jù)流是這個樣子的:
后端改造如下:
前端放棄使用 axios 發(fā)起 HTTP 請求,而是改用 EventSource。
從代碼中可以發(fā)現(xiàn)前端在 EventSource message 接收結(jié)束時,還調(diào)用了一個 feedback 接口做反饋。這是因?yàn)樵谑褂?Pipe 輸出時,后端沒有記錄 AI 答復(fù)的文本,考慮到前端已經(jīng)處理了文本,這里就由前端做一次反饋,把本次 AI 答復(fù)的內(nèi)容完整回傳給后端,后端再更新 session 中存儲的對話信息,保證對話上下文的完整性。
feedback 接口的實(shí)現(xiàn)比較簡單:
我這里只是給出一種簡單的做法,實(shí)際產(chǎn)品中可能要考慮的會更多,或者應(yīng)該在后端自行處理 session 內(nèi)容,而不是依靠前端的反饋。
最終的效果大概是這個樣子:
限制訪問頻次
由于 Open AI 也是有免費(fèi)額度的,所以在調(diào)用頻率和次數(shù)上也應(yīng)該做個限制,防止被惡意調(diào)用,這個也可以通過 session 來處理。我這里也提供一種比較粗糙的處理方式,具體請往下看。實(shí)際產(chǎn)品中可能會寫 Redis,寫庫,加定時任務(wù)之類的,這方面我也不夠?qū)I(yè),就不多說了。
針對訪問頻率,我暫定的是 3 秒內(nèi)最多調(diào)用一次,我們可以在調(diào)用 Open AI 成功之后,在 session 中記錄時間戳。
當(dāng)一個新的請求過來時,可以用當(dāng)前時間減去上次記錄的chatgptRequestTime,判斷一下是不是在 3 秒內(nèi),如果是,就返回 HTTP 狀態(tài)碼 429;如果不在 3 秒內(nèi),就可以繼續(xù)后面的邏輯。
關(guān)于請求次數(shù)也是同樣的道理,我這里也寫得很簡單,實(shí)際上還應(yīng)該有跨天清理等邏輯要做。我這里偷懶了,暫時沒做這些。
同一個話題也不能聊太多,否則傳給 Open AI 的 prompt 參數(shù)會很大,這就可能會耗費(fèi)很多 Token,也有可能超過 Open AI 參數(shù)的限制。
切換話題
客戶端應(yīng)該也有切換話題的能力,否則 session 中記錄的信息可能會包含多個話題的內(nèi)容,可能導(dǎo)致與用戶的預(yù)期不符。那我們做個接口就好了。
結(jié)語
總的來說,Open AI 開放出來的智能對話能力可以滿足基本需求,但是還有很大改進(jìn)空間。我在文中給出的代碼僅供參考,不保證功能上的完美。
附上源碼地址,可以點(diǎn)個 star 嗎,球球了[認(rèn)真臉]。
參考
?[1]體驗(yàn)鏈接: https://blog.wbjiang.cn/chatgpt
[2]ChatGPT: https://openai.com/
[3]注冊: https://beta.openai.com/login/
[4]SMS-ACTIVATE: https://sms-activate.org/cn
[5]Web工作臺: https://chat.openai.com/chat
[6]文檔: https://beta.openai.com/
[7]案例: https://beta.openai.com/examples
[8]openai: https://www.npmjs.com/package/openai
[9]生成一個 API Key: https://beta.openai.com/account/api-keys
[10]Key concepts: https://beta.openai.com/docs/introduction/key-concepts
[11]WebSocket 聊天功能: https://blog.wbjiang.cn/chat
[12]data-only server-sent events: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format
[13]源碼地址: https://github.com/cumt-robin/vue3-ts-blog-frontend