Strona główna / Artykuły

Jakość RAG w czasie tworzenia indeksu: typy fragmentów, czyszczenie danych i etykiety do wyszukiwania

Jakość RAG w czasie indeksowania: tworzenie fragmentów tekstu, tabel i obrazów, usuwanie szumów generowanych przez parser oraz dodawanie etykiet do wyszukiwania napisanych przez LLM, które w przybliżeniu zredukowały o połowę nieudane wyszukiwania.

Wydawca: REACTAPP.TOP12 min czytania2701 słów

Źródła: The Part of RAG Where Quality Is Actually Won

Gdzie demo przekształca się w system produkcyjny

Parser RAG przekazuje uporządkowaną listę węzłów, a każdy węzeł wie, z której sekcji pochodzi. Wszystko, co dzieje się od tego momentu aż do chwili, gdy model odczytuje zapytanie, to praca wykonywana w czasie tworzenia indeksu, i to właśnie ten etap odróżnia demo od rozwiązania, które można uruchomić w środowisku produkcyjnym. Proces ten dzieli się na trzy etapy. Węzły są przekształcane w fragmenty, których istnieją trzy rodzaje, każdy z własnym narzędziem do ich tworzenia. Fragmenty są oczyszczane z materiałów, które parser sumiennie wyodrębnił, ale które model nie powinien widzieć. Każdy fragment otrzymuje krótki, wygenerowany etykietę od modelu językowego. Podczas wewnętrznej oceny dokumentów bankowych te etykiety zmniejszyły o mniej więcej połowę liczbę nieudanych prób wyświetlenia informacji, co czyni je największą korzyścią w całym procesie.

Jest to również najmniej efektowne podejście. Prawie nikt nie pisze o usuwaniu hałasu, i to właśnie dlatego istnieje tak wiele rozwiązań w tym temacie. Podane poniżej dane pochodzą z małej wewnętrznej oceny, a nie z rygorystycznych testów porównawczych, więc należy je traktować zgodnie z tym faktem.

Trzy rodzaje fragmentów tekstu, trzy narzędzia do ich tworzenia

Proste rozwiązanie wykorzystuje jeden narzędzie do dzielenia tekstu na fragmenty: tekst jest podzielony i sprawa jest załatwiona. Prawdziwe dokumenty zawierają co najmniej trzy rodzaje treści, które zachowują się zupełnie inaczej. Akapit prozy, tabela opłat oraz zrzut ekranu wymagają odrębnego potraktowania, a próba przetworzenia ich wszystkich za pomocą jednego narzędzia powoduje pojawienie się niepełnych tabel i błędnych kroków w procesie.

Fragmenty tekstu odpowiadają strukturze

Rozdzielanie według liczby znaków jest niemożliwe. Zamiast tego każdy wiersz jest najpierw wpisywany jako nagłówek, numerowany krok lub zwykłe zdanie, a zasady łączenia zależą od tych typów. Dzięki temu numerowany krok nie łączy się z niepowiązanym akapitem. Zbyt długie teksty są przecinane na końcu zdań, a przecinanie w środku zdania jest ostateczną opcją awaryjną. Sąsiadujące fragmenty lekko się nakładają, dzięki czemu procedura przechodząca przez granicę pozostaje zrozumiała z obu stron.

Tabele pozostają nienaruszone

Tabele są tworzone oddzielnie, ponieważ ich znaczenie leży w ich układzie. Narzędzie do tworzenia znajduje wiersz nagłówkowy, powtarza go w każdej części, jeśli tabela musi zostać podzielona, i uporządkowuje wiersze. Odrzuca również tabele, które nie zawierają rzeczywistej treści, najczęściej spis treści, który parserzy wyświetlają jako uporządkowaną siatkę nagłówków i numerów stron.

W poprzedniej wersji tego systemu istniała wada dokładnie w tym miejscu. Spisy treści były wykrywane za pomocą prostego testu – wiele wierszy kończyło się małą liczbą. Tabela opłat również odpowiadała temu opisowi, więc rzeczywisty harmonogram opłat mógł zniknąć z indeksu bez żadnego ostrzeżenia. Nowy test jest bardziej precyzyjny: na prawdziwej stronie z treścią numery stron stale rosną, natomiast kwoty opłat, takie jak 35 dolarów, 30 dolarów i 7 dolarów, mogą zarówno spadać, jak i rosnąć. Tabela jest uznawana za element indeksu tylko wtedy, gdy końcowe liczby nigdy nie spadają. Harmonogramy opłat teraz pozostają, strony z treścią nadal się pojawiają, a jedno warunek usunął błąd.

Obrazy są sprawdzane pojedynczo

Każdy obraz może kosztować pieniądze, dlatego każdy z nich próbuje kolejno trzech metod i zatrzymuje się przy pierwszej, która zadziała.

  1. Droga wolna wykorzystuje tekst, który parser już wyodrębnił. Jeśli dokument zawiera użyteczny opis lub adnotację, która nie jest zastępczym tekstem typu „image”, to właśnie ten tekst jest używany, a żaden model wizyjny nie jest uruchamiany. W wielu dokumentach od jednej trzeciej do połowy obrazków korzysta z tej drogi, co stanowi czystą oszczędność.
  2. Szlak płatny wywołuje model wizji. Ma to zastosowanie, gdy nie ma dostępnego tekstu do użycia, a obraz jest większy od małego podziałnika. Dokładnie sformułowany prompt sprawia, że model najpierw dokonuje klasyfikacji. Jeśli obraz to logo lub ornament, model odpowiada jednym słowem „DECORATIVE”, a dana część jest odrzucana. W przeciwnym razie wypełnia dwa pola. W polu SCREEN znajduje się jedna linia opisująca przeznaczenie ekranu, na przykład ekran wprowadzania danych transakcji dla kont biznesowych, sformułowana w taki sposób, aby znajdowała się blisko pytań użytkownika we przestrzeni wektorowej. Pole CONTENT wymienia wszystkie etykiety pól, przyciski oraz widoczny kod w kolejności czytania, dostarczając wyszukiwaczom dokładne ciągi znaków, które mogliby wpisać. Dzięki temu skrynka zrzutu ekranu może zostać znaleziona zarówno według swojego przeznaczenia, jak i według umieszczonego na niej tekstu.
  3. Ścieżka awaryjna wykorzystuje nagłówek. Jeśli obie metody zawiodą, ale sekcja jest znana, zapisywany jest placeholder z informacją o tym, jaka jest to obrazka i gdzie się znajduje, dzięki czemu można do niej dotrzeć poprzez odpowiednią sekcję podczas wyszukiwania.

W sprawdzeniu rozmiaru istnieje niewielki błąd. Bardzo małe obrazki omijają proces analizy wizualnej na podstawie progu szerokości i wysokości, ale obie wymiary muszą być małe, więc bardzo cienka i bardzo szeroka linia rozdzielcza przechodzi dalej i nadal jest wysyłana do modelu. To powoduje niewielkie straty finansowe związane z dekoracjami, bez wpływu na poprawność wyników, i znajduje się to na liście zadań do poprawy.

Diagram pokazuje trzy ścieżki obrazków sprawdzane w kolejności. Model wizualny jest uruchamiany raz podczas indeksowania, a każde kolejne zapytanie jest bezpłatne.

Oczyszczanie tego, co parser zrobił poprawnie

Wierność jest właśnie tym, co sprawia, że parserzy stanowią tutaj kłopot. Wyodrębniają one całą stronę: pieczątkę „TAJNE” powtarzaną na 200 stronach, numery stron, stopki z informacjami o prawach autorskich, zdanie przerwane przez przerwę stronic. Każda z tych elementów staje się osobnym fragmentem, a wszystkie one stanowią zbędne informacje. Zbędne informacje zakłócają również ranking, ponieważ zapytanie użytkownika może równie łatwo trafić na baner, jak i na procedurę.

Gdy więc powstaną takie fragmenty, przechodzą one przez proces oczyszczania, w którym każdy krok jest skierowany na konkretny rodzaj zbędnych informacji występujących w rzeczywistych dokumentach. W następującej kolejności:

  1. Filtrowanie standardowych elementów usuwa same numery stron, pieczątki wersji oraz linie z linkami typu „skontaktuj się z nami” lub „przeczytaj więcej” umieszczone na początku. Zbiór reguł zmienia się w zależności od typu pliku, ponieważ pliki PDF, pliki Word oraz prezentacje mają po swojemu specyficzne zbędne informacje.
  2. Usuń powtarzające się elementy. Tekst, który pojawia się identycznie na wielu stronach, jest zbędny. Zdanie „CONFIDENTIAL, INTERNAL USE ONLY” na piętnastu stronach jest liczone i usuwane, natomiast fraza „See page 3” pojawiająca się dwa razy na jednej stronie zostaje, ponieważ nie obejmuje kilku stron.
  3. Popraw uszkodzone fragmenty. Fragment rozpoczynający się w środku zdania lub z niekompletnym punktem został rozdzielony w niewłaściwym miejscu. Jeśli może połączyć się z poprzednim fragmentem w tej samej sekcji, łączą się one. W przeciwnym razie jest oznaczony jako kontynuacja, dzięki czemu jego etykieta zawiera odpowiedni kontekst.
  4. Złącz samotne fragmenty. Fragment składający się z 40 znaków, znajdujący się na granicy sekcji, jest wchłaniany przez sąsiedni fragment, ale tylko wtedy, gdy oba należą dokładnie do tej samej sekcji i mają ten sam nagłówek. Nic nie łączy się między sekcjami, a krótka informacja, która rzeczywiście stoi samodzielnie, pozostaje bez zmian.
  5. Proces usuwania duplikatów łączy zdanie powtórzone pod jednym nagłówkiem w jeden fragment. Jeśli pojawi się ono pod dwoma nagłówkami, zachowane zostaną obie kopie, ponieważ dzięki dołączonemu ścieżce sekcji zawierają one różne informacje. Obrazy są całkowicie wyłączone z tego procesu, gdyż dwa podobne obrazy ekranu to nadal dwa odrębne zrzuty ekranu.

Żadna z tych zasad nie jest skomplikowana. Każda powstała w wyniku obserwacji, jak rzeczywisty dokument pokonuje prostszą wersję, a razem wyjaśniają one, dlaczego zapytanie trafia do określonej procedury, a nie do informacji dotyczących praw autorskich.

Etykieta wyszukiwania

Następuje krok, który przynosi największe korzyści, ale który najczęściej pomija się w większości systemów. Weźmy prosty fragment w tym stylu:

Wpisz kwotę i naciśnij F4.

Zawiera on ścieżkę sekcji Wire Transfer > Initiation > Amount Entry, co już pomaga. Przed włączeniem jednak model językowy tworzy również krótką etykietę do wyszukiwania składającą się z 18 do 22 słów, przechowywaną razem z tym fragmentem. Dla tego fragmentu etykieta brzmi mniej więcej tak:

Krok procedury: wpisz kwotę przelewu, a następnie naciśnij F4, aby potwierdzić rozpoczęcie przelewu bankowego.

Rozważmy efekt. Bez etykiety instrukcja znajduje się w przestrzeni embeddingów obok wszystkich innych poleceń nakazujących wpisanie wartości i naciśnięcie klawisza. Z etykietą znajduje się blisko informacji o przelewach bankowych i potwierdzeniach, co to słownictwo, którego użyłby rzeczywisty użytkownik. Przeformułowane pytanie typu „jak potwierdzić kwotę przy przelewie bankowym” znajduje wtedy tę sekcję. W wewnętrznej ocenie usunięcie etykiet zmniejszyło o około połowę przypadków nieudanego odnalezienia informacji. Był to mały test na pojedynczym korpusie, więc należy go traktować jako silną wskazówkę, a nie jako uniwersalną zasadę.

Ta technika wywodzi się z rozwiązania Anthropic Contextual Retrieval, które wykazało, że umieszczenie zdania kontekstowego specyficznego dla danego fragmentu przed każdym z nich przed indeksowaniem znacznie zmniejsza liczby nieudanych wyszukiwań. W tym przypadku została dostosowana do treści, w których istotne są dokładne identyfikatory. Kody transakcji oraz klawisze funkcyjne muszą być kopiowane bez żadnych zmian i nigdy nie mogą być parafrrazowane, a każda etykieta zaczyna się od słowa określającego rolę, takiego jak „Krok procedury” lub „Tabela referencyjna”, dzięki czemu fragmenty grupują się z odpowiednimi rodzajami pytań.

Co widzi osoba tworząca etykiety

Etykieta jest tak dobra, jak kontekst przekazany modelowi, który ją tworzy. Sam fragment tekstu nie wystarcza, ponieważ to właśnie on stracił swoje znaczenie po wycięciu z strony. Dlatego każda prośba o etykietę zawiera kilka elementów:

  • sam tekst fragmentu
  • ścieżkę sekcji oraz jej nagłówek
  • zagłówki kolumn, gdy fragment to tabela
  • poprzedni i następny fragment, aby pokazać, co je otacza
  • krótkie podsumowanie całego dokumentu, dla lepszego zrozumienia kontekstu

Podsumowanie dokumentu pochodzi z oddzielnego wywołania dokonanego przed rozpoczęciem etapu nadawania etykiet. Zbiera przykłady fragmentów z początku, środka i końca, a także jedną tabelę i jedno zdjęcie, po czym zwraca dwa lub trzy zdania określające typ dokumentu, jego odbiorców oraz produkty i kody, które się w nim znajdują. Każda prośba o nadanie etykiety zawiera następnie to podsumowanie. Wyrażenie „wprowadź kwotę” powinno być oznaczone inaczej w instrukcji dotyczącej przelewu bankowego niż w informacji o pożyczce, a to właśnie podsumowanie pomaga modelowi zrozumieć, jaki dokument jest analizowany.

Sąsiednie fragmenty zasługują na dokładniejszą analizę, ponieważ pojęcie „poprzedni i następny” nie oznacza dwóch fragmentów fizycznie sąsiadujących ze sobą. Fragment obok może należeć do zupełnie innego procederu. Dlatego sąsiednie fragmenty są wybierane według stopnia powiązania, w trzech poziomach, biorąc pierwszy, który pasuje:

  1. najbliższy fragment w tym samym ścieżce sekcji
  2. w przeciwnym razie najbliższy fragment w tej samej sekcji nadrzędnej
  3. w przeciwnym razie najbliższy fragment pod tą samą nagłówkiem

Kandydatów sprawdza się, rozpoczynając od bieżącego fragmentu, a wszystko poza odpowiednią sekcją jest pomijane. Dzięki temu model widzi następny fragment, który rzeczywiście należy do tego. Każdy sąsiadowy fragment jest skracany do kilkuset znaków, co zapewnia wystarczający kontekst bez konieczności przekazywania całych fragmentów.

W przykładzie F4 model widzi, że poprzedni fragment w jego sekcji dotyczył wyboru beneficjenta, a następny – potwierdzenia przelewu. Dzięki temu może stworzyć precyzyjną etykietę dotyczącą wprowadzania kwoty przelewu, a nie niejasny podsumowanie. Dobrze dobrane fragmenty sąsiednie zapewniają dokładność etykiet.

Rysunek pokazuje, jak model etykiet czyta fragmenty sąsiednie z tej samej sekcji, a następnie tworzy jedno konkretne zdanie, które jest przechowywane obok fragmentu i dostarczane do obu kanałów wyszukiwania.

Dlaczego to jest opłacalne

Trzy cechy sprawiają, że ten sposób jest praktyczny i nie generuje dużych kosztów.

  • Koszt jednorazowy. Etykieta jest generowana podczas indeksowania i przechowywana, więc zapytania nigdy za nią nie płacą.
  • Niski koszt jednostkowy. Mały, szybki model etykietuje fragmenty w grupach po 25, przy jednoczesnym działaniu kilku grup, a koszt wynosi niecały cent za fragment.
  • Spokojne awarie. Partia, w której wystąpiły błędy w etykietowaniu, jest po prostu indeksowana bez etykiet, podczas gdy proces trwa dalej. Te fragmenty zachowują swój tekst i ścieżkę sekcji, po prostu nie otrzymują dodatkowego wzmocnienia, więc przerwa wpływa na jakość, ale nigdy nie zatrzymuje indeksowania.

Etykieta znajduje się w osobnym polu, oddzielnie od treści fragmentu. To rozdzielenie ma znaczenie później, podczas wyszukiwania, gdy etykieta otrzymuje większą wagę w wyszukiwaniu według słów kluczowych. Kluczowe jest teraz to, że najdroższe przetwarzanie odbywa się przed każdą wyszukiwką i to właśnie sprawia, że zapytania w formie parafraz działają.

Determinizm i wersjonowany prompt

Generowanie etykiet jest deterministyczne: temperatura zero oraz stałe ziarno oznaczają, że ponowne indeksowanie dokumentu daje te same etykiety. To jest podstawą twierdzenia, że koszt jest płatny tylko raz. Ponowne indeksowanie nigdy nie mienia wektorów, jedynie je odtwarza.

Determinizm zależy również od wersjonowania promptu. Ziarno generuje identyczny wynik tylko dla identycznego wejścia, a tekst instrukcji jest częścią tego wejścia. Edytowanie promptu w celu wzbogacenia treści pomiędzy wersjami powoduje, że każde kolejne indeksowanie daje różne etykiety, dzięki czemu indeks niepostrzeżenie dzieli się na dwie generacje, a nic o tym nie informuje. Dlatego prompt jest zapisywany z tagiem wersji, ładowany przy uruchamianiu procesu indeksowania za pomocą tego tagu i zmieniany wyłącznie poprzez śledzoną, celową aktualizację. Reprodukowalność pomiędzy wersjami musi być celowo budowana, ponieważ samo ziarno jej nie zapewni.

Wkładanie wektorów i przechowywanie

Po oczyszczeniu, umieszczeniu i oznakowaniu ostatnim krokiem w procesie tworzenia indeksu jest generowanie wektora. Jeden model wkładania wektorów obejmuje wszystkie typy fragmentów tekstu: text-embedding-3-large, skrócony do 1536 wymiarów, dzięki czemu tekst, tabele i zrzuty ekranu są porównywane z zapytaniem w tym samym przestrzeni.

Do narzędzia wstawiania wprowadzana jest złożona sekwencja znaków w ustalonej kolejności: prefiks strukturalny, etykieta, a następnie tekst treści. Prefiks wykorzystuje informacje już znane na temat danego fragmentu. Obejmują one typ dokumentu, który można odgadnąć na podstawie ścieżki sekcji, np. harmonogram opłat lub procedura przelewu bankowego, a także nagłówek i ścieżkę sekcji; w przypadku obrazków dodatkowo stronę i lokalizację. Oznaczenie fragmentu przypominającego procedurę jako procedury skłania do zadawania pytań dotyczących sposobu jej wykonania.

W tym etapie do tekstu dodawana jest również ścieżka sekcji, co było źródłem błędu w starszej wersji. Wtedy prefiks był wpisywany do fragmentu podczas jego oczyszczania, a każda późniejsza edycja mogła dodać go ponownie, co potajemnie psuło wektor. Obecny kod usuwa wszelkie przechowywane prefiksy, a następnie tworzy nowy w momencie wgrania. Znajduje się on w jednym miejscu i jest dodawany dokładnie raz, tuż przed tym, jak model go odczyta, co wyeliminowało całą grupę błędów związanych z podwójnymi prefiksami.

Gotowy wektor jest zapisywany w bazie danych razem z treścią fragmentu, etykietą, metadanymi strukturalnymi oraz kolumną pełnego tekstu, z której korzysta wyszukiwanie według słów kluczowych. Następnie dokument staje się aktywny. Jeśli jakiś krok w procesie zakończy się błędem, dokument jest oznaczony jako odrzucony, a przyczyna jest zachowywana w celach diagnostycznych, podczas gdy wcześniejsza aktywna wersja pozostaje dostępna do wyszukiwania przez cały czas. Dokument częściowo zindeksowany nigdy nie jest widoczny.

Jak wygląda ukończony rząd

Aby to uwyraźnić, oto fragment „Wprowadź kwotę i naciśnij F4” w jego aktualnej formie w sklepie. Każde pole pochodzi z poprzedniego kroku.

text              : "Enter amount and press F4."
                     (built + cleaned + section-prefixed)
retrieval_context : "Procedure step entering the amount
                     and pressing F4 during wire transfer
                     initiation."          (the label)
chunk_type        : TEXT
section_path      : "Wire Transfer > Initiation >
                     Amount Entry"         (Part 2)
preceding_heading : "Amount Entry"
table_headers     : null                  (TABLE only)
page_start / end  : 12 / 12
image_object_key  : null                  (IMAGE only)
embedding_model   : text-embedding-3-large
embedding_dim     : 1536
embedding         : [ ...1536 floats... ] (body+label)
text_search_vector: label weighted high, body lower

W bloku tabeli nagłówki kolumn wypełniają miejsce, gdzie blok tekstowy zawiera wartości null. Blok obrazu przechowuje klucz bloba, typ MIME oraz pozycję na stronie, przy czym dane CONTENT znajdują się w polu text, a informacje SCREEN w polu retrieval_context. Wszystkie trzy rodzaje bloków mają ten sam układ wierszy. Proces wyświetlania polega na wykorzystaniu dwóch kolumn: embedding do wyszukiwania wektorowego oraz text_search_vector do wyszukiwania według słów kluczowych, przy czym obie są wyprowadzane z treści bloku wraz z etykietą. Pozostałe kolumny służą do lokalizacji bloku, jego wyświetlenia lub ograniczenia do odpowiedniego użytkownika.

To jest cały wynik z czasu tworzenia indeksu: takie wiersze, po jednym na blok, wyczyszczone, oznaczone i przekształcone we wektory, czekające na pierwsze zapytanie.

Literatura pokrewna

  • Rozmiar i nakładanie się bloków w RAG: krok, limity tokenów i styki — Jak podzielić długą książkę na bloki dla RAG: wybór rozmiaru i stopnia nakładania się, zrozumienie kroku, przestrzeganie limitu tokenów do embeddingów, przechowywanie metadanych oraz zobaczenie nakładania się w rzeczywistym podziale.