W poniedziałek wieczorem rysowałem na kartce trzech agentów. Pierwszy odpowiada na pytanie, drugi kwestionuje odpowiedź, trzeci rozstrzyga spór. Nazwy mieliśmy już wymyślone, plan w czterech etapach też. We wtorek rano, zanim zacząłem cokolwiek budować, otworzyłem kod mojej wyszukiwarki wiedzy. Chciałem tylko sprawdzić, co dokładnie dostaje agent, kiedy prosi o dokumenty. Dostawał pięć rzeczy: treść fragmentu, ścieżkę pliku, nazwę sekcji, kategorię i ocenę trafności. Daty nie dostawał.
I to był koniec planu z trzema agentami, przynajmniej na razie. Prowadzę agencję AI i automatyzacji dla firm 10 - 50 osób, a wszystko, co radzę klientom, najpierw sprawdzam na sobie. Ten tekst jest o tym, jak omal nie zbudowałem drogiej nadbudowy nad błędem, który siedział w jednym brakującym polu. Obiecałem go w poprzednim wpisie, bo naprawa zasługuje na osobną historię. Nie dlatego, że była trudna. Dlatego, że była łatwa, a ja przez cały wieczór patrzyłem w zupełnie inną stronę.
Spis treści
- Skąd wziął się pomysł na trzech agentów?
- Co właściwie dostawał mój agent z wyszukiwarki?
- Jak wyglądała naprawa, krok po kroku
- Co się zmieniło w odpowiedziach?
- Dlaczego sceptyk by tego nie znalazł?
- Gdzie ta naprawa jest słaba? Uczciwie
- Jak sprawdzić to u siebie, zanim coś dobudujesz
- Co zostaje z tej historii
- Słownik pojęć
Skąd wziął się pomysł na trzech agentów?
Zacznę od tego, co było wcześniej, bo bez tego nie zrozumiesz, dlaczego kartka z trzema agentami w ogóle powstała.
W sierpniu przepuściłem mojego asystenta przez benchmark na 900 przebiegów. Sędzia, czyli osobny model oceniający odpowiedzi, oznaczył 94 z nich jako halucynacje. Najgłośniejszy przypadek: agent zapytany o cennik naszego systemu mailingowego podał kwotę sprzed dwóch miesięcy, z pakietami, których już nie sprzedajemy. Podał ją pewnie, ze źródłem, w ładnym zdaniu. Gdyby to poszło do klienta, klient dostałby ofertę, która wygląda na przygotowaną rzetelnie i jest nieprawdziwa. Opisałem to dokładnie w tekście o cennikach, więc tutaj tylko przypomnę: w bazie leżały cztery wersje tej samej ceny i żadna nie była oznaczona jako nieaktualna.
Wieczorem tego samego dnia zacząłem myśleć jak każdy właściciel firmy, któremu pracownik popełnił błąd: trzeba kogoś, kto to sprawdzi, zanim wyjdzie. I tak powstał pomysł. Pierwszy agent produkuje odpowiedź. Drugi, sceptyk, kwestionuje wszystko, co nie jest zerojedynkowe, i wskazuje słabe miejsca w rozumowaniu. Jeśli się nie zgadzają, rozmawiają. Jeśli rozmowa kończy się patem, wchodzi trzeci, sędzia, i orzeka. Miałem nawet całkiem niezłą myśl o pętlach: nie ustalać z góry, ile rund dyskusji, tylko przed każdą kolejną pytać, czy ma sens.
Policzyłem to na szybko. Sam sceptyk, jako osobny agent z własnym dostępem do bazy wiedzy, to mniej więcej dwa i pół raza więcej tokenów na każde zadanie. Na każde, na zawsze, bo sceptyk nie wie, które zadanie jest trudne, więc musi sprawdzać wszystkie. Zaakceptowałem ten koszt w głowie w jakieś dziesięć sekund. Błąd z cennikiem był na tyle wstydliwy, że byłem gotów zapłacić.
Tego wieczoru zajrzałem jeszcze do literatury, bo nauczyłem się przy artykule naukowym, że zanim cokolwiek zbudujesz, sprawdź, czy ktoś już to zmierzył. Wzorzec ma nazwę: debata wielu agentów. I wyniki pomiarów nie są dla niego łaskawe, ale to temat na osobny tekst. Tu ważne jest coś innego. Nawet gdyby literatura mówiła, że debata działa świetnie, w moim przypadku i tak nie miałaby czego naprawić. Dowiedziałem się tego dopiero następnego ranka.
Co właściwie dostawał mój agent z wyszukiwarki?
Moja wyszukiwarka wiedzy działa tak jak większość takich narzędzi, które dziś sprzedaje się małym firmom pod hasłem „AI na waszych dokumentach". Pytanie zamienia się w liczby, baza zwraca kilka najbardziej pasujących fragmentów dokumentów, a te fragmenty trafiają do modelu jako kontekst. Model czyta kontekst i odpowiada. Pisałem o tym, jak zbudowałem ją od zera i czemu w ogóle uznałem, że mi się to opłaca.
Wyobraź sobie, że nowy pracownik dostaje od Ciebie zadanie: „Podaj klientowi aktualny cennik". Nie ma dostępu do segregatora. Zamiast tego dostaje od asystentki pięć kserówek. Na każdej jest fragment jakiegoś dokumentu, nazwa pliku, z którego pochodzi, nazwa rozdziału, dział firmy i ocena, jak bardzo kserówka pasuje do pytania. Nagłówek strony z datą został ucięty przy kopiowaniu. Na wszystkich pięciu.
Co zrobi ten pracownik? Weźmie kserówkę, która najlepiej pasuje do pytania, i przepisze z niej cenę. Nie dlatego, że jest głupi. Dlatego, że na żadnej z pięciu kartek nie ma informacji, która pozwoliłaby wybrać inaczej. Gdyby zapytał asystentkę, z kiedy jest ta kartka, usłyszałby: nie wiem, ucięło się.
Dokładnie w tej sytuacji był mój agent. Funkcja wyszukiwania zwracała treść, ścieżkę, sekcję, kategorię, trafność. Pięć pól. Daty wśród nich nie było, bo kiedy pisałem tę funkcję wiosną, nie przyszło mi do głowy, że będzie potrzebna. Wtedy baza miała jedną wersję każdego faktu. Cztery miesiące później miała cztery wersje cennika i tę samą funkcję.
Najgorsze jest to, że dzień wcześniej, zaraz po benchmarku, dopisałem do instrukcji agenta całą sekcję o aktualności danych. Jeśli podajesz kwotę, dopisz datę dokumentu. Jeśli znalazłeś dwie wersje, wymień obie. Nie wybieraj sam. Dobre reguły, nadal je mam. Tyle że agent fizycznie nie mógł ich wykonać. Nie da się podać daty, której się nie dostało. To tak, jakbyś wywiesił w biurze kartkę „zawsze sprawdzaj datę na dokumencie" i dalej rozdawał kserówki z uciętym nagłówkiem. Reguła była. Danych nie było.
Szczerze? Było mi głupio. Spędziłem wieczór, projektując sceptyka, który miał kwestionować rozumowanie agenta, a rozumowanie agenta było w porządku. Znalazł dokument, przeczytał, zacytował wiernie. Zawiodło narzędzie, które mu ten dokument podało.
Jak wyglądała naprawa, krok po kroku
Trzy zmiany w jednym pliku. Kilkadziesiąt linii. Jedno przedpołudnie, z czego większość zajęło mi sprawdzanie, czy niczego nie zepsułem. Opiszę je po kolei, bo każda ma swoje „dlaczego", a to „dlaczego" przyda Ci się bardziej niż sam kod.
Funkcja, która czyta datę z dysku
Pierwsza zmiana to jedna krótka funkcja. Dostaje ścieżkę do pliku, z którego pochodzi fragment, i oddaje datę jego ostatniej modyfikacji. Tyle. System operacyjny trzyma tę datę przy każdym pliku, trzeba było tylko po nią sięgnąć.
Mogłem to zrobić inaczej: dopisać datę do indeksu przy budowaniu bazy, tak żeby siedziała obok treści fragmentu. Byłoby czyściej. Nie zrobiłem tak z dwóch powodów. Po pierwsze, przebudowa całego indeksu na moim laptopie to godziny, a chciałem mieć poprawkę od razu. Po drugie, data czytana z dysku pokazuje stan faktyczny także wtedy, gdy ktoś zmienił plik już po zaindeksowaniu. Indeks starzeje się między przebudowami, dysk nie.
Mała rzecz, ale uczy czegoś ogólnego: najszybsza naprawa to nie zawsze ta najelegantsza. To ta, która działa dziś i nie wymaga ruszania czegoś, co już działa.
Data przy każdym fragmencie, nie w stopce
Druga zmiana to miejsce, w którym data się pojawia. Kontekst, który trafia do modelu, to po prostu długi tekst: kilka fragmentów jeden pod drugim, każdy z nagłówkiem, skąd pochodzi. Dopisałem datę do tego nagłówka. Teraz każdy fragment zaczyna się od linii w stylu: dokument taki, sekcja taka, dokument z 27 czerwca 2026.
Kusiło mnie, żeby zrobić to prościej i wypisać wszystkie daty raz, na końcu kontekstu, w formie listy. Nie zrobiłem tak, bo model czyta fragment i cytuje fragment. Jeśli data stoi dwadzieścia linii niżej, w zbiorczym spisie, to przy cytowaniu musi ją sobie dopasować. Czasem dopasuje, czasem nie. Data przy fragmencie nie wymaga dopasowywania. Jest tam, gdzie oczy.
Wracając do kserówek: to tak, jakby asystentka zamiast ucinać nagłówek, dopisywała datę długopisem na górze każdej kartki. Nie na osobnej karteczce z listą. Na każdej kartce.
Jedno zdanie instrukcji na górze kontekstu
Trzecia zmiana to cztery linijki tekstu, które poprzedzają fragmenty. Mówią mniej więcej tak: przy każdym fragmencie podana jest data dokumentu; jeśli podajesz kwotę, termin albo ustalenie, przywołaj tę datę; jeśli dwa fragmenty mówią co innego o tym samym, wymień obie wersje z datami, zamiast wybierać jedną.
Czyli ta sama reguła, którą dzień wcześniej wpisałem do instrukcji agenta. Tylko że teraz stoi obok danych, których dotyczy, a nie w ogólnym opisie zachowania, trzy ekrany wyżej. I teraz da się ją wykonać, bo daty są.
Jest jeszcze jeden skutek, którego nie planowałem, a z którego cieszę się najbardziej. Mój drugi agent, ten od wiedzy firmowej, korzysta z tej samej funkcji budującej kontekst. Dostał poprawkę bez jednej zmiany we własnym kodzie. Nie musiałem go ruszać, testować, pilnować. Naprawa u źródła rozlała się sama na wszystko, co ze źródła korzysta. Nadbudowa, czyli sceptyk, działałaby tylko tam, gdzie bym ją podłączył.
Co się zmieniło w odpowiedziach?
Sprawdziłem na tym samym pytaniu, które wcześniej dało błąd. Nie na nowym, wymyślonym pod poprawkę. Na tym, które mnie zawstydziło.
Przed poprawką agent podawał cenę sprzed dwóch miesięcy, pewnie i z nazwami pakietów. Po poprawce podał cenę aktualną, czyli abonament miesięczny plus jednorazowe wdrożenie. Do tego, bez proszenia, wypisał tabelę: jak ta cena zmieniała się od maja, przez czerwiec i lipiec, do sierpnia, z datami dokumentów przy każdej wersji. I na końcu dopisał, których dokumentów nie cytować klientom, bo zawierają nieaktualne warunki.
Zero zmian w modelu. Zero nowych agentów. Zero dodatkowych tokenów na zadanie, bo kilka dat w kontekście to ułamek procenta objętości. Ta sama baza, z tymi samymi czterema wersjami cennika w środku. Jedyna różnica: agent w końcu widział, co trzyma w ręku.
Dwie uczciwe uwagi, żebyś nie wziął tego za więcej, niż jest. Po pierwsze, nie powtórzyłem całego benchmarku na 900 przebiegów po tej zmianie. Sprawdziłem pytanie, które zawiodło, i kilka pokrewnych. Liczbę „ile halucynacji zniknęło" podam, kiedy ją zmierzę, nie wcześniej. Po drugie, ta tabela ewolucji cen jest w odpowiedzi dlatego, że instrukcja każe wymieniać wszystkie wersje. Przy pytaniu od klienta nie chciałbym jej tam widzieć. Ale to już kwestia tego, komu agent odpowiada, a nie tego, czy wie, z kiedy jest dokument.
Dlaczego sceptyk by tego nie znalazł?
To jest dla mnie najważniejszy fragment tej historii i powód, dla którego piszę o jednej funkcji cały tekst.
Sceptyk, którego projektowałem, miał badać rozumowanie. Dostaje wniosek pierwszego agenta i szuka w nim słabych miejsc: nieuzasadnionych przeskoków, założeń bez pokrycia, wniosków z jednego przypadku. To ma sens przy błędach logicznych. Ale błąd z cennikiem nie był błędem logicznym. Rozumowanie agenta wyglądało tak: zapytano o cennik, znalazłem dokument o cenniku, w dokumencie jest 30 000, odpowiadam 30 000. Każdy krok poprawny. Sceptyk badający te kroki nie znalazłby dziury, bo w rozumowaniu jej nie było.
Dziura była w danych, których obaj by nie dostali. Sceptyk korzystałby z tej samej wyszukiwarki, więc dostałby te same kserówki z uciętym nagłówkiem. Mógłby co najwyżej powiedzieć: a sprawdziłeś, czy to aktualne? Na co pierwszy agent odpowiedziałby zgodnie z prawdą: nie mam jak. I sędzia rozstrzygałby spór między dwoma agentami, z których żaden nie miał informacji potrzebnej do rozstrzygnięcia. Trzy modele, dwa i pół raza więcej tokenów, i ten sam błąd na wyjściu.
Pomyśl o tym jak o audycie w firmie. Zatrudniasz audytora, żeby sprawdzał faktury przed zapłatą. Audytor jest świetny: liczy, porównuje z umową, sprawdza NIP. Ale faktury dostaje od księgowej, która przy skanowaniu ucina górną część strony z datą wystawienia. Audytor nie wykryje, że płacisz fakturę sprzed roku, bo nie ma czego wykryć. Drugi audytor też nie wykryje. Komisja trzech audytorów też nie. Naprawą nie jest kolejny audytor. Naprawą jest skaner, który nie ucina daty.
Diagnoza „model się myli" bywa w rzeczywistości „narzędzie nie podaje mu informacji". I to jest zasada, którą zabrałem z tej historii na stałe: zanim zbudujesz warstwę, która ma wyłapywać błędy agenta, sprawdź, czy agent w ogóle dostaje dane potrzebne, żeby tych błędów nie popełniać. Warstwa kontroli kosztuje przy każdym zadaniu. Brakujące pole kosztuje raz.
Gdzie ta naprawa jest słaba? Uczciwie
Nie chcę, żebyś wyszedł z tego tekstu z przekonaniem, że data z dysku rozwiązuje problem aktualności wiedzy. Nie rozwiązuje. Daje agentowi sygnał, którego wcześniej nie miał. Sygnał, nie wyrok. Trzy słabości, które znam.
Nowszy nie znaczy obowiązujący. Agent widzi teraz, że jeden dokument jest z czerwca, a drugi z sierpnia. Ale to człowiek wie, że sierpniowy obowiązuje, a czerwcowy jest historyczny. Mogło być odwrotnie: czasem w sierpniu ktoś robi notatkę z rozmowy, w której wraca do starych warunków, i ta notatka jest nowsza, choć nie opisuje stanu obowiązującego. Dlatego instrukcja każe wymieniać obie wersje, a nie wybierać nowszą. Decyzja, która jest prawdą, zostaje u mnie. Agent ma mi pokazać, że wersji jest więcej niż jedna. Nie ma za mnie wybierać.
Dla części dokumentów daty nie ma wcale. Stare skany z serwera plików, dokumenty przeniesione w całości z innego dysku, pliki, które dostały datę kopiowania zamiast daty powstania. W takich przypadkach funkcja oddaje puste pole, a w kontekście pojawia się napis „data nieznana". Zrobiłem to celowo. Wolę, żeby agent widział wprost, że nie wie, z kiedy jest dokument, niż żeby dostał w tym miejscu ciszę. Cisza wygląda jak brak problemu. „Data nieznana" wygląda jak to, czym jest.
Cała ta naprawa jest więc najtańszą warstwą z możliwych, a nie ostatnią. Następne warstwy to porządek w samych dokumentach: nagłówki „nieaktualne od", daty obowiązywania przy cennikach, jedno miejsce prawdy dla każdej liczby, która się zmienia. To już nie jest praca dla programisty. To praca dla kogoś, kto zna firmę.
Jak sprawdzić to u siebie, zanim coś dobudujesz
Jeśli masz w firmie jakiekolwiek narzędzie, które odpowiada na pytania na podstawie Waszych dokumentów, kupione albo zbudowane, oto co bym zrobił przed wydaniem złotówki na „lepszą wersję".
1. Poproś o surowy kontekst jednego zapytania. Nie o odpowiedź. O to, co narzędzie faktycznie podało modelowi, zanim model odpowiedział. Dobry dostawca pokaże to bez problemu, przy własnym narzędziu wystarczy jedna linia wypisująca kontekst na ekran. Jeśli nikt nie potrafi Ci tego pokazać, masz większy problem niż daty. 2. Przeczytaj ten kontekst jak nowy pracownik. Czy na podstawie tego, co tam jest, dałoby się odpowiedzieć poprawnie? Czy widać, skąd pochodzi każdy fragment i z kiedy jest? Jeśli Ty, znając firmę, nie dałbyś rady wybrać właściwej wersji na podstawie tych kartek, model też nie da. 3. Szukaj brakującego pola, nie brakującej reguły. Odruch po błędzie jest zawsze ten sam: dopisać do instrukcji „pamiętaj, żeby...". Zanim to zrobisz, sprawdź, czy instrukcję da się w ogóle wykonać na danych, które model dostaje. Reguła bez danych to pobożne życzenie wywieszone na ścianie. 4. Zaczynaj od warstwy, która nic nie kosztuje przy każdym zadaniu. Brakujące pole w kontekście naprawia się raz. Dodatkowy agent kontrolny płaci przy każdym pytaniu, do końca życia systemu. Jeśli oba rozwiązania adresują ten sam błąd, kolejność jest oczywista. A jeśli nie wiesz, czy adresują ten sam błąd, wróć do punktu drugiego. 5. Sprawdź na pytaniu, które zawiodło. Nie na nowym. Na tym, które Cię zabolało. Jeśli po poprawce odpowiedź jest inna i lepsza, masz dowód. Jeśli jest taka sama, to naprawiłeś coś innego niż to, co było zepsute.
Zauważ, że tylko czwarty punkt dotyczy w ogóle AI. Pozostałe cztery to zwykłe sprawdzenie, czy ktoś dostał to, czego potrzebuje do pracy. Robisz to z ludźmi od lat. Z agentem trzeba tylko pamiętać, że nie przyjdzie zapytać.
Co zostaje z tej historii
Kartka z trzema agentami leży w szufladzie. Nie wyrzuciłem jej, bo literatura o debacie modeli jest ciekawsza, niż się spodziewałem, i jeszcze do niej wrócę. Ale nie buduję tego. Nie dlatego, że to nie działa. Dlatego, że mój błąd nie był błędem, który taka konstrukcja mogłaby wyłapać.
Przez jeden wieczór byłem przekonany, że mam problem z rozumowaniem AI, i projektowałem rozwiązanie na miarę tego problemu: drogie, wielowarstwowe, z sędzią na końcu. Następnego ranka zajrzałem do pięciu pól, które moja wyszukiwarka oddawała agentowi, i problem skurczył się do jednego brakującego. To jest, swoją drogą, najczęstszy wzorzec, jaki widzę u klientów, którzy przychodzą do mnie z hasłem „nasza AI się myli". Bardzo rzadko myli się model. Prawie zawsze ktoś mu czegoś nie podał.
Jeśli masz zabrać z tego tekstu jedną rzecz, to tę: kiedy agent AI popełnia błąd, pierwsze pytanie nie brzmi „jak go kontrolować". Brzmi „co dokładnie dostał". Odpowiedź na to drugie często załatwia pierwsze. U mnie załatwiła jedną funkcją, w jedno przedpołudnie, zamiast trzema agentami w kilka tygodni. Sprawdziłem to na własnej firmie, zanim komukolwiek doradzę.
Słownik pojęć
- Wyszukiwarka wiedzy (RAG) - narzędzie, które na pytanie wyszukuje w dokumentach firmy najbardziej pasujące fragmenty i podaje je modelowi AI jako materiał do odpowiedzi. Model nie „zna" dokumentów, czyta tylko to, co wyszukiwarka mu poda. Dlatego jakość odpowiedzi zależy od tego, co trafia do kontekstu.
- Kontekst - cały tekst, który model dostaje przed udzieleniem odpowiedzi: instrukcja, fragmenty dokumentów, pytanie. Wszystko, czego nie ma w kontekście, dla modelu nie istnieje.
- Data modyfikacji pliku - znacznik czasu, który system operacyjny zapisuje przy każdym pliku, gdy ten zostaje zmieniony. Łatwo dostępny, ale zmienia się przy każdej edycji, nawet kosmetycznej, więc nie zawsze mówi, kiedy dokument naprawdę powstał.
- Token - jednostka, w której modele AI liczą tekst i w której rozliczają jego koszt. Mniej więcej trzy czwarte słowa. „Dwa i pół raza więcej tokenów" oznacza w praktyce dwa i pół raza wyższy rachunek za każde zadanie.
- Debata wielu agentów - wzorzec, w którym kilka modeli odpowiada, kwestionuje się nawzajem i dochodzi do wspólnego wniosku, czasem z dodatkowym modelem w roli sędziego. Zmierzony w literaturze, z wynikami, które nie są tak dobre, jak podpowiada intuicja.
Zanim dobudujesz sędziego, zajrzyjmy, co dostaje Twój agent
Jeśli masz w firmie narzędzie AI, które odpowiada na podstawie Waszych dokumentów, i czasem podaje rzeczy nieaktualne, napisz do mnie na [email protected]. Pierwsze, co zrobię, to poproszę o surowy kontekst jednego zapytania. W większości przypadków, które widziałem, odpowiedź na pytanie „dlaczego się myli" jest w tych kilku kartkach, a nie w modelu. I naprawia się ją taniej, niż dostawca Ci powie.
Więcej o tym, jak wygląda współpraca ze mną, od pierwszej rozmowy po wdrożenie.
własnej firmie cybulski.ai JC


