Strona główna / Artykuły

Dlaczego RAG zawodzi przy rzeczywistych dokumentach: dzielenie na fragmenty, wyszukiwanie hybrydowe, ponowna sortacja oraz metody oceny.

Dlaczego RAG zawodzi przy rzeczywistych dokumentach i wzorce, które to naprawiają: projekt dwufazowy, wybór sposobu dzielenia na fragmenty, wyszukiwanie hybrydowe, ponowna klasyfikacja oraz ocena samego procesu wyszukiwania.

Wydawca: REACTAPP.TOP7 min czytania1518 słów

Źródła: Production-Ready RAG Architecture: Core Patterns Explained

Dlaczego dema zawodzą przy rzeczywistych danych

Tutorialiki dotyczące RAG zazwyczaj działają na uporządkowanym pliku PDF lub wyeksportowanym treściach z Wikipedii, co stanowi niewłaściwe warunki testowe. Jeśli ten sam proces zostanie zastosowany do rzeczywistych materiałów firmy o nieregularnym formacie, zgłoszeń klientów oraz rozległych wewnętrznych witryniki, odpowiedzi zaczynają być niekompletne, wymyślone lub świadomie błędne. Dwie koncepcje wyjaśniają niemal każdą awarię w warunkach produkcyjnych.

  1. RAG to przede wszystkim system wyszukiwania, a dopiero potem system generowania treści. Gdy wyszukiwanie dostarcza niewłaściwy kontekst, model praktycznie nie ma szans na udzielenie poprawnej odpowiedzi, bez względu na swoje możliwości.
  2. Ogólna jakość jest ograniczona przez najslabszy etap, a nie przez średni. Silny model embeddingów nie może uratować narzędzia do dzielenia tekstu na fragmenty, które rozdzieli zdanie na dwie części.

Dwie fazy

Każdy system RAG składa się z fazy indeksowania offline oraz fazy wyszukiwania online:

Indexing (offline):   documents → chunks → embeddings → vector store
Query (online):       question → embedding → retrieval → context assembly → LLM

Ustawienia produkcyjne dodają mechanizmy cacheowania, ponownego rankowania, ograniczeń, monitoringu oraz logiki awaryjnej wokół tej podstawy, ale nadal stanowi ona fundament dla wszystkiego innego, a żadne narzędzie nie może naprawić słabej bazy.

Podział na fragmenty przynosi największe korzyści

Większość systemów zawodzi przy dzieleniu na fragmenty, a ten błąd pozostaje niewidoczny aż do momentu, gdy próbuje się naprawić błędną odpowiedź po trzech kolejnych etapach. Jest to istotne, ponieważ modele wbudowane działają najlepiej na spójnych fragmentach tekstu, okna kontekstowe są skończone, zbyt duże fragmenty wprowadzają szum, a zbyt małe pomijają fakty niezbędne do udzielenia odpowiedzi.

Podział o stałej wielkości polega na dzieleniu tekstu co N tokenów przy 10 do 20 procentach nakładania się fragmentów. Jest to prosty i przewidywalny sposób, ale nie bierze pod uwagę granic zdań i akapitów, więc może podzielić definicję bez żadnego zauważenia. Jest to powszechny punkt wyjścia, ale rzadko najlepszy wybór.

Recyrkulacyjne lub hierarchiczne dzielenie na fragmenty najpierw bierze pod uwagę naturalne przerwy, podwójne nowe linie, następnie pojedyncze nowe linie, potem zdania, a na końcu słowa, i ucieka się do tego rozwiązania tylko wtedy, gdy dana sekcja jest nadal zbyt duża. Większość frameworków obecnie używa właśnie tej metody, a jest to wyraźne ulepszenie w porównaniu z prostym dzieleniem o stałej wielkości.

Dzielenie uwzględniające strukturę dokumentu kieruje się jego własnym układem: nagłówkami, sekcjami, blokami kodu i tabelami. W przypadku dokumentacji technicznej oraz formatu Markdown często okazuje się najskuteczniejsze. Zachowaj powiązania rodzic-dziecko, aby móc skupić się na małym, precyzyjnym fragmencie, a następnie rozszerzyć analizę na całą sekcję rodzicielską, gdy potrzebny jest większy kontekst.

Dzielenie semantyczne wykorzystuje model embeddingowy lub model językowy do znalezienia miejsc, w których temat rzeczywiście się zmienia, i dokonuje tam podziału. Dzięki temu powstają najbardziej spójne fragmenty, ale przy tym pojawia się rzeczywisty dodatkowy koszt, ponieważ każda decyzja dotycząca granicy wymaga wywołania modelu, więc zazwyczaj jest stosowane tylko w przypadku korpusów, gdzie jakość wyszukiwania usprawiedliwia takie nakłady.

Rozsądne domyślne ustawienia:

  • Zacznij od dzielenia w sposób rekurencyjny lub z uwzględnieniem struktury, unikając metody o stałej długości.
  • Dąż do fragmentów liczących około 300 do 800 tokenów, a następnie dostosuj to do swojego zestawu oceny, a nie do liczby z bloga.
  • Zachowuj metadane w każdym fragmencie, takie jak dokument źródłowy, tytuł sekcji oraz numer strony lub akapitu. Filtrowanie i cytowanie będą tego później potrzebować.
  • Zebierz mały zbiór rzeczywistych pytań i sprawdź, co zostanie wyświetlone, zanim zaczniesz używać modelu embeddingów. Niewłaściwe dzielenie na fragmenty potajemnie psuje wyniki, więc popraw to przed dostosowywaniem czegokolwiek dalej w łańcuchu.

Embeddingi i magazyny wektorowe

Modele embeddingowe umieszczają tekst w przestrzeni wysokowymiarowej, gdzie bliskość semantyczna przekształca się w bliskość geometryczną, mierzoną zwykle podobieństwem kosinowym lub iloczynem skalarnym. Model ma znaczenie, ale jakość danych i dzielenie ich na fragmenty zazwyczaj mają większe znaczenie niż to, jaki model jest używany w procesie. Do głównych kategorii należą uniwersalne modele otwarte, embeddingi specjalistyczne do tekstu prawniczego, medycznego lub kodu, oraz własne API. Wybieraj na podstawie słownictwa swojej dziedziny, a nie tylko rankingu na liście liderów.

Prawdziwym zadaniem magazynu wektorowego jest szybkie szukanie przybliżonego najbliższego sąsiada, ponieważ dokładne porównanie metodą siłowej nie jest skalowalne poza małe zbiory. Większość magazynów wykorzystuje indeksy oparte na grafach, takie jak HNSW, lub indeksy oparte na klastrach, takie jak IVF, dzięki czemu zapytanie nie musi być porównywane z każdym wektorem. W zamian za subliniowy czas wykonywania zapytań pojawia się niewielka, dostosowalna szansa na pominięcie prawdziwego najbliższego sąsiada. Rzadko dostosowuje się to na wczesnym etapie, ale świadomość jego istnienia pomaga, gdy zastanawiamy się, dlaczego nie został pobraany oczywiście odpowiedni fragment.

Wybierając magazyn, sprawdź, czy filtrował on dane na podstawie metadanych, czy natywnie obsługuje hybrydowe wyszukiwanie wektorów w połączeniu ze słowami kluczowymi (BM25), jak łatwo jest go uruchomić lokalnie w porównaniu z wersją zarządzaną, oraz jaka jest jego odporność na awarie, możliwość replikacji i skalowalność przy danym obciążeniu. Do popularnych opcji należą Qdrant, Weaviate, Pinecone, Chroma, pgvector oraz OpenSearch k-NN.

Na początku marka sklepu ma mniejsze znaczenie niż cztery inne czynniki: uporządkowane dane, solidne podziałki, odpowiednie metadane oraz możliwość łączenia wyszukiwania wektorowego z wyszukiwaniem według słów kluczowych. Wyszukiwanie hybrydowe występuje praktycznie we wszystkich poważnych implementacjach, ponieważ czyste wyszukiwanie wektorowe nadal pomija takie literalne elementy jak kody błędów, SKU produktów oraz ich identyfikatory, które są łatwo znajdowane przy użyciu wyszukiwania według słów kluczowych. Połączenie wyników o wysokiej i niskiej gęstości jest zazwyczaj kontrolowane za pomocą jednego współczynnika, dzięki czemu jedno zapytanie może skupiać się na znaczeniu lub na dokładnych terminach w zależności od przypadku użycia. Koncepcyjna struktura hybrydowa wygląda w ten sposób, chociaż dokładna API różni się w zależności od sklepu:

# Conceptual shape of a hybrid query — exact API varies by vector store
results = vector_store.hybrid_search(
    query=user_question,
    alpha=0.5,          # weight between dense (1.0) and keyword (0.0) search
    filters={"doc_type": "runbook", "product": "checkout-service"},
    top_k=25,
)

Poza podobieństwem top-k

Wybór top-k najbliższych fragmentów to dopiero początek procesu wyszukiwania, a nie jego koniec. Rzeczywisty ruch internetowy wciąż ujawnia te same problemy: najbliższy fragment nie jest najbardziej przydatny, fakty zawarte w odpowiedzi są rozproszone między kilkoma fragmentami, modele zbyt mało zwracają uwagę na treść znajdującą się w środku długiego kontekstu, nawet jeśli tam jest (efekt zagubienia w środku), a model embeddingowy może nie rozumieć słownictwa specyficznego dla danej dziedziny.

Techniki, które warto dodać, mniej więcej według zwrotu z wysiłku:

  • Ponowna klasyfikacja. Użyj taniego wyszukiwania wektorowego, aby pozyskać szeroki zbiór kandydatów – na przykład 20 do 50, a czasami nawet 100 – a następnie przelicz ich oceny za pomocą cross-encodera lub małego LLM, zanim zostaną one wybrane do promptu. Jest to jedna z najlepszych dodatkowych metod w procesie przetwarzania danych. Cross-encoder ocenia zarówno zapytanie, jak i dokument razem, co jest znacznie dokładniejsze niż porównywanie dwóch oddzielnie obliczonych wektorów, ale musi być uruchamiany dla każdego kandydata i nie może obejmować całej kolekcji. Wyszukiwanie za pomocą bi-encodera jest tanie i przybliżone, choć zawęża zakres wyników, natomiast cross-encoder jest kosztowny, ale precyzyjny i sprawdza tylko te elementy, które pozostały.
  • Wyszukiwanie hybrydowe, łączące wektory gęste z wyszukiwaniem słów kluczowych rzadkich, dzięki czemu dokładne tokeny nie są pozostawione przypadkowi.
  • Transformacja zapytania: przepisanie pytania, utworzenie hipotetycznego dokumentu odpowiedzi i wyszukiwanie w nim (w stylu HyDE) lub podział złożonego pytania na podpytania, które są wyszukiwane niezależnie.
  • Filtrowanie metadanych, które zawęża kandydaty według typu dokumentu, zakresu dat lub produktu przed uruchomieniem wyszukiwania wektorowego, a nie po nim.
  • Wyszukiwanie dokumentu nadrzędnego – dopasowanie na małym, precyzyjnym fragmencie, ale zwrócenie jego większej sekcji nadrzędnej, aby kontekst był kompletny.
  • Mierzenie skuteczności wyszukiwania samodzielnie

    Zbiór do oceny jest lepszy od intuicji. Napisz od 20 do 50 realistycznych pytań, zaznacz, które fragmenty powinny zostać wyświetlone dla każdego z nich, i sprawdź bezpośrednio, czy właściwy fragment znajduje się w czołowej 5 lub czołowej 10 (Recall@k). Dodaj sędziego w postaci LLM do oceny trafności tylko wtedy, gdy ręczna inspekcja zdobędzie twoje zaufanie. Jeśli jakość wyświetlania fragmentów nie jest nigdy mierzona oddzielnie od jakości odpowiedzi, czas jest tracony na optymalizację niewłaściwego etapu. Narzędzia takie jak Ragas obejmują specyficzne dla RAG metryki, więc nie musisz ręcznie budować całego systemu.

    LlamaIndex czy LangChain, koncepcyjnie

    LlamaIndex skupia się na danych, oferując rozwinięte abstrakcje dla mechanizmów indeksowania, wyszukiwania i przetwarzania zapytań, co sprawia, że jest naturalnym rozwiązaniem w sytuacjach, gdy kluczowym problemem jest duża ilość dokumentów wymagających skutecznego wyszukiwania. LangChain natomiast ma zastosowanie uniwersalne, powstał na bazie łańcuchów i agentów, i posiada znacznie większy ekosystem integracji. Jest bardziej elastyczny, ale jednocześnie bardziej złożony, gdy potrzebne jest wyłącznie wyszukiwanie. Żadne z tych rozwiązań nie jest czymś magicznym – oba opierają się na tych samych podstawowych mechanizmach. Wybierz jedno, dogłębnie opanuj jego koncepcje i unikaj dogmatyzmu, ponieważ wiedza architektoniczna jest przydatna niezależnie od używanej składni.

    Podsumowanie

    1. Indeksowanie i wyszukiwanie to odrębne etapy, więc należy je projektować niezależnie.
    2. Podział na fragmenty to ważna decyzja projektowa, a nie standard ramy roboczej, który należy automatycznie przyjąć.
    3. Magazyny wektorowe stanowią infrastrukturę, a korzyści wynikają z filtrowania i wyszukiwania hybrydowego, a nie z samej marki.
    4. Jakość pozyskiwania informacji musi być mierzona oddzielnie, niezależnie od jakości odpowiedzi.
    5. Frameworki to narzędzia, a nie architektura, więc należy poznać odpowiednie wzorce i traktować je jako elementy, które można zastąpić.