Strategie chunkingu w RAG do użycia w produkcji: o stałej wielkości, semantyczne oraz rodzic-dziecko
Praktyczny przegląd techniki chunkingu w RAG, od dzielenia na elementy o stałej wielkości i rozwiązań rekurencyjnych po metody typu rodzic-dziecko, propozycje, późne chunkingi, RAPTOR, tabele, kod oraz testy wydajności.
Źródła: Chunking Strategies for Production RAG: From Fixed-Size to Context-Aware and Multimodal Retrieval
Załóżmy, że pracownik zajmujący się roszczeniami w towarzystwie ubezpieczeniowym pyta wewnętrznego asystenta: jaka jest konieczna zatwierdzenie dla roszczenia o odszkodowanie za uszkodzenia spowodowane wodą w wysokości 40 000 euro i co należy sprawdzić przed wypłatą? Asystent korzysta z aktualnego podręcznika dotyczącego roszczeń. Pliki zostały poprawnie przetworzone, OCR zadziałało, proces wstępnej obróbki usunął niepotrzebne elementy, model embedding wybrał właściwy podręcznik, a mechanizm ponownego sortowania umieścił go na górze listy.
Odpowiedź nadal jest niekompletna.
Doświadczony likwidator nie byłby zaskoczony. Zasada ta nie znajduje się w jednym akapicie. Wyrażenie narracyjne stanowi, że roszczenia dotyczące podejrzanych wcześniejszych uszkodzeń wymagają niezależnej inspekcji przed wypłatą. Poniżej znajduje się tabela uprawnień. Na następnej stronie znajduje się diagram przepływu, który pokazuje, że najpierw należy również przeprowadzić weryfikację oszustwa i potwierdzenie prawa własności. System dokumentów zawiera identyfikator podręcznika, jego wersję oraz region, do którego ma zastosowanie.
Dla osoby zajmującej się tym tematem nie są to cztery oddzielne fakty. Są to jedna zasada biznesowa wyrażona w czterech różnych formatach: akapit określa warunek, tabela wskazuje, kto może zatwierdzić roszczenie, diagram pokazuje kolejność sprawdzeń, a metadane wskazują, która wersja obowiązuje.
Standardowy splitter ustawiony na 500 tokenów może umieścić akapit w jednym fragmencie, górną połowę tabeli w drugim, odpowiedni wiersz tabeli w trzecim, a wydobyty tekst z diagramu w czwartym. Każdy fragment jest ważny. Każda próba embeddingu kończy się sukcesem. Panel sterowania pozostaje w kolorze zielonym. Mimo to wiedza została fundamentalnie przearanżowana. Podczas wyszukiwania system musi powiązać typ twierdzenia z przedziałem ilościowym, przyporządkować go osobie upoważnionej do zatwierdzenia, znaleźć wymagania inspekcji oraz odzyskać niezbędne sprawdzenia – wszystko to na podstawie fragmentów, które same w sobie nie dają możliwości odczytania pełnego kontekstu. Model językowy otrzymuje te fragmenty i tworzy niekompletną odpowiedź.
Dlatego debugowanie systemów RAG wprowadza w błąd. Zespoły wymieniają modele embeddingu, zmieniają wartość top-k, kupują silniejsze narzędzia do ponownego rankowania lub przepisują prompty. Czasami to pomaga, ale częściej jedynie kompensuje szkody spowodowane niewłaściwą segmentacją. Niska jakość systemów RAG jest często wynikiem problemów z dzieleniem tekstu na fragmenty, udawanymi jako problemy z modelem językowym.
Większość dyskusji na temat dzielenia tekstu na fragmenty sprowadza się do jednej liczby: 256 tokenów, 512 lub 1 000, wraz z informacją o stopniu nakładania się tych fragmentów. To są uzasadnione pytania dotyczące implementacji, ale kwestia architektoniczna jest inna:
Cóż musi pozostać razem, aby fragment nadal miał to samo znaczenie po samodzielnej ekstrakcji?
Następna część omawia od najprostszych strategii po techniki pojawiłe się w latach 2024 i 2025, wskazuje, gdzie każda z nich się opłaca, i kończy się propozycją rozwiązania praktycznego umożliwiającego obsługę złożonych dokumentów korporacyjnych.
Miejsce dzielenia tekstu na fragmenty w procesie przetwarzania
Dzielenie tekstu na fragmenty nie jest izolowanym krokiem w przetwarzaniu tekstu. Stanowi granicę pomiędzy rozumieniem czystych dokumentów a tworzeniem możliwego do wyszukiwania zasobu wiedzy. Wszystko, co znajduje się wcześniej w łańcuchu przetwarzania, decyduje o tym, co można podzielić, a wszystko później zależy od tego, czy fragmenty zachowują wystarczająco dużo znaczenia, struktury i informacji o pochodzeniu, aby można je było prawidłowo odnaleźć.
Wyobraź sobie pełną ścieżkę. Źródła takie jak pliki PDF, tabele, obrazy, JSON, bazy danych, API i kod są pobierane, analizowane, oczyszczane i normalizowane. Dopiero wtedy warstwa dzielenia na fragmenty decyduje, w jaki sposób każdy typ treści stanie się jednostką do wyszukiwania. Narracja może wykorzystywać podział strukturalny lub semantyczny. Złożone zasady mogą być odpowiednie dla reprezentacji na poziomie propozycji. Tabele wymagają granic uwzględniających ich schemat. JSON powinien odpowiadać obiektom biznesowym. Obrazy potrzebują opisów wizualnych powiązanych z oryginałem.
Dlatego system produkcyjny rzadko decyduje się na jedną, stałą wielkość fragmentów dla wszystkiego. Jeden korpus może zawierać reprezentacje na poziomie dokumentu, sekcji, akapitu, propozycji i tokena jednocześnie. Pary wyszukiwania typu „rodzic-dziecko” używają małych fragmentów dla precyzji oraz większych fragmentów dla kontekstu, natomiast późne dzielenie na fragmenty, wyszukiwanie kontekstowe i RAPTOR odbudowują informacje, które w przeciwnym razie zostałyby usunięte podczas dzielenia.
Proces dzielenia na fragmenty nie kończy się również wtedy, gdy istnieją wektory. Metadane fragmentów, wersje dokumentów, powiązania rodzic-dziecko, kontrole dostępu oraz odniesienia do źródeł muszą przetrwać proces indeksowania, wyszukiwania, ponownego rankingu, budowy kontekstu oraz generowania wyników. Ocena zamyka ten cykl: nieudane wyszukiwania, powtarzający się kontekst, błędne cytaty oraz zmieniające się wzorce zapytań powinny wpływać na decyzje dotyczące dzielenia dokumentów na fragmenty. W praktyce proces ten polega mniej na dzieleniu dokumentów, a bardziej na projektowaniu jednostek wyszukiwania, w oparciu o które funkcjonuje wszystko inne.
Główny kompromis
Weźmy krótki fragment polityki:
High-risk corporate customers must complete Enhanced Due Diligence.
Facilities above EUR 10 million require Group Credit Committee approval.
Fully cash-collateralised facilities may follow the exception defined in Section 7.4.3.
Zachowanie wszystkiego razem chroni kontekst, ale daje szeroki, niewyraźny wektor. Agresywne dzielenie zapewnia precyzyjne wyszukiwanie, jednak reguła zatwierdzenia może odsunąć się od klasy klienta, do której jest stosowana, a wyjątek może zostać oddzielony od reguły, którą modyfikuje.
To właśnie stanowi całe napięcie: małe fragmenty poprawiają precyzję, duże fragmenty zachowują kontekst, a systemy produkcyjne wymagają świadomego równowagi. Każda technika dzielenia to inna droga w tym kompromisie, a żadna nie eliminuje potrzeby zrozumienia dokumentów oraz pytań, które faktycznie zadają użytkownicy.
Proste punkty odniesienia
Dzielenie o stałej wielkości
Dzielenie o stałej wielkości dzieli tekst na okna o ustalonej długości, zwykle wyrażoną w tokenach, z opcjonalnym nakładaniem się. Jest proste, deterministyczne i tanie.
Liczenie tokenów zazwyczaj jest skuteczniejsze niż liczenie znaków, ponieważ modele embeddingowe rozpoznają tokeny. Pięćset znaków w języku niemieckim lub chińskim może zawierać znacznie mniej znaczących jednostek niż taka sama liczba znaków w języku angielskim. Metoda ta koduje tekst na tokeny, przegląda je w ustalonych odstępach i dekoduje każde okno. Przeplatanie się oznacza, że każde okno zaczyna się przed zakończeniem poprzedniego.
- Dobrze sprawdza się przy: jednolitej prozie, podstawowych benchmarkach, sytuacjach, gdzie najważniejsza jest prostota działania, oraz w wczesnych prototypach wymagających najpierw funkcjonalnego procesu przetwarzania.
- Nie działa, gdy: znaczenie zależy od struktury. Określona warunek może się zakończyć przy tokenie 499, podczas gdy jego wyjątek zaczyna się przy tokenie 501, a granica ta nie wie nic o logice biznesowej.
Jakie ma znaczenie przeplatanie się
Przeplatanie się fragmentów stanowi zabezpieczenie przed błędami w określaniu granic. Bez niego zdanie może zostać podzielone na dwa fragmenty, z których żaden nie funkcjonuje samodzielnie. Dzięki niemu każdy fragment zawiera elementy sąsiadującego z nim, dzięki czemu oba pozostają zrozumiałe.
Cena tego podejścia jest rzeczywista: więcej wektorów, większe wydatki na embeddingi, większa ilość pamięci przechowywania oraz listy wyników wypełnione niemal identycznymi fragmentami tekstu. Wskaźnik dwudziestu procent to raczej zwyczaj niż reguła, więc należy go traktować jako parametr do porównania z metrykami odzyskiwania informacji.
Podział na fragmenty o stałej wielkości zyskuje na znaczeniu nie dzięki optymalności, lecz dzięki zapewnieniu stabilnej bazy odniesienia. Jeśli bardziej zaawansowana metoda nie potrafi w sposób mierzalny przewyższyć jej efektywność w danym korpusie, dodatkowa złożoność nie jest uzasadniona.
R Recurencyjne dzielenie
Dokumenty korporacyjne zawierają już przydatne podziały w postaci nagłówków, sekcji, akapitów, zdań i list. Rozdzielanie rekurencyjne wykorzystuje te elementy, zanim ucieknie się do dowolnych punktów cięcia. Na przykład rekurencyjny podzielacz znaków z LangChain najpierw próbuje użyć podwójnych nowych wierszy, które zazwyczaj oznaczają akapity. Jeśli fragment nadal jest zbyt duży, próbuje pojedynczych nowych wierszy, następnie interpunkcji zdaniowej, potem spacji i wreszcie pojedynczych znaków. Limit wielkości stanowi górny próg, który uruchamia każdy z tych alternatywnych rozwiązań.
Kolejność separatorów ma większe znaczenie niż sama wartość wielkości. W przypadku dobrze zorganizowanych polityk, procedur, instrukcji i dokumentacji technicznej jest to często doskonały standard domyślny.
Jedna uwaga dotycząca procesu produkcji: jeśli narzędzie do wyodrębniania już wyekstrahowało hierarchię, nie wyrzucaj jej i nie prosz odtwarzacza o ponowne jej znalezienie. Sekcja numerowana 7.4 w ramach rozdziału dotyczącego pożyczek już wskazuje, które teksty należą do siebie. Zachowaj tę strukturę w metadanych i wykorzystaj ją do określania granic podziałów. Mówiąc prościej, nie niszcz struktury podczas dzielenia na fragmenty, a potem nie próbuj jej odbudowywać podczas pobierania danych.
Dzielenie rekurencyjne zakłada, że struktura w przybliżeniu odpowiada znaczeniu, ale nie zawsze tak jest. Jeden akapit może zawierać trzy niespowiązane ze sobą zasady, a pięć akapitów może opisywać jeden proces. Wtedy właśnie przydają się metody oparte na znaczeniu.
Jednostki zdaniowe i akapitowe
Narzędzia takie jak spaCy i NLTK oferują wytrenowane detektory zdań, które radzą sobie z problemami wynikającymi z prostego dzielenia na zdania, np. skrótami, liczbami dziesiętnymi, tytułami i elipsami. Dzielenie na akapity jest prostsze i często skuteczne, ponieważ akapit to grupa wybrana celowo przez autora. W przypadku czystych dokumentów dzielenie na akapity oraz łączenie krótkich sąsiadujących fragmentów o długości nie mniejszej od określonego minimum stanowi rozsądne pośrednictwo pomiędzy podejściem opartym na stałej długości a podejściem semantycznym.
Kosztem jest zmienna wielkość fragmentów. Niektóre akapity zawierają 50 tokenów, a inne 800, co komplikuje grupowanie, zwiększa opóźnienie podczas tworzenia reprezentacji wektorowych i utrudnia przewidywanie rozmiaru indeksu. To, czy lepsze granice tego usprawiedliwiają, zależy od korpusu.
Sufitki przesuwne jako narzędzie do poprawy dokładności
Okno przesuwane to metoda dzielenia na fragmenty o stałej wielkości, w której nakładanie się fragmentów jest traktowane jako mechanizm wyszukiwania, a nie tylko środek zapobiegania problemom z granicami. Rozumowanie jest takie: wektor zapytania najlepiej pasuje do fragmentu, który bezpośrednio na nie odpowiada. Jeśli ten fragment znajduje się po obu stronach granicy w danej segmentacji, żaden z fragmentów nie może uzyskać wysokiej oceny. Przy dużej mierze nakładania się każdy fragment o rozsądnej długości pojawia się w całości przynajmniej w jednym fragmencie.
Większa mierza nakładania się poprawia dokładność wyszukiwania, ale kosztem redundancji. Powyżej około 30 do 40 procent list top-k wypełniają się niemal identycznymi elementami, które zużywają zasoby kontekstowe bez dodawania nowych faktów, natomiast 10 do 25 procent nadaje się dla większości korpusów. Oceniaj stopień nakładania się, mierząc wskaźnik wykrywania duplikatów obok wskaźnika Recall@k, ponieważ poprawa Recall@5 przy podwojeniu liczby duplikatów może nie poprawić jakości odpowiedzi.
Strategie uwzględniające znaczenie
Dzielenie semantyczne
Semantyczne dzielenie tekstu zastępuje regułę opartą na liczbie tokenów pytaniem: czy temat zmienił się na tyle, by rozpocząć nową jednostkę? Wyobraźmy sobie sekcję, w której pierwsze dwa zdania dotyczą wymagań inspekcyjnych, trzecie odnosi się do limitów płatności, a czwarte wymienia zasady przeciwdziałania oszustwom. Stały narzędzie do dzielenia tekstu nie jest w stanie dostrzec tych zmian tematycznych, podczas gdy dobrze dostrojony narzędzie semantyczne potrafi to zrobić.
Zwykła metoda składa się z trzech etapów. Najpierw za pomocą modelu językowego dzieli się tekst na zdania. Następnie każde zdanie lub niewielki zakres sąsiadujących zdań jest przetwarzane za pomocą modelu sentence-transformer. Potem oblicza się odległość kosinową pomiędzy sąsiadującymi reprezentacjami i umieszcza granicę tam, gdzie odległość przekracza określony próg procentowy, zazwyczaj pomiędzy 85. a 95. procentilem odległości w danym dokumencie.
To progiem jest kluczowa ustawienie. Jeśli jest zbyt agresywne, powiązane koncepcje rozpadają się na małe, bezsensowne fragmenty. Jeśli jest zbyt łagodne, odrębne tematy pozostają połączone, przywracając szum, który chciało się usunąć.
Powoduje to również wyższe koszty, ponieważ przed ustaleniem granic konieczne jest przeprowadzenie pełnego procesu wstawiania danych. W przypadku krótkich, uporządkowanych dokumentów koszt ten ma niewielki wpływ. Natomiast w długich, nieregularnie sformatowanych tekstach znalezienie rzeczywistych zmian tematycznych może znacząco poprawić efektywność wyszukiwania. Jeden ważny ograniczenie: metoda zakłada, że tematy zmieniają się stopniowo i w sposób wykrywalny pomiędzy zdaniami. W gęstej prawniczej lub regulacyjnej prozie, gdzie jedno zdanie może zawierać trzy warunki i dwie wyjątki, odległości na poziomie zdań generują zanieczyszczone sygnały.
Dzielenie na fragmenty rodzic-dziecko
To jeden z najbardziej wpływowych wzorców w produkcyjnych systemach RAG, ponieważ rozwiązuje problem, który ignoruje podejście oparte na pojedynczych jednostkach: najlepsza jednostka do wyszukiwania nie jest najlepszą jednostką do generowania.
Weźmy fragment składający się z 2000 tokenów, obejmujący tematy takie jak ograniczenia, wyjątki, mechanizmy kontroli i procedury eskalacji. Jeśli wplecemy go w całości, zachowamy kontekst, ale uzyskamy niewyraźny wektor. Podzielenie go na fragmenty po 200 tokenów poprawia dokładność wyszukiwania, jednak dopasowany fragment może nie zawierać informacji niezbędnych modelowi do prawidłowego rozumowania.
Rozdzielanie na fragmenty typu „rodzic-dziecko” pozwala oddzielić te dwie funkcje. Rodzic to większy, spójny fragment, np. cała klauzula, procedura lub podsekcja. Dzieci to mniejsze, bardziej skoncentrowane fragmenty pochodzące z niego: pojedyncze warunki, progi lub wyjątki. Tylko dzieci są wplecane i wyszukiwane. Gdy jakiś fragment typu dziecko się dopasuje, jego identyfikator rodzica pobiera większy tekst źródłowy, który właśnie jest czytany przez model.
LlamaIndex i LangChain oferują wersje tego rozwiązania poprzez hierarchiczną strukturę węzłów, wyszukiwanie od małych do dużych obiektów oraz wyodrębnianie dokumentów nadrzędnych. Każdy element potomny przechowuje w metadanych identyfikator dokumentu nadrzędnego, a po odnalezieniu danych następuje rozszerzenie wyników o odpowiednie elementy nadrzędne przed skompilowaniem kontekstu.
W środowiskach regulowanych identyfikatory muszą mieć wyraźną linię pochodzenia. Czytelny identyfikator łączący dokument, wersję i sekcję jest bezpieczniejszy niż losowy UUID, ponieważ element potomny z wersji 7.2 odnoszący się do dokumentu nadrzędnego z wersji 7.1 tworzy kontekst, który wydaje się prawdopodobny, ale jest błędny pod względem operacyjnym. Ten model funkcjonuje również w różnych formatach: element potomny może być zserializowanym wierszem tabeli, opisem wizualnym diagramu lub propozycją – wszystkie one dzielą ten sam dokument nadrzędny reprezentujący jedną logiczną sekcję.
Dzielenie na fragmenty propozycji
Weźmy zdanie z polityki kredytowej: ryzykowni klienci korporacyjni wymagają wzmocnionej weryfikacji oraz corocznej oceny, a komitet musi zatwierdzić takie transakcje, gdy ich ekspozycja przekroczy ustalony limit. Zawiera ono trzy odrębne fakty. System wyszukiwania, który odpowiada na pytanie, jak często są sprawdzani tacy klienci, nie powinien wymagać analizy progów zatwierdzenia, aby znaleźć odpowiedni wynik.
Rozbicie propozycji na mniejsze części polega na podziale tekstu na atomowe, samodzielne stwierdzenia, z których każde można pobrać osobno. Wyodrębnianie odbywa się zazwyczaj w momencie tworzenia indeksu za pomocą modelu językowego, który korzysta z starannie skonstruowanego promptu zachowującego wszystkie warunki, progi, określenia i wyjątki, rozwiązuje problemy z zaimkami tam, gdzie to możliwe w tekście, oraz nie wnioskuje niczego, czego nie zostało wyraźnie stwierdzone. W przypadku powyższego zdania wynikiem mogą być trzy stwierdzenia: tacy klienci wymagają dokładnej weryfikacji, wymagają corocznego przeglądu oraz przy poziomie ekspozycji przekraczającym ustalony próg wymagają zatwierdzenia przez komitet.
Każda propozycja otrzymuje własne wektorowe reprezentacje, dzięki czemu przestrzeń wektorowa staje się przestrzenią atomowych faktów, a nie fragmentów tekstu, co zwiększa precyzję przy zadaniach wymagających szukania konkretnych faktów. Obowiązują dwa ograniczenia dotyczące generowania wyników:
- Propozycja nie jest źródłem prawdy. Jest to reprezentacja wygenerowana przez model. W pracach regulowanych należy najpierw uzyskać informacje za pomocą propozycji, a następnie wrócić do autorytatywnego tekstu w celu uzasadnienia, rozumowania i cytowania. Propozycja to wpis w indeksie, a nie twierdzenie.
- Koszty rosną gwałtownie. Jeden dokument składający się z 10 000 tokenów może dać setki propozycji, z których każda wymaga wywołania funkcji embedding, więc indeks szybko się powiększa.
Przywracanie utraconego kontekstu
Późne dzielenie na fragmenty
Konwencjonalne dzielenie na fragmenty polega najpierw na ich rozdzieleniu, a następnie na osobnym embedowaniu każdego z nich. Późne dzielenie na fragmenty, wprowadzone przez Jina AI w 2024 roku, odwraca część tej kolejności.
Późne dzielenie na fragmenty najpierw przepuszcza cały dokument przez model embeddingu długiego kontekstu, aby uzyskać reprezentacje na poziomie tokenów uwzględniające kontekst. Granice fragmentów są ustalane później poprzez połączenie wektorów tokenów znajdujących się w obrębie każdego okna. Różnica w porównaniu z prostym używaniem większych fragmentów polega na tym, że jednostka wyszukiwania pozostaje mała. Tekst nadal jest skoncentrowany, ale jego wektor odzwierciedla środowisko, w którym się pojawił. Zaimki, skróty, odniesienia wzajemne oraz terminy zdefiniowane wcześniej są rozstrzygane już w fazie embeddingu, a nie pozostawiane modelowi podczas generowania tekstu.
Koszt infrastruktury jest rzeczywisty. Potrzebny jest model embeddingu obsługujący długi kontekst, który dostarcza wyników na poziomie tokenów, dodatkowej pamięci do indeksowania oraz starannego kodu do agregacji danych. Nie może on również naprawić błędnej segmentacji: źle zserializowana tabela lub diagram umieszczony podczas przetwarzania wstępnego pozostaje nieuchwytny. Rozwiązuje on jedynie problem utraty kontekstu podczas procesu embeddingowania, a nic więcej. W przypadku korpusów z intensywnymi zależnościami między zdaniami, takimi jak przepisy, umowy i instrukcje, poprawa jakości embeddingów może mieć znaczący wpływ.
Pobieranie informacji w kontekście
System pobierania informacji w kontekście firmy Anthropic, opublikowany w 2024 roku, podchodzi do tego samego problemu izolacji w inny sposób. Nie zmienia architektury embeddingów, lecz w czasie tworzenia indeksu wzbogaca tekst każdego fragmentu, który ma być odnaleziony.
Zdanie izolowane określające ograniczenie może nie pasować do zapytania zawierającego informacje o klasie klienta, jurysdykcji oraz organie zatwierdzającym, ponieważ te terminy znajdują się w otaczającym dokumencie. Dlatego przed indeksowaniem dla każdego fragmentu generuje się krótkie stwierdzenie kontekstowe specyficzne dla danego dokumentu, na przykład wskazujące, z której sekcji i jakiej wersji polityki pochodzi dany fragment oraz co on definiuje. To stwierdzenie jest umieszczane na początku, a całość jest w ten sposób włączana do dokumentu.
Zgodnie z oceną Anthropic, połączenie embeddingów kontekstowych z algorytmem BM25 kontekstowym zmniejszyło liczbę nieudanych wyszukiwań w grupie 20 najlepszych wyników o 49 procent, a dodanie funkcji ponownego sortowania obniżyło tę liczbę o 67 procent. Te dane pochodzą z własnych korpusów Anthropic i nie stanowią gwarancji. Rzeczywiste korzyści zależą od Twoich dokumentów, zapytań, modelu embeddingów oraz ustawień wyszukiwania. Ważna nie są same liczby, lecz fakt, że dodanie kontekstu specyficznego dla dokumentu do izolowanych fragmentów może znacząco pomóc, gdy ważne informacje znajdują się poza granicami tych fragmentów.
Najskuteczniej pomaga w przypadku niejednoznacznych fragmentów: błądzących wierszy tabeli, opisów obrazków, odniesień do zdefiniowanych terminów oraz fragmentów, których zakres pochodził od sekcji nadrzędnej, do której już nie należą. Naturalnie łączy się również z wyszukiwaniem w strukturze rodzic-dziecko. Obowiązuje jedna zasada: generowany kontekst służy jako pomoc w wyszukiwaniu, a nie jako źródło informacji. Jeśli każdemu fragmencie nada się ten sam ogólny opis, wektory staną się zbyt podobne, co utrudni wyszukiwanie. Dobra kontekstualizacja dodaje tylko to, co jest potrzebne do zrozumienia danego fragmentu bez odniesienia do jego sąsiadów.
RAPTOR: kilka poziomów abstrakcji
RAPTOR, skrót od Recursive Abstractive Processing for Tree-Organized Retrieval, adresuje problem wspólny dla systemów wyszukiwania jednopoziomowego: drobne fragmenty tekstu dobrze odpowiadają na wąsko sformułowane pytania faktograficzne, ale mają trudności z pytaniami wymagającymi syntezy informacji z różnych części. Porównajmy pytanie o to, kto zatwierdza konkretną twierdzenie, z prośbą o całe ramy kontrolne organizacji dotyczące tego typu twierdzeń. Pierwsze ma jedną autorytatywną odpowiedź w tabeli, natomiast drugie wymaga dowodów pochodzących z reguł inspekcji, upoważnień delegowanych, polityk zabezpieczeń i odzyskiwania danych, procedur przeglądowych oraz ścieżek eskalacji, prawdopodobnie z różnych sekcji i dokumentów.
RAPTOR tworzy hierarchię. Podstawową warstwę stanowią fragmenty źródłowe. Powiązane fragmenty są grupowane na podstawie podobieństwa w kontekście, a model językowy podsumowuje każdą taką grupę. Te podsumowania są następnie ponownie grupowane i podsumowywane, w ten sposób aż kilka podsumowań wysokiego poziomu będzie reprezentować cały dokument lub korpus. Podczas wyszukiwania pytanie konkretne może odpowiadać fragmencie najniższego poziomu, natomiast pytanie ogólne – podsumowaniu wyższego poziomu, dzięki czemu jeden system może obsługiwać oba przypadki bez konieczności zmian w architekturze.
Głównym problemem w produkcji jest pochodzenie danych. Streszczenia są interpretacjami, a interpretacje mogą zniekształcić progi, przypadki krawędziowe i wyjątki – właśnie te szczegóły są najważniejsze w pracach regulowanych. Użyj węzłów wyższego poziomu, aby znaleźć odpowiednie części korpusu, a następnie wróć do oryginalnych fragmentów przed generowaniem. Streszczenie pomaga w lokalizacji dowodów, natomiast to oryginalny tekst służy jako podstawa do rozumowania modelu. RAPTOR zwraca koszty w przypadku długich polis, ram regulacyjnych oraz dużych podręczników, gdzie użytkownicy w jednej aplikacji zadają zarówno szczegółowe, jak i syntetyczne pytania. W przypadku krótkich zbiorów lub jednolitych typów zapytań taka złożoność rzadko się opłaca.
Strukturyzowana i nie tekstowa zawartość
Tabele: struktura to znaczenie
Wróć do tabeli uprawnień. Zawiera ona wyraźne relacje: typ roszczenia – uszkodzenie spowodowane wodą, przedział kwoty powyżej 25 000 euro, osoba upoważniona – regionalny menedżer roszczeń. Gdy przekształci się to w ciąg słów, relacje między kolumnami, które nadają znaczenie poszczególnym wartościom, znikają, dlatego tabele wymagają specjalnego traktowania uwzględniającego ich strukturę.
W przypadku małych tabel najlepiej jest zachować ich całość, szczególnie gdy zapytania porównują wiersze lub wymagają pełnego zakresu warunków. Należy to zapisać w taki sposób, aby powiązania między kolumnami a wierszami przetrwały, na przykład poprzez przekształcenie każdego wiersza w pary etykieta-wartość, które określają nazwę każdej kolumny. W przypadku dużych tabel wiersze tworzą bardziej precyzyjne fragmenty, ale każdy z nich musi zawierać swoje nagłówki, ponieważ trzy same wartości bez etykiet nie mają żadnego znaczenia.
Pod serializacją kryje się ważniejsza decyzja. Jeśli użytkownicy zadają pytania deterministyczne, takie jak wyświetlenie wszystkich osób zatwierdzających transakcje przekraczające określoną kwotę, metodę wyszukiwania wektorowego może w ogóle nie być odpowiednim narzędziem. Tabele przetwarzane za pomocą SQL lub dataframeów będą skuteczniejsze od wszelkich metod embeddingu przy wyszukiwaniu według dokładnych kryteriów. Zasada brzmi: nie niszczyć wyraźnej struktury tylko po to, by wszystko wyglądało jak tekst.
JSON i zstrukturyzowane rekordy
To samo rozumowanie dotyczy JSON. Zapis zawierający pola segment, rating, region, amount, approver oraz flagę recenzji już pokazuje, które pola należą do siebie, więc zastosowanie narzędzia do dzielenia na tokeny do zserializowanego JSON ignoruje to. Małe zapisy powinny pozostać nienaruszone, z czytelnym formularzem wyszukiwania, który wyświetla każde pole wraz z jego etykietą. W przypadku dużych obciążeń danych należy znaleźć naturalne jednostki biznesowe, takie jak klienci, konta, transakcje, wydarzenia filtrowania lub decyzje w sprawach, i podzielić dane wokół nich.
Wewnątrz zapisu należy oddzielić pola semantyczne od tych, które można filtrować. Pola, których dotyczą pytania w języku naturalnym, powinny znajdować się w embeddingu. Pola takie jak region, wersja, jednostka biznesowa i data wejścia w życie zazwyczaj lepiej sprawdzają się jako filtry metadanych w indeksie niż jako część wektora. Przedfiltrowanie na poziomie metadanych jest szybsze i dokładniejsze przy ograniczeniach kategorycznych, a także pomaga utrzymać embedding w kontekście znaczenia.
Kod: składnia stanowi granicę
Kod stwarza ten sam problem w innym obszarze. Ogólny narzędzie do dzielenia plików, które przecina plik w Pythonie po 500 tokenach, może oddzielić funkcję od jej instrukcji zwracania lub klasę od jej metod. Przy wyszukiwaniu kodu granice powinny odpowiadać strukturze składni: funkcje, metody, klasy i moduły. Moduł ast w Pythonie potrafi przetworzyć kod źródłowy na drzewo i wyodrębnić każdą funkcję, funkcję asynchroniczną oraz klasę jako całość, dzięki czemu fragmenty kodu odpowiadają semantyce języka, a nie są jedynie przybliżone na podstawie liczby znaków. Dla repozytoriów w wielu językach narzędzie tree-sitter oferuje jedną interfejs przetwarzania dla dziesiątek z nich i radzi sobie z przypadkami krawędziowymi, których nie potrafią regularne wyrażenia. Struktura rodzic-dziecko sprawdza się naturalnie: najpierw pobiera się dokładnie metodę, a następnie rozszerza się informacje o jej klasie lub pliku, gdy model potrzebuje szerszego kontekstu.
Diagramy i obrazy
Nie traktuj tekstu OCR jako pełnego przedstawienia obrazu. Diagram przepływów przekazuje znaczenie za pomocą strzałek, sekwencji, rozgałęzień, grupowania oraz układu przestrzennego – elementów, których OCR nie potrafi uchwycić. Użyj modelu zdolnego do analizy obrazów, aby stworzyć opis tego, co komunikuje obraz, a nie tylko słowa, które się w nim pojawiają. Przechowuj wskaźnik do oryginału. Wygenerowany opis stanowi wpis w indeksie, natomiast oryginalny obraz jest tym, co system pokazuje jako dowód. W systemach regulowanych cytat powinien odnosić się do oryginalnego obrazu, a nie do jego opisu.
Kierowanie według typu treści
Prawdziwe dokumenty są zróżnicowane. Jeden plik manualny może zawierać reguły opisowe, tabelę źródeł, diagram procesowy, metadane w formacie JSON oraz wplecione odniesienia. Nie ma żadnego sensownego powodu, by używać jednego narzędzia do przetwarzania wszystkich czterech typów dokumentów, ponieważ każdy z nich niesie znaczenie w inny sposób: akapit dzięki ciągłości językowej, tabela poprzez przecięcia wierszy i kolumn, diagram dzięki pozycji, kierunkowi i grupowaniu, a JSON za pomocą wyraźnych kluczy.
Odpowiedzią w tym przypadku jest router do dzielenia na fragmenty – dysponent, który identyfikuje typ treści każdego wydzielonego bloku i przekazuje go odpowiedniej strategii przetwarzania. Router ten nie musi być zbyt zaawansowany; jego wartość polega na tym, że sprawia, iż routing według typu treści staje się świadomym wyborem projektowym, a nie przypadkowym skutkiem zastosowania jakiegoś ogólnego narzędzia dzielącego. W przypadku podręcznika z twierdzeniami tekst narracyjny trafiałby do strukturalnego lub semantycznego dzielenia, tabela autorytetów – do serializacji uwzględniającej strukturę tabeli, diagram przepływu – do modelu wizualnego służącego do jego opisu, a JSON z metadanymi – do separacji uwzględniającej właściwości obiektów.
Każdy powstały fragment powinien zawierać te same podstawowe metadane, takie jak identyfikator dokumentu, wersja, identyfikator rodzica, sekcja i obszar. Ta wspólna struktura umożliwia późniejszym etapom rozpoznanie, że wiersz w tabeli, akapit i opis diagramu stanowią powiązane ze sobą dane z jednej sekcji.
Gdy większe jednostki przeważają
Znaczna część powyższych postępów zmierza w kierunku większej drobności, co może stworzyć fałszywe wrażenie, że im mniejsze, tym lepsze. Badania nad LongRAG badały przeciwny kierunek, sprawdzając, czy znacznie większe jednostki pobierania danych – w niektórych eksperymentach nawet do około 4000 tokenów – mogą przewyższyć drobnoziarniste metody pobierania danych w przypadku złożonych pytań wieloetapowych. Wyniki nie pokazały, że duże fragmenty są powszechnie lepsze. Pokazały natomiast, że drobność pobierania danych powinna odpowiadać drobności zapytania.
Pytanie o to, jak często należy przeglądać określony typ konta, wymaga odpowiedzi o rozmiarze propozycji. Prośba o pełną strukturę kontroli dla danego typu twierdzeń wymaga dowodów z kilku obszarów, a drobnoziarniste metody pobierania danych mogą zmusić system do składania szerokiej odpowiedzi z wielu izolowanych fragmentów, przez co błędy kompilacji pojawiają się nawet wtedy, gdy każdy fragment jest poprawny.
Wybór i pomiar
Jak wybrać strategię
Żadna strategia dzielenia na fragmenty nie jest najlepsza we wszystkich przypadkach. Wybór zależy od struktury dokumentu, rzeczywistych typów zapytań, budżetu opóźnień, tolerancji na koszty indeksowania oraz projektu generowania wyników. Typ zapytania ma takie samo znaczenie jak typ dokumentu. Ten sam zestaw zasad może służyć do wąskich wyszukiwań dotyczących minimalnej częstotliwości przeglądania oraz do szerokich zapytań wymagających wyjaśnienia całego frameworku, a oba przypadki wymagają różnej drobności odzyskiwania informacji. Jeśli twoje dane oceniające obejmują tylko jeden typ, strategia uznana za najlepszą może nie spełniać oczekiwań w rzeczywistym ruchu użytkowników.
Ogólny przewodnik:
- Jednolity tekst, wczesny prototyp: o stałej wielkości z umiarkowanym nakładaniem się fragmentów.
- Dobrze strukturyzowane podręczniki i procedury: dzielenie rekurencyjne lub oparte na strukturze, przy zachowaniu hierarchii w metadanych.
- Długa narracja o niestabilnej formacie: dzielenie semantyczne, jeśli dodatkowy koszt przynosi mierzalną korzyść.
Benchmark zamiast domysłów
Rzut oka na kilka fragmentów tekstu i stwierdzenie, że wyglądają one rozsądnie, pomaga przy debugowaniu, ale to nie jest ocena. Prawdziwe pytanie brzmi, czy dana strategia pomaga systemowi odzyskać dowody potrzebne do odpowiedzi na zapytania faktycznie wysyłane przez użytkowników.
Stwórz zbiór do oceny obejmujący różne potrzeby: wąski fakt powiązany z pojedynczym wierszem tabeli, pytanie wymagające wielu dowodów, które potrzebuje zdania narracyjnego oraz opisu procesu, pytanie warunkowe dotyczące wyjątku oraz pytanie syntetyczne łączące różne sekcje. Powiąż każde zapytanie z identyfikatorami jednostek do odzyskania, które powinny zostać znalezione.
Metryki odzyskiwania informacji same w sobie nie są wystarczające. Zapisz, jak duży jest indeks w porównaniu z bazą o stałej wielkości, jakie są koszty embedowania na dokument, opóźnienia przy indeksowaniu i odzyskiwaniu danych, liczbę tokenów kontekstowych na zapytanie, wskaźnik duplikatów, poprawność odpowiedzi oraz poprawność cytowania. Trzy dodatkowe punkty w Recall@5 stanowią niską wartość, jeśli rozmiar indeksu wzrasta czterokrotnie, a wyniki wypełniają się prawie identycznymi kopiami.
W typowym korpusie polityk prawdopodobnym wzorcem jest to, że rozdzielanie rekurencyjne przewyższa rozdzielanie o stałej wielkości o kilka punktów przy równym koszcie indeksowania, a segmentacja semantyczna dodaje nieco więcej przy umiarkowanym dodatkowym koszcie; metoda rodzic-dziecko poprawia wyniki przy znacznym wzroście kosztu indeksowania, natomiast połączenie propozycji z elementami rodzicowskimi osiąga najwyższe wyniki przy znacznie większym koszcie i złożoności. Wyszukiwanie kontekstowe może osiągnąć podobne wyniki przy mniejszym wzroście kosztu indeksowania, co może uzasadnić użycie tego modelu podczas tworzenia indeksu, natomiast RAPTOR może ustępować metodzie rodzic-dziecko w przypadku szczegółowych faktów, ale przewyższać ją przy syntezie informacji. Traktuj to jako ilustrację charakteru kompromisów, a nie jako wynik pomiarowy. Odpowiednia wartość operacyjna zależy od wymagań dotyczących dokładności, ograniczeń czasowych, budżetu na tworzenie indeksu oraz od tego, czy każda dodatkowa funkcja uzasadnia swoją złożoność.
Nie optymalizuj tylko procesu wyszukiwania
Załóżmy, że duże nakładanie się treści poprawia Recall@5, ale pięć najważniejszych wyników zawiera teraz cztery niemal identyczne fragmenty z jednego zdania, co wypycha inną istotną sekcję poza dostępną przestrzeń kontekstową. Jeden wskaźnik się poprawił, a system stał się gorszy. Należy ocenić cały proces – od wyszukiwania, przez ponowne sortowanie i tworzenie kontekstu, aż po generowanie odpowiedzi – pytając na każdym etapie, czy potrzebne dowody przetrwały, czy duplikaty zajęły użyteczną przestrzeń, czy odpowiedź jest poprawna i uzasadniona, oraz czy cytat odnosi się do właściwego źródła i wersji.
W systemach regulowanych poprawność cytowania nie jest kwestią drugorzędną. Odpowiedź może być faktograficznie poprawna, ale opierać się na niewłaściwej wersji zasady. To nadal stanowi naruszenie regulacji i pozostaje niewidoczne, chyba że źródło cytatu zostanie wyraźnie sprawdzone.
Architektura produkcyjna
Główną zmianą w sposobie myślenia jest to, że dojrzały pipeline przedsiębiorstwa prawie nigdy nie potrzebuje jednego uniwersalnego narzędzia do dzielenia treści. Potrzebuje warstwy routingu, która kieruje każdy blok treści do odpowiedniej strategii, przechowuje wspólne metadane dotyczące pochodzenia we wszystkich powstałych fragmentach oraz zawiera wyraźne linki prowadzące z powrotem do autorytatywnego źródła.
Dla podręcznika dotyczącego roszczeń prawidłowym rozwiązaniem jest następujące: sekcje narracyjne są dzielone na części strukturalne lub semantyczne; złożone klauzule reguł przekształcają się w propozycje z zachowaniem elementu nadrzędnego; małe tabele są przekształcane w całość; duże tabele dzielą się na grupy wierszy uwzględniające nagłówki; diagramy otrzymują opisy w modelu wizualnym; a rekordy JSON są oddzielane za pomocą obiektów. Każda grupa, niezależnie od typu, zawiera te same identyfikatory: ID dokumentu, wersja, ID elementu nadrzędnego, sekcja, obszar oraz odniesienie do oryginalnego pliku. To wspólne pochodzenie jest równie ważne jak same grupy, ponieważ umożliwia powiązanie wiersza z tabeli, klauzuli i opisu procesu jako dowodów z jednej sekcji danej wersji.
Z powrotem do roszczenia związkiego ze szkodami wodnymi
Nigdy nie chodziło o to, czy 500, czy 800 tokenów jest odpowiednią liczbą. Źródło zawierało różne rodzaje dowodów. Akapit wskazywał, że konieczna jest niezależna inspekcja. Tabela określała, kto zatwierdza przedział kwotowy. Diagram przedstawiał kroki sprawdzania przed dokonaniem płatności. Metadane wskazywały, która wersja i region są stosowane.
Dobrze zaprojektowana warstwa dzielenia tekstu zachowuje każdą część w jej oryginalnej strukturze i odnotowuje, że dzielą one wspólny element. Dzięki temu proces wyszukiwania ma różne opcje: pytania dotyczące zatwierdzenia przywołują wiersz z tabeli, pytania dotyczące inspekcji – odpowiednią klauzulę, pytania procesowe – opis z diagramu, a złożone pytania łączą kilka powiązanych elementów i rozszerzają się do ich wspólnego rodzica. To właśnie jest różnica pomiędzy dzieleniem tekstu a projektowaniem jednostek do wyszukiwania.
Kolejność, która ma znaczenie, nie jest ustalona – najpierw aspekt semantyczny, potem propozycyjny, przy czym zwycięża najnowsza idea. Raczej: należy poznać źródło, poznać zapytania, zachować istniejącą strukturę, wybrać odpowiedni poziom szczegółowości wyszukiwania, przywrócić brakujący kontekst, zachować pochodzenie danych, zmierzyć skuteczność wyszukiwania i odpowiedzi oraz dodawać złożoność tylko tam, gdzie jest to konieczne.
Ostatnia lista kontrolna przed indeksowaniem
Zanim fragmenty trafią do procesu embeddingowania, każdy z nich powinien spełniać trzy warunki:
- Dostępny do wydobycia. Na tyle konkretny, by odpowiadać istotnym zapytaniom, ale nie na tyle szeroki, by luźno pasować do wszystkiego.
- Interpretowalny. Zawierający wystarczający kontekst, by sam w sobie zachować swoje znaczenie, lub posiadający niezawodny sposób na odzyskanie tego kontekstu poprzez sprawdzenie elementu nadrzędnego.
Gdy brakuje jednego z tych elementów, warstwa dzielenia na fragmenty przenosi swoje problemy na etapy wyszukiwania, ponownego rankowania, budowy kontekstu i generowania wyników. Magazyn wektorów chętnie zindeksuje słabą jednostkę, API do tworzenia wektorów zwróci wektor bez żadnych zastrzeżeń, a model może napisać na jego podstawie pewną odpowiedź – żadne z tych rozwiązań nie wskazuje jednak, że reprezentacja jest poprawna. Naprawa uszkodzonej jednostki jest najtańsza, zanim stanie się elementem wektora.
Przed wdrożeniem należy również zaplanować logowanie, obsługę sekretów i konfiguracji, deterministyczne identyfikatory, wersjonowanie modeli i zapytań, śledzenie pochodzenia fragmentów, kontrole dostępu, zestawy danych do oceny, zarządzanie cyklem życia indeksu oraz monitorowanie jakości wyszukiwania, opóźnień i kosztów.