Większość prototypów RAG pod maską wygląda tak samo. Ktoś wrzuca PDF do potoku, tnie tekst na równe fragmenty po 512 tokenów, wrzuca je do bazy wektorowej i uznaje zadanie za wykonane. Na potrzeby szybkiego demo na korytarzu może to wyglądać imponująco. W środowisku produkcyjnym taki system się załamuje.

Stały fragment (chunk) nie dba o to, co przecina. Może rozciąć umowę prawną w samym środku klauzuli odszkodowawczej. Może upchnąć pięć niepowiązanych punktów końcowych API w tym samym oknie kontekstowym, tonąc model w szumie. Może zmusić Cię do pobierania większej liczby fragmentów niż to konieczne, co zwiększa opóźnienia i marnuje tokeny. Rezultatem są połowiczne odpowiedzi, halucynacje i sfrustrowani użytkownicy.

Rozłożyliśmy naszą warstwę wyszukiwania na części pierwsze i zbudowaliśmy ją od nowa. Efektem jest system, który osiągnął 95-procentowy recall, jednocześnie redukując opóźnienia o 40 procent. Oto dokładnie, jak to zrobiliśmy.

Dlaczego stałe fragmenty zawodzą na produkcji

Domyślne 512 tokenów nie jest wyborem projektowym. Jest to produkt uboczny wczesnych okien kontekstowych modeli embeddingowych i wygodnych ustawień domyślnych bibliotek. Łatwo to wdrożyć, ale poleganie na tym jest katastrofalne.

Dokumenty nie są jednorodne. Klauzula prawna może mieć siedemset tokenów bez wyraźnego podziału. Jeśli przetniesz ją przy pięciuset dwunastu, stworzysz dwa osierocone fragmenty. Gdy prawnik lub oficer ds. zgodności zapyta o limity odpowiedzialności, system zwróci tylko połowę zobowiązania. Model językowy wyhalucynuje brakującą część lub, co gorsza, zaprzeczy istnieniu limitu.

Dokumentacja API cierpi na odwrotną przypadłość. Fragment o wielkości pięciuset tokenów może pochłonąć cały moduł: nagłówki uwierzytelniające, kody błędów, limity prędkości i schematy webhooków. Gdy programista zapyta, jak obsłużyć AUTH_4027, retriever zwróci mieszankę niepowiązanych funkcji. Model nie ma innego wyjścia, jak tylko uśrednić je w generyczną papkę.

Zły chunking zwiększa również opóźnienia. Słabe fragmenty oznaczają, że potrzebujesz większego top-k, aby pokryć dany temat. Więcej fragmentów to dłuższe prompty. Dłuższe prompty oznaczają wolniejszą generację i wyższe rachunki. Doświadczenie użytkownika umiera przez tysiąc małych cięć.

Dopasuj fragment do dokumentu

Przestaliśmy liczyć tokeny, a zaczęliśmy czytać materiał. Właściwa strategia chunkingu zależy od struktury źródła.

Dokumenty prawne wymagają rekurencyjnego chunkingu znakowego z granicami uwzględniającymi klauzule. Splitter szanuje hierarchię: najpierw szuka nagłówków sekcji, potem numerowanych paragrafów, a na końcu naturalnych przerw między zdaniami. Nigdy nie przerywa podklauzuli ani nie rozdziela frazy zobowiązującej między fragmenty. Gdy pobierasz fragment dotyczący odszkodowania, otrzymujesz pełną klauzulę, limit oraz wyjątki.

Dokumentacja API wymaga chunkingu uwzględniającego strukturę. Analizujemy ją według definicji funkcji, a nie budżetu tokenów. Każdy fragment zawiera pełną sygnaturę funkcji, opisy jej parametrów oraz bezpośrednio sąsiadujące notatki dotyczące obsługi błędów. Jeśli programista szuka konkretnej metody, otrzymuje cały kontrakt, a nie fragment uwięziony wewnątrz przypadkowego podziału.

Zgłoszenia wsparcia (support tickets) są zaszumione i nieliniowe. Wątek może zacząć się od raportu o błędzie, wprowadzić obejście problemu i zakończyć się wewnętrzną notatką o eskalacji. Semantic chunking wykrywa zmiany tematu poprzez mierzenie podobieństwa osadzeń (embeddings) między zdaniami. Pozwalamy na przerwy tylko w naturalnych granicach tematycznych, dzięki czemu rozmowa o błędach logowania pozostaje oddzielona od kontynuacji dotyczącej cykli rozliczeniowych.

Wiki były najtrudniejsze. Są rozległe, pełne odnośników i luźno zorganizowane. Zastosowaliśmy agentic chunking, w którym lekki model LLM czyta stronę i decyduje o podziałach na podstawie spójności tematycznej. Kosztuje to nieco więcej podczas ingestii, ale powstałe fragmenty są samowystarczalne i gotowe do wyszukiwania. Strona o najlepszych praktykach wdrażania dzieli się na logiczne jednostki: sprawdzenia wstępne, procedury rollback i konfigurację monitoringu, zamiast na przypadkowe bloki tekstu.

Hybrydowe wyszukiwanie: słowa kluczowe i wektory razem

Gęste wyszukiwanie wektorowe rozumie znaczenie. Jest jednak fatalne w przypadku dokładnych ciągów znaków. Jeśli użytkownik szuka precyzyjnego kodu błędu, takiego jak AUTH_4027, lub nazwy klienta, np. "Stark Industries", osadzenia wektorowe mogą chybić celu, ponieważ optymalizują pod kątem bliskości koncepcyjnej, a nie dokładności na poziomie znaków.

Czyste wyszukiwanie słów kluczowych przez BM25 ma odwrotną wadę. Znajdzie AUTH_4027 perfekcyjnie, ale przegapi koncepcyjny pomost między "authorization failure" a "login denied".

We run both in parallel. BM25 and vector search operate independently over the same corpus. Their result lists are merged using Reciprocal Rank Fusion, which reorders candidates by balancing their positional ranks. You do not need calibrated weights. You simply get the precision of exact match and the intuition of semantic search in a single ranked list.

Then we add a cross-encoder reranker. This is a separate model that scores each passage against the original query, producing a relevance signal far finer than either retriever alone. It adds about 50 milliseconds of latency. It increases recall by 15 percent. If you care about answer quality, that trade is non-negotiable.

Query Expansion: Fix the Search Before It Starts

Bad queries are the dirty secret of every retrieval system. Users do not write like your embedding space. They type "it broke." They paste truncated stack traces. They use internal jargon your index has never seen.

We transform the query before it ever touches the index. First, we expand a single query into three to five diverse search terms. If the original is "payment failed," we also search for "transaction error," "billing declined," and "charge unsuccessful