case-study czerwiec 2026 · 6 min czytania

Zbudowałem wyszukiwarkę po dokumentach, która nie zmyśla — oto jak

RAG, który zamiast wymyślać odpowiedzi, pokazuje plik i linię, z której je wziął. Jak działa hybryda BM25 + embeddingi + RRF i dlaczego pojedyncza metoda zawodzi.

Marcin Ołdak
Zbudowałem wyszukiwarkę po dokumentach, która nie zmyśla — oto jak — MJ.OLDAK

Zwykły chatbot zapytany o twoje dokumenty często wygląda na pewnego siebie i mówi coś, czego w dokumentach nie ma. Zbudowałem coś odwrotnego: zadajesz pytanie po polsku, dostajesz odpowiedź — i dokładnie ten plik i tę linię, z której odpowiedź pochodzi.

O co chodzi

Wyobraź sobie asystenta wiedzy, który zna twoje dokumenty: regulacje, procedury wewnętrzne, umowy, dokumentację. Pytasz go normalnym językiem — “jaka jest procedura przy reklamacji powyżej 5 tysięcy?” — a on nie tylko odpowiada, ale pokazuje, skąd to wie: nazwa pliku, konkretne linie.

To różnica między “ufaj mi na słowo” a “sprawdź sobie sam”. W biurze rachunkowym czy w banku to nie jest detal — to warunek, żeby w ogóle używać takiego narzędzia. Jak odpowiedź dotyczy terminu podatkowego albo zapisu w umowie, nikt nie zaryzykuje decyzji na podstawie tekstu, którego nie da się zweryfikować. Cytat plik:linia zamienia “ładnie brzmiącą odpowiedź” w coś, co możesz kliknąć i przeczytać u źródła.

Pod maską

Pod spodem siedzą dwa różne sposoby szukania, które zwykle stawia się przeciwko sobie. U mnie pracują razem.

Każda metoda zwraca własny ranking. Łączę je przez RRF (Reciprocal Rank Fusion) — fuzję rang, która nie potrzebuje porównywalnych “wyników punktowych”, tylko pozycji na obu listach. Fragment, który jest wysoko u jednej albo u drugiej metody, ląduje wysoko w finalnym wyniku.

pytanie
  ├──> BM25 (po słowach)      ──> ranking A
  └──> embeddingi (po sensie) ──> ranking B

                 └──> RRF (fuzja rang) ──> top fragmenty

                                └──> odpowiedź + cytat plik:linia

Kluczowy detal, przez który to się nie zmyśla: cytat jest walidowany w kodzie, nie obiecany przez model. W trybie asystenta model pisze odpowiedź naturalnym językiem, ale przypisany cytat program sprawdza programatycznie — czy ten plik i te linie faktycznie istnieją i zawierają to, na co się powołuje. Jest też tryb ekstraktywny, który w ogóle nie generuje prozy, tylko zwraca dosłowne fragmenty.

Całość jest zaprojektowana na degradację zamiast wywrotki: jak padną embeddingi, schodzi na sam BM25; jak padnie LLM, schodzi na tryb ekstraktywny — i każdy taki spadek jest adnotowany, żebyś wiedział, w jakim trybie dostałeś odpowiedź. Bez zewnętrznych zależności (czysta biblioteka standardowa), licencja MIT.

Co z tego masz

Najważniejsza i najbardziej uczciwa rzecz: hybryda nie “zawsze wygrywa”. Nie o to chodzi. Chodzi o odporność.

Na zestawie testowym całość trafiła w 9/9 zapytań w top-k. Ciekawe jest dlaczego: samo BM25 raz gubiło parafrazę (pytanie sformułowane innymi słowami niż dokument), same embeddingi raz gubiły rzadki token (dosłowne, nietypowe słowo). Żadna pojedyncza metoda nie była bezbłędna. Dopiero RRF trzymał oba przypadki w top-k — bo wystarczyło, że jeden z dwóch silników złapał fragment.

Wniosek, jak budujesz RAG na produkcję: nie wybieraj “lepszej” metody. Łącz słowa + znaczenie, miej zestaw pytań testowych odzwierciedlający realne zapytania i mierz trafienia. Inaczej trafisz na dzień, w którym twój jedyny silnik akurat zgubi pytanie, którego nie przewidziałeś.

Dowód

Kod jest publiczny: github.com/martin0ne/docs-qa. Nie powstał “na czuja” — najpierw specyfikacja, potem adversarialny review (celowe szukanie dziur w założeniach), a implementacja w stylu TDD: 47 testów, które pilnują m.in. tego, że walidacja cytatów faktycznie odrzuca cytat, którego nie ma w źródle.

Jeśli zastanawiasz się, dlaczego chatboty w ogóle zmyślają i co RAG ma z tym wspólnego — wyjaśniam to bez żargonu w osobnym tekście: czemu chatboty zmyślają.

Najczęstsze pytania

Czym jest RAG z cytatami plik:linia?

To wyszukiwarka po dokumentach, która na pytanie zadane naturalnym językiem odpowiada i pokazuje dokładnie ten plik i te linie, z których wzięła odpowiedź — zamiast prosić, żebyś ufał jej na słowo.

Czym różni się BM25 od embeddingów?

BM25 szuka leksykalnie, po słowach — świetne dla rzadkich tokenów jak numer artykułu czy symbol, gubi się przy parafrazie. Embeddingi szukają semantycznie, po znaczeniu — łapią parafrazę, ale potrafią przeoczyć dosłowne, rzadkie słowo. Dlatego łączę oba.

Czym jest RRF (Reciprocal Rank Fusion)?

To fuzja rang, która łączy rankingi BM25 i embeddingów bez potrzeby porównywalnych wyników punktowych — liczy się tylko pozycja na obu listach. Fragment wysoko u którejkolwiek metody ląduje wysoko w finalnym wyniku.

Skąd pewność, że system nie zmyśla cytatów?

Cytat jest walidowany w kodzie, nie obiecany przez model. Program sprawdza programatycznie, czy wskazany plik i linie faktycznie istnieją i zawierają to, na co się powołuje. Jest też tryb ekstraktywny, który zwraca dosłowne fragmenty bez generowania prozy.

Czy hybryda zawsze wygrywa z pojedynczą metodą?

Nie — i nie o to chodzi. Chodzi o odporność. Na teście całość trafiła 9/9, bo gdy BM25 zgubiło parafrazę, a embeddingi rzadki token, RRF trzymał oba przypadki w top-k. Wystarczyło, że jeden z dwóch silników złapał fragment.

Tagi

Powiązane artykuły.