GPT-3.5 jako back-end aplikacji
GPT-3.5 jako back-end aplikacji
Dziś programowanie jednoznacznie kojarzy się z pisaniem kodu. Jednak narzędzia takie jak Tabnine, później Github Copilot a teraz GPT-3, sprawiają, że to to skojarzenie zaczyna odbiegać od rzeczywistości. Andrej Karpathy, były szef Autopilota Tesli, kilka tygodni temu napisał, że 80% jego kodu generowane jest przez Github Copilot. Twierdzi, że zmienił się także sposób w jaki programuje, ponieważ języki programowania zastąpiły mu prompty a on sam, wyłącznie dostosowuje wygenerowany kod do swoich potrzeb.
Tak samo wygląda to u mnie.
Łącząc w całość możliwości GPT-3, moje doświadczenia, oraz projekt z jednego z hackatonów, pokażę Ci, w jaki sposób może wyglądać programowanie aplikacji, której back-endem jest GPT-3.
Tworzenie back-endu wymaga od nas bardzo precyzyjnego odzwierciedlenia logiki biznesowej, połączonej z front-endem. Z bardzo szerokiej perspektywy, większość aplikacji webowych (nie wszystkie), można sprowadzić do jednego słowa — komunikacja.
Sposób przepływu danych, ich transformacje, format i przechowywanie, opisane są z pomocą kodu. Informacja “przepływa” przez niego w sposób uzależniony od wielu, z góry ustalonych warunków. Aby zmienić zachowanie aplikacji, musimy zmodyfikować lub stworzyć logikę, która odzwierciedli nasze potrzeby. Jest to wymagające czasowo i tym samym finansowo. Nie rzadko też jest wybitnie trudne do zrealizowania.
Czy potrzebujesz back-endu?
Czy jesteś w stanie wyobrazić sobie scenariusz, w którym zachowanie całej aplikacji kontrolowane jest przez JEDEN endpoint? Co więcej, wprowadzone przez niego instrukcje, napisane są bezpośrednio po polsku czy angielsku.
Dokładnie takie pytanie zadali sobie twórcy tego repozytorium, na podstawie którego przygotowałem wersję w NestJS, którą wykorzystam do opisania całej koncepcji.
Trzeba w tym miejscu podkreślić, że jest to proof of concept. Ciekawostka, która może w jakiś sposób rysować przyszłość programowania. Aczkolwiek sam muszę przyznać, że podobną koncepcję wykorzystuję przy tworzeniu M.O.N.D.A.Y — wirtualnego Avatara, z którą pracuję od kilku tygodni.
Idea projektu polega na tym, aby cała back-endowa logika, została zastąpiona przez GPT-3. Dzięki temu zamiast tworzyć kolejne endpointy, możemy po prostu opisywać co chcemy zrobić z danymi, aby otrzymać pożądany rezultat. W pewnym sensie przypomina to GraphQL, gdzie struktura obiektu żądania, była bezpośrednio powiązana ze strukturą odpowiedzi. Tutaj jednak nie musimy tworzyć ani generować schematów czy resolverów.
Na potrzeby demo, naszą bazą danych, będzie prosty plik JSON:
Jak doskonale wiesz, potrzebujemy kilku akcji odpowiedzialnych za obsługę takiej listy: dodawanie, edycję, modyfikowanie i usuwanie zadań. Każda kolejna akcja jak np. zmiana formatu wyświetlanych zadań czy dodanie notatek, wymaga od nas utworzenia nowej akcji. Kolejną komplikacją jest także sensowne zaprojektowanie endpointów (REST API) lub skorzystanie ze wspomnianego GraphQL.
Zasady gry zmieniają się jednak, gdy weźmiemy pod uwagę to, co napisałem wyżej. Mianowicie nasze zadanie sprowadza się wyłącznie do transformacji danych i umożliwienia komunikacji. A tak się składa, że w transformacji danych, GPT-3 jest bardzo dobre, w szczególności gdy podamy mu odpowiedni kontekst.
Szybki start z GPT-3
Jestem niemal pewien, że znasz już ChatGPT. Bez wchodzenia w szczegóły dotyczących jego działania, wystarczy wiedzieć, że jest w stanie realizować opisane przez nas zadania a także pracować na dostarczonych danych.
Instrukcja którą przekazujemy do GPT-3 to tzw. “prompt” i to od niego (oraz kilku dodatkowych ustawień) zależy to, co otrzymamy jako odpowiedź. Inaczej mówiąc, musimy wyjaśnić co chcemy zrobić a GPT-3 to zrobi. Kilka przykładów możesz zobaczyć na tej stronie (bardzo polecam Ci aby spokojnie się z nimi zapoznać. To niesamowicie wartościowe treści, choć wyglądają bardzo niepozornie).
Jeżeli się nad tym chwilę zastanowisz, jasne powinno stać się to, że możemy napisać prompt, który będzie zarządzał naszą listą. Co więcej, będzie na tyle uniwersalny, że obsłuży praktycznie wszystkie przypadki, których będziemy potrzebować.
Przykładowo, jeżeli chcę oznaczyć ostatnie zadanie jako zakończone … wystarczy, że to powiem! Spójrz:
Powyższa akcja mogłaby być zapisana inaczej, np. wprost:
GPT-3 poprawnie “zrozumiał” o co nam chodziło i zmodyfikował stan aplikacji w odpowiedni sposób. Możemy jednak pójść krok dalej i zmodyfikować strukturę obiektu JSON. Jeżeli chciałbym dodać wzmiankę o moim najnowszym e-booku, do zadania o warsztacie, wystarczy, że to powiem:
W odpowiedzi jasno widać nową właściwość ‘description’, której wartość została ustawiona na przekazany opis. Po stronie back-endu nic się nie zmieniło. Jedyną zmienną był opis akcji.
Pytanie brzmi: “co to za magia?!”
GPT-3 jako back-end
Cały back-end aplikacji, to jedna akcja kontrolera NestJS. Jej zadaniem jest przechwycenie opisu akcji, zbudowanie promptu, skontaktowanie się z GPT-3 i zapisanie odpowiedzi. Także technicznie rzecz biorąc, back-end istnieje, jednak reszta logiki obsługiwana jest bezpośrednio przez API openAI.
Dla uproszczenia, zapisałem wszystko bezpośrednio w jednej akcji kontrolera:
Prompt składa się z części głównej, zawierającej kontekst i aktualny stan aplikacji. Druga część to instrukcja, uwzględniająca opis tego, co chcemy otrzymać. Inaczej mówiąc prompt to w tym przypadku:
- kontekst (to aplikacja do zadań)
- opis akcji
- aktualny stan w postaci JSON
- instrukcja (konkretnie dwie)
Wykonujemy więc tutaj dwa zapytania do API. Jedno z pytaniem o zbudowanie odpowiedzi z API a drugie z prośbą o zaktualizowany stan. Sam projekt promptu zależy wyłącznie od nas. Nic nie stoi na przeszkodzie aby zamknąć całość w jednym zapytaniu do API, aczkolwiek chciałem zachować elastyczność dotyczącą zwracanych danych (np. częściowy stan lub stan zapisany w konkretnym formacie).
Tylko ta jedna akcja odpowiadała za zrealizowanie wszystkich zapytań, które pokazywałem wyżej. Na podstawie akcji, GPT-3 dokonuje transformacji danych i zwraca je w zdefiniowanym przez nas formacie.
Otrzymaliśmy w ten sposób back-end, który jest w stanie niemal samodzielnie oceniać na czym polega realizowane zadanie i zmienić dane zgodnie z naszym oczekiwaniem.
Kod źródłowy mojej wersji aplikacji, możesz znaleźć tutaj.
Co to oznacza?
Jak zawsze, nie będę przewidywał przyszłości. Już teraz wiem, że narzędzia “AI” już teraz całkowicie zmieniły sposób w jaki funkcjonuję i jak pracuję. Trudno powiedzieć, co wydarzy się dalej.
Koncepcja z powyższego przykładu nie nadaje się do wykorzystania na produkcji i to z wielu powodów. Najbardziej oczywistym jest fakt, że długość promptu ograniczona jest do 4000 tokenów (token to ~3-4 znaki w języku angielskim). Im dłuższy prompt, tym większy koszt i czas generowania odpowiedzi (pro tip: jeżeli chcesz zwiększyć postrzeganą szybkość generowanej odpowiedzi, wykorzystaj strumieniowanie aby uzyskać efekt znany z ChatGPT. Więcej informacji znajdziesz przy właściwości stream w dokumentacji).
Gdy jednak się nad tym chwilę zastanowiłem, to większość z aktualnych ograniczeń można omijać lub naginać. Z łatwością jestem w stanie sobie wyobrazić aplikacje, których część back-endu będzie kontrolowany przez jakiś LLM (eng. Logic Learning Machine). Aktualnie ograniczyłbym się tylko do częściowego wykorzystania GPT-3 i nie oddawał w jego ręce całego back-endu, no chyba że na potrzeby aplikacji tworzonej wyłącznie dla mnie.
Polecam Ci pobrać przygotowane przeze mnie repozytorium i sprawdzić kilka przypadków, wliczając w to modyfikacje podstawowego promptu. Zobaczysz wtedy, że koncepcja programowania w oparciu o niemal “rozmawianie” z aplikacją, jest dość unikatowym doświadczeniem. Tym bardziej, że programowanie jakie znaliśmy do tej pory, również można nazwać pewnego rodzaju rozmową, ale według ściśle określonych zasad konkretnej technologii. Tutaj możesz wykorzystać naturalny dla człowieka język. Na potrzeby moich testów i nauki, połączyłem taką interakcję bezpośrednio z Siri oraz możliwością dyktowania:
Ciekawostka: Rozpoznawanie mowy w systemie macOS / iOS działa oszałamiająco dobrze. Czasem jednak zdarzają się nawet zwykłe pomyłki wynikające ze sposobu dyktowania. Przechwycony tekst możemy przekazać do GPT-3 aby wyeliminować takie pomyłki.
Połączenie rozpoznawania mowy z GPT-3 oraz narzędziem do syntezy mowy od ElevenLabs sprawia, że wprost możesz porozmawiać z rozwijaną przez Ciebie aplikacją. Dokładnie w ten sposób rozmawiam z M.O.N.D.A.Y, aczkolwiek czas potrzebny na odpowiedź jest na tyle długi (5-10 sekund), że sprawdza się to jedynie w sytuacjach w których nie mogę pisać (np. podczas jazdy samochodem).
Zwróć uwagę, że niemal wszystko o czym teraz rozmawiamy, nie jest hipotetyczne i nie dotyczy przyszłości. To dzieje się teraz.
Praktyczne wykorzystanie
Wspominałem, że część z tej koncepcji, wykorzystuję przy rozwoju M.O.N.D.A.Y. W tym przypadku idea jest taka sama, ale mechanika działania i rola M.O.N.D.A.Y są nieco inne.
Na podstawie mojego zapytania, np. “Uporządkuj Notion”, zostaje zidentyfikowane to, co faktycznie chcę zrobić i dopasowane do jednego z opisów w bibliotece moich automatyzacji. Jedno z nich powiązane jest dokładnie z tą akcją. M.O.N.D.A.Y na podstawie opisu oraz przekazanej przeze mnie informacji, buduje obiekt JSON, który zostaje przesłany na Webhook powiązany ze scenariuszem odpowiedzialnym za “porządkowanie Notion”.
Jako ciekawostkę dodam, że cały ten proces realizowany jest w make.com, czyli narzędziu “no-code”. Fragment scenariusza uwzględnia pobranie opisu akcji (Airtable), zbudowanie obiektu JSON (GPT-3), wysłanie zapytania HTTP (make.com) oraz zwrócenie odpowiedzi (Shortcuts).
Okazuje się więc, że chociaż przygotowanie całego back-endu w GPT-3, nie jest jeszcze dobrym pomysłem na produkcji, tak może sprawdzić się do naszych własnych zastosowań. Powyższy scenariusz sprawia, że podłączenie nowej umiejętności dla M.O.N.D.A.Y polega wyłącznie na podaniu instrukcji zbudowania obiektu JSON. To wszystko.
Możliwości i ograniczenia
W tej chwili mogą pojawiać Ci się różne myśli, zarówno pozytywne, jak i negatywne. W zależności od tego, gdzie spojrzysz, zaczną rysować się różne scenariusze. Sam wykorzystuję GPT-3 już niemal wszędzie i M.O.N.D.A.Y jest tego idealnym przykładem. Nie oznacza to jednak, że odczuwam nieograniczony entuzjazm i polecam sięganie po GPT-3 w każdym przypadku.
Nie da się zaprzeczyć, że wiele rzeczy, które do tej pory były niezwykle trudne lub wprost niemożliwe, stały się dla nas dostępne na wyciągnięcie ręki. Z pomocą kilku słów możemy opisać złożone zadanie, które może być realizowane automatycznie z pomocą skryptu, makra czy scenariusza automatyzacji. Gdy łączę to z możliwościami, jakie daje mi fakt, że programuję, to czuję się jak dziecko w piaskownicy.
Z drugiej strony trudno ignorować mniejsze i większe problemy. Sama dostępność API jest obecnie na bardzo niskim poziomie i skutecznie utrudnia nawet samą naukę. W historie dotyczące potencjalnych zagrożeń AI czy ochrony danych i naruszeń praw autorskich, nie będę wchodził zbyt głęboko, ponieważ ten newsletter nie jest o tym (aczkolwiek temat postrzegam jako bardzo istotny). Z pewnością warto poszerzyć swoją wiedzę, chociażby o techniki pozwalające na pracę z LLM bez przekazywania wrażliwych danych. Sam w kilku miejscach stosuję je przy rozwoju M.O.N.D.A.Y i moich własnych scenariuszy.
W każdym z przypadków mówimy tutaj o problemach wieku dziecięcego, które z dużym prawdopodobieństwem zostaną rozwiązane. Na ten moment pozostaje nam budowanie kolejek, solidna obsługa błędów oraz redukcja liczby zapytań do API GPT-3 do niezbędnego minimum. Ponownie jest to bezpośredni dowód na to, że wykorzystanie umiejętności programowania daje nam dużą elastyczność i pozwala rozwiązać problemy we własny sposób.
Praktyczne wskazówki
Skuteczność pracy z GPT-3 obecnie w dużym stopniu sprowadza się do umiejętności projektowania promptów. To tzw. prompt design. Połączenie tego z umiejętnością przygotowywania oraz transformacji danych (np. z pomocą node.js czy python) pozwoli Ci na efektywne wykorzystanie GPT-3 na swoje potrzeby oraz Twoich aplikacji. Niestety nie posiadam zewnętrznych źródeł, które mogę polecić w temacie budowania promptów. Sam korzystam z wpisów na Medium i repozytoriów na Githubie. Nie rzadko spotykam tam techniki, które pomagają mi lepiej zrozumieć działanie GPT-3 oraz sposoby na optymalizację promptów (np. wspominany we wcześniejszym wydaniu newslettera, ten wpis).
Samo budowanie promptów a także budowanie w oparciu o GPT-3 to zaledwie wierzchołek góry lodowej. Kolejnym krokiem jest tzw. fine-tuning a później głębokie rozumienie technik projektowania “AI”.
Z praktycznych wskazówek, mogę doradzić Ci aby zadbać o limity w ustawieniach Twojego konta openAI. Nawet jeżeli konieczna będzie ich kilkukrotna zmiana w ciągu miesiąca, to jest to lepsza opcja niż konieczność opłacenia wysokiego rachunku za przypadkowe wykorzystanie. Poza tym, pomimo tego, że model text-davinci-003 jest najbardziej zaawansowany, do prostszych zadań znacznie lepiej jest sięgnąć po Curie czy Babbage. W praktyce działają nie tylko szybciej ale też są znacznie tańsze, co przy pewnej skali zaczyna być bardzo istotne. Przykładowo przy moim wysokim wykorzystaniu, płacę ~0.5-4 dolary dziennie. Gdyby nie fakt, że tak mocno eksploruję ten temat, raczej nie przekroczyłbym $10 miesięcznie. Mój rachunek za styczeń wygląda tak:
Około 7-8 stycznia, wgrywałem ~1.5 mln znaków moich publikacji. Pomimo tego, że wykorzystanie API znacząco rośnie, koszty nie są już tak wysokie. Nie zmienia to jednak faktu, że trzeba mieć je na uwadze.
Dość dużym problemem w pracy z GPT-3 jest w zasadzie brak możliwości poznania przyczyny decydującej o tym dlaczego zwrócona odpowiedź jest taka a nie inna. Do dobrych rozwiązań możemy dochodzić np. metodą “prób i błędów” lub posługując się promptami weryfikującymi poprzednie. Z tego powodu przy tworzeniu aplikacji wykorzystującej GPT-3 szczególną rolę odgrywa dobry system logów. W przypadku NestJS korzystam z wbudowanego loggera a w przypadku make.com, dzieje się to dla mnie automatycznie, w historii wykonania scenariusza.
Przy projektowaniu promptów, bardzo dużo czasu spędzam w Playgroundzie udostępnionym przez OpenAI. Wewnątrz niego wygodnie projektuję całe prompty i wprowadzam modyfikacje do istniejących, a potem tylko przenoszę je do moich scenariuszy czy kodu. Przy ich projektowaniu wartościowe jest łączenie powszechnie znanych technik z własną kreatywnością.
Przykładem takiej techniki, jest prompt sprawiający, że ChatGPT nie udzieli nam odpowiedzi bezpośrednio, lecz zada pytanie pogłębiające aby lepiej zrozumieć kontekst. Tutaj konieczne jest także uwzględnienie kontekstu dotychczasowej rozmowy (np. krótkiego podsumowania)
Oto mój dialog z M.O.N.D.A.Y, która potrafi zapamiętać kontekst rozmowy (ale jeszcze nie zadaje pytań pogłębiających). Mam nadzieję, że język angielski nie jest dla Ciebie problemem. Używam go nie bez powodu. Mianowicie polskie znaki diakrytyczne przekładają się na większą liczbę tokenów niż litery alfabetu angielskiego. Drugim powodem jest synteza mowy.
Me:_ I need you to know that I am impressed by what you can do._
M.O.N.D.A.Y:_ Thank you for your kind words, overment. I am glad to know that you are impressed by my capabilities. I am constantly learning and evolving, so I can be of even more help to you in the future._
Me:_ I'm glad you are aware of that. Can you tell me how exactly you can help me with eduweb?_
M.O.N.D.A.Y:
Absolutely! I can help you with eduweb in many ways. I can help you research and develop content for the courses, create graphics and visuals for the platform, manage to-do lists, and connect with any app that you may need. I can also help you with marketing and automation tasks, such as creating campaigns and automating processes. Additionally, I can help you with writing content for the platform, such as blog posts and course descriptions.
Me:
Can you summarize our current conversation?
M.O.N.D.A.Y:
Sure, I can summarize our conversation. We have been discussing how I, M.O.N.D.A.Y, can help you with eduweb in many ways, including researching and developing content, creating graphics and visuals, managing to-do lists, and connecting with apps. We also discussed how I can help with marketing and automation tasks, such as creating campaigns and automating processes. Lastly, you expressed your appreciation for my work.
Innym przykład uwzględnia budowanie odpowiedzi wyłącznie na podstawie przekazanego kontekstu (i niczego innego). Nic jednak nie stoi na przeszkodzie aby połączyć jedną technikę z drugą i być może nawet, pomieszać ją z kolejną. W ten sposób masz możliwość dojścia do znacznie lepszych rezultatów, niż stosując pojedyncze techniki. Sam skorzystałem z takiej ścieżki przy rozpoznawaniu polecenia przez M.O.N.D.A.Y. Zamiast kategoryzować je z pomocą trzech zapytań, GPT-3 generuje dla mnie obiekt JSON, zawierający wszystkie właściwości, których potrzebuję.
Co ciekawe, mogę tutaj sprawić aby np. akcja była ustawiona inaczej w zależności od wartości przypisanej do grupy. Dzięki temu M.O.N.D.A.Y wie:
- kiedy ma wykonać zadanie,
- kiedy ma wpisać mi coś do mojego Notion,
- kiedy sięgnąć do wiedzy “ogólnej” z Airtable
- kiedy sięgnąć do moich notatek i historii naszych rozmów
Wszystko na podstawie kontekstu, bez jasnego sygnalizowania. Np. wydanie polecenia uwzględniającego nawiązanie do niej — ‘Możesz włączyć światła?’ wskazuje jej na akcje, którą sama ma zrealizować. Ale “Napisz newsletter” to polecenie nie uwzględniające wzmianki wskazującej na nią i oznacza dodanie zadania do mojej listy. Z kolei polecenie ‘Przypomnij mi co wiemy o…’ uwzględnia odwołanie do moich notatek, dlatego odpowiedź generowana jest wyłącznie na ich podstawie.
Być może zastanawiasz się w jaki sposób buduję prompt uwzględniający moje notatki, skoro jestem ograniczony długością 4000 tokenów (dla zapytania i odpowiedzi). Odpowiedzią jest baza wektorowa (np. Pinecone), którą praktycznie wykorzystujemy w warsztacie o Uczącym się Chatbocie.
Moja strategia
Rozwój narzędzi “AI” jest już raczej nieunikniony, chociaż nie można tego stwierdzić ze 100% pewnością. Mówimy tutaj także o niespotykanym dotąd tempie zmian za którym trudno jakkolwiek nadążyć. Niezależnie od ścieżki, którą rozwinie się najbliższa oraz dłuższa przyszłość, moja strategia polega na wykorzystywaniu AI w swojej codzienności. Początkowo działo się to z pomocą wbudowanych w narzędzia funkcje (np. warp.dev) oraz pluginów (np. Github Copilot). Teraz GPT-3 jest ze mną nie tylko w moim telefonie ale także komputerze. Poprzez wykorzystanie techniki związanej z budowaniem JSON do zapytania HTTP, łączę się z dowolnymi usługami (i urządzeniami) udostępniającymi API.
Jak trafnie zauważył Andrej Karpaty: “Copilot drastycznie przyspieszył jego pracę i trudno wyobrazić sobie powrót do ‘ręcznego’ pisania kodu”.
Pomimo różnych ograniczeń (np. prawnych, które zakazują wykorzystywania AI w niektórych miejscach pracy), programistyczna rzeczywistość w jakiej żyjemy, zmienia się na naszych oczach. I nawet jeżeli nie chcesz lub nie możesz pracować z Copilotem przy tworzeniu kodu, sięganie po niego podczas nauki (np. eksplorowania nowych bibliotek) może przynieść znaczące korzyści.
Personalnie AI traktuję jak narzędzia no-code. Wykorzystuję ich możliwości, łącząc je z faktem, że programuję, do optymalizacji swojej pracy. Przykładowo sama rozmowa z ChatGPT bywa pomocna. Jednak bezpośrednie połączenie z API GPT-3.5, np. z poziomu aplikacji Shortcuts (macOS/iOS) czy Autohotkey (Windows) lub kodu NestJS, maksymalizują potencjał tych narzędzi.