Zapytaj ogólny LLM o politykę zwrotów w twojej firmie albo o to, co było w zgłoszeniach supportu w zeszłym tygodniu, a usłyszysz albo “nie wiem”, albo coś wiarygodnie brzmiącego, ale zmyślonego. Jego wiedza kończy się tam, gdzie kończyły się dane treningowe. Nigdy nie widział twoich dokumentów i nie może w trakcie rozmowy ich sprawdzić. Retrieval-augmented generation (RAG) naprawia dokładnie to: zanim model odpowie, osobny krok znajduje odpowiedni fragment w twoich danych i przekazuje go modelowi jako część prompta.
Jak naprawdę działa retrieval-augmented generation
Zwykłe wywołanie LLM to jeden krok: prompt wchodzi, odpowiedź wychodzi, korzystając wyłącznie z tego, czego model nauczył się podczas treningu. RAG dzieli to na dwa: najpierw wyszukanie, potem generowanie.
- Twoje dokumenty są wcześniej dzielone na chunki, zamieniane na wektory (embeddingi) i zapisywane w bazie wektorowej.
- W momencie zapytania pytanie użytkownika jest zamieniane na embedding w ten sam sposób i porównywane z zapisanymi chunkami, żeby znaleźć najbliższe dopasowania.
- Najlepsze dopasowania trafiają do prompta jako kontekst i dopiero wtedy LLM generuje odpowiedź.
To jak egzamin z materiałami pod ręką w porównaniu z egzaminem bez nich. LLM bez materiałów odpowiada wyłącznie z pamięci, dlatego zmyśla, gdy pytanie wykracza poza to, co zapamiętał. RAG podsuwa mu najpierw właściwą stronę, więc odpowiada na podstawie czegoś, co ma przed sobą, a nie czegoś, co sobie dopowiada.
Sam model nigdy się nie zmienia. Jego wagi zostają dokładnie takie, jak przed dodaniem RAG; retrieval to jedyny nowy element. To właśnie dlatego RAG jest tani w utrzymaniu aktualności: edytujesz dokument, a kolejne zapytanie widzi już zmianę.
Budowa pipeline’u RAG: chunkowanie i embedding danych
Ingestion to ta połowa pipeline’u, która działa, zanim ktokolwiek zada pytanie. Zamienia twoje dokumenty w coś, co da się przeszukać przez podobieństwo.
Dokumenty są za długie, żeby zamienić je na embedding jako jedną całość. 20-stronicowy PDF zamieniony na jeden wektor trochę pasuje do każdego zapytania, a dobrze do żadnego, więc najpierw dzielisz go na chunki. Prosty podział na fragmenty stałej długości z zachodzeniem na siebie wystarczy na start:
| |
500 znaków z zachodzeniem 50 to sensowny punkt startowy. Pipeline’y produkcyjne zwykle dzielą tekst według liczby tokenów, a nie znaków, korzystając z tokenizatora, dzięki czemu granica chunka nie wypada w środku słowa. Zachodzenie na siebie sprawia, że zdanie rozdzielone między dwa chunki i tak pojawia się w całości przynajmniej w jednym z nich.
Każdy chunk trafia potem do embeddingu i zostaje zapisany. Chroma to dobra pierwsza baza wektorowa, bo działa w tym samym procesie bez potrzeby stawiania serwera, a jej domyślna funkcja embeddingu (all-MiniLM-L6-v2) nie wymaga klucza API:
| |
To cała faza ingestion: podział na chunki, embedding, zapis. Działa raz na dokument i ponownie za każdym razem, gdy dokument się zmienia.
Jeśli nie chcesz instalować bazy wektorowej lokalnie, większość z nich (Chroma, Weaviate, Milvus) ma oficjalny obraz i działa dobrze w kontenerze; zobacz czym jest Docker i do czego służy, jeśli wcześniej nie pracowałeś z kontenerami.
Jak działa retrieval: embedding zapytania i ranking dopasowań
W momencie zapytania ten sam model embeddingu zamienia pytanie użytkownika na wektor, a baza zwraca zapisane chunki leżące najbliżej niego, zależnie od skonfigurowanej metryki odległości — cosine similarity i kwadrat odległości L2 to te dwie, które zobaczysz najczęściej:
| |
Zapytanie nigdy nie używa słowa “zwrot” (refund), a mimo to trafia na właściwe dopasowanie. To właśnie dlatego warto korzystać z embeddingu zamiast grepa: dwa teksty lądują blisko siebie w przestrzeni wektorowej, gdy znaczą coś podobnego, a nie gdy zawierają te same słowa.
Ostatni krok łączy pobrane chunki w prompt:
| |
prompt trafia do dowolnego API chat completion, którego już używasz. RAG nie wymaga konkretnego modelu ani dostawcy — to schemat decydujący, co trafia do prompta przed wysłaniem. Jeśli takie wywołanie API wymaga klucza, trzymaj go poza obrazem tak samo jak każdy inny sekret: jako zmienną środowiskową albo zamontowany sekret, nigdy jako build argument. Zmienne Środowiskowe i Secrets w Kontenerach Docker wyjaśnia tę różnicę.
RAG kontra fine-tuning: co rozwiąże twój problem
Oba mają uczynić LLM lepszym w twoim konkretnym zastosowaniu, ale zmieniają różne rzeczy.
| RAG | Fine-tuning | |
|---|---|---|
| Co się zmienia | Nic w modelu; dodajesz krok retrievalu | Same wagi modelu, przez dodatkowy trening |
| Aktualizacja wiedzy | Edytujesz lub dodajesz dokument, kolejne zapytanie to widzi | Ponowny trening (albo kolejna runda fine-tuningu) |
| Dobre do | Faktów, aktualnych danych, wszystkiego, co często się zmienia | Tonu, formatu odpowiedzi, wzorców rozumowania specyficznych dla zadania |
| Koszt aktualizacji | Niski — bez uruchamiania treningu | Wysoki — potrzebny trening i zbiór danych |
| Zawodzi, gdy | Retrieval pomija albo źle ocenia chunk | Potrzebnego zachowania nie da się sprowadzić do przykładów wejście-wyjście |
Bot supportowy, którego polityka zwrotów zmienia się co kwartał, to naturalny przypadek dla RAG: dofinetunuj go do polityki z poprzedniego kwartału, a będzie pewny siebie i błędny w sprawie nowej. Model, który za każdym razem musi zwrócić konkretny schemat JSON albo trzymać się określonego stylu rozumowania, jest bliżej problemu fine-tuningowego — żadna ilość pobranego kontekstu nie zmieni sposobu, w jaki model formatuje swoją odpowiedź. Wiele systemów produkcyjnych korzysta z obu naraz: model po fine-tuningu odpowiada za ton i strukturę, a fakty dostarcza RAG.
Gdzie zawodzi retrieval-augmented generation
Dema RAG wyglądają na proste, bo demo ma trzy dokumenty i jedną oczywistą odpowiedź. Systemy produkcyjne mają tysiące dokumentów, a każdy z poniższych punktów potrafi po cichu zepsuć odpowiedź:
- Chunkowanie ignorujące strukturę. Podział na fragmenty stałej długości, który przecina tabelę na pół albo oddziela nagłówek od wprowadzanego przez niego akapitu, tworzy chunki, które bez kontekstu brzmią jak bełkot — a bełkot źle się embeduje i źle wyszukuje.
- Nieaktualny indeks. Wyszukiwanie wektorowe nie ma pojęcia o czasie. Jeśli dokument zostanie zaktualizowany, ale nigdy ponownie zaembedowany, to nadal stara wersja jest tą pobieraną, a nic w pipeline nie sygnalizuje, że jest błędna.
- Właściwy chunk istnieje, ale nie znajduje się wystarczająco wysoko w rankingu. Retrieval top-k zwraca tylko k najbliższych dopasowań; jeśli prawdziwa odpowiedź to 15. najbliższy chunk, a pobierasz 5, model nigdy jej nie zobaczy.
- Za dużo kontekstu, źle rozmieszczonego. Wepchnięcie dziesięciu pobranych chunków do prompta nie znaczy, że model waży je wszystkie po równo. Modele mierzalnie gorzej korzystają z informacji leżącej w środku długiego kontekstu niż z tej na początku czy na końcu.
- Brak ewaluacji poza “odpowiedź z dema wyglądała dobrze”. Bez zestawu pytań i oczekiwanych odpowiedzi regresja retrievalu spowodowana zmianą chunkowania albo błędem reindeksacji przechodzi niezauważona.
Większość z tego to problemy retrievalu, nie modelu, i każdy jest widoczny, jeśli sprawdzisz pobrane chunki zamiast tylko końcowej odpowiedzi.
Jak zbudować swój pierwszy pipeline RAG
Zbuduj najpierw wersję trzyetapową: podziel na chunki garść prawdziwych dokumentów, zaembeduj je w Chroma albo innym lokalnym magazynie i podłącz pobrane chunki do prompta dokładnie tak, jak pokazano powyżej. Potem zadaj mu 10 pytań, których spodziewasz się od prawdziwych użytkowników. Sprawdź, czy pobrane chunki zawierają odpowiedź, zanim ocenisz wygenerowaną odpowiedź — zły retrieval czyni złą odpowiedź nieuniknioną, bez względu na to, jak napisany jest prompt. Dopiero gdy ten cykl sprawdza się na prawdziwych pytaniach, warto dostrajać rozmiar chunków, zmieniać model embeddingu albo dodawać reranker.