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