Podstawy
2 godz. 43 min · OpenAI · Full-stack i Programowanie
Adam GospodarczykChociaż dokładne działanie dużych modeli językowych nie jest zrozumiałe nawet dla twórców tych narzędzi, to i tak istnieje szereg dobrze znanych faktów. Zapoznanie się z nimi pozwoli Ci lepiej wyobrazić sobie sposób działania LLM bez konieczności wchodzenia w głębokie detale techniczne. To z kolei w sposób bezpośredni przełoży się na skuteczność projektowanych promptów. W pierwszej części kursu skupiamy na wysokopoziomowym spojrzeniu na modele firmy OpenAI, uwzględniając ich aktualne możliwości oraz aktualne ograniczenia.
Instrukcje kierowane do modeli takich jak GPT-3.5-Turbo czy GPT-4, mogą realizować najróżniejsze zadania. Pomimo tego ich ogólna struktura uwzględnia pewne schematy z których możemy korzystać. W zależności od celu jaki chcemy osiągnąć, możemy uwzględniać lub pomijać poszczególne elementy promptu. I chociaż wspomniane modele są w stanie świetnie radzić sobie z nieustrukturyzowanymi danymi, to w przypadku projektowania instrukcji, bardzo istotne jest zachowanie precyzji oraz wyraźne oddzielenie jej fragmentów. Na praktycznych przykładach zobaczysz, jak łatwo niewłaściwe formatowanie może negatywnie wpływać na generowaną odpowiedź. Poznasz także ogólne zasady, które pomogą Ci kształtować instrukcje w sposób, który zwiększy prawdopodobieństwo wygenerowania odpowiedzi, której oczekujesz.
Zero-shot, Few-shot, Chain of Thought czy Tree of Thoughts — to techniki projektowania promptów, dzięki którym znacząco zwiększysz ich skuteczność. I choć każda z nich zwykle opiera się o bardzo proste koncepcje, tak ich praktyczne wykorzystanie nie zawsze jest oczywiste. W tym kursie nie tylko poznasz przykłady ich zastosowania, ale także będziesz w stanie połączyć je z ogólnymi zasadami tworzenia promptów, co jeszcze bardziej ulepszy tworzone przez Ciebie instrukcje. Ciekawostka: Technika Tree of Thoughts w niektórych przypadkach zwiększa skuteczność GPT-4 z 4% do nawet 74%.
Ostatnia część kursu uwzględnia kilka przykładów, których celem jest pokazanie Ci szerokiego kontekstu w którym umiejętność projektowania promptów, umożliwia wyjście daleko poza zwykłe odpowiadanie na pytania i generowanie tekstu. Zobaczysz, jak tworzone przez Ciebie instrukcje mogą zostać wykorzystane w aplikacjach webowych oraz systemach uwzględniających realizujących złożone zadania z pomocą dużych modeli językowych. Mowa tutaj o pamięci długoterminowej, embeddingu danych oraz nawet autonomicznych agentach, zdolnych do posługiwania się narzędziami. Pamiętaj jednak, że wszystkie z wymienionych tematów zostały omówione tylko z szerokiej perspektywy, ponieważ ich faktyczna implementacja, wykracza poza zakres tego kursu.
Ten kurs został zaprojektowany z myślą o osobach chcących rozpocząć naukę projektowania promptów dla dużych modeli językowych, takich jak GPT-3.5-Turbo czy GPT-4. Wcześniejsze doświadczenie oraz zaawansowane umiejętności techniczne czy programistyczne nie są wymagane. Wiedza z tego kursu da Ci podstawy do dalszej nauki projektowania promptów oraz pozwoli Ci uzyskać szerszą perspektywę na temat roli promptów przy codziennej pracy z narzędziami takimi jak ChatGPT czy integracjami dużych modeli językowych z aplikacjami.
Jednym z bardziej kluczowych elementów projektowania promptu jest sterowanie
tym, w jaki sposób będzie sformatowana wygenerowana odpowiedź.
Przykładem takiego formatowania, które jest niezwykle użyteczne i daje ogromne
możliwości, jest zwracanie odpowiedzi w formacie JSON.
Taka odpowiedź może zostać np.
programistyczne wykorzystana do wykonania
połączenia z zewnętrznym API w celu pobrania dodatkowych informacji.
Inaczej mówiąc, możemy w ten sposób sprawić, aby nasza aplikacja nie tylko
była w stanie wygenerować odpowiedź, ale także kontaktować się z różnego rodzaju
zewnętrznymi usługami, a nawet urządzeniami.
Wystarczy tylko połączenie do sieci oraz odpowiednie interfejs.
Oczywiście samo formatowanie może być mniej zaawansowane i np.
dotyczyć generowania listy, wykorzystania
składni Markdown czy nawet składni HTML bądź innego rodzaju kodu.
Niezależnie od tego, jaki dokładnie rodzaj formatowania chcemy uzyskać, jest kilka
ogólnych zasad, które musimy brać tutaj pod uwagę.
Przykładowo, jeżeli chodzi o obiekty JSON, to dokładnie ta informacja może
być wykorzystywana przez aplikację, a więc z punktu widzenia kodu interesuje nas
wyłącznie ten fragment, ponieważ cała reszta nie zostanie wykorzystana.
W związku z tym w naszym interesie leży
to, aby generowane przez model odpowiedzi nie zawierały dodatkowych tokenów, za
które musimy płacić i na które musimy czekać.
Zatem istotne jest zaprojektowanie
promptu w taki sposób, aby zwracał tylko oczekiwany format i nic więcej.
W praktyce coś takiego bywa dość trudne ze
względu na to, że domyślne zachowanie modelu bardzo często sprawia, że w
generowanej odpowiedzi pojawia się dodatkowy komentarz.
Przykładem takiego komentarza jest to
dodatkowe zdanie, które widzieliśmy w tym wcześniejszym przykładzie.
I choć rzeczywiście pozornie dość łatwo
było mi zaprojektować prompt, na podstawie którego została wygenerowana odpowiedź bez
dodatkowego komentarza, to w praktyce sytuacja jest nieco bardziej złożona.
Po pierwsze, struktura generowanego obiektu JSON może być zdecydowanie
bardziej złożona niż tylko jedna właściwość.
Wówczas musimy wyraźnie określić nie tylko to, jakich informacji poszukujemy, ale
także jakie mogą być ich domyślne wartości
w sytuacji, gdy tekst napisany przez użytkownika nie posiada żadnej wskazówki,
która może zostać wykorzystana jako wartość.
Oczywiście tutaj znowu, pomimo tego, że mamy dość rozbudowane zdanie, nadal udało
mi się uzyskać odpowiedź nie zawierającą dodatkowego komentarza, a poza tym
wszystkie wartości, które zostały tutaj zwrócone są jak najbardziej poprawne.
Rzecz w tym, że w momencie gdy będziemy mieć aplikację, bardzo często będzie to
wyglądało tak, że będziemy poruszać się w kontekście Chatu.
Oczywiście z punktu widzenia
programistycznego możemy wykorzystywać ten prompt tylko do pojedynczych interakcji
i szczerze mówiąc jest to całkiem dobra opcja.
Niekiedy jednak będziemy wymagać od modelu tego, aby poruszał się w ramach
konwersacji, ale nadal będzie trzymać się bardzo ściśle tego, co zostało określone w
instrukcji systemowej, a coś takiego nie jest do końca oczywiste.
Przykładowo tutaj mamy konwersację, która w tym przypadku została przetłumaczona na
język angielski, również uwzględniając kolejne interakcje.
Jednak w pewnym momencie umieściłem tutaj zdanie do przetłumaczenia, które
jednocześnie stanowi pewnego rodzaju instrukcję.
Wówczas model zamiast zrealizować zadanie, które zostało zrealizowane w instrukcji
systemowej, po prostu zrealizował moją prośbę.
Coś takiego z punktu widzenia aplikacji jest wprost nieakceptowalne.
W związku z tym musimy zawsze projektować
prompt z myślą o tym, żeby możliwie jak najbardziej zwiększyć prawdopodobieństwo
tego, że model będzie trzymał się zdefiniowanych przez nas zasad.
No i tutaj mamy dokładnie odzwierciedlenie tej samej konwersacji, ale już jej ostatni
fragment różni się, ponieważ nawet pomimo tego, że podałem tutaj potencjalną
instrukcję, to asystent i tak trzymał się swojej głównej roli.
Zatem w tej lekcji chciałem Ci tylko
nakreślić to, że masz ogromny wpływ na to, w jaki sposób będzie zachowywał się model
i w jaki sposób będzie generować odpowiedzi.
W tym wszystkim jednak zachowanie
odpowiedniego formatowania przez całą dyskusję nie zawsze jest takie oczywiste.
Aby zwiększyć szanse na to, że zawsze będziemy otrzymywać oczekiwany format,
zdecydowanie musimy zwracać tutaj uwagę na rzeczy takie jak podkreślanie tego, aby
zachowanie było trzymane przez całą konwersację, a poza tym dobrze jest
przewidywać możliwe interakcje ze strony użytkownika.
Przykładowo tutaj spodziewam się, że do
tłumaczenia będziemy przesyłać różnego rodzaju instrukcje i w związku z tym
bardzo wyraźnie zaznaczam, że nawet jeżeli takowe się pojawią, to model ma je
zignorować i po prostu trzymać się swojej głównej roli.
Oczywiście to, w jaki sposób będzie
konkretnie wyglądał Twój prompt zależy od sytuacji i zadania, które adresujesz.
Dodam do tego, że bardzo dobrym pomysłem
jest nie tylko napisanie instrukcji, ale także podanie jednego lub więcej
przykładów pokazujących to, w jaki sposób model ma się zachowywać.
Dzięki temu zwiększamy
szanse na to, że nie tylko będzie trzymać
się swojej roli, ale także, że będzie poprawnie formatował generowaną odpowiedź.
Technikę czy bardziej zasadę, o której teraz mówię, wykorzystałem także w naszym
wcześniejszym prompcie, z pomocą którego zastosowałem jedno z zachowań modelu
dotyczące niechęci związanych z wyrażaniem swoich emocji.
Dokładnie w ten sam sposób możemy
wykorzystać te przykłady po to, aby jeszcze mocniej podkreślić naszą
instrukcję. Jeżeli chodzi o stosowanie takich przykładów
o tym będziemy sobie jeszcze mówić, a do tego czasu chciałbym jeszcze tutaj
ponownie zwrócić uwagę na LangChain, który od strony programistycznej może
zostać wykorzystany do tego, aby parsować odpowiedzi generowane przez model.
Przykładowo tutaj mam wygenerowany przez ChatGPT fragment kodu, który jest w
stanie pobierać obiekt JSON z wygenerowanej przez model odpowiedzi.
Dodatkowo w niektórych sytuacjach bardzo
pomocne okaże się precyzyjne określanie schematu obiektu JSON, który ma zostać
wyciągnięty z odpowiedzi oraz także ewentualna możliwość obsługi błędu.
Chodzi o to, że czasem może zdarzyć się tak, że pomimo dobrze napisanego projektu
uzyskamy odpowiedź, która nie będzie wystarczająca do tego, aby zbudować np.
obiekt JSON.
Wówczas będziemy w stanie ponowić zapytanie i wykonać nieco inny prompt,
którego zadaniem będzie podjęcie próby naprawienia tej sytuacji.
Oczywiście zdaję sobie z tego sprawę, że
temat dotyczący formatowania obiektu JSON może być nieco zbyt techniczny
a zatem chciałbym tylko wrócić do naszego
głównego wątku, czyli zasadniczo dążenia do tego, aby generowana odpowiedź
wyglądała dokładnie tak, jak tego oczekujemy.
Zawsze bierz tutaj pod uwagę elementy takie jak bardzo konkretna instrukcja,
która dąży do tego, aby zwrócić tylko oczekiwaną odpowiedź bez dodatkowych
komentarzy, a także to, aby podać kilka przykładów, aby model był w stanie
wychwycić różne schematy i tym samym zwiększać swoją skuteczność.
Jeżeli chodzi o pracę z odpowiednim
formatowaniem, myślę, że do tego będziemy jeszcze wracać.
Do tego czasu bardzo zachęcam, aby podjąć przynajmniej kilka prób polegających na
tym, aby napisać taki prompt, który będzie zwracał np.
numerowane listy bądź tłumaczenia bez jakiegokolwiek dodatkowego komentarza.
Jednocześnie jako pewnego rodzaju utrudnienie możemy np.
napisać prompt, który z jednej strony
będzie tłumaczył wiadomości dostarczone przez użytkownika, ale jednocześnie będzie
dawał mu możliwość wydania także polecenia polegającego np.
na tym, aby tłumaczenie było wygenerowane w określonym stylu, np.
mowie potocznej.
Myślę, że teraz śmiało przed uruchomieniem
kolejnej lekcji możesz spróbować napisać taki prompt.
Powodzenia!