在 MicroPython 中使用 OpenAI API - DEV Community
https://dev.to/codemee/zai-micropython-zhong-shi-yong-openai-api-2epc
OpenAI 雖然提供有 [color=var(--link-branded-color)]Python 的官方套件, 不過如果你是要在 MicroPython 中使用 OpeAnAI 的 API, 並不能直接套用, 這時就要回歸到 OpenAI API 最根本的 HTTP Post API 了。 最簡單的 OpenAI APIOpenAI 是透過 HTTP Post 提供服務, 以聊天為例, 用的是 [color=var(--link-branded-color)]ChatCompletion 服務: 因此, 最簡單的 OpenAI HTTP Post API 的程式就像是這樣:
[color=var(--syntax-text-color)][color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]requests[color=var(--syntax-text-color)]API_KEY [color=var(--syntax-error-color)]= [color=var(--syntax-string-color)]'你的 OpenAI API 金鑰'[color=var(--syntax-text-color)]response [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]requests[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]post[color=var(--syntax-text-color)]( [color=var(--syntax-string-color)]'https://api.openai.com/v1/chat/completions'[color=var(--syntax-text-color)], [color=var(--syntax-text-color)]headers [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]{ [color=var(--syntax-string-color)]'Authorization'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'Bearer ' [color=var(--syntax-error-color)]+ [color=var(--syntax-text-color)]API_KEY [color=var(--syntax-text-color)]}, [color=var(--syntax-text-color)]json [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]{ [color=var(--syntax-string-color)]'model'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'gpt-3.5-turbo'[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"messages"[color=var(--syntax-text-color)]: [color=var(--syntax-text-color)][{[color=var(--syntax-string-color)]"role"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"user"[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"你好"[color=var(--syntax-text-color)]}]}[color=var(--syntax-text-color)])[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]status_code[color=var(--syntax-text-color)])[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]reason[color=var(--syntax-text-color)])[color=var(--syntax-text-color)]reply [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]json[color=var(--syntax-text-color)]()[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]reply[color=var(--syntax-text-color)][[color=var(--syntax-string-color)]"choices"[color=var(--syntax-text-color)]][[color=var(--syntax-literal-color)]0[color=var(--syntax-text-color)]][[color=var(--syntax-string-color)]"message"[color=var(--syntax-text-color)]][[color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]])
執行結果如下:
[color=var(--syntax-text-color)]>>> %Run openai_pc.py 200 OK 您好!我是语言模型AI的GPT-3,有什么可以帮助您的吗?
改用 MicroPython既然是使用 HTTP Post, 哪麼只要從 requests 模組改成 urequests 模組, 應該就可以原封不動照搬程式了, 我們來試看看:
[color=var(--syntax-text-color)][color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]network [color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]time[color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]urequests[color=var(--syntax-comment-color)]# 連線至無線網路[color=var(--syntax-text-color)]sta[color=var(--syntax-error-color)]=[color=var(--syntax-text-color)]network[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]WLAN[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]network[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]STA_IF[color=var(--syntax-text-color)])[color=var(--syntax-text-color)]sta[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]active[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]True[color=var(--syntax-text-color)]) [color=var(--syntax-text-color)]sta[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]connect[color=var(--syntax-text-color)]([color=var(--syntax-string-color)]'你的無線網路名稱'[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]'無線網路密碼'[color=var(--syntax-text-color)]) [color=var(--syntax-declaration-color)]while [color=var(--syntax-error-color)]not [color=var(--syntax-text-color)]sta[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]isconnected[color=var(--syntax-text-color)]() [color=var(--syntax-text-color)]: [color=var(--syntax-declaration-color)]pass[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-string-color)]'Wi-Fi連線成功'[color=var(--syntax-text-color)])[color=var(--syntax-text-color)]API_KEY [color=var(--syntax-error-color)]= [color=var(--syntax-string-color)]'你的 OpenAI API 金鑰'[color=var(--syntax-text-color)]response [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]urequests[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]post[color=var(--syntax-text-color)]( [color=var(--syntax-string-color)]'https://api.openai.com/v1/chat/completions'[color=var(--syntax-text-color)], [color=var(--syntax-text-color)]headers [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]{ [color=var(--syntax-string-color)]'Authorization'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'Bearer ' [color=var(--syntax-error-color)]+ [color=var(--syntax-text-color)]API_KEY [color=var(--syntax-text-color)]}, [color=var(--syntax-text-color)]json [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]{ [color=var(--syntax-string-color)]'model'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'gpt-3.5-turbo'[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"messages"[color=var(--syntax-text-color)]: [color=var(--syntax-text-color)][{[color=var(--syntax-string-color)]"role"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"user"[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"你好"[color=var(--syntax-text-color)]}]}[color=var(--syntax-text-color)])[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]status_code[color=var(--syntax-text-color)])[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]reason[color=var(--syntax-text-color)])[color=var(--syntax-text-color)]reply [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]json[color=var(--syntax-text-color)]()[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]reply[color=var(--syntax-text-color)][[color=var(--syntax-string-color)]"choices"[color=var(--syntax-text-color)]][[color=var(--syntax-literal-color)]0[color=var(--syntax-text-color)]][[color=var(--syntax-string-color)]"message"[color=var(--syntax-text-color)]][[color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]])
不過執行後就會看到 OpenAI 伺服器端回覆 400 錯誤:
[color=var(--syntax-text-color)]>>> %Run -c $EDITOR_CONTENT Wi-Fi連線成功 400 b'Bad Request' Traceback (most recent call last): File "<stdin>", line 32, in <module> KeyError: choices
同樣的錯誤, 搬到 MicroPython 上會出錯, 第一個懷疑的就是中文編碼的問題, 如果把程式中傳遞的 "你好" 改成純英文試看看:
[color=var(--syntax-text-color)][color=var(--syntax-text-color)]... [color=var(--syntax-text-color)]json [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]{ [color=var(--syntax-string-color)]'model'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'gpt-3.5-turbo'[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"messages"[color=var(--syntax-text-color)]: [color=var(--syntax-text-color)][{[color=var(--syntax-string-color)]"role"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"user"[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"hello"[color=var(--syntax-text-color)]}]}[color=var(--syntax-text-color)]...
再執行一次就會發現可以正確執行:
[color=var(--syntax-text-color)]>>> %Run -c $EDITOR_CONTENTWi-Fi連線成功200b'OK'Hello there! How may I assist you today?
顯然問題就是出在 [color=var(--link-branded-color)]urequests.post 對於 [color=var(--link-branded-color)]json 參數的處理。 json 模組的中文處理在 [color=var(--link-branded-color)]urequests.post 中會使用 json 模組 (MicroPython 中 json 與 ujson 是同一個模組) 的 dumps 函式將 Python 字典轉成字串格式的 JSON 資料, 可是它的輸出結果會保留以 UTF-16 編碼的中文字, 例如:
[color=var(--syntax-text-color)][color=var(--syntax-error-color)]>>> [color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]json[color=var(--syntax-error-color)]>>> [color=var(--syntax-text-color)]json[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]dumps[color=var(--syntax-text-color)]({[color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"你好"[color=var(--syntax-text-color)]})[color=var(--syntax-string-color)]'{"content": "[color=var(--syntax-literal-color)]\u4f60\u597d[color=var(--syntax-string-color)]"}'
[color=var(--syntax-text-color)][color=var(--syntax-error-color)]>>> [color=var(--syntax-text-color)]json[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]dumps[color=var(--syntax-text-color)]({[color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"你好"[color=var(--syntax-text-color)]}).[color=var(--syntax-text-color)]encode[color=var(--syntax-text-color)]([color=var(--syntax-string-color)]'utf8'[color=var(--syntax-text-color)])[color=var(--syntax-string-color)]b[color=var(--syntax-string-color)]'{"content": "[color=var(--syntax-literal-color)]\xe4\xbd\xa0\xe5\xa5\xbd[color=var(--syntax-string-color)]"}'
這樣結果就對了。不過因為要自行處理字典轉 json 格式位元組的工作, 所以就不能直接在 urequests.post 中使用 json 參數傳入字典了。 改用 data 參數傳入 json 資料urequests.post 有 data 參數可以直接傳入要送給伺服端的資料, 因此我們就可以將程式改成如下:
[color=var(--syntax-text-color)][color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]network [color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]time[color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]urequests[color=var(--syntax-error-color)]import [color=var(--syntax-text-color)]ujson[color=var(--syntax-comment-color)]# 連線至無線網路[color=var(--syntax-text-color)]sta[color=var(--syntax-error-color)]=[color=var(--syntax-text-color)]network[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]WLAN[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]network[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]STA_IF[color=var(--syntax-text-color)])[color=var(--syntax-text-color)]sta[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]active[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]True[color=var(--syntax-text-color)]) [color=var(--syntax-text-color)]sta[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]connect[color=var(--syntax-text-color)]([color=var(--syntax-string-color)]'你的無線網路名稱'[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]'你的無線網路密碼'[color=var(--syntax-text-color)]) [color=var(--syntax-declaration-color)]while [color=var(--syntax-error-color)]not [color=var(--syntax-text-color)]sta[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]isconnected[color=var(--syntax-text-color)]() [color=var(--syntax-text-color)]: [color=var(--syntax-declaration-color)]pass[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-string-color)]'Wi-Fi連線成功'[color=var(--syntax-text-color)])[color=var(--syntax-text-color)]API_KEY [color=var(--syntax-error-color)]= [color=var(--syntax-string-color)]'你的 OpenAI API 金鑰'[color=var(--syntax-text-color)]response [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]urequests[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]post[color=var(--syntax-text-color)]( [color=var(--syntax-string-color)]'https://api.openai.com/v1/chat/completions'[color=var(--syntax-text-color)], [color=var(--syntax-text-color)]headers [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]{ [color=var(--syntax-string-color)]'Content-Type'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'application/json'[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]'Authorization'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'Bearer ' [color=var(--syntax-error-color)]+ [color=var(--syntax-text-color)]API_KEY [color=var(--syntax-text-color)]}, [color=var(--syntax-text-color)]data [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]ujson[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]dumps[color=var(--syntax-text-color)]({ [color=var(--syntax-string-color)]'model'[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]'gpt-3.5-turbo'[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"messages"[color=var(--syntax-text-color)]: [color=var(--syntax-text-color)][{[color=var(--syntax-string-color)]"role"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"user"[color=var(--syntax-text-color)], [color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]: [color=var(--syntax-string-color)]"你好" [color=var(--syntax-text-color)]}).[color=var(--syntax-text-color)]encode[color=var(--syntax-text-color)]([color=var(--syntax-string-color)]'utf8'[color=var(--syntax-text-color)])[color=var(--syntax-text-color)])[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]status_code[color=var(--syntax-text-color)])[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]reason[color=var(--syntax-text-color)])[color=var(--syntax-text-color)]reply [color=var(--syntax-error-color)]= [color=var(--syntax-text-color)]response[color=var(--syntax-text-color)].[color=var(--syntax-text-color)]json[color=var(--syntax-text-color)]()[color=var(--syntax-declaration-color)]print[color=var(--syntax-text-color)]([color=var(--syntax-text-color)]reply[color=var(--syntax-text-color)][[color=var(--syntax-string-color)]"choices"[color=var(--syntax-text-color)]][[color=var(--syntax-literal-color)]0[color=var(--syntax-text-color)]][[color=var(--syntax-string-color)]"message"[color=var(--syntax-text-color)]][[color=var(--syntax-string-color)]"content"[color=var(--syntax-text-color)]])
這樣一來, 就可以正確叫用 API 了:
[color=var(--syntax-text-color)]>>> %Run -c $EDITOR_CONTENT Wi-Fi連線成功 200 b'OK' 你好!有什么我
|