Mentionsy
DevTalk #130 – O RAG do Eksploracji Kodu z Łukaszem Szydło
Czy RAG faktycznie rozwiązuje problem dokumentacji, która nigdy nie jest aktualna? Jak sprawić, by LLM odpowiadał na pytania o Twój kod bez wrzucania całego repozytorium do kontekstu? I dlaczego embeddingi to nie jedyne rozwiązanie? O tym wszystkim opowie Łukasz Szydło – architekt, konsultant i trener, specjalizujący się w tematach architektury i Domain-Driven Design. Łukasz na […]
The post DevTalk #130 – O RAG do Eksploracji Kodu z Łukaszem Szydło appeared first on DevTalk.
Szukaj w treści odcinka
Dzień dobry, nazywam się Maciej Aniserowicz i zapraszam was serdecznie na kolejny odcinek podcastu DevTalk.
Dziś z nami wyjątkowy gość i bardzo ciekawy temat.
Łukasz Szydło o RAG.
Przed tobą DevTalk.
Cześć Łukasz.
Powiedz, please, na samym początku, proszę tak każdego od jakiegoś czasu, powiedz, proszę, kilka słów o sobie, żebyś to ty się przedstawił, a nie ja ciebie, to może będzie bardziej szczerze.
Oj, no, na początku ostro.
No, jestem w sumie architektem, konsultantem, trenerem.
Personalnym jeszcze nie, ale to może następny etap w mojej karierze.
Teraz również z firmą Circle K, w której od już ponad roku jestem solution architektem.
Specjalizuję się w tematach związanych z architekturą, z domain-driven design i generalnie ze sprawianiem, żeby development projektów był szybszy i tańszy.
Brzmi jak bardzo wielka obietnica.
Zobaczymy, jak nam dzisiaj stanisz i przyspieszysz to wszystko, bo dziś rozmawiamy o technologiach bardzo nowoczesnych.
Rozwiń proszę ten skrót na początku.
Zacznijmy może z takiego popłapu, zakładając, że niektórym osobom to się obiło o uszy, ale w sumie nie bardzo wiedzą, co to jest.
A później przejdziemy do rzeczy, które także starzy wyjadacze powinni znaleźć jako interesujące.
Spoko.
RAC, czyli Retrieval Augmented Generation, to jest technika, która pozwala na dostosowywanie tego, co w danym momencie powinniśmy wrzucić do kontekstu naszego LLM-a.
To na początku może tak trochę ogólnie, abstrakcyjnie, ale o co chodzi, jaki problem próbujemy przez takiego raga rozwiązać?
Wszyscy wiemy, że nasze LLM-y mają jakiś ograniczony kontekst, ograniczoną liczbę tokenów, ograniczoną liczbę słów, którą jesteśmy w stanie wrzucić w prompta.
I kiedy pytamy tego LLM-a o takie proste rzeczy, weź mi wygeneruj obrazek, albo podsumuj mi jakiś film z YouTube'a, tudzież podcast, to wtedy wszystko, co potrzebujemy, wrzucimy się w ten kontekst mieści.
ale w momencie, kiedy zaczynamy, czy chcemy zacząć wykorzystywać naszego LLM-a do tego, żeby on nam zaczął odpowiadać na pytania związane z jakimiś specyficznymi rzeczami z naszej domeny.
Na przykład chcielibyśmy wrzucić mu wszystkie książki wszystkich filozofów, którzy pisali przez ostatnie dwa tysiące lat, czy nawet dłużej, dwa i pół tysiąca lat.
Nie mamy tyle mocy obliczeniowej najczęściej u siebie w firmach, żeby wytrenować sobie model na tych wszystkich danych, bo generalnie jest to cholernie drogie.
Nie każdy ma możliwość wzięcia takiej super wydajnej karty graficznej i postawienia jej u siebie w firmie, a nie każdy też ma na tyle budżet, żeby na chmurze sobie coś takiego odpalić i zacząć te modele trenować.
Raczej korzystamy z gotowych już wytrenowanych modeli.
Będę się wtrącał po kolei, jako będę udawał lejka, a może nim też jestem.
Ale przecież czaty i inne klody i inne AI przecież przeczytały wszystkie książki i już to wszystko wiedzą, więc dlaczego mamy wrzucać im te książki po raz kolejny?
Tutaj chciałem dać taki przykład, żeby zobrazować sam problem, ale AI nie przeczytał rzeczy, które są własnością intelektualną naszej firmy.
Czyli jeżeli my chcielibyśmy, żeby nasz AI zaczął nam odpowiadać na tematy związane z naszym projektem, związanym z naszym kodem, związanym z naszą firmą, z naszą domeną, którą mamy u siebie, no to to nie są dane, do którego firmy miały dostęp podczas trenowania.
Czyli mówimy o dużych zbiorach danych, które nie są zawarte już w tych gotowych modelach i są za duże, żeby po prostu je wkleić do prompta.
Duży temat to jest w branży medycznej, gdzie oni mają ogromne ilości danych na temat próbleków, które przeprowadzają, danych pacjentów, danych, które nie są dostępne dla...
Mamy w naszych systemach ogromne ilości kodu, czasami mamy trochę dokumentacji, mamy informacje o pracach, które wykonywaliśmy w postaci różnych tasków na Jira czy innych narzędziach.
Generalnie mamy mnóstwo wiedzy, która jest całkiem użyteczna, tylko w jaki sposób trzeba temu AI wiem przekazać.
I jak już ustaliliśmy, że nie jesteśmy w stanie sobie wytrenować tego naszego modelu, bo chcemy skorzystać z modelu gotowego, to mamy dwie kolejne opcje do wyboru.
Opcja numer dwa to jest tak zwany fine tuning.
Czyli możemy wziąć ten generyczny model, który sobie wzięliśmy tam od OpenAI'a czy od Antropica i go fine tunować, czyli zrobić tak, douczyć go.
Wziąć te rzeczy, które mamy i super wydajną kartę graficzną, czy gdzieś jakiś tam system na chmurze i próbować zrobić fine tuning i go douczyć.
No tylko to jest rozwiązanie dosyć drogie i znowu, ono by się nam sprawdzało wtedy, gdyby to był jakiś skończony zbiór danych, który już się nie za bardzo będzie zmieniał i wtedy możemy sobie sfine tunować ten model i ten model będzie nam odpowiadał na nasze pytania.
Ale jak to jest z naszą wiedzą w firmie?
Co chwilę dochodzi nowa, co chwilę pojawiają się nowe zadania, co chwilę pojawiają się nowe wymagania, co chwilę pojawiają się nowe fragmenty kodu i tak dalej.
Jeżeli chcielibyśmy pójść w taki fine tuning, no to musielibyśmy robić taki ciągły fine tuning.
że co chwilę jak nam się pojawiają jakieś nowe, super aktualne dane, które też chcielibyśmy, żeby nasz LLM wiedział i znał, a najczęściej jak zadajemy jakieś pytania, to raczej nie zadajemy pytania o tym, co się stało w zeszłym miesiącu, albo jak coś zrobiliśmy dwa lata temu, tylko chcemy mieć jak najbardziej aktualny stan wiedzy tego naszego LLM.
Więc ostatnim możliwością wprowadzenia danych do takiego LLM-a, to jest właśnie wrzucenie tych danych po prostu w prompta.
Zrobienie tak dużego prompta, żeby te wszystkie dane, których potrzebujemy, do tego prompta wrzucić, a następnie dać LLM-owi zadanie, weź odpowiedź mi na pytanie na podstawie tych informacji, które wrzuciłem Ci w prompt.
Trochę tak, jak robimy czasami podsumowania właśnie filmów na YouTubie czy podcastu.
Więc ja wrzucamy transkrypcję i mówimy podsumow.
Tylko, że transkrypcja, czy do takiego filmu na YouTubie, czy podcastu, to jest coś, co jeszcze nam się w tego prompta zmieści.
Ale w momencie, kiedy to nam się w tego prompta nie mieści, no to potrzebujemy mieć narzędzie, które na podstawie tego naszego prompta, którego zadaliśmy, plus jeszcze pewnie paru innych informacji,
odpyta jakąś bazę danych, w której będziemy trzymać te wszystkie informacje w odpowiedni sposób, odpyta i wrzuci do tego kontekstu naszego LLM-a tylko tę część wiedzy, tylko tę część informacji, tylko tę część opisów, która jest istotna w kontekście tego pytania.
Czyli i to jest właśnie ten rack.
Retrieval, czyli właśnie pobieramy Augmented Generation, czyli jesteśmy w stanie odpowiedzieć na pytanie, czyli LM Generation, czyli LM jest w stanie nam wygenerować tę odpowiedź na podstawie danych, które wyciągnie sobie na podstawie takiego prompta.
Z założeniem, że ten kompletny zbiór danych, tak jak mówiliśmy wcześniej, jest zbyt wielki, więc trzeba używać podzbioru za pomocą jakichś tam...
technik.
No i z tego co mówisz, to jest bardziej praktyczne i bardziej wydajne niż fine tuning modelu, już nie mówiąc nawet o trenowaniu całkowicie własnych.
Jesteśmy w branży IT, a z tego co mówisz, to ten rack przydaje się, znaczy zresztą też sam z niego wielokrotnie korzystałem z tej techniki w różnych innych również zastosowaniach.
Zawężamy to już teraz do IT i do oprogramowania do kodu, czy jeszcze na razie idziemy trochę szerzej?
To powiedz może, jak to działa tak w skrócie.
A właśnie, jak to działa.
To bardzo mocno zależy od tego, jaki mamy zestaw danych, czyli jakie mamy dane, które chcielibyśmy, żeby były właśnie źródłem tych informacji.
Mamy dane nieustrukturyzowane,
różnego rodzaju właśnie dokumenty, teksty, książki, czasami to są nawet jakieś dokumentacje, specyfikacje, które piszemy.
Generalnie tekst w różnego rodzaju formie.
I mamy czasami dane ustrukturyzowane.
Takie dane, które są gdzieś w naszej bazie danych, które mają swój konkretny schemat, który możemy sobie przy pomocy zapytania SQL-owego czy grafowego jesteśmy sobie w stanie te dane wyciągnąć.
Więc to pierwsza rzecz, od jakiej to zależy, jak one są zbudowane, to jest to, jaki mamy zestaw danych.
Reagi najczęściej są używane do tych danych nieustrukturyzowanych, bo tych mamy najczęściej najwięcej.
Te ustrukturyzowane to już ktoś kiedyś jakiś schemat wrzucił, tam możemy sobie SQL-em coś tam wyciągnąć i jesteśmy generalnie z tego zadowoleni.
głównym tematem są dane nieustrukturyzowane.
I teraz, jak mamy taki zestaw dokumentów, to pierwsza rzecz, którą musimy z nim zrobić, to musimy go poczankować.
Trochę taki anglicyzm będę używał.
Poczankować?
Tak, musimy go pokroić, a następnie musimy wygenerować z niego wektory, które w nomenklaturze są nazywane tak zwanymi embeddingami.
No to znaczy, że po to, żebyśmy mogli stworzyć tę bazę danych, ta baza danych to generalnie to jest taki, można powiedzieć, taki ogromny indeks, którego, tak jak zwykła baza danych, tak, odpytujemy, a następnie mówimy, ej, zwróć mi rzeczy, które są dla mnie przydatne.
Więc ten pierwszy proces to jest de facto stworzenie tego indeksu.
Bierzemy te dokumenty, kroimy je na mniejsze kawałeczki i tutaj do tego krojenia też używamy często LLM-a.
żeby ten LLM przeanalizował trochę wstępnie te teksty, te dokumenty i pokroił je na takie fragmenty, które mają generalnie sens.
Czyli jeżeli mamy jakieś prace naukowe, no to żeby być może na jakieś paragrafy je pokroił, być może wyciągnął już jakieś tam informacje o tym, gdzie najlepiej jest to robić.
Więc tutaj mamy pierwszy zestaw modeli, które będą na to czankować.
Chodźcie o rozbicie dużej jakiejś tam treści.
Na mniejszy kawałeczki z tego względu.
No dlatego, że to ostatecznie, co my chcemy zrobić, to chcielibyśmy właśnie wziąć ich z tych małych kawałeczków i z tych małych kawałeczków wyciągnąć te małe kawałeczki z tego dokumentu, które są istotne dla naszego pytania.
Wyobraź sobie, że masz jakąś pracę naukową, która, albo nawet książkę, o, wyobraź sobie, że masz książkę, lalkę powiedzmy, tak, Bolesława Prusa, ale ciebie interesuje tylko... Nie, to dopóźnij Blue Book, Ewansa.
Mrząbuk Ewansa, kurczę, że w okulskim dzień zacząć mówić o tym do Blue Booka Ewansa, ale dobrze, niech będzie.
Załóżmy, że masz Blue Booka Ewansa, no i ty byś chciał tylko i wyłącznie dowiedzieć się czegoś na temat value objectów.
I na końcu masz indeks, prawda?
Zawsze na końcu książki masz indeks, który mówi, o, value object, taki klucz, na których stronach to on jest.
Ale to nie cała strona jest o value objectie, tylko o value objectie jest pewien paragraf.
Więc to, co ty byś chciał zrobić, żeby twój LLM mógł ci opowiedzieć na temat value objectów, to ty byś chciał z tego Bluebooka wyciągnąć tylko te fragmenty stron, te paragrafy, które się rzeczywiście tyczą tylko tych value objectów.
i ewentualnie jakieś elementy z nimi powiązane, ale to niekoniecznie muszą być paragrafy, które są blisko siebie.
Więc jeśli ty podzielisz sobie Bluebooka właśnie takimi mniejszymi, na mniejsze czanki niż strony, czy niż w ogóle rozdziały, czy cała książka, to właśnie dzięki temu potem jesteś w stanie tak mocno jak podzielisz, tak drobno jak podzielisz, tak drobne kawałki jesteś w stanie z tego raga wyciągać.
I dzięki temu, właśnie jak podzielisz sobie na takie małe, to wtedy z tych małych fragmentów wrzucisz do tego kontekstu tylko te rzeczy, ostatecznie, czyli wyciągniesz z tego raga tylko te fragmenty, które się tyczą wali obiektów.
I dzięki temu zetwórzesz dwie rzeczy.
Pierwsza, twój kontekst staje się krótszy, więc mniej płacisz.
za LLM-a, bo zużywasz mniej tokenów, a druga, jest też mniejsza szansa na halucynacje, dlatego, że on ma w sobie tylko te dane, które rzeczywiście dotyczą się tematu, który ciebie interesuje, czyli tylko tych value objectów.
Można by zrobić wtedy tak, że drogi LLM-ie, zapomnij wszystko, co wiesz na temat value objectów, co sobie wyczytałeś z internetu,
Tutaj masz wszystko, co powinieneś wiedzieć, bo interesują nas wyłącznie informacje z tej książki.
Możemy tak zrobić, tak.
I oczywiście z tego, co mówisz, to chodzi bardziej jednak o przełożenie tej praktyki po prostu na informacje, których on nie posiada, czyli dostarczenie tego mu w postaci kontekstu, czego on nawet nie ma wcześniej.
Jeśli założylibyśmy, że nasz LLM nic nie wie o value objectach, albo prawie nic, a my chcemy mu te informacje dołożyć, żeby on był w stanie o value objectach nam coś opowiedzieć, to właśnie wtedy coś takiego byśmy zrobili.
Czyli chcielibyśmy Bluebooka podzielić na fragmenty.
Z każdego takiego fragmentu zrobilibyśmy tak zwany embedding.
No właśnie, embednik to jest po prostu wektor, czyli ciąg, wektor w wielowymiarowej przestrzeni, czyli de facto haszujemy nasz fragment tekstu do jakiegoś wektora w przestrzeni po to, żeby później, kiedy mamy z każdego takiego fragmentu zrobiony taki wektor, te bazy danych, do których to wrzucamy, to są tak zwane wektorowe bazy danych i one się specjalizują w tym,
że one w bardzo szybki sposób, bardzo łatwo są w stanie znaleźć wektory, które są podobne do siebie.
Czyli jeżeli mam fragment o agregacie, to ten fragment będzie wektorem, który będzie bardziej odległy niż fragment, który jest o value objectie.
Ale fragment na przykład o subdomenach będzie jeszcze bardziej odległy od nich obu.
Ten wektor o subdomenach będzie jeszcze bardziej odległy od tych pozostałych dwóch, bo one są w ogóle ze sobą niezwiązane.
A fragment lalki będzie w ogóle gdzieś indziej.
Tak.
A o Wokulski to już tak inna zupełnie bajka.
Jak wygląda ten... Będę cię trochę z tego cisnął.
Jak wygląda ten wektor?
Tyle masz cyfr, ile masz wymiarowy wektor.
Bo tak jak wektor na fizyce, możesz mieć dwuwymiarowy wektor i on wtedy będzie miał tylko x, y. Możesz mieć trójwymiarowy wektor i on będzie miał x, y, z. A możesz mieć n-wymiarowy wektor, który ma tych parametrów bardzo dużo.
Bo to są wektory w wielowymiarowych przestrzeniach.
tyle, ile masz tych liczb w tym ciągu znaków, to znaczy, że tylowymiarową przestrzeń wektorową używasz do tego, żeby szukać podobieństw pomiędzy... Tak, dobrze powiedziałem, na przykład ciąg liczb, a nie cyfr.
I teraz tak, mamy tekst w dowolnym języku właściwie, tak?
Możemy mieć bluebook po angielsku, możemy mieć w tłumaczeniu po polsku i jak weźmiemy ten sam paragraf przetłumaczony z angielskiego na polski i zrobimy z tego ten wektor, ten embedding, to one będą w miarę blisko siebie, tak?
Nie jestem pewien, zakładam, że tak, ale nie odpowiem ci ze stuprocentową pewnością, w jaki sposób język wpływa na to.
Wydaje mi się, że to jest kwestia LLM-a, który jest odpowiedzialny za generowanie tego embeddingu.
Jeśli ten LLM będzie potrafił obsługiwać wielojęzykowe treści, nie będzie na przykład wyspecjalizowany tylko i wyłącznie w języku angielskim, bo powiedzmy, tak, możemy tak zrobić, im ten LLM ma węższy zakres,
tym będzie mniejszy, będzie mniej pamięci zużywał, dzięki temu też będzie bardziej oszczędny.
Jeśli mamy takiego LLM-a, który potrafi obsługiwać wiele różnych języków, to wtedy tak, to wtedy one będą bardzo blisko siebie.
Zapytałem, bo używamy czegoś takiego u siebie, pewnie zaraz do tego też przejdziemy, jak powiesz przykładach, to ja powiem o naszym z kolei.
No i on był robiony na podstawie języka polskiego, ale z kolei teraz jak te dane wyciągam, rozmawiając z nim, to niezależnie od tego, czy zapytam go po...
polsku, po białostocku czy po angielsku, to on zwraca mi te same informacje.
No i super, to znaczy, że zostało to użyte w taki sposób, że język nie ma dla niego znaczenia.
Ale to może być na dwóch poziomach zrobione, tak?
To może być na poziomie samego prompta, że jakby prompt jest tłumaczony do jakiegoś, nie wiem, zawsze do angielskiego, tak?
No dobra, więc tak, mamy ten wektor.
Inaczej, mamy jakiś tam zbiór danych, mamy podzielony ten zbiór danych na małe czanki.
Z tych czanków robimy za pomocą specjalnego LLM-a robimy embeddingi.
Wrzucamy je do bazy wektorowej.
Czyli ten fragment tekstu plus wektor.
Czyli jakby nie sam wektor wrzucamy do bazy, tylko ten wektor jest wskaźnikiem na tego czanka.
A następnie w momencie, kiedy robimy zapytanie do takiej bazy wektorowej, oto znajdź mi... Jeżeli ktoś robi zapytanie, to następnie to zapytanie też jest zamieniane na embedding.
czyli też na ten wektor.
I ten wektor jest wysłany do tej wektorowej bazy danych i my mówimy, weź mi znajdź inne wektory, które są podobne do tego wektora.
Dzięki temu, jeżeli ja wtedy zapytam o value object, na czym polega modelowanie value objectów, to zostanie zamienione na, przez LLM zostanie zamienione na wektor.
Do bazy danych wektorowej pójdzie zapytanie, znajdź mi podobne wektory do tego wektora.
Następnie te teksty, które są z tymi podobnymi wektorami teczanki tego tekstu zostaną wyciągnięte z tej bazy danych wektorowej.
Następnie można jakby w prostej wersji od razu moglibyśmy wrzucić do kontekstu LLM-a.
W realnej wersji następuje jeszcze tak zwany re-ranking.
czyli jest jeszcze jeden taki LLM-ik, agent, który odpowiada za to, żeby te dane posegregować pod kątem istotności, które są rzeczywiście mocno związane z tym naszym zapytaniem, a które są mniej z tym zapytaniem związane, no bo być może ktoś używa, że tak powiem, tych samych słów do opisu.
Mamy w tekście pomidor, ktoś pyta o warzywa, ktoś pyta o owoce i pomidor pasuje pod obie rzeczy.
Więc ten re-ranker, on jeszcze analizuje ten tekst, który wyszedł i sprawdza, czy on rzeczywiście jest powiązany z tym zapytaniem, które zostało wysłane.
I tak już przygotowany fragment tych danych zostaje wrzucony do kontekstu LLM-a, żeby ten mógł odpowiedzieć na źródłowe pytanie, które zostało zadane.
Dzięki temu zamiast wrzucać do LLM-a całą wielką książkę Erika Evansa, wrzucamy mu parę tylko paragrafów, które tyczą się dokładnie value objectów i niczego więcej.
Czyli tutaj już mamy jakby pierwszy etap, to jest przetworzenie tych danych, które chcemy i umieszczenie ich w tej odpowiednio przygotowanej bazie.
I zakładam, że na etapie tego przygotowywania i uzupełniania tej bazy tymi informacjami również można w jakiś sposób je przetwarzać inaczej, nie tylko robić z nich headbendingi i wrzucać goły tekst, ale na przykład przygotować jakieś streszczenie, jakieś tagi czy coś takiego, czy jakieś informacje, które potem mogą być wykorzystane przy wyciągnięciu z kolei tych informacji z tej bazy.
Są też bardziej złożone techniki.
W najprostszej wersji są tylko te embeddingi i nic tam się więcej nie dzieje.
W bardziej złożonych pipeline'ach jest tak zwany dodawany kontekst do każdego chunka albo do któryś tam chunków.
plus też rzeczy związane z podsumowaniami, które kierują nas do, ale tutaj już zaczynamy przechodzić na powiedzmy taki drugi sposób realizacji tych realizacji ragów, bo to co sobie powiedzieliśmy tutaj to jest
raczej, może być tak, rach powinniśmy traktować jako pewien wzorzec, który może mieć wiele różnych implementacji.
Pewien, pewien pattern, tak jak mamy, nie wiem, CQRS-a, możemy go zarówno zaimplementować w .necie, jak i możemy go zaimplementować w Javie, możemy go zaimplementować na osobnych bazach danych, możemy zaimplementować go na jednej bazie danych, pewien ogólny wzorzec, który na różne sposoby można zaimplementować i podobnie jest w przypadku raga.
Jakby jego cel jest zawsze taki sam, najmniej te dane, które są związane z moim kontekstem, z jakiegoś dużego zbioru danych, ale możemy to robić na różny sposób, dlatego że tutaj te bazy danych wektorowe, one mają pewną wadę, a mianowicie właśnie taką, że one nie są za bardzo taki kontekst, wrażliwe na kontekst.
jest jakby no podobna struktura zdania, podobna struktura tekstu, no to one tych danych będą nam zwracały mniej lub więcej.
Nie mamy gwarancji takiej, że rzeczywiście dostaniemy tylko te rzeczy, które jakby semantycznie są związane z tym naszym zapytaniem.
Możemy dostać dane, które po prostu brzmią podobnie albo mają podobną strukturę, tak?
I z takiego grafu tej bliskości tych danych jesteśmy w stanie, możemy też te dane uzyskiwać.
Więc nie zawsze będziemy mieli dobre rezultaty, szczególnie też jeśli te dane wejściowe będą, że tak powiem, w średniej jakości.
No bo są tak nieustrukturyzowane, że ani to czankowanie tam nie do końca dobrze poszło, pozostały poczankowane na wskroś pewnych tematów i być może...
algorytm do tworzenia embeddingów, bo też nie zawsze można dawać dobre dane, wiecie jak to jest w teorii, wszystko ładnie działa, a potem w praktyce jak przychodzą te rzeczywiste dane, które wrzucamy, to nagle się okazuje, że nie ma takich rezultatów jak na YouTubie.
No to mam nadzieję, że zaraz o tym też porozmawiamy, ale właśnie, bo jeszcze przyszło mi do głowy, powiedziałeś o semantycznym dopasowaniu, czyli znaczeniowym, bo to generalnie o to chodzi, żeby to tym się różni od normalnego pokrojenia stron na paragrafy i wrzucenia do zwykłego SQL Servera czy innej baski i odpalenia na tym indeksowania do wyszukiwania full text searchem powiedzmy, że właśnie tu nie musimy użyć dokładnie tych słów, które zostały użyte w danym paragrafie, tylko chodzi konkretnie o to znaczenie, tak, że możemy zadać pytanie potem po swojemu
No i oczekiwanie jest takie, że otrzymamy te dane niezależnie od tego, czy użyjemy dokładnie tego ciągu słów czy innego.
No i to nam na początku obiecywały właśnie te wektorewe bazy danych i LLM-y, które produkowały embeddingi, że to rzeczywiście będzie działać bardzo dobrze.
W praktyce działa różnie.
Bardzo jest istotna jakość tych danych wejściowych, którą mamy.
A też jest tak, że część tych danych wejściowych, to ona już też ma jakąś strukturę.
To szczegóły nie widać w danych medycznych, gdzie oni mają swoje własne, jak to nazwać, pewne modele domenowe, że jakby wiedzą, czego szukają.
Na przykład szukają tego, że jest jakaś choroba i ta choroba ma jakieś swoje przyczyny i jest jakieś lekarstwo, które działa na tę przyczynę.
ale mamy zdefiniowane obiekty i relacje, obiekty i jakie są relacje między tymi obiektami w danym tekście.
Tak jakby możemy wiedzieć, czego szukamy.
I drugim sposobem, taką alternatywą do tego, żeby właśnie robić embeddinki są tak zwane grafragi.
Czyli to, co robi grafrag, to ten grafrag, on właśnie próbuje zamienić ten tekst, który mamy, na graf wiedzy.
Czyli nie mamy już bazy danej wektorowej, w której trzymamy sobie te nasze embeddingi jako wektory i tam szukamy po prostu takiego właśnie podobieństwa na zasadzie tekstowego, takiego no semantycznego, tak, ale na zasadzie podobieństwa wektorów.
tylko staramy się zbudować ten graf, z którego jak już znajdziemy jakiś jeden węzeł w tym grafie, to jesteśmy w stanie znaleźć elementy, które są z nim powiązane i są do niego podobne.
To są tak zwane grafragi.
I powiedzmy działa to trochę podobnie, ten pipeline przygotowywania takiego grafraga działa dosyć podobnie.
Tylko, że zamiast na początku chunkować to tak, jak LLMowi się wydaje, to LLM stara właśnie się na początku znajdować encję i budować relacje, czyli stara się zbudować taki ogólny model encji i relacji pomiędzy właśnie tymi...
encjami, które znajdzie w tym tekście, a następnie podpina te konkretne embeddingi już pod konkretne encje i relacje i buduje z tego taki dwu lub trzypoziomowy graf, po którym my jesteśmy w stanie przechodzić i szukać.
Więc zamiast bazy wektorowej jesteśmy w stanie sobie użyć na przykład takiego Neo4j'a.
jako bazy grafowej i stworzyć właśnie taki graf wiedzy i zamiast pytać i generować, w sensie i zamiast, jak mamy zapytanie z naszego LLM-a, to wcześniej co robiliśmy?
Braliśmy to zapytanie i zamieniliśmy je na wektor, żeby tym wektorkiem uderzyć do bazy wektorowej i znaleźć podobne.
Tutaj możemy wziąć to zapytanie i na przykład zamienić je na cyphera, czyli język do odpytywania bazy danych grafowej i wyciągnąć sobie te elementy z naszego grafu, dlatego że już znamy pewną konkretną strukturę.
która jest dla nas istotna.
I w tych rozwiązaniach jesteśmy w stanie dostać dużo, jesteśmy w stanie dostać powiedzmy bardziej przewidywalne rezultaty, jeżeli lepiej wiemy, czego szukamy.
Czyli jeżeli potrzebujemy takiego rozwiązania, które jest super generyczne, mamy nieustrukturyzowane dane i po prostu chcemy jakiekolwiek zapytania na nim robić, one niekoniecznie muszą być dokładne, to wtedy wektorowe podejście jest super.
W momencie, kiedy my mamy chociaż trochę wiedzy,
na temat tego, jak te nasze dane są ustrukturyzowane, o czym jest ta nasza książka.
My wiemy na przykład w przypadku tej książki o DDD, że bierzemy sobie na przykład te grafy, które są na poszczególnych częściach książki.
Tam jest taki graf, pewnie i konceptami.
I jest tam taki rysunek, że encje, w sensie, że agregat zawiera encje, encje zawierają value objecty, to jest związane z tym, to jest związane z tym, nie?
Mogłaby być to swego rodzaju właśnie taka ontologia, która byłaby wykorzystana do tego, żeby następnie wokół tych encji i relacji między nimi znajdować kolejne teksty, które są związane właśnie z tymi konkretnymi encjami i z relacjami między nimi.
I zamiast robić to przy pomocy właśnie bazy wektorowej, jesteśmy to w stanie robić przy pomocy bazy grafowej.
No ciekawe, tylko to tak jak mówisz, to już wymaga większej ilości wiedzy na temat samych tych informacji, które posiadamy na samym początku, no plus więcej pewnie pracy, żeby faktycznie te dane odpowiednio oporządzić przed wrzuceniem ich do bazy, bo to zrobienie wektorów z paragrafów po kolei, czy nawet bardziej logicznego podziału niż same paragrafy, to brzmi jednak prościej niż wyłuskanie o czym to jest i jak te CUS-ie między sobą i jakie mają relacje zanim nie wrzucimy ich dalej.
To jest ta wersja trudniejsza i jest to też wersja bardziej kosztowna.
no to im będziemy mieli tych danych więcej, tym więcej będzie nas kosztowało zrobienie takiego raga.
A jeżeli chcielibyśmy sobie zrobić właśnie takiego graf raga, no to tej mocy obliczeniowej od LLM-a potrzeba jeszcze więcej.
A tokeny lecą.
A tokeny lecą, nie?
Żeby Ci podać przykład, 10 tysięcy tokenów, czyli to jest około dziesięciostronicowy dokument, który jest przez ten pipeline grafragowy transformowany do tej właśnie...
bazy wiedzy, na takim lepszym modelu, bo żeby mieć w miarę dobre wyniki, no to możemy użyć małych, tanich modeli, ale na nich te wyniki są średnie, no to to jest na tym takim dobrym, lepszym modelu to jest około 3,5 dolara.
za jedno stronicowy dokument.
Za dziesięciostronicowy dokument, przepraszam.
W praktyce zakładam, że może być nawet i trochę więcej.
No plus ta wiedza, tak jak wspominałeś na początku, ona musi być aktualizowana, żeby ta baza faktycznie spełniała swoją rolę cały czas, a nie tylko w momencie utworzenia.
Wskaza się, wskaza się.
No ale to jakby założenie chyba jest takie, że rezultat będzie tego warty, tak?
Takie jest założenie, zdecydowanie.
Ale wiesz, jak to jest w praktyce z nowymi technologiami, że eksperymentujemy, próbujemy i często te rezultaty wychodzą różnie.
Albo mamy bardzo dobre rezultaty, ale nas to bardzo dużo kosztuje, przez to, że musimy używać tych najlepszych modeli, które zżerają nam dużo tokenów i rezultaty wychodzą dobre, albo chcemy mieć rozwiązanie, które jest trochę bardziej oszczędne,
ale wtedy musimy się pogodzić z tym, że jakość może być o drobne niż.
Okej, to mówiliśmy na razie o takich zastosowaniach, można powiedzieć, że ogólnych.
Mówiłeś trochę o medycznych, tu mówiliśmy o książce Evansa, wspomniałeś w ogóle o tym, żeby sobie gadać z treścią lalki Prusa, a jakbyśmy to zawęzili do świadka programistycznego, czyli do raga na naszym codebase'ie.
Tak, to jest element, czy rozwiązanie, które jest używane bardzo często, dlatego że tutaj właśnie mamy ten problem.
W jaki sposób zbudować, jeżeli mamy jakiegoś LLM-a, który generuje nam kod, czy który opisuje nam kod, czy w jakikolwiek sposób wchodzi w interakcję z naszym kodem, no to często nasze bazy kodu są większe niż okno kontekstowe.
Pomimo tego, że już słyszy się tak o tym, że już są LLM-y, które mają milion na przykład tokenów w kontekście i już mamy w głowie, że za chwilę ranki nie będą potrzebne, bo te okna kontekstowe już będą tak duże, że jakby całe repozytoria będą nam się mieściły w tym...
oknie konteksty, będziemy zawsze całe repo wrzucać i już będzie super.
Na razie tak to nie wygląda.
Na razie badania, z którymi się ostatnio zapoznawałem, pokazują, że niestety, ale pomimo tego, że LLM wspiera to okno kontekstowe, to im więcej elementów do niego wrzucimy, tym mniejsza jest dokładność odpowiedzi.
Więc pomimo tego, że milion mogę wrzucić, to muszę się liczyć z tym, że jeżeli bym wrzucił nie milion, a 100 tysięcy tokenów, to byłyby tokeny, które są związane mocno z tematem zapytania, które daję, z prompta, który daję, to nie dość, że będę miał odpowiedź lepszą, to jeszcze będę miał odpowiedź szybszą i tańszą.
I że jak za każdym razem będę wrzucał cały mój codebase, to będę miał drogo i jeszcze niedokładnie.
Nie, no bo to nie ma siły, tak?
No w fizyki nie oszukasz.
Jak masz więcej tokenów, to więcej analizy, no to musi to potrwać dłużej albo drożej, bo po prostu musimy jakiś mocniejszy sprzęcik potem uruchomić, żeby to się podpięliło.
Z perspektywy właśnie pracy programistycznej to mamy tak, mamy kod, mamy jakąś dokumentację i tu mówię o jakiejś zewnętrznej dokumentacji, niekoniecznie o komentarze w kodzie.
Mamy jakąś jirę, czy coś takiego, jakieś inne etikety.
Historie projektu w GIT-cie.
Mamy listę kontrybutorów z GitHub'a i pull requestów i tak dalej.
Czy to wszystko w jakiś sposób jest danymi, czy to wszystko są dane, które możemy tak przetworzyć, żeby wrzucić je właśnie do jakiejś bazy i potem z tym rozmawiać?
Tak, to są wszystko takie dane, tylko musimy się mocno zastanowić, które dane są dla nas najistotniejsze.
Na przykład, jakiej jakości jest nasza dokumentacja, którą aktualnie mamy.
Czy to jest dokumentacja, która rzeczywiście jest aktualna i my chcemy, żeby ona była źródłem wiedzy dla naszego LLM-a?
Czy to jest dokumentacja, która opisuje stan rzeczy sprzed dwóch miesięcy, czterech miesięcy, pół roku, bo nikt tego na bieżąco nie aktualizuje.
A czekaj, wtedy właśnie taki rag, jeśli pominiemy tą naszą nieaktualną dokumentację, to on może być aktualną dokumentacją, która tworzy się na bieżąco na podstawie stanu faktycznego.
Może być, może być i generalnie do tego dążymy, nawet nie tylko dokumentacją, ale też generalnie źródłem wiedzy na temat naszego CodeBase'u, na temat naszej bazy kodu, która może być wykorzystywana zarówno do tego, żeby zadawać pytania i mieć informacje na temat tego, jak ten system aktualnie działa, ale też, żeby budować kontekst dla agentów.
AI-owych, które, żeby wygenerować dla nas, nie wiem, jakąś speckę, jakieś wymagania, czy nawet fragmenty kodu, które mają być, to one też potrzebują wiedzieć, ok, to który z tych fragmentów kodu jest dla mnie istotny, żebym ja sobie go wrzucił do kontekstu, żebym na podstawie jego mógł sobie to zrobić.
I w niektórych narzędziach, w niektórych pluginach
ręcznie jest to robione, że mówimy mu, dobra, weź, zwróć uwagę na ten plik, na ten plik, na ten plik, na ten plik, weź uwagę na tą metodę, na tą metodę.
Mówimy mu tylko, weź tam, napisz mi specke dla takiego i takiego feature'a z takimi i takimi rzeczami i on to robi pod spodem i widzimy, co on robi.
Ale to, kogo on może właśnie sobie zapytywać, to może sobie zapytywać takiego raga,
który zwróci mu te fragmenty kodu, które są dla niego istotne.
I w standardowych rozwiązaniach, bo jest sporo takich rozwiązań, które to robią, w standardowych rozwiązaniach... Pauzę tu wcisnę, bo właśnie teraz zbudujemy twoje authority, bo o tym nie powiedziałeś na początku.
Jak już tak zawęziliśmy bardzo zakres tej naszej dzisiejszej rozmowy, to powiedz, czym się teraz zajmujesz.
Ja teraz się zajmuję wspólnie z Marcinem Markowskim i z Szymonem.
Zajmujemy się tworzeniem narzędzia, które właśnie potrafi automatycznie na podstawie kodu wygenerować diagramy i dokumentację.
Więc jeżeli ktoś z Was jest zainteresowany, to zapraszam na stronkę noesis.vision.
Taka trochę reklama.
Więc reklama reklamą, ale też jakby tym samym mówimy, że wiesz o czym mówisz i o czym będziesz mówił dalej, bo w sumie teoria ragów była najpierw teraz wchodzimy bardziej w zakres specjalizacji i ekspertyzy Twojej aktualnej.
Tak, ale co jest jeszcze ciekawe właśnie, bo to, co ja wam powiedziałem, to wam powiedziałem, jak to się standardowo robi, a to jak my to robimy, to my to robimy jeszcze odrobinkę inaczej.
Bo jak standardowo jest rozwiązywany ten problem właśnie w analizie kodu.
Pamiętacie, na początku, czy pamiętasz, tak?
Na początku mówiliśmy o tym czankowaniu.
I tutaj, jak mamy nasz kodzik, no to też chcemy go w jakiś sposób, w jakiś sposób chcemy go po... poczankować.
Więc to, co robimy, to najczęściej bierzemy ten kod w jakimś tam języku, który jest napisany i budujemy z niego drzewo AST, czyli nasz standardowy abstract syntax tree, po to, żeby zobaczyć, jakie... żeby jakby...
do standardowej formy go rozpisać, tak żebyśmy mieli drzewko, które nam pokazuje, jakie są relacje między poszczególnymi fragmentami.
To jest to struktura, którą zawsze też buduje sobie kompilator w trakcie kompilacji.
Tak jest.
Ustrukturyzowanie kodu.
Dokładnie, dla każdego pliku robimy sobie takie drzewko AST, bo to jest zawsze robione per plik, a nie per cały system.
Więc bierzemy tekst kodu, robimy z niego AST, następnie staramy się jakoś podzielić to na tyle sensownie, czyli zrobić te czanki, bo wiemy, że kod, on w ogóle właściwie nie jest językiem nieustrukturyzowanym, czyli czankowanie kodu to nie jest to samo, co czankowanie jakiegoś dokumentu, który jest napisany prozą.
Tylko tutaj wiemy, że to jest metoda, że to jest to obiekt, że to jest klasa.
Więc tutaj to czankowanie jest trochę bardziej inteligentne, bo my już wiemy, mniej więcej jesteśmy w stanie przeanalizować, jakie są te ograniczniki, żeby sobie jakieś tam sensowne czanki z tego zrobić.
A to AST pozwala nam też zobaczyć, jakie są zależności pomiędzy tymi poszczególnymi rzeczami.
Także widzimy, że ta metoda wywoła jakąś tam inną metodę i tak dalej.
Tak więc jeżeli mamy czanki per metody i mamy jakieś zależności między nimi, to tu już mamy trochę lepszy zestaw danych.
Więc mamy trochę łatwiejsze chunkowanie.
Robienie z tego embeddingów jest właściwie takie samo.
Robimy te embeddingi LLM-em, wrzucamy do naszej bazy danych, czasami określone jakimiś jeszcze dodatkowymi metadanymi i jesteśmy w stanie sobie po tym kodzie, my jesteśmy w stanie sobie wyszukiwać.
Najczęściej jest to zrobione tak, że bazy wektorowe pozwalają nam i właśnie robić takie szukanie semantyczne oparte o wektor, ale też pozwalają nam robić takie szukanie fulltext searchowe, jeśli ono będzie lepszym rozwiązaniem w przypadku kodu, czy czasami jakieś tam wyszukiwania pod kątem jakichś konkretnych atrybutów.
W zależności od tego, jak dużo tych metadanych dokładamy do tego naszego fragmentu kodu.
Znowu, zaznaczając to, że kod jest czymś ustrukturyzowanym, a nie po prostu prozo.
Więc jesteśmy w stanie wyłuskać z niego trochę więcej informacji.
No i to wrzucamy sobie do wektorowej bazy danych i następnie jesteśmy w stanie, czy nasz agent, w momencie, kiedy my mówimy, proszę, powiedz mi, w jaki sposób działa rabatowanie w mojej aplikacji,
on jest w stanie znaleźć te fragmenty kodu, które odpowiadają za rabatowanie, wrzucić je sobie do kontekstu, a następnie przeanalizować je i wypluć bardzo ładny opis, który mówi, twoje rabatowanie działa w taki sposób, być może nawet jest w stanie wygenerować jakiś diagram sekwencji, który to opisuje.
Więc tak to działa najczęściej.
Jakie mamy tutaj może wyzwania z tym rozwiązaniem?
Pierwszy to jest jak zwykle koszt.
Czyli żeby wziąć jakiś większy fragment kodu, odpowiedniego poszatkować i zrobić z niego te embeddingi naszymi elemami.
Im większa baza kodu, tym wyższy będzie rachuneczek.
Jesteśmy przyzwyczajeni do tego, że no daj spokój, tam ile kosztuje LLM, przecież płacę za niego 20-40 dolarów na miesiąc, czasami może 200, no ale jak już zaczynamy pracować na takim produkcyjnym kodzie, którego jest sporo, to te rachuneczki bardzo szybko potrafią wbijać, szczególnie, że tych
zapytań do takiego LLM-a, to potrafi się robić dziesiątki tysięcy, a czasami nawet setki tysięcy, żeby każdego takiego czanka sobie przekonwertować, a później sobie jeszcze nad tym pracować.
Plus też są takie teorie, że aktualne cynniki tych narzędzi to nie są docelowe cynniki tych narzędzi.
Ja myślę, że to nawet nie jest teoria, tylko to jest nieuniknione.
Patrząc na to, ile oni palą kasy, to prędzej czy później jakoś te ceny muszą się urealnić.
Warto jest uczyć się optymalizacji wykorzystania tych narzędzi już nawet na tym etapie, zanim przyjdzie bieda.
Zdecydowanie, zdecydowanie tak.
Czyli my optymalizujemy tę część właśnie, jak zrobić z naszego kodu...
właśnie jedni robią wektory, a drudzy robią graf wiedzy.
Bo tak pamiętacie, mieliśmy dwa rodzaje tych naszych raków.
Jedne, które wrzucały do bazy wektorowej, a drugie, które wrzucały do bazy grafowej.
Dzięki temu byliśmy w stanie mieć relacje pomiędzy poszczególnymi tymi elementami.
Więc to, co my robimy w Noesisie, co my w związku z tym, że mamy do czynienia z kodem, który ma jakąś strukturę,
to my stwierdziliśmy, że my nie podejdziemy do niego, do tego kuder, do tekstu, który ma jakieś tam AST.
AST ma swoje ograniczenia, jest tylko w obrębie jednego pliku, więc żeby zrobić cały ten mechanizm, żeby uzyskać powiedzmy informacje na temat całego use case'u, od kontrolera, po serwis, po repozytorium, plus jeszcze jakieś elementy domenowe, no to musielibyśmy wziąć to AST w jakiś sposób i merge'ować ze sobą i tak dalej.
Byłoby to dosyć trudne, jest to co kompilator zresztą robi.
Więc to, w tę stronę, w którą my poszliśmy, to my powiedzieliśmy, dobra, my nie będziemy pracować na tekście, tylko będziemy pracować na kodzie.
Czyli my piszemy narzędzie, które jest specyficzne dla danego języka.
Czyli jeżeli mamy .NET, to bazujemy na kodzie .NET-a i używamy Roslina do tego, żeby przeanalizować...
całą aplikację w procesie kompilacji.
Jeśli mamy Java, to tutaj także analizujemy bytecode przy użyciu ASM-a i Java Pressera i tego typu narzędzi.
Czyli my analizujemy całą bazę kodu właśnie w tym procesie kompilacji, wyciągamy wszystkie informacje na temat metod, obiektów, które w tym kodzie są i robimy z tego taki właśnie wielki graf wiedzy na temat całego tego kodu, zero LLMA w tym momencie.
LLM jest używane później, ale w tym momencie mamy używanie 0 LLM, a więc mamy tak.
Przewidywalność, bo skoro to jest deterministyczny algorytm, to tam nie ma żadnych halucynacji, ani złych wyników.
Koszt.
Znowu, nie używamy LLM, a tylko robimy to w sposób deterministyczny, więc jest to super tanie.
No i szybkie, bo znowu nie używamy elema, więc jesteśmy w stanie zrobić to bardzo szybko i uzyskujemy z tego graf całego naszego codebase.
I teraz jaki problem się pojawia?
Problem się pojawia taki, że ja tak naprawdę, czy my, nie jesteśmy w większości przypadków zainteresowani takim codebase, który się składa tylko i wyłącznie z obiektów i z metod, bo moglibyśmy sprowadzić.
Nie cały nasz codebase to są de facto jakieś obiekty i metody, nawet jeśli to nie jest język obiektowy.
tylko język funkcyjny.
To mamy zawsze jakieś byty, jakieś zachowania, jakieś obiekty i zachowania.
To może być typ, to może być funkcja, ale zawsze mamy te dwa elementy ze sobą.
Czasami mamy samo zachowanie, czasami mamy sam obiekt w języku funkcyjnym, że tu są nasze zachowania, tu są nasze obiekty czy tam struktury danych, czy typy, może bardziej poprawnie, a w języku obiektowym mamy te dwie rzeczy ze sobą związane w jednej konstrukcji.
Ale de facto każdy...
program, który mamy, jesteśmy w stanie sprowadzić do właśnie czegoś takiego, że to jest zestaw obiektów i zachowań i te obiekty i zachowania w jakiś sposób wchodzą ze sobą w interakcję.
I to jest ten nasz graf, który my budujemy.
I teraz kolejnym etapem jest to, pamiętacie, mówiłem wcześniej o tych ontologiach.
Że żeby zwiększyć dokładność tych naszych odpowiedzi, to my byśmy chcieli wiedzieć w tym naszym tekście, które elementy są bardziej istotne, a które są mniej istotne.
Bo my chcemy odpowiadać na pytania, tam był mój przykład z tą branżą medyczną, że mieliśmy właśnie jakieś lekarstwa, tutaj mieliśmy jakieś choroby, tutaj mieliśmy jakichś pacjentów i mamy jakąś tam relację między nimi, że pacjent ma taki objaw, ten objaw jest taki lek i dzięki temu jestem w stanie zapytać elema, hej, jeśli mam takie i takie objawy, to jakie powinienem wziąć leki?
Tak nie róbcie nigdy, nie?
ale powiedzmy w teorii moglibyśmy właśnie tak zrobić.
Jeśli mielibyśmy taką ontologię, to ten LLM szukałby tych elementów.
I w naszym kwocie jest podobnie.
Czyli nie wszystkie klasy, nie wszystkie obiekty są dla nas tak samo ważne.
nie wszystkie oznaczają to samo.
W jednym projekcie na przykład każda paczka to jest jakiś moduł, albo na przykład część paczek to są moduły, a część paczek to są warstwy, czy tam namespace'ów w dotnecie, prawda?
Czyli jak mam tylko napisane, że to jest jakiś namespace, to ja jako człowiek, jak czytam ten kod, to się domyślam, a, to jest namespace, który się nazywa web, to znaczy, że to jest pewnie warstwa jakaś taka webowa, frontendowa, a to jest namespace, który się nazywa pricing, a, to to jest nazwa domenowa, więc to jest pewnie ten namespace oznacza moduł.
Dla nas to jest jakby oczywiste, że potrafimy odróżnić, który namespace co oznacza.
Dla takiego LLM-a nie zawsze jest się w stanie domyślić, analizując ten kod na różne sposoby, ale to jest znowu to droższe rozwiązanie, które nie daje nam gwarancji, bo tutaj mogą być jakieś halucynacje, ale to, co moglibyśmy temu LLM-owi powiedzieć, to nałożyć pewną ontologię na ten nasz kodzik i powiedzieć te namespacy to są moduły,
Te klasy to są na przykład moje repozytoria, a te klasy to są moje obiekty domenowe, to są moje serwisy, to są moje endpointy, które wyrzucają na zewnątrz jakieś feature'y, na przykład nasze kontrolery, to mogłyby być metody na kontrolerach, to mogłyby być endpointy.
I gdybyśmy mieli właśnie taką...
taką ontologię, czyli gdybyśmy mogli powiedzieć temu LLM-owi, co jest czym w tym grafie, że które z tych jego węzłów to są takie ważne węzły, które nas interesują, a które to można by było totalnie olać i nic z tym nie robić, no to by było super.
I my właśnie coś takiego w naszym narzędziu robimy, czyli my łączymy ten nasz automat, który robi taki ogólny graf wiedzy o samym kodzie i mówimy naszym użytkownikom, słuchajcie, napiszcie z boku w takim prostym DSL-u konwencję.
Tak trochę jak to się robi w Archunicie, albo jak to się robi w, w dotnęcie chyba też jest odpowiednik, nie?
Archunita, czyli tego narzędzia do pisania testów jednostkowych, testów architektonicznych, tak samo jak piszemy testy jednostkowe.
Mówimy w tym DSL-u, opiszcie nam, co jest czym, tak?
Czyli na przykład wszystkie metody, które są w klasach takich i takich, to są moje endpointy, a wszystkie klasy, które się kończą na coś tam, to są moje encje, a wszystkie klasy, które dziedziczą po komendzie, to są moje komendy.
I każdy ma inną ontologię, każdy ma trochę inne znaczenie w swoich projektach przykłada, więc to jest rzecz, którą każdy opisuje w swoim systemie.
I my łącząc te dwie rzeczy ze sobą, czyli łącząc ten nasz graf, który wypluwamy z tymi konwencjami, które są, tworzymy analogiczny graf wiedzy, jak robią to LLM-y, tylko że w sposób w pełni deterministyczny.
Czyli mamy taką deterministyczną bazę wiedzy.
Czyli jeżeli mamy teraz tę bazę kodu, bazę wiedzy na temat naszego kodu, wiemy, które elementy są ważne, istotne, które są ze sobą w jakiś sposób połączone w tym grafie, jesteśmy w stanie zadawać mnóstwo ciekawych pytań.
Jesteśmy na przykład w stanie zadać mu pytanie właśnie, nie wiem, w jaki sposób działa feature pricingu.
i on wtedy wie, a, feature pricingu, no to muszę znaleźć jakiś endpoint, czyli jakieś takie wejście do tego mojego use case'a, które jest związane z pricingiem.
Jak już mam ten endpoint, no to z tego grafu jestem w stanie wyciągnąć wszystkie elementy, które są dokładnie z tym konkretnym featurem powiązane.
Więc jestem w stanie wyciągnąć dokładnie te fragmenty kodu, które są realizowane w tym jednym konkretnym use case'ie.
i te wrzucić tylko do kontekstu mojego LLM-a, żeby on mi je być może opisał i wygenerował dokumentację, być może zrefaktoryzował ten feature w jakiś tam inny sposób.
Więc my używamy tego do raz wygenerowania dokumentacji, właśnie na przykład dla każdego endpointu, który mamy w tym.
Wyobraźcie sobie takiego...
Wyobraźcie sobie takiego swaggera, takiego API swaggera, takiego swagger huba, który daje nam informacje o tym, o dokumentacji naszych endpointów, tylko że oprócz tego, że daje informacje na temat kontraktu, to jeszcze potrafi opisać, jak to pod spodem wszystko działa.
I te opisy, to jest dopiero ten moment, kiedy wchodzi AI.
Czyli kiedy my już wiemy, które elementy są istotne, które warto jest opisywać, bo po co mi jest na przykład opis, po co mi wysłać do LLM-a kod mapera, który mapuje mi, prawda, jeden fragment kodu na drugi kod.
Albo jakiegoś takiego nieistotnego fragmentu związanego z nie wiem, z jakąś tam serializacją, albo podobnymi rzeczami.
Ja chcę tylko wysłać ten taki najistotniejszy, najbardziej istotny kod.
Więc to, co my zrobiliśmy, to my właśnie przeanalizowaliśmy ten fragment tworzenia tych ragów w taki czysto AI-owy, LLM-owy i myśleliśmy, kurczę, w przypadku kodu my moglibyśmy to zrobić lepiej.
Wadą naszego rozwiązania jest to, że my musimy mieć parcel per język.
Czyli jak robimy to w formie tekstowej, no to konwertery do AST mamy właściwie dla każdego języka.
Więc jesteśmy w stanie wspierać, elementowe rozwiązania na ten moment są wspierać każdy język.
Nasze rozwiązanie, ono musi być bazowane, musi być zaimplementowany per poszczególny język.
Jak ma być lepiej, to nie musi być łatwo.
Ja od jakiegoś czasu, nie wiem czy się bawiłeś, ale od jakiegoś czasu bawię się Cloud Codem od jakichś trzech tygodni i wrzucam to w różne codebazy i zadaję mu pytania, na przykład kto pracuje w projekcie i jest najlepszym tutaj programistą i tak dalej, albo to jest największym wyzwaniem i on...
Bardzo fajnie to robi, bo on pokazuje, jakich komend używa.
Na przykład bierze sobie ostatnie 100 komitów z Gita, analizuje komit messages, analizuje, co tam się zmieniało i faktycznie jego odpowiedzi są bardzo fajne.
Więc to jest taki, jak to nazwać, rak dla ubogich?
Też nie ubogich, bo tam też to konie lecą.
Myślę, że tam pod spodem też może siedzieć jakiś mini-rag, który to analizuje.
Wątpię, żeby on za każdym razem do kontekstu wkładał absolutnie wszystko, raczej ten agent.
Wszystko nie, on sobie wybiera pliczki, które są lokalnie, on to jakoś selektywnie robi, nie wrzuca całego codebase'u.
Właśnie, więc też jakiegoś raga musi mieć.
W każdym razie polecam do pobawienia się, jeśli ktoś jest zainteresowany, jak to w ogóle może wyglądać.
Wiesz, czym eksperymentujemy?
Eksperymentujemy z tym, żeby na podstawie tego grafu wiedzy, który mamy, generować ClodMD pliki dla poszczególnych modułów, żeby właśnie miał jeszcze więcej informacji na temat tego, co się w danym module dzieje.
Okej, żeby za każdym razem nie musiał tego od nowa analizować.
Dokładnie, żeby dodać mu dodatkową informację na temat, co tutaj jest i takie automatyczne generowanie tych plików MD to też jest coś, co mamy na swojej roadmapie.
Takiemu zwykłemu programiście czy programistce do czego to może być potrzebne?
No w sumie te kilku rzeczy.
Najbardziej to do tego, żeby nie być tak często wyrywanym na spotkania.
Bo to, co my próbujemy zrobić, to wiecie, Cloud Code to jest narzędzie, z którego szukają deweloperzy.
To jest to, że przychodzą do nich ludzie i cały czas pytają, dobra, ale powiedz mi, jak to teraz działa, co to teraz robi, bo ja potrzebuję, nie wiem, jestem product ownerem, potrzebuję wymyślić jakiś nowy feature, jestem jakimś innym zespołem, tak, potrzebuję tej wiedzy, bo chciałbym zrozumieć, jak to rozwiązanie działa, albo coś tam skopiować od siebie, bo chciałbym się z tobą zintegrować, muszę mieć jakąś większą wiedzę.
Przynajmniej takie jest moje doświadczenie, a wydaje mi się też wielu deweloperów, to jest to, że jesteśmy notorycznie odrywani od pracy i 17 razy powtarzamy to samo i tłumaczymy innym ludziom, czy to ktoś nowy przychodzi do projektu, czy właśnie ktoś z innego zespołu, czy jakiś producent, czy jakiś tester, czy ktokolwiek inny, nie?
W momencie, kiedy my bylibyśmy w stanie dać im taką deterministyczną wiedzę na temat tego, co się w naszym systemie dzieje w postaci zarówno diagramów, które jesteśmy w stanie automatycznie wygenerować i to robimy, jak i właśnie opisu tekstowego.
poszczególnych endpointów, czyli właśnie ustrukturyzowanego w takiej formie, że oni są w stanie nawet eksplorować sobie ten kod, czy są w stanie nawet wygenerować dokumentację przy czymś na wiki, na jakiegoś konsluensa, na cokolwiek, to to sprawia, że taki deweloper po prostu będzie rzadziej odrywany od pracy.
My sobie wyobrażamy to trochę na takiej zasadzie, że to jest taka druga linia supportu.
Wiesz, jak na przykład mamy zespół deweloperski i są jacyś użytkownicy i oni mają jakieś problemy, no to to nie jest tak, że oni od razu dzwonią do zespołu deweloperskiego, prawda?
Mamy najczęściej jakąś pierwszą linię supportu, która próbuje jakieś problemy rozwiązać, potem drugą linię supportu.
Zespół najczęściej jest trzecią linią supportu, która jest robiona, nie?
I teraz to jest dla klientów, dla użytkowników zewnętrznych, ale dla ludzi wewnątrz też moglibyśmy mieć tę samą koncepcję.
Czyli zamiast, jakby jak często ludzie chcieliby napisać swojemu właśnie, nie wiem, product może ownerowi, może komuś innemu, tak?
Zamiast pytać mnie tak, read the fascinating manual.
Tylko, że tego manuala nie ma.
A nawet jak jest, to jest nieaktualny.
Więc oni wolą przyjść bezpośrednio, takiego dewelopera co chwilę wyciągać albo na jakieś spotkania, albo właśnie na jakieś odpytki, żeby tę wiedzę wyciągnąć.
Znaczy wiesz, podejrzewam, że ty też, tak samo jak i ja i pewnie większość naszych słuchaczek i słuchaczy mają doświadczenie z tą pierwszą linią supportu na różnego rodzaju infoliniach, gdzie też są pewnie wpięte różne systemy typu RAG i przez te pieprzone boty nie da się przebić do tego człowieka wyoutsourcowanego, który też nic nie wie i dopiero on musi do tej trzeciej linii supportu nas przełączać.
Dlatego jest kwestia jakości.
Dlatego jest kwestia jakości.
Mam takie doświadczenie właśnie, kiedy w końcu wyjdę z tego flowu AI-owego i się do jakiegoś konsultanta dobiorę.
Ja ciągle wciskam to zero w telefonie, jeszcze nie słucham, nie czekam, aż skończę gadać.
Właśnie, tak.
Ale czasami rzeczywiście zdarzają się takie, które na jakieś tam proste pytanie potrafią odpowiedzieć.
I teraz chodzi o to, żeby taki rak nie odpowiedział na wszystkie pytania.
Ona ma być taką ogólnie informacyjną, taką, że jak ktoś się przygotowuje na spotkanie ze mną, to nie przychodzi na nie zielony, tylko jest się w stanie trochę odpytać tego raga, albo tego czata, albo nawet przeczytać na wiki te dokumentacje, które ja wygenerowałem, żeby on chociaż przyszedł już z jakąś wiedzą, a nie, że mamy godzinne spotkanie i ja przez pół godziny...
jak coś działa, żebyśmy w ogóle mogli przejść do takiego drugiego elementu wymyślania czegoś.
Niekoniecznie osoba z innego działu, to może być też nowa osoba w zespole, czyli onboarding może być o wiele łatwiejszy, ponieważ zamiast poświęcać dwa tygodnie na przechodzenie wspólnie po kodzie i siedzenie w tym ID,
jak to się mówi brzydko, dupa w dupę, to po prostu można dać takie coś i powiedzieć, weź se, popytaj, popatrz i w ciągu jednego dnia można pewnie zrobić więcej niż... Skaza się.
No, okej.
Skaza się, chociaż tyle, nie?
Zobacz, o ile rzeczy my teraz pytamy, czata, żeby nam powiedział, tak?
Kiedyś trzeba było zadzwonić do kolegi, który zna się na marketingu, trzeba było zadzwonić do kolegi, który się zna na, nie wiem, na robieniu jakiegoś wideo, ej, weź mi, powiedz, jak ty byś to zrobił, nie?
To jest ta nasza pierwsza linia supportu, że już nie muszę bezpośrednio do tego kogoś iść, tylko mam kogoś, kto mi to rzeczywiście stanie wystarczająco dobrze wytłumaczyć.
Żeby ta komunikacja w zespole była, więc mówię, jak pytasz, co ma z tego taki zwykły deweloper, to zwykły deweloper ma z tego właśnie to, że nie zawracają mu głowy tak bardzo, jak wcześniej, nie?
Bo ma taką pierwszą linię supportu stworzoną przy użyciu właśnie, ma aktualną dokumentację, tak?
Być może przy użyciu Unesys, może przy użyciu jakiegoś innego narzędzia, takiego czysto LLM-owego,
Często drugą linią supportu jest osoba, która jest wyznaczona w zespole, która akurat w tym sprincie jest tym punktem kontaktu, żeby wszystkich nie wyrywać.
To wtedy masz taką drugą linię supportu, że jest taki jeden wyznaczony.
Dobra, to ty w tym tygodniu odpowiadasz na wszystkie pytania i jesteś naszym drugą linią supportu.
a zespół staje się wtedy taką trzecią linią supportu dla zewnątrz.
Dzięki temu jest w stanie się skupić na tej pracy, którą rzeczywiście wykonuje, a nie jest rozszarpywany po 17 spotkaniach, żeby tłumaczyć 23 raz te same rzeczy.
Inny ciekawy case, to jest znowu może nie dla dewelopera, tylko bardziej dla osoby, która zarządza projektem.
Co robisz, kiedy zwalnia ci się osoba, która ma bardzo dużo wiedzy na temat tego, jak działa system.
Załamujesz rączki i chlipiesz troszkę.
A potem dajesz podwyżkę.
A potem dajesz podwyżkę, albo jak się nie uda, no to chlipiesz jeszcze bardziej.
A w momencie, kiedy właśnie nie budujemy takich osób, które mają wszystko w głowie i nie jest tak, że one, bo to i dla nich jest problem, bo one muszą być na wszystkich spotkaniach i brać udział w podejmowaniu tych decyzji, bo one mają tą wiedzę, więc to jest i dla tej osoby trudne i dla organizacji trudne, jak takiej osoby nie ma.
Więc w momencie, kiedy ta wiedza staje się bardziej dostępna dla wszystkich,
to generalnie nasza praca staje się raz bardziej asynchroniczna, mniej zależymy od siebie i jesteśmy bardziej produktywni, bo każdy jest się w stanie skupić dokładnie na tym, na czym aktualnie potrzebuje.
Przynajmniej w jakimś tam rzędzie, tak to nie jest oczywiście pana całą na wszystko, ale mówię dokumentacja, tak nie narzędzie, które budujemy, ono jest jakby wtórne, ale jakby sam fakt posiadania aktualnej dokumentacji, aktualnej wiedzy.
Kiedyś nie mieliśmy swaggerów i trzeba było iść do kogoś albo zerkać w czyjś kodzik, jaką on ma tam API, co on tam wystawia, jakie on ma parametry.
Albo nawet zawołać kogoś i wytłumacz mi, jak ten twój fragment, twój mikroserwis działa, żebym mógł się z nim zintegrować.
Dzisiaj wchodzę sobie na swaggera, patrzę sobie, co tam jest, widzę wszystko, wiem, jak się zintegrować i nawet nie muszę za bardzo pytać.
I teraz to samo my moglibyśmy mieć właśnie dla tej wiedzy domenowej, która jest zawarta w naszym kodzie.
Dokładnie analogiczną rzecz moglibyśmy próbować robić.
No tak, z kodu można dużo bardzo wywnioskować, ale właśnie tak jak mówiliśmy wcześniej, jak dołożyć do tego jeszcze całą historię z repozytorium, tikety i całą tą otoczkę, to w ogóle mamy właściwie dokumentację na każdym etapie, tak?
Tak, to jest nasza przyszłość.
My na razie potrafimy tylko i wyłącznie analizować kod jako snapshot, ale w przyszłości chcemy też dołożyć właśnie analizę historii i jeszcze pewnie parę innych ciekawych feature'ów.
Ale to, na czym teraz się skupiamy, to jest właśnie ten element diagramowania, czyli z jednej strony, jeżeli ktoś tworzy diagramy i musi utrzymywać je właśnie, żeby były aktualne, a w praktyce nikt tego nie robi, to dzięki temu, że mamy właśnie ten deterministyczny ekran, jesteśmy w stanie generować diagramy i one są zawsze up to date po każdym komicie.
Albo z drugiej strony właśnie generowanie dokumentacji.
Taki trzecim punktem to jest właśnie to wspieranie LLM-ów do tego, żeby właśnie ten kontekst engineering był jeszcze bardziej precyzyjny, jeszcze lepszy.
Z tego co wiem, to właśnie większość startupów skupia się na tym, żeby wszystko robić przy pomocy AI, a my właśnie poszliśmy w tym kierunku, ok, co właśnie się stanie, jeśli za chwilę ten AI będzie na tyle drogi.
że, no, że trzeba będzie jakieś optymalizacje wprowadzić, nie?
To w tym wypadku jakby my jesteśmy odpowiedzią też na to pytanie, nie?
Jak to robić rzeczywiście w miarę tanio i deterministycznie i używać tylko w tych rzeczach, w których on jest bardzo dobry, a w tych, w których są deterministyczne rozwiązania, to użyjmy, nie?
Jeszcze jeden przykład ci podam ciekawy.
Na przykład dużo tutoriali, które czasami oglądam, polega na tym, że
wygeneruj mi projekt na przykład, nie?
I ktoś mówi elemowi, weź mi wygeneruj projekt w Springboocie z czymś tam, nie?
No kurczę, przecież mam Spring initializera, wystarczy sobie, że sobie w jednym formularzu wypełnię trzy pola, buch i mam to.
mniej dwutlenku węgla, gwarancję, że to jest dobrze zrobione i tyle, tak?
Po co mam używać LLM-a do scaffoldowania projektu?
Każda technologia ma swój naprawdę bardzo dobry scaffolder, który zużyje, tak powiem, tego dwutlenku węgla dużo, dużo mniej i zrobi to dobrze.
AI sobie już nie w środku.
Jak już mam ten scaffolding, to powiem mu, dobra, tak, to teraz doszuć mi jakieś feature, które mnie interesują.
konkretna, a nie generujska folding.
Nie, że my musimy się nauczyć, które elementy opłaca się robić AI-em.
których nie, które możemy robić ciągle w taki sposób deterministyczny, żeby też nie popaść w to, że teraz wszystko będziemy robić przy pomocy LLM-a.
Moim zdaniem nie ma takiej konieczności i za chwilę coraz więcej właśnie wejdziemy w to plateau of productivity w tym takim słynnym diagramie Gartnera.
Teraz jesteśmy jeszcze trochę tak na szczycie hype'u, może powoli zaczynamy z niego schodzić.
A jak wejdziemy właśnie na to plateau w productivity, to już jasne dla nas będzie, że te rzeczy się robi elemenem, a tych rzeczy się elemenem po prostu nie opłaca.
Bo jak można, to zadziała wystarczająco dobrze, ale po prostu się nie opłaca.
My jako DevStyle mieliśmy jedną taką publiczną przygodę właśnie z Ragiem i to robił nasz wspólny znajomy Tomek Stolarczyk nam.
w jednym ze szkoleń, czyli Domain Drivers, mamy bota na Discordzie, który się nazywa Dyddamian, jako że to jest od DDD i możesz tego bota zapytać właśnie chociażby o value objecty, jako że to jest szkolenie z Domain Driven Design i on wtedy przechodzi przez wszystkie transkrypcje, wszystkich lekcji w całym szkoleniu
I zwraca ci tytuł lekcji, w której dane słowo występuje właśnie tak semantycznie.
Kto to mówi, cytat tej osoby, a następnie jeszcze link do platformy ze znacznikiem czasowym, kiedy to dokładnie jest mówione.
I to właśnie dlatego wcześniej podpytywałem o to, czy można dodawać jakieś atrybuty do tych embeddingów, ponieważ tu właśnie jest dokładnie tak zrobione, że cała ta transkrypcja jest podzielona na czanki, to akurat było robione ręcznie, potem z każdego czanka było wygenerowane podsumowanie, potem z każdego podsumowania były wygenerowane tagi i dodatkowo właśnie ten znacznik czasowy plus link do platformy edukacyjnej, kiedy dokładnie to jest mówione.
To była robota ręczna, no bo AI nie ogląda wideo, tylko operuje właśnie na transkrypcjach.
Czy to było, że było na podstawie napisów, więc tam jeszcze znaczniki czasowe były być może w tym tekście nawet uwzględnione.
I to faktycznie po prostu były zrobione te embeddingi, Tomek włożył to wszystko w kudranta, w bazę wektorową, tylko też nie chcieliśmy, aby ten AI był tam wykorzystywany tak bardzo mocno, bo zależało nam na tym determinizmie.
Dlatego tam nie ma wymyślania przez AI, co to jest ten value object, czy jak tam znaleźć jakieś granice.
On nawet nie analizuje tego, co ma zwrócić, tylko bierze ten tekst bezpośrednio z tej bazy wektowej, wrzuca to jako odpowiedź i jako rozwinięcie dodaje właśnie link do lekcji, w której można sobie bezpośrednio kliknąć i przejść do dokładnie tej sekundy, kiedy to jest opowiadane.
Ale samo też pytanie, no ktoś może zapytać, w sumie kiedyś na jednym z forów była taka akcja, że ktoś pod jakąś tam dyskusją napisał, a teraz podaj mi przepis na jajecznicę, no i admin odpisał, oczywiście już podaję.
więc się okazało, że te komentarze tam są moderowane na jednym z forów w sposób po prostu automatyczny, żeby czegoś takiego uniknąć, bo normalnie z tego bota można publicznie na naszym Discordzie korzystać, w Discordzie dedykowanym do tego szkolenia, więc AI jest wpięty tylko w jedno miejsce, czyli analizuje, czy pytanie dotyczy programowania.
Jeśli nie, to zwracam, że chętnie bym z Tobą pogadał, ale ja tylko o oprogramowaniu, więc nara.
A potem AI jest używany, LLM jest używany tylko do stworzenia embeddingu właśnie z pytania i tyle.
Dzięki temu to jest praktycznie darmowe, chociaż przetworzenie tego wszystkiego na pewno swojej tam kosztowało.
To, że właśnie wykryliście to, że jest pewna struktura w tych danych i byliście w stanie te dane, w tym wypadku manualnie, bo nie było ich dużo, ustrukturyzować, to to bardzo prawdopodobnie dobrze wpłynęło na rezultaty, że te embeddingi były wygenerowane na podstawie chunków, które mają sens.
Kiedy robi to LLM, nie mamy tutaj takiej gwarancji, że on to rzeczywiście pochunkuje w taki sposób.
To jest cała sztuka na ten element chunkowania.
w ogóle dużo pracy wykonywanej przez RLM-y, jednak warto jest nadzorować, co nie?
Jeszcze nie jesteśmy na etapie, gdzie można całe swoje życie oddać RLM-owi i tylko czekać, gdzie nas to poniesie.
Pytanie, czy kiedykolwiek dojdziemy do tego etapu.
Mam nadzieję, że nie, bo życie może być fajne, jak się je przeżywa samemu.
Ale to już zagłębiamy się w takie rzeczy, jak wspomniałeś, zacząłeś od tego, jakby wrzucić wszystkie książki filozoficzne w RLM-a, no to proszę, my wrzuciliśmy troszkę tutaj na koniec.
Lubisz majsterkować?
Zawsze chciałem majsterkować, ale nie jestem w tym najlepszy.
W takim razie, gdyby rag był jakimś utensylium kuchennym, naczyniem, narzędziem, to czym by był?
Kurde.
Kukidu z Thermomixa.
Wy tego nie widzicie, ale widzę minę Maćka teraz.
Jest takie narzędzie do gotowania, które się nazywa Thermomix.
To jest taka... To wiesz, co to jest.
Miska z mieszadłem.
I ta miska z mieszadłem ma taki ekran, który pozwala ci wyszukiwać przepisy.
I potem prowadzi cię krok po kroku, jak te przepisy wykonywać.
I to się nazywa... Ta przystawka, która to umożliwiała, to się nazywało Kukidu.
Więc mi się to skojarzyło właśnie z cookie dough w Thermomixie.
Jestem usatysfakcjonowany tą odpowiedzią.
Dobra, dzięki bardzo Łukasz za dzisiaj.
Mamy nadzieję, że z tej rozmowy dowiedzieliście się, co to jest RAG, gdzie warto go stosować, jak do tego mniej więcej podejść oraz w kontekście programistycznym, do czego może być przydatne i na co uważać.
Dzięki wielkie.
Dziękujemy.
Ostatnie odcinki
-
DevTalk #137 – O Wypaleniu w IT z Olą Kunysz
02.02.2026 13:40
-
DevTalk #136 – O Testach Kontraktowych z Łukasz...
19.01.2026 17:29
-
DevTalk #135 – O Architekturze Ewolucyjnej z Ma...
12.01.2026 15:50
-
DevTalk #134 – O DevOps 2025 z Piotrem Zimochem
29.12.2025 14:31
-
DevTalk #133 – O Długu Technologicznym z Oskare...
24.11.2025 12:50
-
DevTalk #132 – O Startupach przy Rewolucji AI z...
10.11.2025 13:49
-
DevTalk #131 – O Przewadze w Świecie LLM-ów z T...
30.10.2025 13:10
-
DevTalk #130 – O RAG do Eksploracji Kodu z Łuka...
27.10.2025 16:36
-
DevTalk #129 – O Programowaniu z AI z Tomaszem ...
13.10.2025 11:37
-
DevTalk #128 – O Roli Lidera i Wyzwaniach Ery A...
29.09.2025 10:13