Programowanie łączące kod z GPT-4
Programowanie łączące kod z GPT-4
Projektowanie logiki aplikacji wymaga precyzyjnego opisania sposobu przepływu oraz przetwarzania informacji. Jeżeli wszystko idzie zgodnie z planem, to jej zachowanie zostaje dopasowane do naszych potrzeb, nie pozostawiając miejsca na dwuznaczności. Zdarzają się jednak sytuacje w których złożoność zadania jest tak duża, że trudno mówić o przełożeniu jej na kod. Jeszcze niedawno takie zadania musiały być realizowane przez ludzi, a dziś, w przypadku wielu z nich, doskonale poradzą sobie duże modele językowe.
Skoro mówimy o zadaniach, których nie opisujemy za pomocą kodu, to znaczy, że nasza kontrola nad ich realizacją jest znacznie mniejsza. Pozostaje nam jednak sterowanie zachowaniem modelu oraz reagowanie na wygenerowane rezultaty. Inaczej mówiąc, sięganie po większe możliwości zmniejsza przewidywalność. A to wymaga zastosowania nowych narzędzi oraz wprowadzenia zmian w procesie projektowania aplikacji.
Rozszerzanie logiki aplikacji o AI
Tworzenie rozwiązań wykorzystujących zapytania do OpenAI nadal wymaga wdrażania szeregu funkcjonalności, które znamy od dawna. Nadal konieczne jest projektowanie architektury, struktury bazy, logiki dostępu do danych, autoryzacji i uwierzytelnienia, sposobie wyszukiwania informacji oraz ich prezentacji po stronie front-endu. W tym wszystkim zapytania do GPT-4 (lub innych modeli) stanowią po prostu kolejny element, który może mieć wpływ na wszystkie pozostałe.
Wyobraź sobie proste zadanie polegające na normalizacji danych z ankiety. Jedno z pytań dotyczące szybkości pisania na klawiaturze umożliwiało wpisanie wartości tekstowej zamiast liczbowej. W rezultacie wyniki nie mogą być łatwo przeanalizowane, bo wśród odpowiedzi znalazły się odpowiedzi nie zawierające wartości liczbowych. Zrealizowanie nawet prostego zadania polegającego na obliczeniu średniej, jest w tym przypadku niemożliwe o ile ktoś ręcznie nie poprawi tych danych.
Jednak mając do dyspozycji GPT-4, to potencjalne rozwiązanie mogłoby uwzględniać przesłanie wyników ankiety do OpenAI w celu uzyskania odpowiedzi na nasze pytanie. Jak widać na poniższym screenie, wszystko wygląda w porządku.
Sytuacja zaczyna się komplikować, gdy do gry wchodzą większe liczby oraz odpowiedzi zawierające więcej niż jedną wartość.
Dla nowego zestawu danych odpowiedź GPT-4 to 90.89, podczas gdy poprawny wynik wynosi 98.14. Pozornie błąd wynika z faktu, że jeden z użytkowników podał dwie liczby w swojej odpowiedzi.
Po dodaniu do promptu sugestii mówiącej o braniu pod uwagę wyłącznie najwyższych wartości dla danego wpisu, model ponownie generuje poprawną odpowiedź. Pojawia się jednak nowy problem w postaci nieustrukturyzowanej odpowiedzi ze strony modelu, która w tym przypadku zawiera dodatkowe wyjaśnienia prowadzące do rozwiązania. Z programistycznego punktu widzenia interesuje nas wyłącznie sam rezultat, a pobranie go z takiej treści staje się kolejnym problemem.
Wydaje się więc, że sensownym rozwiązaniem będzie zapytanie wyłącznie o wartość liczbową i nic więcej. W ten sposób zwrócona odpowiedź może być wykorzystana w dalszej logice aplikacji.
Niestety w takiej sytuacji wynik przestaje być poprawny. Jednym z uzasadnień jest fakt, że pominięcie wyjaśnienia daje mniej przestrzeni na "rozumowanie", które przydaje się w realizowaniu bardziej złożonych zadań. Natomiast ogólna zasada mówi o tym, że modele językowe nie są zbyt dobre w obliczeniach. I chociaż radzą sobie z niektórymi zadaniami, to ryzyko pomyłki jest tutaj szczególnie wysokie.
Dodatkowym elementem, który należy brać pod uwagę jest także sposób wygenerowania odpowiedzi. Nawet jeśli podkreślimy, że chodzi nam o liczbę, nie mamy pewności, że nie otrzymamy dodatkowego komentarza, np. "Odpowiedź to: ...".
Aby zaadresować wszystkie problemy, musimy zmienić strategię przetwarzania tych informacji. W związku z tym, że model jest świetny w przetwarzaniu tekstu, to jego zadanie będzie polegać na normalizacji wyników i wypisaniu najwyższych wartości dla każdego wpisu.
Następnie zadbamy o poprawne formatowanie odpowiedzi, dzięki jednemu z narzędzi dostępnych w bibliotece LangChain (wersja JavaScript). Wówczas zwiększymy prawdopodobieństwo uzyskania tablicy wartości tekstowych, które możemy zamienić na liczby.
No i ostatecznie wykorzystujemy metodę reduce do obliczenia średniej otrzymując wynik z dokładnością do 14 miejsca po przecinku. Zadanie jest rozwiązane a nasz kod zadziała dla zestawów danych, które nie przekraczają dopuszczalnego limitu kontekstu dla danego modelu. W przypadku dłuższych list, musielibyśmy podzielić je na mniejsze fragmenty.
LangChain
Powyższy przykład jasno pokazuje jak istotną rolę odgrywa podział odpowiedzialności pomiędzy modelem a kodem. Łącząc możliwości jednego i drugiego, możemy wzajemnie adresować ograniczenia, przełamując kolejne bariery tego, co jest wykonalne, a co nie.
Połączenie z LLM (Large Language Model) polega na podłączeniu do zewnętrznego API, w którym nie ma nic skomplikowanego. Wyzwanie stanowi jednak odpowiednie przygotowanie instrukcji (tzw. promptów) oraz sterowanie zachowaniem modelu tak, aby realizował zadania zgodnie z oczekiwaniem.
Niedeterministyczna natura modeli oraz różne ograniczenia dodatkowo komplikują ich praktyczne, produkcyjne zastosowanie. Z tego powodu przede wszystkim należy unikać ich stosowania w przypadku krytycznych elementów systemu (więcej na ten temat znajdziesz w tym materiale). Mogą jednak świetnie sprawdzić się w realizacji wybranych zadań, pełniąc rolę wspierającą lub przejmując pewne procesy, które do tej pory wykonywał człowiek.
Aby ułatwić pracę z LLM od programistycznej strony, już teraz powstaje szereg narzędzi adresujących powtarzalne zadania. Przykładem z ekosystemu JavaScript/Python jest LangChain. Jest to biblioteka oferująca odpowiedzi na pytania dotyczące np. pracy z danymi, stosowania pamięci długoterminowej, projektowania logiki dla złożonych zadań, a nawet tworzenia autonomicznych agentów. Warto dodać, że poza LangChain obiecująco wygląda także Vercel AI SDK, które obecnie oferuje minimum funkcjonalności do rozpoczęcia pracy z modelem w połączeniu np. z Next.js czy SvelteKit.
LangChain oferuje wiele metod oraz sugeruje rozwiązania, które nie zawsze muszą sprawdzić się w naszym przypadku. Chociażby w zadaniu, przez które przechodziliśmy wcześniej, całkowicie pominąłem formatowanie promptu z pomocą wbudowanych metod, ponieważ uznałem, że w tym przypadku komplikowanie logiki nie jest potrzebne. Podobne decyzje podejmuję także przy innych moich projektach, nierzadko korzystając zaledwie z kilku metod oferowanych przez LangChain i projektując pozostałe samodzielnie.
Jeśli chcesz zacząć pracę z LangChain, to według mojego doświadczenia warto zwrócić uwagę na kilka rzeczy. Są to m.in.:
- w przypadku bezpośredniej interakcji z OpenAI bez złożonej logiki wystarczy openai-node i nie ma potrzeby sięgania po LangChain
- do pracy z dokumentami, podziałem tekstu, wczytywaniem różnego rodzaju treści czy stron www, zdecydowanie warto sięgnąć po LangChain oraz narzędzia z sekcji "Data Connection"
- w sekcji "Data Connection" i "Document Loaders" godne uwagi są narzędzia do wczytywania treści stron www oraz różnych serwisów (np. Notion)
- nierzadko praca z danymi polega na ich podziale, otagowaniu oraz zaindeksowaniu w celach późniejszego wyszukiwania z pomocą wyszukiwania opartego o słowa kluczowe, full-text, lub znaczenie (semantic / similarity search). Zwykle LangChain sugeruje skorzystanie z baz wektorowych, ale możesz wykorzystać także silniki wyszukiwania takie jak ElasticSearch czy Algolia Search. Zasadniczo rozwijanie swoich umiejętności w zakresie pracy z danymi oraz i wyszukiwania jest bardzo pomocne.
- ze względu na dużą warstwę abstrakcji narzucaną przez LangChain, korzystanie z Chain oraz Agents wypada bardzo przeciętnie. Pomimo wprowadzanych zmian, nadal trudno jest zmodyfikować domyślne instrukcje sugerowane przez twórców LangChain. Można jednak czerpać inspirację z zastosowanych przez nich strategii do tworzenia własnej logiki wykorzystującej np. generowanie odpowiedzi na podstawie długich plików.
Warto jednak zaznaczyć, że obecne zastrzeżenia kierowane w stronę LangChain mogą się szybko zdezaktualizować lub zostać zaadresowane przez inne narzędzia. Jednocześnie wartość, jaką oferuje ta biblioteka już teraz, jest ogromna w kontekście elementów integracji kodu z LLM.
Od czego zacząć?
W przypadku nauki programowania, popularnością cieszyły się proste aplikacje do zarządzania zadaniami czy połączenia z API pogodowym. Nauka zastosowania dużych modeli językowych może uwzględniać projekty o podobnej skali, np.:
- klon ChatGPT umożliwiający rozmowę z obsługą formatowania składni oraz streamowania odpowiedzi generowanych przez model
- narzędzie do zarządzania listą zadań łączące się z API aplikacji z której korzystamy (np. Todoist)
- projektowanie pojedynczych umiejętności asystenta AI wykorzystujących stosowanie prostych mechanizmów pamięci długoterminowej oraz możliwości przetwarzania większych zestawów danych (np. podsumowań lub tłumaczeń tekstu)
Wcześniej jednak konieczne jest przynajmniej minimalne poznanie teorii dotyczącej LLM, projektowania promptów oraz baz wektorowych (których praktyczne, podstawowe zastosowanie jest bardzo proste). Łącząc tę wiedzę z praktyką, możemy szybko dojść do rozwiązań, które jasno pokażą nam, na co należy zwracać szczególną uwagę podczas pracy z Dużymi Modelami Językowymi. Jednym ze źródeł wiedzy, które mogę polecić, jest mój ostatni kurs o projektowaniu promptów, który wprowadzi Cię także w zagadnienia związane z LLM (głównie GPT-4).
Eksperymentowanie oraz eksplorowanie możliwości dużych modeli językowych w połączeniu z kodem to świetna zabawa. Jednocześnie może uwzględniać zaangażowanie na forach dyskusyjnych oraz discordzie LangChain czy OpenAI i prowadzić np. do takich wiadomości:
I chociaż nie wszystkie interakcje muszą kończyć się ofertami pracy, to jest to także świetny sposób na poznawanie osób mierzących się z podobnymi problemami. To z kolei stwarza przestrzeń do wymiany doświadczeń i skuteczniejszej nauki.
To tyle na dziś!
Adam
- Logika aplikacji połączona z AI
- GPT-4 jednak przydaje się w liczeniu
- Przydatne narzędzia LangChain
- Od czego zacząć?