2024年9月21日 星期六

MicroPython 學習筆記 : 用 urequests 串接 OpenAI 大語言模型

這周本來都在測試 OpenAI 與 Gemini 等 LLM, 周四突然想到 ESP8266/ESP32 用 MicroPython 不知道能否串接 OpenAI 等模型, 於是找出開發板, 複習 2022~2023 年鑽研一陣子的 xtools 函式庫, 經過這幾天的把玩, MicroPython 大致回魂了. 

不過卻發現了 xtools 的一個問題, 由於 MicroPython 在 v1.23 版用 tsl 模組替換了 ussl 模組, 使得 xtools 在呼叫 urequests 與 xrequests 的函式時會出現找不到 ussl 的錯誤, 所以我又把韌體刷回 1.19 或 1.22 版, 順便改寫 xtoold 函式庫增加了傳貼圖的 line_sticker() 與傳圖片的 line_image() 函式 (但圖片不知為何傳不出來). 

晚飯後開始來嚐試參考將之前用 requests 串接 OpenAI 模型的函式改寫為 MicroPython 版 :

def ask_gpt(prompt, api_key, model='gpt-4o-mini'):
    url='https://api.openai.com/v1/chat/completions'
    headers={'Content-Type': 'application/json',
             'Authorization': f'Bearer {api_key}'}
    json={'model': model,
          'messages': [{'role': 'user', 'content': prompt}]} 
    response=xrequests.post(url=url,headers=headers, json=json)
    reply=response.json()
    return reply['choices'][0]['message']['content']

參考 : 


但不管使用 urequests 或 xrequests 的 post() 函式提出請求都出現錯誤, 紀錄如下 : 

>>> import xrequests   
>>> def ask_gpt(prompt, api_key, model='gpt-4o-mini'):
    url='https://api.openai.com/v1/chat/completions'
    headers={'Content-Type': 'application/json',
             'Authorization': f'Bearer {api_key}'}
    json={'model': model,
          'messages': [{'role': 'user', 'content': prompt}]} 
    response=xrequests.post(url=url,headers=headers, json=json)
    reply=response.json()
    return reply['choices'][0]['message']['content']
>>> api_key='在此輸入 OpenAI API key'    
>>> prompt='Hello!'    
>>> ask_gpt(prompt, api_key)    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in ask_gpt
  File "xrequests.py", line 124, in post
  File "xrequests.py", line 61, in request
OSError: -40   

查詢 ChatGPT 得知 OSError: -40 通常是指 SSL/TLS 連線錯誤, 有可能是把韌體刷回 v1.23 以前版本之故, 因為 MicroPython v1.23 就是用 TLS 取代 SSL 來解決安全連線問題. 

搜尋谷歌找到下面這篇文章 : 


其範例程式碼如下 :


作者直接使用 urequests 模組的 post() 就能順利地得到回應, 我在想 v1.23 版韌體的 urequests 應該也有修改, 於是把一顆 ESP32 刷回最新版 v1.23 來測試. 不使用 xtools 函式庫, 用基本的 network 連上 WiFi :

MicroPython v1.23.0 on 2024-06-02; Generic ESP32 module with ESP32
Type "help()" for more information.

>>> import network   
>>> import time    
>>> def connect_wifi(ssid, password):  
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(ssid, password)    
    while not wlan.isconnected():
        print('Connecting to WiFi...')
        time.sleep(1)    
    print('Network Config:', wlan.ifconfig())
    
>>> ssid="ASUS-RT-AX3000"    
>>> password="123456"       
>>> connect_wifi(ssid, password)   
True
Connecting to WiFi...
Connecting to WiFi...
Connecting to WiFi...
Network Config: ('192.168.50.48', '255.255.255.0', '192.168.50.1', '192.168.50.1')

連網成功後匯入 urequests, 然後修改上面的 ask_gpt() 函式, 改用 urequests.post() :

>>> import urequests
>>> def ask_gpt(prompt, api_key, model='gpt-4o-mini'):
    url='https://api.openai.com/v1/chat/completions'
    headers={'Content-Type': 'application/json',
             'Authorization': f'Bearer {api_key}'}
    json={'model': model,
          'messages': [{'role': 'user', 'content': prompt}]} 
    response=urequests.post(url=url,headers=headers, json=json)
    reply=response.json()
    return reply['choices'][0]['message']['content']

>>> prompt='Who are you?'     
>>> api_key='在此輸入 OpenAI API key'  
>>> ask_gpt(prompt, api_key)  
'I am an AI language model created by OpenAI, designed to assist with a variety of tasks, including answering questions, providing information, and generating text based on prompts. How can I help you today?'

跟它打招呼 : 

>>> prompt='Hello'   
>>> ask_gpt(prompt, api_key)      
'Hello! How can I assist you today?'   

但提示詞不能用英文以外的語言, 因為 MicroPython 只支援英文 : (見下面補充)

>>> prompt='嗨'     
>>> ask_gpt(prompt, api_key)    
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 9, in ask_gpt
KeyError: choices

真不錯, 這樣一來跑 MicroPython 的開發板也能藉助 AI 增強功能了. 

補充 :

我拿一顆 ESP8266 刷 v1.23 版韌體如法炮製卻不行 : 

>>> ask_gpt(prompt, api_key)   
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in ask_gpt
  File "requests/__init__.py", line 186, in post
  File "requests/__init__.py", line 95, in request
OSError: -40

詢問 ChatGPT 答覆為 ESP8266 的硬體與其 MicroPython 堆疊對 SSL/TSL 的支援有限. 所以要用 MicroPython 作 AI 應用必須採用 ESP32, 一般 4MB Flash 的就可以, 需要處理音訊或圖片則可用 16MB Flash 的 ESP32. 


2024-09-26 補充 : 

今天將上面以中文詢問出現錯誤的訊息 ChatGPT, 原來原因不是 MicroPython 不支援中文, 而是跟傳 Line Notify 訊息出錯原因一樣, 使用 urequests.post() 時應該將 payload 轉成字串後呼叫 encode() 以 utf-8 編碼為 bytes 類型用 data 參數傳送 (可見 MicroaPython 的 urequests 與 CPython 的 requests 模組不同, requests 用 json 參數傳遞字典沒問題, 但 xrequests 不行, 需用 data 參數) :

def ask_gpt(prompt, api_key, model='gpt-4o-mini'):
    url='https://api.openai.com/v1/chat/completions'
    headers = {
        'Content-Type': 'application/json',  
        'Authorization': f'Bearer {api_key}'
        }
    # 建立 data 參數字典
    data={
        'model': model,
        'messages': [{'role': 'user', 'content': prompt}]
        }
    # 將字典轉成字串後再編碼成 UTF-8
    payload=ujson.dumps(data).encode('utf-8')  
    # 發送 POST 請求
    response=urequests.post(url, headers=headers, data=payload)
    if response.status_code == 200:
        reply=response.json() # 轉成字典
        return reply['choices'][0]['message']['content']
    else:
        return response.json()  # 返回錯誤信息

測試結果中英文都 OK :

>>> prompt='Hello'    
>>> reply=xtools.ask_gpt(prompt, openai_api_key)    
>>> print(reply)   
Hello! How can I assist you today?   
>>> prompt='嗨'    
>>> reply=xtools.ask_gpt(prompt, openai_api_key)   
>>> print(reply)     
嗨!你好!有什么我可以帮助你的吗?

注意! 此處如果提示詞是中文, ask_gpt() 的回應預設會用簡體中文的 unicode 字串表示, 需用 print() 輸出才會解碼為中文, 例如 :

>>> prompt='你是誰?'    
>>> xtools.ask_gpt(prompt, openai_api_key)    
'\u6211\u662f\u4e00\u4e2a\u4eba\u5de5\u667a\u80fd\u52a9\u624b\uff0c\u65e8\u5728\u56de\u7b54\u95ee\u9898\u3001\u63d0\u4f9b\u4fe1\u606f\u548c\u5e2e\u52a9\u7528\u6237\u89e3\u51b3\u95ee\u9898\u3002\u6709\u4ec0\u9ebc\u6211\u53ef\u4ee5\u5e6b\u52a9\u4f60\u7684\u55ce\uff1f'
>>> print(xtools.ask_gpt(prompt, openai_api_key))    
我是ChatGPT,一個由OpenAI開發的人工智慧語言模型。我的目的是幫助回答問題、提供資訊和進行對話。你有什麼想知道的嗎?

此函式已新增到 xtools.py 函式庫裡, 參考 :


沒有留言 :