#136 Prosto i praktycznie wyjaśniamy: AI

2025, Jan 10

“Prosto i praktycznie wyjaśniamy: AI” to nie slogan, a misja! Patoarchitekci dekonstruują świat LLM, GenAI i tokenizacji. Przygotujcie się na deszcz akronimów i burzę mózgów!

Od promptów po embeddingi, od RAG do AI Agents. Poznajcie 30 kluczowych pojęć AI, które zmienią Wasze integracje z LLM. Chunking, reranking i function calling już nie będą czarną magią!

Chcesz być AI-native? Posłuchaj i zaimplementuj! Niech Twój kod szepce do modeli, a chatboty śnią o elektrycznych owcach. Patoarchitekci czekają – odpal ten podcast szybciej niż LLM generuje bzdury!

Słuchasz Patoarchitektów dzięki PROTOPII – firmie, w której Łukasz i Szymon działają na co dzień, wspierając zespoły IT na każdym etapie: od projektowania, przez wdrożenia i migracje, aż po optymalizację i zabezpieczenia. Oferujemy też mentoring i szkolenia dostosowane do potrzeb każdej firmy, niezależnie od wielkości. Sprawdź nas: protopia.tech

Discord 👉 https://discord.gg/78zPcEaP22

Linki i ciekawe znaleziska:

Transkrypcja

Szymon Warda: Cześć! Słuchacie Patoarchitektów. Prowadzą Szymon Warda…

Łukasz Kałużny: I Łukasz Kałużny. Wszystkie linki do tego odcinka znajdziecie na Patoarchitekci.io lub gdzieś tu na dole, jak zawsze ogarniecie. To co? Witamy po krótkiej przerwie i w nowym roku.

Szymon Warda: Tak, cóż za wspaniałej przerwie.

Łukasz Kałużny: Przynajmniej nie nagrywamy tego o ósmej.

Szymon Warda: Normalnie wracamy o siódmej. Dobrze Łukaszu, o czym dzisiaj?

Łukasz Kałużny: Dzisiaj słownik pojęć, o którym wam wspominaliśmy, że przygotowujemy, a dokładniej słownik pojęć użycia gotowych LLM-ów, czyli Large Language Models. I patrzyliśmy na to i zebraliśmy pojęcia, które występują przy tym, kiedy wykorzystujemy te LLM-y, żeby zbudować aplikację, chatbota, jakieś integracje, inne tego typu elementy. Czyli to co też od Was się pojawia w komentarzach: jestem DevOpsem i nie do końca rozumiem czym są te embeddingi, RAG-i, o których ciągle wspominacie. Więc to z naszego takiego codziennego doświadczenia wybraliśmy 30-32 pojęcia, z którymi się zetkniecie i z którymi my na co dzień w Protopii mamy do czynienia, kiedy tłumaczymy całą tą budowę. To będą najczęstsze pojęcia, które traficie i postaramy Wam się pokazać przez flow budowania aplikacji czym są te podstawowe pojęcia.

Szymon Warda: Czyli jednym słowem nie silimy się na to, żeby zbudować jeden ostateczny, finalny, końcowy i najlepszy słownik o LLM-ach, tylko podchodzimy do tego praktycznie.

Łukasz Kałużny: Tak. No i jak lecimy to chyba podstawowe pojęcia, czyli to co wrzucamy do środka aplikacji, czyli pierwszym pojęciem, z którym się zetkniecie, to będzie prompt. I najbardziej prosto określamy taką instrukcję, polecenie, które przekazujemy do tego modelu AI-owego. Czyli ono określa kontekst, co ten model ma wykonać. I jeżeli popatrzycie to może być pytanie, jakaś prośba, szczegółowa instrukcja, w zależności co, z czym pracujemy. No i chyba teraz oddam Tobie Szymon głos, czym jest tak naprawdę ten prompt pod spodem i co się z nim dzieje.

Szymon Warda: No dobrze, no to teraz trochę wyjdziemy od problemu generalnie, no bo komputery jako komputery to wolą operować na liczbach, a nie na tekście, tekst jest liczbą, więc musimy zamienić. No i to mamy teraz problemy takie, jak zamienić ten tekst, który pisaliśmy, na liczby? Mamy kilka podejść, możemy na przykład zrobić taki myk, że na przykład podzielić per każdy znak unicode’owy, wtedy plusy są tego takie, że mamy skończony relatywnie dość mały zbiór tych możliwości, kombinacji. Czyli załóżmy A to jest jedynka i tak dalej, sobie przypisujemy, więc tego jest relatywnie mało, zakres unicode’u, więc niedużo. Problem jaki w tym momencie się tworzy jest taki, że nie mamy kontekstu tak naprawdę, nie mamy kontekstu słowa, mamy kontekst pojedynczego słowa, ale nie zdania i tak dalej. Więc jest to mało wygodne. Drugą skrajnością, w którą możemy wejść, to załóżmy kontekst całego słowa. Czyli dzielimy per białe znaki. Super fajnie, mamy kontekst całego słowa, łatwiej się operuje i tak dalej, ale słów mamy drastycznie dużo powiedzmy sobie tak delikatnie. I nagle możliwość tej kombinacji, zakres tych liczb, które musimy przypisać jest bardzo, bardzo duży i co więcej, bardzo łatwo będzie nam coś ominąć, jeżeli jakiegoś słowa nie widzieliśmy. Jest to zero-jedynkowe, czyli jak słowo nie występowało, to nasz model nie będzie mógł o tym myśleć. No więc teraz znajdźmy jakiś środek. Możemy tokenizować to, czyli dzielić na takie tokeny, elementy, którym nasz model będzie przypisywał jakąś wartość liczbową, to są właśnie tokeny. Element, który dzieli na te tokeny, to właśnie jest tokenizator, na poziomie takim pośrednim, czyli podsłowie tak naprawdę. Czyli w tym momencie dzielimy na pewne fragmenty każde słowo, czasami one zahaczają o znaki puste, czasami to są całe słowa, czasami zahaczają o słowo, znak pusty i przejście do kolejnego słowa. Modele są bardzo różne i tokenatory są bardzo, bardzo różne. Ale jakie mamy w tym momencie plusy? Po pierwsze, obsługujemy słowa, których nie znamy, bo one mogą składać się z kilku tokenów. Po drugie, rozumiemy kontekst całego słowa, ale też czas częsty kolejnego słowa. Rozumiemy znaki puste i specjalne, które dają się mimo wszystko i zbiór tych danych możliwych jest relatywnie niewielki i kontrolujemy go. Więc tym są właśnie tokeny i tokenizatory.

Łukasz Kałużny: Ja bym chyba dodał jeszcze taki wizualizacyjny, może wrzucimy tu obraz, ci, którzy oglądają wideo, słowo friendship, bo świetnie to pokazuje te rozbicie słowa friendship na dwa tokeny friend i ship. Tutaj pokazujące co to jest.

Szymon Warda: Tak, bo widzę, że w ogóle użyłeś wkładu z tej strony, która właśnie pokazuje jak działają tokenizatory. [niesłyszalne 00:05:00]

Łukasz Kałużny: To jest z platform.openai.com/tokenizer. Wrzucimy wam linka i też na ekranie możecie zobaczyć. Fajną rzeczą, która jest, dodali to, że pokazuje ID tokenu. Czyli można sobie pod słowem zobaczyć teraz jaki token odpowiada danemu słowu, jaki to jest zbiór tokenów.

Szymon Warda: Dla mnie dużo fajniejsze w tej stronie jest to, że on pokazuje jak się różne tokenizatory dla różnych modeli, bo tam w prawym górnym rogu jest dropdpwn, który pokazuje jak który model tokenizuje, jak to w ogóle wygląda. I na podstawie różnic fajnie faktycznie widać podejście do tego. Ogólnie będzie w opisie, zerknijcie jak będzie was interesowało.

Łukasz Kałużny: Dobra.

Szymon Warda: Sporo daje.

Łukasz Kałużny: I teraz lecimy, bo pojawia się coś takiego jak trzy pojęcia, one są ze sobą zebrane, jeżeli mówimy. Są input tokeny, output tokeny i context window. Czyli input token to jest liczba tokenów, które my możemy wysłać w tym prompcie do środka tego modelu. Czyli jeżeli coś tam wpisujecie, dołączacie do tego dane, to jest jakaś ilość. Czyli mówimy, że np. model może przyjąć osiem tysięcy, co jest teraz takim standardem, osiem tysięcy tokenów wejściowych i potem możemy powiedzieć…

Szymon Warda: Co jest też często elementem wyceny. Modele, które mają dłuższą liczbę tokenów przyjmują często są po prostu droższe.

Łukasz Kałużny: Tak i potem macie output tokeny, czyli ile tokenów ten model może z siebie maksymalnie wyrzucić. I teraz ważną rzeczą, pojawia się coś, co nazywamy context window, czyli ile możemy zrobić input tokenów i output tokenów, żeby przetworzyć, czyli ile model z tego wykorzysta. I rzecz, z którą trzeba się pogodzić, bo często słyszymy, że jest jakiś kontekst, jakaś historia jak używamy chatGPT czy CloudA’ę. I to jest trochę kłamstwo dla dzieci, ponieważ tak naprawdę za każdym razem model jest bezstanowy, jego odpowiedzi są bezstanowe, więc my do środka wysyłamy historię poprzednich pytań i odpowiedzi, jako załączając w specjalny sposób, że: hej drogi modelu, teraz mam takie pytanie, ale ja z tobą rozmawiałem o tym. I teraz suma input i output tokenu nie może przekroczyć tego okna kontekstowego w sumie. To jest istotny element.

Szymon Warda: Naprowadziłeś teraz na completion, bo teraz skoro powiedziałeś jak to działa, że faktycznie model generuje token po tokenie tak naprawdę, dzięki czemu tak też pokazuje ładnie to chatGPT, jak ta strona, wchodzimy, ładnie generuje sobie po kolei. No to właśnie w tym momencie dla modelu nie ma znaczenia czy on generuje kolejny znak na zasadzie tego, że to było: ej, uzupełnij mi co będzie kolejne czy po prostu jako faza tej pętli i odpowiedzi tak naprawdę. I wchodzimy właśnie w completion, czyli ten element, że on dogenerowuje token po tokenie i możemy korzystać z tego, że go uzupełni, albo po prostu tak w ogóle generuje odpowiedź. Dobrze, message, message Łukaszu.

Łukasz Kałużny: Tak, jak jesteśmy, powiedzieliśmy sobie, że on wybiera najbardziej prawdopodobne następne tokeny, to completition…

Szymon Warda: O tym jeszcze powiemy.

Łukasz Kałużny: Tak, to completition jest takim podstawowym właśnie efektem, który używaliśmy. I pojawiło się teraz w wielu modelach coś, co nazywamy chat completition. I tutaj chodzi o to, że ten sposób wysłania danych do modelu jest ustrukturyzowany w message. I to, co my sobie wpisujemy w chat’cie, to nazywamy najczęściej user prompt, user message. I to jest po prostu nasza wiadomość, którą my wysyłamy do modelu, która zawiera jakieś polecenie dane do przetworzenia. Często może zawierać poprzednie wiadomości, które stają się właśnie częścią kontekstu, żeby dawał to, że ten model myśli. Jeżeli popatrzycie, to odpowiedź z takiego modelu nazywamy assistant message, czyli to co nam model odpowiada. I pod spodem pojawia się jeszcze trzecie, które jest najważniejsze, system message, system prompt, czyli polecenia jak ten model tak naprawdę ma się zachowywać. Z założenia tam wrzucamy te wszystkie elementy, które mówią w jaki sposób masz reagować. Czyli jesteś przyjaznym asystentem, która jest najczęstsza. System prompt od chatGPT czy CloudA to jest kilka stron tekstu w jaki sposób ma się zachowywać. Moglibyśmy powiedzieć, jeżeli mamy jakieś integracje w takim modelu, do czego potem przejdziemy pod koniec, to moglibyśmy powiedzieć: masz dostępne narzędzia XYZ. Jak użytkownik zapyta się o to co jest na narzędziu, to wyszukaj to np. w Bingu czy w Wikipedii, tych słów kluczowych i to nam steruje całym wykorzystaniem.

Szymon Warda: Teraz przejdźmy kawałek do tego co już zahaczyliśmy, czyli tak naprawdę jak te modele potuningować. Ogólnie zmianę parametrów modelu, to że jak on ma się zachowywać, osiągamy przez hiperparametry, hyperparameters. I to mamy takie trzy najważniejsze: temperatura, top K i top P. O co w ogóle z tym chodzi? No jak już powiedziałeś i powiedzieliśmy sobie, że wybieramy sobie jakieś tokeny. No ok, ale wiemy, że np. po słowie kasza będziemy mieli cały zbiór: mamy gryczana, manna i pozostałe rzeczy. No i teraz problemy jakie mamy, to jest to, jak ten model będzie wybierał. Po pierwsze, ile będzie generował możliwości. Po drugie, jakie słowa będzie wybierał. Po trzecie, które słowo wybierze finalnie. Dobra i do tego właśnie mamy, wyobraźmy sobie, że jesteśmy w restauracji. Mamy dwie metody samplingu tak naprawdę, takie skrajności. Gridy, to jest takie, że wykorzystamy zawsze najbardziej prawdopodobną odpowiedź, czyli ta która z reguły występuje. Czyli załóżmy, jak jesteśmy w jakiejś restauracji będziemy zamawiali zawsze to samo najbardziej popularne danie. Skrajnością drugą jest random sampling, czyli totalna losowość. To powoduje, że token jeden może nie mieć porównania z drugim po prostu. Z tych wszystkich tokenów, które dostaliśmy, wybieramy którykolwiek. No i teraz jak to kontrolować? Mamy top K i top P. Zacznijmy od top K. Top K, przede wszystkim dostajemy tokeny. Co robi top K? Top K wycina, czyli bierze te tokeny, bierze K tokenów, które dostał. No okej, potem dalej to leci do top P. Co robi top P? To P jest ogólnie sprytne, bo top P robi taki myk, że idzie po tych tokenach po kolei sortując po prawdopodobieństwie i zbierze token po tokenie, tokenie i sumuje ich prawdopodobieństwo, aby sumaryczne prawdopodobieństwo, czyli masa probabilistyczna, była powyżej wartości P. Czyli zbieramy słowa, które są prawdopodobne do pewnego stopnia. No i dobra, kolejnym elementem to jest temperatura. Co robi temperatura? Temperatura robi taki myk, że może być poniżej 0 lub powyżej 0. Jeżeli patrzymy na to co dalej model robi z naszymi wagami probabilistycznymi, to jest to, że on bierze tą wagę i dzieli przez temperaturę. Jaki jest tego efekt? Efekt jest tego taki, że jeżeli mamy temperaturę poniżej zera, czyli dzielimy przez wartości od zero przecinek jakaś wartość do jedynki, to w tym momencie rozszerzamy zakresy odległości tych słów. Czyli ten model, mniejsza szansa, że zahaczy jedno słowo o drugie, bo one są bardziej rozdzielone, są bardziej deterministyczne, wszystko fajnie. Natomiast jeżeli dzielimy przez wartość większą od jedynki, właściwie można tak powiedzieć, to w tym momencie co się dzieje? To w tym momencie zbliżamy te wartości do siebie. I teraz łączymy, teraz połączmy to razem, jak to wygląda. Jak działa taki model? Pierwsze, to on liczy prawdopodobieństwo dla każdego tokenu, potem bierze pod uwagę temperaturę i zmienia te finalne wagi. Potem aplikuje top K, czyli wybiera top K najbardziej prawdopodobnych tokenów. Potem aplikuje top P, czyli sumuje aż pewna wartość będzie osiągnięta. Potem normalizuje to prawdopodobieństwo, a na koniec wybiera finalny token tak naprawdę i tak właśnie go otrzymujemy.

Łukasz Kałużny: Tak, to jest całość, jak to wygląda pod spodem, a najczęściej operujemy na testach na suwaku od temperatury po prostu, jeżeli popatrzymy.

Szymon Warda: Z reguły tak.

Łukasz Kałużny: I zwykle efekty będą wystarczające. Jak przechodzimy do efektów, teraz pojęcie, na którą jest klauzula sumienia, prawdopodobnie bardziej opłaca Wam się napisać dobrze zrobienie dobrego prompta i zadbanie o dobrego jakościowego RAG-a, czyli dostawianie tych danych z naszych dokumentów i innych rzeczy, niż fine tuning, czyli rzecz, która jest mokrym snem: wytrenujemy własny model. Jeżeli mówimy o gotowych LLM-ach, to fine tuning to jest dostarczanie specyficznego właśnie zestawu danych treningowych dotyczącego naszego problemu, który my mamy. I taki format najczęściej dostarczamy w postaci JSON L, czyli to jest multiline, taki JSON, w którym będzie pytanie i oczekiwana odpowiedź. I on nadpisuje to, czego model jest nauczony. Tylko żeby miało to sens w zależności od tego, który gotowy model wybieramy, to trzeba dostarczyć dobrych jakościowo danych, takich pytań i odpowiedzi, można powiedzieć, że od groma.

Szymon Warda: Dużo, bardzo dużo.

Łukasz Kałużny: Tak. I potem przez to, że te modele się zmieniają, np. jeżeli korzystamy z gotowych modeli jako API, to też trzeba pamiętać o całym procesie aktualizowania tego data setu, sprawdzania, itd. Stąd z naszego doświadczenia na początku lepiej się skupić na dobrym zbudowaniu właśnie promptów i dobrym ładowaniu tych danych i trzymaniu. Więc chyba przejdźmy sobie do tego czym jest tak naprawdę prompting.

Szymon Warda: A dokładnie prompt engineering. Ja tu bym powiedział, że prompt engineering to jest temat większości szkoleń online obecnie, że tak powiem. No ale tak naprawdę to nad tym się dużo, dużo, dużo takich gorszych jakościowo szkoleń skupia. Ale okej, ale o co chodzi? Mi to bardzo mocno przypomina fakt, jak jeszcze wiele, wiele lat temu, ponieważ już nie jesteśmy młodymi ludźmi, mimo tego co byśmy chcieli uważać, to jest to jak się uczyliśmy całej składni zapytań googlowych, co które znaki oznaczają i jak to w ogóle zrobić. No to czym jest prompt engineering? Prompt engineering jest procesem dopasowywania zapytania w języku naturalnym do modeli LLM-owych, naszych dużych modeli, żeby otrzymać określony wynik. Dostosowywanie słów, dostosowywanie formy, dostosowywanie wszystkiego co potrzebujemy, żeby ten model zachowywał się prawidłowo tak naprawdę. I tak co Łukasz powiedział, może to nie brzmi seksownie, bo wiadomo, że fine tuning i trenowanie modelu brzmi dużo lepiej i każdy by się chciał tym zająć, ale realnie prompt engineering to jest ten obszar, który wam da najwięcej wartości. Mało kto będzie potrzebował wejść w fine tuning tak naprawdę.

Łukasz Kałużny: Tak. I jeżeli teraz pójdziemy to tam pojawi się kilka technik podejścia do tego. Pierwszą jest Zero-Shot Learning. Czyli po prostu wpisujemy instrukcję, mówimy co ma zrobić, opisujemy precyzyjnie zadanie, ale nie mówimy jak ma je wykonać, cokolwiek i liczymy, że ta odpowiedź będzie prawidłowa. Problem z tym jest taki, że nie mamy w miarę stabilności tej odpowiedzi. Czyli to jest taka świetna rzecz, że bierzemy i zaczynamy sobie dyskusję na chat’cie, ale raczej nie oczekujemy tego w aplikacji. I tu pojawia się taki złoty środek, który nazywamy Few-Shot Learningiem. Czyli technika, gdzie to wszystko dokładnie opisujemy co ma być zrobione, ale oprócz tego w tym prompcie dostarczamy od dwóch do pięciu przykładów jaki ma być wynik tego zadania, jaki ma być efekt. I to jest taki złoty środek pomiędzy: wrzucę coś na pałę i będzie działało, a pomiędzy wrzucę tonę tokenów na inpucie, żeby dokładnie doprecyzować. I ten Few-Shot Learning będzie najczęściej wykorzystywaną techniką. I słuchajcie i teraz taka rzecz, którą wykorzystuję w codziennej pracy, bo słuchajcie prompt engineering to nie jest żadna, żadna magia. I to, co zwykle polecam robić, możecie też to zobaczyć na naszym Discordzie, którą pokazuje tam Pato od kuchni, jak nasz podcast jest zautomatyzowany pod spodem, jeżeli chodzi o wydawanie.

Szymon Warda: Ja to nazywam kanałem: gdzie Łukasz tłumaczy, czemu coś nie zadziałało jak powinno zadziałać.

Łukasz Kałużny: Tak albo wylewa swoje żale, że coś się zmieniło w modelach Anthropicu. Ale wracając do tego, to słuchajcie, taki prompt, jeżeli macie przykładowy szablon prompta i chcecie go dopieścić, to ja go wrzucam właśnie w CloudA’ę czy w chatGPT i proszę go o rozbudowanie i doprecyzowanie. Czyli mam swój wstępny prompt i potem tuninguję go sobie LLM-em, żeby był lepszy, w szczególności dopracowuję… Najgorszą rzeczą są te przykłady. Więc prościej jest nam powiedzieć: wygeneruj mi kilka przykładów i powiedzieć co ma w nich zmienić, żeby było dobrze i dopiero je potem wykorzystać. To jest taki złoty środek w tej technice budowy, więc nie musicie lecieć na wszystkie kursy prompt engineeringu, bo sprowadzą się do, jak to się ładnie mówi, meta prompt engineeringu.

Szymon Warda: Dobrze, to teraz lecimy, bo mówiłeś, że właśnie Few-Shot Learning jest złotym środkiem. Ja bym powiedział, że nie do końca. Bo czemu, o co chodzi? Mamy cały Chain-of-Thought, CoT. O co w tym chodzi? Chodzi o to, że pewne zdania lepiej jest rozbijać na mniejsze problemy. Wiem, wielka oczywistość została wypowiedziana. Co jest w tym momencie ważne? To jest to, że to się ładnie skupia, załóżmy zadając modelowi pytania odnośnie obliczenia czegoś, wywnioskowania czegoś, gdzie ta liczba kroków, nawet o których byśmy my myśleli, będzie trochę większa. I Chain-of-Thought właśnie to jest takie powiedzenie modelowi: okej, to wpierw rozbij to na poszczególne kroki, a później odpowiedz na każdy krok po kolei biorąc pod uwagę wynik poprzedniego kroku. I teraz jak to rozbicie na kolejne kroki może się odbywać? Ano może odbywać się na dwa sposoby. Albo to model sam wpierw rozbija, albo właśnie korzystamy z Zero-Shot Learning, gdzie dajemy modelowi, załóżmy że: dla takiego problemu rozbijasz na takie kroki i on dzięki temu doucza się, wie już co zrobić tak naprawdę. Bardzo przydatne Chain-of-Thought jest w kontekście tego zrozumienia jak model do tego doszedł. To jest przydatne, nawet dla tego ludzkiego elementu. I ma zastosowanie właśnie głównie przy takich rzeczach logicznych, przydaje się bardzo.

Łukasz Kałużny: Tak i tutaj idąc za tym pojawia się coś co nazywamy prompt chainningiem, czyli budujemy sobie łańcuch promptów. To jest właśnie taka już implementacja faktycznie. Idąc na przykład, jak ja to wykorzystuję przy podcaście. Rozdziały, które widzicie, np. jak teraz oglądacie ten odcinek na YouTubie, nie są generowane przeze mnie w żaden sposób, czy Szymona, czy przez człowieka, tylko dokonuje tego CloudA 3.5 Sonet, czyli model od Anthropica jako API. I on np. gdybyśmy dali mu to za jednym razem, dali mu po prostu transkrypcję, zrobiłby to beznadziejnie. O tak, co zresztą próbowałem zrobić w jednym prompcie. Więc tutaj co się dzieje, to tak jak tworzymy na przykładzie naszego podcastu, wrzucamy transkrypcję ze znacznikami czasu w formie SRT, do której jest pierwsza sekwencja, to jest pogrupuj wszystkie wątki, które były w podcaście w takie małe, daj znaczniki kiedy się zaczął, kiedy skończył, dodaj cytaty. Następną częścią, która się dzieje, to pogrupuj to w duże rozdziały. I ostatnim promptem, który wychodzi potem, korzysta z tego, jest wygeneruj mi napisy już w postaci HH:MM:SS plus nazwa tego rozdziału. Czyli zobaczcie całe te zadanie, tak jak Szymon powiedział o Chain-of-Thought i rozbijaniu, na koniec technicznie robi się to, że wygeneruj mi takie np. rozdziały do rozdziały. To są trzy prompty, który każdy następny korzysta z poprzedniego polecenia. Więc ja wysyłam trzy strzały do takiego API LLM-a. Pierwsze jest wyciągnij mi wszystkie wątki w podcaście, następnie pogrupuj je logicznie, a trzeci dopiero wygeneruj znaczniki czasowe na podstawie tego, co ten ostatni ci zwrócił.

Szymon Warda: Dobra, to lecimy do RAG-ów, czyli elementu z którego będziecie korzystali, na pewno będziecie mieli najwięcej wartości.

Łukasz Kałużny: Tak, może jeszcze czym jest pojęcie RAG? Czym jest pojęcie RAG, bo zawsze używamy skróconego.

Szymon Warda: To Łukasz daj, wytłumacz, bo ty bardzo, bardzo lubisz.

Łukasz Kałużny: Retrieval Augmented Generation. Ale pójdźmy teraz w detale, bo taka jest właściwa nazwa, a chodzi o detale tej techniki.

Szymon Warda: Dobra, to zacznijmy od podstaw, podstaw, żebyście rozumieli. Mówiliśmy sobie, że maszyny lubią operować sobie na liczbach. No okej, mieliśmy sobie te tokeny. Tokenom możemy przypisać jakąś liczbę. Ale to nie jest takie proste, ponieważ o ile w najprostszej wersji moglibyśmy zrobić coś takiego właśnie, że tokenowi przypisujemy liczbę, to realnie w tym momencie wracamy do czego? Wracamy do Lucyny czyli Solara czyli Elastica. Upraszczam bardzo mocno, proszę nie rzucajcie kamieniami w komentarzach, bo to jest uproszczenie. Wiem, że Lucyna działa odrobinkę inaczej, ale bear with me, jak to mówią Anglicy. Czyli pewnemu tokenowi przypisujemy jakąś liczbę. Okej, byśmy zrobili to tylko w ten sposób, no to fajnie, ale tracimy ten kontekst, który się dzieje dookoła. A właśnie na tym kontekście nam zależy. Więc chodzi właśnie o to, żeby zamienić sensownie i z pewną wartością nasz token na liczbę. Jak to możemy w ogóle zrobić? Może cofnijmy się może wcześniej, czemu właściwie to robimy? Ano dlatego, żeby móc powyszukiwać słowa podobne i słowa, które występują koło siebie, słowa, które występują jeden po drugim, stworzyć takie kohorty gdzie one, które występują. Dobra, to teraz żeby wam dać kontekst jak się rozwijał ten sam proces. On się rozwijał bardzo mocno, chociaż ze względu np. na Lucynę. Mamy tą zamianę. Np. najprostsza, najstarsza wersja, mamy prosty word embedding, czyli robimy Word2vec albo Gloss. Ogarniamy kontekst konkretnego zdania, może dokumentu. Fastex ogarnia odmianę, czyli tzw. morfologizację, czyli ucinamy polskie końcówki fleksyjne. Teraz mamy Context Word Embeddings. Tam mamy te modele, które zaczniecie już poznawać po nazwach, które czasami występują. Mamy ELMo, czyli on w tym momencie dodaje kontekst do konkretnego tokenu, daje kontekst całego swojego zbioru w którym występuje. Przykład czemu to jest ważne i np. moment, gdzie np. Elastic albo Solar wymiękają. Polskie słowo “soli”. Tak jak mówimy np.: nie ma soli. Nie wiemy czy chodzi o sól, jako przyprawę, czy chodzi o rybę. Dopiero z tym kontekstem globalnym jesteśmy w stanie dowiedzieć się o czym właściwie była mowa. Dobra, potem mamy jeszcze BERT-a, który ogarnia co jest przed i co jest po danym słowie, czyli dodaje do kontekstu. Mamy też GPT. Brzmi znajomo, prawda? No to on używa tylko to co jest po lewej stronie słowa, prawda? Bo on generuje kolejne, kolejne, kolejne, tokeny. Tych sposobów jest dość sporo. Jest Sentence Embedding, jest Document Embedding, jest Transformer-based Embedding, jeszcze są Combined Approaches, czyli łączymy różne podejścia i mieszamy tymi embeddingami. Ale cały clue polega właśnie na tym, jak złapać kontekst tak, żeby dalej słowo było czytelne, o czym opisujemy, ale też mieć jak największy kontekst tego, co byśmy chcieli opisywać tak naprawdę.

Łukasz Kałużny: Tak i w zależności potem od tego API, które wybierzecie do budowania tych osadzeń w bazie, budowy tych wektorów, one mają już swój model wypracowany tego, jeżeli na to popatrzymy.

Szymon Warda: No więc teraz podejdźmy do tych wektorów. To jest po prostu wektor, który ma jeden rząd i bardzo, bardzo, bardzo wiele kolumn. I teraz gdzie te parametry przechowujemy? W bazach wektorowych. Dlatego przez ten cały rok mieliśmy tyle informacji, że każda baza była bazą wektorową, od Postgresa przez Redisa przez każdą inną bazę. A no teraz czemu właściwie mamy w takim razie te wektory? Z bardzo prostego powodu, ponieważ na wektorach bardzo łatwo liczy się podobieństwo. Czyli bierzemy takie dwa wielowymiarowe wektory sobie opisujące jak to się zachowuje i liczymy między nimi podobieństwo. Najprostszą formą liczenia prawdopodobieństwa jest liczenie cosinusa pomiędzy tymi dwoma wektorami. Wiem, że teraz słyszy się co innego, itd., ale upraszczam, po prostu liczymy kont rozwarcia między wektorami.

Łukasz Kałużny: Tak, najprościej też zwizualizować wektor czasami na mapie i pokazać go. To jest w bardzo uproszczony sposób, że słowa są rozrzucone po takim wykresie 3D i są to punkty w przestrzeni. Ja to bardzo Szymon upraszczam, ale żeby zwizualizować to na początek.

Szymon Warda: Ja bardziej upraszczam to na poziomie takiego dwuwymiarowego, czyli mamy trójkąt. Jak wektory są do siebie podobne, jeżeli to są te same wektory, to generalnie kąt między nimi będzie 0 tak naprawdę. Jeżeli będą całkowicie inne, no to mamy kąt dziewięćdziesiąt stopni, prawda? I operujemy w tym zakresie. Dlatego właśnie tak bardzo mówi się tak dużo o bazach wektorowych, żeby właśnie liczyło się prawdopodobieństwo. Nie tylko prawdopodobieństwo liczymy i do czego można to zastosować? Bo to trochę więcej można zrobić. Możemy znalezienie obiektu podobnego, do tego, który podaliśmy, czyli dajemy jakiś dokument, mówimy: ej znajdź mi podobne. Brzmi znajomo. Możemy też pójść na poziomie dokumentu, możemy powiedzieć: to słowo, znajdź mi podobne dokumenty gdzie występują podobne słowa, podobne wektory. To samo korzystają oczywiście modele LLM-owe, one zapodają i mówią daj mi kolejne. Szukanie anomalii, czyli dajemy dokumenty i mówimy: znajdź mi jak najbardziej inny albo te, które odstają od siebie. Wyszukiwanie różnych rzeczy, których nie powinno być w danym zbiorze. Może go trochę być. Kolejny to jest duplikacja, czyli np. całe wyszukiwanie plagiatów. No to właśnie na tym polega. Jeżeli dwa dokumenty są dzisiaj bardzo, bardzo podobne to znaczy, że coś tam musiało zajść tak naprawdę. Albo ewentualnie usuwanie, to co my sami robimy, usuwanie dokumentów podobnych, usuwanie danych, które duplikują się w pewien sposób.

Łukasz Kałużny: Mówimy teraz cały czas o dokumentach, jak załadować te dane do bazy wektorowej. I przed tym musimy sobie powiedzieć coś, co się nazywa Document Chunking. Chodzi o to, że mamy jakiś określony rozmiar, który jest sensownie w tej bazie danych przechowywać. Czyli to będzie np. jeden Chunk się gdzieś przyjmuje w taki defaultowy ostatnimi czasy, że jest to tysiąc tokenów, które przechowujemy. To teraz bardzo upraszczam. Czyli mówimy sobie, że jest jeden Chunk, który jest embedowany, to będzie tysiąc tokenów. Jak popatrzymy, chodzi o to, żeby dokument, który się nie mieści w tym zakresie podzielić. Czyli pozwala nam przetwarzanie dłuższych modeli niż mamy, możemy przechować, przetworzyć przez model embedingowy, czy przetworzyć, wrzucić do naszego potem i wykorzystać to w naszym prompcie, który wysyłamy do tego modelu. Jeżeli popatrzymy, to to wszystko wymaga odpowiedniej strategii podziału tego tekstu. I najprostsze podziały to jest po prostu: weź mi to, potnij po liczbie tokenów, czyli nie patrz na kontekst i potnij nam to np. co tysiąc tokenów. Mogą być bardziej rozbudowane strategie, które rozumieją w jakiś sposób semantykę. Np. to dobrze wygląda w stosie Microsoftowym i w Azure AI Searchu, który jest bazą wektorową również i posiada te wszystkie integracje i mówi np. przetwarzając z wykorzystaniem OCR-a dokument: zamień mi np. ten dokument PDF-a na Markdowna i potnijmy to po strukturze paragrafów. Zostawię Wam linka, będziecie mogli zobaczyć, też to opisywaliśmy w poprzednich shortach, taki duży sample jak jest cięty dokument. Ale chodzi o to, żeby go jak najbardziej inteligentnie pociąć.

Szymon Warda: No i mamy jeszcze opcję tak naprawdę chunk overlapping, czyli mówimy, że te chunki nie są zero-jedynkowe, tylko zachodzą na siebie.

Łukasz Kałużny: Tak, ale do tego jeszcze pójdziemy potem, po paru innych pojęciach, jako takie zamknięcie pod klamrą całości. Dobra, jak popatrzycie sobie, to jak mówimy o tym podziale, to jest cały retrieval pipeline, czyli ładowania tych danych i wyciągania ich. I dzielimy go na dwie fazy. Jedna to faza ładowania, preprocessingu i załadowania do bazy wektorowej, a druga jest faza inferencingu, czyli wykorzystania tego już faktycznie z LLM-em. I faza ładowania, to będzie po pierwsze, przetworzenie dokumentów, jak je wrzucacie na koniec dnia na tekst i podzielenie tego na Chunki. Czyli macie Wordy, PDF-y, Excele, cokolwiek i przerobienie tego na tekst i podzielenie. Następnie dla każdego z tych Chunków generujemy embeddingi właśnie modelem embeddingowym, jakimś API. I na końcu zapisujemy to w bazie danych jako wektor. I teraz bardzo ważne z zapisem, w bazie danych zapisujemy kilka elementów. Czyli zapisujemy zazwyczaj skąd pochodzi ten dokument, czyli jaka była nazwa dokumentu, nazwa pliku, kiedy. Zapisujemy sam Chunk, czyli ten oryginalny tekst, który użyliśmy i do tego embedding. W samplu Microsoftowym, który Wam podrzuciliśmy, który dobrze definiuje taki indeks, to tam np. są jeszcze kolumny semantyczne, czyli dokładny nagłówek, np. nagłówek pierwszego poziomu, drugiego i trzeciego, z którego pochodzi tekst, żeby dać jeszcze więcej kontekstu LLM-owi, do którego to wrzucimy, ale też człowiekowi, żeby powiedzieć skąd to pochodziło, żeby zobaczyć i trochę nadać rozliczalności temu modelowi, żeby zrozumieć skąd on w ogóle wziął tą odpowiedź. I teraz jak mówię o wykorzystaniu, tu będzie clue całości. Jak Wy wysyłacie zapytanie i ten RAG mieli to pod spodem, to to co się dzieje tak naprawdę, to wasze pytanie jest przerabiane również na embeddding, wysyłane w postaci wektora do bazy wektorowej i baza wektorowa korzystając z tego, co Szymon powiedział, o przeszukiwaniu, similarity searchu, wyciąga te wasze odpowiedzi. I teraz magia skąd LLM pracuje na naszych danych polega na tym, że Wasza integracja dołącza te dane, zwrócone Chunki, dołącza po prostu jako kolejne elementy prompta, który wysyłacie do LLM-a. I na tym kończy się cała magia pod tytułem Bring Your Own Data.

Szymon Warda: Jest to dość proste. Dobrze, Łukaszu powiedziałeś, że szukamy podobieństwa po wektorach, co nie jest do końca prawdą, bo mamy takie rzeczy jak Hybrid Search. Czyli czym to jest? No bo szukanie po wektorach ma swoje plusy, bez dwóch zdań. Zdecydowanie jest faktycznie fajne, działa może na pewne rzeczy. Jednak takie zwykłe full textowe wyszukiwanie też, szczególnie z tak bardzo rozbudowanym pipeline’em jaki możemy mieć, też ma swoje wartości. Więc teraz którą wersję wybrać? Ano nie wybierajmy żadnej, tylko połączmy. W tym momencie co my robimy? Robimy to, że model otrzymując zapytanie odpala dwa wyszukiwania, jedno właśnie wektorowe, a drugie właśnie pełnotekstowe. I łącząc dane z tych dwóch zapytań, idę duplikując to oczywiście, dopiero to idzie jako to wejście i jest dołączane właśnie do inputu, ponieważ jak już powiedzieliśmy modele są bezstanowe.

Łukasz Kałużny: Tak.

Szymon Warda: Jeżeli teraz wchodzimy w ogóle w ten obszar właśnie takiego Full-Text Searcha i wchodzimy w obszar dokumentów i wyciągania, to trzeba powiedzieć o jednej rzeczy. Dokument dokumentowi równy nie jest, o tym doskonale wiemy. Ktokolwiek kto bawił się Full-Text Searchem albo nawet wyszukiwaniami Google’a, które też są formą w Full-Text Searcha, doskonale wie, że np. wyniki z Wikipedii są generalnie bardziej wiarygodne niż np. z komentarzy na Onecie czy gdziekolwiek indziej, albo na Facebooku. I o co chodzi? Chodzi teraz o to, że chcielibyśmy jakoś odzwierciedlić tą wiarygodność dokumentów tak naprawdę. Bo jeżeli dokumenty są zwracane, one są zwracane w jakiejś kolejności. I co to jest za kolejność? A to jest kolejność taka lucynowa, taka semantyczna, czyli jak ważne było to słowo w kolejnym dokumencie, jak to słowo było ważne w całym naszym zbiorze, itd. I to co robimy przez reranking to jest to, że do pewnych dokumentów, do pewnych źródeł przypisujemy wyższą wagę znaczenia, czyli finetune’ujemy nasz zbiór danych i jak ważny on jest. Czyli informujemy nasz model, naszego search’a o tym, co podjąć jakieś wagi. Dzięki temu uzyskujemy taki element, że nasz model wie o wiarygodności danych zbiorów, co odbija się oczywiście też na jakości odpowiedzi i odbiera się na to, że możemy bardzo ładnie dostosować jak on się zachowa i na co będzie bardziej patrzył.

Łukasz Kałużny: Tak i jak mówimy o tuningu to pojawiają się właśnie chunk overlapping, o którym wspomniałeś. Czyli bardzo prosto, Chunki muszą na siebie nachodzić. Czyli np. że tekst jest zdeduplikowany po to, żeby jeżeli szukamy jakiejś frazy, jakiejś odpowiedzi na pytanie, żeby zwrócić jak najwięcej Chunków, które będą miały wiarygodny kontekst i będą ze sobą łączyły ten kontekst. Czyli mówimy: weź mi zrzuć sąsiadujące fragmenty i zrób zakładkę po 20% np., po 20, 25% w Chunkach, żeby zwrócić jak najlepszą jakość poprzez właśnie rerankingi, hybrid searche, żeby to złożyć. I potem na bazie tego, taką ostatnią techniką, najbardziej wspomaga… Inaczej, to jest już takie creme de la creme po chunk overlappingu, który powinien być stosowany w ogóle… Powiedzmy sobie wprost, chunk overlapping w jakiejś formie powinien być stosowany jako default. Ja np. się cieszę, że wiele teraz z tych rzeczy już to robi automatycznie i domyślnie, to jest query expansion. I to jest technika, która przerabia nam oryginalne zapytanie na kilka różnych przykładów, czyli pozwala znaleźć lepiej i lepiej odpytać. I teraz to będzie bardzo dziecinny prompt, bo będzie on, normalnie stosuje się bardziej zaawansowane, ale brzmi on następująco: wygeneruj mi trzy alternatywne wersje zapytania zachowując jego znaczenie. Czyli polega to na tym, że zanim zaczniemy w ogóle generować embedding i przeszukiwać naszego RAG-a, tak naprawdę korzystając z jakiegoś mniejszego modelu np. GPT-4o mini czy od Anthropica Haiku. Przerabiamy pytanie użytkownika, żeby wyszukać i dopiero wysyłamy do cięższego modelu te gotowe, wynalezione w ten sposób dane.

Szymon Warda: Dobra, to co? Teraz idziemy jak to wszystko zintegrować?

Łukasz Kałużny: Tak, raczej jak dodać jeszcze więcej inteligencji.

Szymon Warda: Tak, to znaczy jak zintegrować. To teraz pierwszy, najprostszy, tzw. function calling. O co z tym chodzi? A no będą takie sytuacje, kiedy nie wszystko w naszego RAG-a wpakujemy albo inny przypadek, że dane i nawet gdybyśmy to w RAG-a wrzucili, będą nieaktualne. Czyli mamy zbiór danych, który jest na tyle zmienny, że wrzucanie tego do RAG-a po prostu nie ma większego sensu. Więc co możemy zrobić? Możemy wykorzystać właśnie function calling, czyli poinformować nasz model, że: ej, mamy taki zbiór danych, ale nie przeszukuj go przez RAG-a, czyli nie w ten sposób, tylko masz sposób na odpytanie się tego zbioru jak co dostać. Dajemy taką funkcję i umożliwiamy naszemu modelowi pobranie danych z innych źródeł niż te najbardziej domyślne.

Łukasz Kałużny: I z drugiej strony też warto dodać, może on również wykorzystywać akcje np.: dodaj mi zadanie do Todoista, czy zrób wpis w kalendarzu, czy przygotuj draft maila.

Szymon Warda: Tak, faktycznie to nie są tylko rzeczy odczytowe, mogą to też być akcje, czyli że coś wykonujemy tak naprawdę w formie czynności.

Łukasz Kałużny: Czyli to pełen CRUD, można podpiąć pełnego CRUD-a pod to. Jeżeli patrzymy, no to mówimy teraz o integracji już w aplikacji, to przydałoby się nam nie dostawać tekstu, tylko jakiś ustrukturyzowany output. I w modelach pojawiło się coś takiego jak wsparcie dla structure output, czyli technika, która wymusza nam, 99% przypadków, to będzie JSON, czyli żeby odpowiedział nam w jakimś konkretnym schemacie i pozwolił nam przygotować ten output, żeby był w określonym schemacie, czyli wynik prompta ma np. być zawsze JSON-em, w tym schemacie w te pola upchnij to. I to będzie w ogóle clue potem całych function calling i structure output, to jest całe clue Waszych integracji, które będziecie wykonywać.

Szymon Warda: Dobra, to jak już mówimy o integracjach, to trzeba powiedzieć jeszcze o jednej bardzo ważnej rzeczy, o strumieniowaniu odpowiedzi. No bo do otrzymania pełnej odpowiedzi od naszego modelu to będzie tak 20-30 sekund. Wy tego może nie czujecie tak naprawdę, ale jak nawet macie tego chata, bo pewnie większość z was miało z nim do czynienia, odpowiedzi ładnie sobie ingeruje kawałek po kawałku i to nie wydaje się, żeby tak długo trwało. A jakbyście tak zatrzymali się i pytanie, po czym chwila przerwy, taka dłuższa chwila, powiedzmy te 10 sekund i nagle dopiero odpowiedź, to nie działałoby najlepiej mówiąc bardzo prosto. A skoro wiemy, że te modele i tak pod spodem generują kawałek po kawałku, to właśnie bardzo ważne jest brzmienie tych odpowiedzi, czyli działanie tak jak chat, czyli że ładnie widzimy co się dzieje, itd. To ma jeszcze kolejny plus, że dzięki temu strumieniowaniu użytkownik ma możliwość przerwania dalszego generowania jeżeli widzi, że odpowiedź jest w ogóle nie na temat, nie o to mu chodziło, więc możemy też trochę zaoszczędzić funduszy jeżeli chodzi o samo działanie, ale przede wszystkim to jest dobry user experience właśnie, żeby po kawałku to generować.

Łukasz Kałużny: Tak, jak sobie pójdziemy dalej to pojawia się, bo tych function callingów, outputów możemy mieć dużo. Jedną z takich technik jest sobie skill orchestration, czyli że tak budujemy system prompt, że ma nam powiedzieć, że zrób XYZ, to wykorzystaj to. Może rzucając przykładem to będzie: przeanalizuj opinie klientów z różnych krajów i przygotuj raport. Czyli jedną z takich rzeczy będzie: przetłumacz mi te wszystkie opinie na jeden język, np. na polski czy na angielski, potem dokonaj oceny sentymentu, następnie kategoryzacji, generuj raport i to wszystko np. wyrzuć nam w JSON-ie. Czyli krok po kroku, w zależności co tam wpadnie, tłumaczymy co on ma z tym robić. To co się pojawiło z myślą dalej, to są agenci AI, AI Agents. Czyli małe kawałki aplikacji, promptów, które mają właśnie być może swojego RAG-a, swój function calling, w którym mają jedno wyspecjalizowane zadanie. Czyli one mają automatycznie nam coś przetwarzać, np. odczytywać z systemu, dostarczać wiedzę. I potem zbiór takich agentów możemy ułożyć w jedną większą całość. Możemy pomyśleć o systemie wsparcia klienta w banku, żeby nie złożył reklamacji. Czyli np. czemu mam opłatę, albo w telefonii komórkowej, czemu mam opłaty na koncie albo czemu mój rachunek jest wyższy niż zazwyczaj. I jeden agent może odpowiadać za pobieranie danych o kliencie, drugi agent może się łączyć do systemu billingowego, trzeci po zebraniu tych danych może napisać taką odpowiedź i przekazać klientowi. Czyli budujemy zbiór takich małych grupek mrówek, które potem łączą to w jedną całość.

Szymon Warda: Dokładnie. Dobrze Łukaszu, lista zakończona. I co? Tyle. W sumie teraz tylko czekamy na wasz feedback czego zapomnieliśmy, czego brakuje, gdzie się pomyliliśmy, itd., bo pewnie się coś pojawi, ale to nawet dobrze.

Łukasz Kałużny: Tak, dokładnie. To była taka nasza lista, jak zobaczycie te podstawowe pojęcia z promptami, techniki promptowania, RAG-i, czy potem te integracje, które, jeżeli zaczynacie z LLM-ami, to pozwolą Wam zrozumieć i zobaczycie, że wynika to po prostu z praktyki jakich pojęć najczęściej będziecie wykorzystywać.

Szymon Warda: Dokładnie. Dobrze.

Łukasz Kałużny: Trzymajcie się.

Szymon Warda: Tyle.

Łukasz Kałużny: Hej.

Szymon Warda: Hej!