Pokażę tutaj uruchomić ChatGPT na ESP8266 - oczywiście nie bezpośrednio, lecz poprzez API OpenAI. Zademonstruję prosty kod wysyłający odpowiednie zapytanie do API poprzez HTTPS, wraz z omówieniem przekazania klucza API oraz obsługi formatu JSON. Rezultatem będzie możliwość odpytania słynnego Chatbota bezpośrednio z ESP, bez użycia komputera czy tam nawet Raspberry Pi.
Temat ten będzie stanowić podstawę dla osób chcących wykonać jakieś DIY z modelami językowymi OpenAI na ESP8266, omówię tu absolutne podstawy, a w przyszłości być może pokażę coś bardziej zaawansowanego.
API OpenAI
Modele językowe OpenAI są dostępne poprzez API, tzn. my tylko wysyłamy do serwerów OpenAI żądanie, opisujące co model językowy ma wykonać, a potem odbieramy ich odpowiedź. Nie uruchamiamy oczywiście ChatGPT na naszym sprzęcie, nie jest ono nawet możliwe do pobrania.
API OpenAI jest bardzo bogate i oferuje różne funkcje (oraz ceny; w zależności od tego jaki model językowy nas interesuje), ale nie ma sensu bym tutaj kopiował Wam ich dokumentacji. Wszystko znajdziecie poniżej:
https://openai.com/blog/openai-api
Szczegółowa dokumentacja dostępna jest tutaj:
https://platform.openai.com/docs/api-reference/making-requests
Krótka lektura uświadamia nam co mamy zrobić. Musimy wysłać żądanie POST o nagłówkach:
- Content-Type: application/json
- Authorization: Bearer NASZ_KLUCZ
- Content-Length: DŁUGOŚĆ_JSONA (dla zasady warto to dodać)
i treści:
Kod: JSON
na endpoint:
https://api.openai.com/v1/chat/completions
Model określa model językowy, z którego korzystamy. Lista modeli dostępa jest tutaj (też wymaga klucza API):
https://api.openai.com/v1/models
więc pobrać ją można np. poprzez CURL:
curl https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY"
Parametr temperature należy do zakresu [0,2], im wyższy, tym odpowiedzi są bardziej losowe, a im niższy, tym bardziej deterministyczne.
Po wysłaniu wspomnianego już pakietu otrzymamy odpowiedź, która wygląda mniej więcej tak:
Kod: JSON
Wszystko wygląda bardzo fajnie, zatem gdzie jest haczyk?
Haczyk jest mały - to API jest płatne. Płacimy za ilość tokenów, czyli zasadniczo tyle ile zużyjemy, tyle musimy opłacić. Szczegóły tutaj:
https://openai.com/pricing
Tokeny są dość zbliżone do słów, ale nie są słowami - wg. samego OpenAI 1000 tokenów to około 750 słów.
Ceny zależą od modelu; oto ceny GPT-4:
Ceny ChatGPT (gpt-3.5-turbo) są nieco mniejsze:
Są też inne modele:
Użyta płytka
Do projektu użyłem płytki NodeMcu v3 CH340, którą można kupić za nawet 17zł:
Visual Code i Platform IO, pierwsze kroki
Do projektu zdecydowałem się użyć Visual Code z PlatformIO, jest to moim zdaniem bardziej wygodne i responsywne środowisko niż Arduino IDE.
https://platformio.org/
Instalujemy darmowe środowisko oparte na licencji MIT, Microsoft's Visual Studio Code, a potem dodatek PIO.
Po zainstalowaniu wszystkiego można zacząć od podstaw:
Kod: text
Oto mój platformio.ini, dla tych którzy nie wiedzą jak naszą płytkę skonfigurować:
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:nodemcuv2]
upload_speed = 115200
monitor_speed = 115200
platform = espressif8266
board = nodemcuv2
framework = arduino
lib_deps = bblanchon/ArduinoJson@^6.21.1
Rezultat, wszystko działa:
Można by postawić tutaj ArduinoOTA i wgrywać wsad przez WiFi, ale to jest temat o OpenAI API i tego się trzymajmy.
ESP8266 i HTTPS
Pierwszym problemem jaki napotykamy jest potrzeba użycia szyfrowanej wersji HTTP, czyli HTTPS. Nieco nam to utrudnia zabawę, pewnie z mojego ulubionego PIC18F67J60 bym już połączenia szyfrowanego nie nawiązał, ale na szczęście na ESP8266 HTTPS jest wspierany i są gotowe na to przykłady. Poniżej jeden z nich:
Kod: C / C++
Tutaj nieco uprościłem sprawę, bo pozwoliłem sobie uruchomić tryb bez weryfikacji certyfikatu:
Kod: C / C++
ale myślę, że w przypadku małego hobbystycznego projektu/demonstrancji to nie jest problem.
Co ten program robi? Zasadniczo dwie rzeczy.
W setup łączymy się z naszą siecią WiFi:
Kod: text
W pętli, co dwie minuty, próbujemy połączyć się z przykładową stroną po HTTPS i wydrukować uzyskaną odpowiedź. Wysyłamy tutaj żądanie GET.
Kod: text
Użyta tutaj w ramach przykładu strona zwraca informacje o wspieranych przez nasze połączenie standardach szyfrowania i zwraca je w formacie JSON:
https://www.howsmyssl.com/a/check
Można też odwiedzić tę stronę normalnie:
https://www.howsmyssl.com/
Program działa:
W przypadku odwiedzin strony z przeglądarki otrzymuję informację, że moja przeglądarka wspiera TLS 1.3. W przypadku użytej platform z ESP jest to tylko wersja TLS 1.2, ale nie jest to na ten moment problemem.
Obsługa JSON
Jak już wiemy, potrzebny będzie nam format zapisu JSON. Przyda się on zarówno przy wysyłaniu danych do serwera, jak i przy jego odbiorze. Czyli zasadniczo musimy umieć wpisać dane do JSONa, oraz potem te dane z niego wyłuskać.
Wpisywanie do JSONa można by zrobić częściowo na sztywno, poprzez operacje na String lub jeden duży sprintf, ale przecież tu mamy do wszystkiego biblioteki, więc po co sobie komplikować?
Załączamy bibliotekę ArduinoJSON przez PlatformIO:
Załączamy nagłówek:
Kod: text
Chcemy utworzyć następujący tekst JSON:
Kod: JSON
Mamy tu wszystko, i zwykłe zmienne (klucz-wartość), i tablicę i obiekty.
Ale dzięki ArduinoJSON wszystko jest bardzo proste:
Kod: text
Jak widać, zmienne ustawiamy za pomocą operatora [], tablicę tworzymy poprzez funkcję createNestedArray, a object w niej możemy utworzyć funkcją createNestedObject. Wszystko wygodnie, ale jak to zamienić na tekst?
Kod: text
I to wszystko - po użyciu tej funkcji w jsonString mamy już tekst JSON reprezentujący wcześniej utworzoną strukturę.
Jeszcze tylko pytanie, czy można w drugą stronę? Z napisu na obiekty? Oczywiście, w ten sposób:
Kod: text
Pierwszy request do API OpenAI
Pora złożyć wszystko w całość. Podstawową wiedzę oraz wysyłanie GET po HTTPS już mamy.
GET wysyłane było tak:
Kod: text
można to łatwo przerobić na POST:
Kod: text
Tyle, że POST wymaga podania zawartości ciała żądania - wskaźnik na dane i długość.
Wsadzimy tam nasz JSON string, o którego generowaniu wspominałem wcześniej:
Kod: text
Trzeba go jeszcze wygenerować - tak jak w poprzednim akapicie:
Kod: text
Ale to nie wszystko. Trzeba jeszcze ustawić pola z nagłówka żądania. Między innymi tutaj znajduje się nasz klucz do API:
Kod: text
Ustawiamy również rodzaj zawartości nagłówka na JSON.
W zasadzie tyle starczy by uzyskać pierwsze rezultaty.
Oto cały kod, poglądowo:
Kod: text
Proszę pamitać o uzupełnieniu tych linijek:
Kod: text
Uruchamiamy i już mamy pierwsze rezultaty:
Oto kilka otrzymanych odpowiedzi w formie tekstu:
Kod: JSON
Kod: JSON
Kod: JSON
Bez dodatkowej konfiguracji pomysły się nieco powtarzają. Oto drugi o samochodziku zdalnie sterowanym:
Kod: JSON
W odpowiedzi są również informacje o ilościach użytych tokenów, co pozwala nam oszacować lepiej ile kosztowało nas dane zapytanie. Pole created to z kolei czas Unix epoch, określa kiedy wygenerowano odpowiedź.
Ulepszamy request do API OpenAI
Warto jest jeszcze jakoś poprawnie obsłużyć otrzymaną odpowiedź. Tak jak pisałem, jest ona w formacie JSON. Można ją łatwo wczytać do wygodnych struktur danych:
Kod: text
Teraz trzeba ją jeszcze przetworzyć.
Obiekt jest łatwo pobrać, tablicę natomiast należy rzutować jeszcze do typu JsonArray:
Kod: text
Dopiero potem można iterować jej elementy:
Kod: text
Warto zwrócić uwagę na różnice w pomieraniu napisu (String) i liczby całkowitej (int).
Reszta powinna być już dość prosta:
Kod: text
Dzięki temu można z formatu JSON np. wyciągnąć samą ostatnią odpowiedź AI i potem np. wysłać ją dalej do systemu ludzkiej mowy, by nasz ESP "przemówił" myślą ChatGPT.
Podsumowanie
To był najprostszy przykład użycia API OpenAI z ESP8266, w pełni oparty na gotowych bibliotekach. Do obsługi HTTPS użyliśmy WiFiClientSecureBearSSL, a z obsługą JSON pomógł nam DynamicJsonDocument. Do skorzystania z jednego z najpotężniejszych modeli językowych starczyła nam płytka z ESP8266 za 17zł oraz klucz API OpenAI, który niestety wymaga opłacenia ich API. W przypadku ChatGPT zabawa kosztuje 0.002$ za 1000 tokenów (średnio 750 słów).
Przed użyciem kodu w poważnym projekcie należałoby go uzupełnić o poprawną weryfikację certyfikatu SSL oraz można by dodać utrzymywanie kontekstu (historii rozmowy) ChatGPT, ale o tym już innym razem.
Czy widzicie jakieś potencjalne zastosowania na ChatGPT na ESP8266 bądź ESP32? Zapraszam do dyskusji.
Fajne? Ranking DIY Pomogłem? Kup mi kawę.
