Historia tylko dla członków
Warstwa RAG, o której nikt nie mówi
Odzyskiwanie przyciąga całą uwagę. Ale to, co dzieje się między odzyskiwaniem a generacją, to moment, w którym systemy cicho odnoszą sukces lub zawodzą.
Pobranie zadziałało idealnie.
Zwrócono pięć chunków, wskaźniki trafności powyżej 0,85, dokładnie dokumenty polityki, których użytkownik potrzebował. Odpowiedź była już tam, w trzecim fragmencie.
Jednak odpowiedź pominęła kluczowe szczegóły. To nie do końca halucynacja. Raczej model przejrzał pobieżnie niż czytał.
Ty debugujesz retrievera. Stunuj model osadzania. Dostosuj rozmiar kawałków. Ale pobieranie nigdy nie było problemem.
Problem leżał gdzie indziej: jak te fragmenty zostały złożone w prompt. W jakiej kolejności się pojawiły. Ile kontekstu ich otaczało. Czy model faktycznie mógłby wykorzystać to, co odzyskałeś.
To jest warstwa, przed którą nikt cię nie ostrzega: co dzieje się między odzyskiem a generacją. Twoja baza wektorów przekazuje odpowiednie fragmenty. Twój LLM generuje odpowiedź. Część pośrodku, warstwa augmentacji, decyduje, czy twoje pobranie faktycznie ma znaczenie.
⭐️ (Jeszcze nie jesteś członkiem Medium? Przeczytaj resztę tego artykułu bez paywalla tutaj.)
Pozycja nadal ma znaczenie
LLM nie odczytują kontekstu jednolicie.
Badacze ze Stanforda i Meta odkryli to w 2023 roku. Ukryli kluczowy fakt w różnych miejscach w odzyskanych dokumentach i mierzyli dokładność. Efekty? Krzywizna w kształcie litery U. Modelki bardzo dbały o to, co było pierwsze (prymat) i to, co ostatnie (nowość). Środek został zaniedbany.
To się poprawiło. GPT-4o i Claude 3.5 Sonnet nie mają tak dużych problemów z pozycją jak GPT-3.5-Turbo. Efekt nie zniknął. Jest zmniejszony, a nie wyeliminowany. Przyczyna architektoniczna pozostaje: mechanizmy uwagi transformerów, dzięki specyficznym cechom normalizacji softmax i "pochłaniaczy uwagi", nadal przypisują nieproporcjonalnie dużą wagę wczesnym tokenom.
Więc: stawiaj na swoje najlepsze fragmenty na pierwszym miejscu. Nic to nie kosztuje i zabezpiecza się przed efektem pozycji. Działa niezależnie od modelu, którego używasz.
Rozwiązanie: przebuduj swoje prompty. Przenieś bloki o najwyższej wartości na początek. Umieść kontekst drugorzędny pośrodku. Rozważ podsumowanie na końcu dla złożonych zapytań.
Co faktycznie dzieje się po pobraniu
Większość tutoriali RAG pokazuje czysty schemat:
Zapytanie → Retriever → LLM → odpowiedź
Strzałka między Retrieverem a LLM ukrywa cały potok przetwarzania.
Ta strzała skrywa wiele:
Twój retriever zwraca kandydatów, zazwyczaj 20–50 najlepszych fragmentów uszeregowanych według podobieństwa embedding. Te surowe wyniki mają problemy. Niektóre fragmenty nakładają się na siebie, powtarzając te same informacje. Niektóre sobie przeczą. Niektóre pasowały pod kątem słów kluczowych, ale tak naprawdę nie odpowiadają na twoje pytanie. Bezpośrednie przekazywanie ich do LLM marnuje tokeny i wprowadza szumy.
Potok przetwarzania przekształca tych surowych kandydatów w zoptymalizowany kontekst. Filtruje według progu trafności. Usuwa niemal duplikaty. Zmienia rangę na podstawie modeli, które lepiej rozumieją relacje zapytania i dokumentu niż tylko osadzanie podobieństwa. Radzi sobie ze sprzecznościami. Rozszerza obiecujące fragmenty, uwzględniając otaczający kontekst. Na koniec wszystko łączy w strukturę promptową, którą LLM faktycznie może wykorzystać.
Pominięcie któregokolwiek z tych kroków oznacza brak jakości.
Największe zyski pojawiają się w ponownym rankingu. Podobieństwo wektorów jest szybkie, ale płytkie. Porównuje skompresowane reprezentacje dokumentów, poświęcając niuanse na rzecz szybkości. Rerankerzy cross-encerów przetwarzają Twoje zapytanie i każdy dokument razem przez pełną uwagę transformera, rejestrując relacje, których embeddingi pomijają.
Schemat, który działa: wyszukiwanie szerokie (top 50 kandydatów), precyzyjne przeklasyfikowanie (utrzymanie top 5). Benchmarki Pinecone pokazują, że to dwuetapowe podejście poprawia jakość wyszukiwania o 14–30% w porównaniu do samego wyszukiwania wektorowego. Systemy produkcyjne konsekwentnie wykazują podobne liczby.
Jeśli chodzi o reranking modeli, Cohere Rerank dobrze radzi sobie z większością zastosowań. Jest wielojęzyczny, co pomaga, jeśli twoje dokumenty nie są wszystkie po angielsku. Dla wdrożeń hostowanych samodzielnie, bge-reranker-v2-m3 BAAI oferuje porównywalną jakość bez kosztów API.
Deduplikacja rozwiązuje problem, którego możesz nie zauważyć, dopóki nie kosztuje cię. Strategie chunkingu wykorzystujące przesuwane okna powodują nakładanie się. Pobierz pięć fragmentów, a trzy mogą zawierać ten sam akapit. To nie tylko zmarnowane żetony. Powtarzalność może skłaniać model do nadmiernego podkreślania powtarzających się treści.
Maksymalna Marginalna Istotność (MMR) radzi sobie z tym dobrze. Idea jest taka: każdy nowy fragment, który dodasz, powinien być istotny, ale też inny niż to, co już wybrałeś. Nie ma sensu dodawać trzech fragmentów mówiących to samo.
Radzenie sobie ze sprzecznościami jest trudniejsze. Kiedy Twój retriever zwraca fragmenty, które się ze sobą nie zgadzają, co powinien zrobić model? Systemy często pewnie cytują błędne źródło, ponieważ pojawiło się ono pierwsze w kontekście.
W przypadku sprzeczności czasowych rozwiązaniem są metadane. Uwzględnij znaczniki czasu i filtruj starsze treści, gdy nowsze obejmują ten sam temat. LlamaIndex ma taką aplikację, która robi to automatycznie.EmbeddingRecencyPostprocessor
W przypadku konfliktów autorytetów warto oceniać źródła. Oficjalna dokumentacja powinna zagłaszać treści tworzone przez użytkowników. Źródła pierwotne powinny przewyższać streszczenia. Jawne etykietowanie metadanych pozwala zaimplementować te preferencje w pipeline.
Gdy istnieje uzasadniona niejasność, uczciwe podejście polega na przedstawieniu obu perspektyw z jasnym przypisaniem autorstwa. Wymuszanie fałszywego konsensusu prowadzi do odpowiedzi brzmiących pewnie, które wprowadzają użytkowników w błąd.
Tokenowy budżet, którego nikt cię nie uczy
Okna kontekstowe mają swoje ograniczenia. Wiesz o tym. Budżetowanie w tych granicach to część, której nikt nie uczy.
Okno kontekstowe 128K brzmi ogromnie, dopóki nie uwzględni się wszystkiego, co rywalizuje o miejsce. Twój system wymaga miejsca. Zapytanie użytkownika przyjmuje tokeny. Musisz zarezerwować pojemność na samą odpowiedź. Pozostaje tylko twój rzeczywisty budżet na odzyskany kontekst.
Rozsądny przydział: 1 500 tokenów na prompt systemowy, 500 na zapytanie użytkownika, 4 000 zarezerwowanych na wyjście. Na modelu 128K pozostaje około 122 000 tokenów dla kontekstu. Sporo, prawda?
Teoretycznie tak. W praktyce więcej nie zawsze znaczy lepiej. Nawet przy ulepszonych modelach długokontekstowych pojawia się moment malejących korzyści, gdy dodatkowe fragmenty dodają szum zamiast sygnału. Model musi przesiać więcej treści, by znaleźć to, co jest istotne.
Idealna wartość to zazwyczaj 4–6 chunków dla większości zapytań. Poza tym każdy dodatkowy chunk rywalizuje o znaczenie z już dołączonymi chunkami. Jeśli nowy fragment nie zawiera naprawdę unikalnych informacji, rozcieńcza zamiast wzbogacać.
Gdy potrzebujesz więcej kontekstu niż pasuje, kompresja jest lepsza niż obcięcie.
Microsoft LongLLMLingua osiąga kompresję 4x, poprawiając dokładność o 21% w testach z pytaniami. Osiąga symboliczne znaczenie dzięki perplexity i zachowuje treści istotne dla zapytań, jednocześnie odrzucając wypełniacze. Skompresowany kontekst zawiera mniej tekstu, ale więcej sygnału.
Alternatywą, RECOMP, jest zarówno kompresja ekstrakcyjna (wybieranie zdań kluczowych), jak i abstrakcyjna (generowanie streszczeń). Obie metody są lepsze niż naiwne skracanie, które odrzuca treści na podstawie pozycji, a nie ważności.
Kompresja najlepiej sprawdza się przy długich dokumentach, gdzie istotne informacje są rozproszone. W typowych scenariuszach wyszukiwania z dobrze uporządkowaną zawartością, lepiej sprawdza się trzymanie 4–6 wysokiej jakości fragmentów niż kompresowanie większych zbiorów.
Architektura promptów, która faktycznie działa
To, jak zorganizujesz końcowy prompt, ma większe znaczenie, niż większość samouczków przyznaje.
Podstawowa architektura składa się z dwóch warstw: trwałego promptu systemowego ustanawiającego reguły oraz dynamicznego promptu użytkownika, który wprowadza kontekst czasu zapytania. Mieszanie tych warstw powoduje niespójność. Trzymaj je osobno.
Twój komunikat systemowy powinien definiować:
- Rola i persona ("Jesteś asystentem wsparcia technicznego dla...")
- Zasady podstawowe ("Odpowiedz tylko w podanym kontekście. Jeśli kontekst nie zawiera odpowiedzi, powiedz to.")
- Specyfikacje formatu ("Cytuj źródła z użyciem notacji [1], [2]")
- Wzorce odmowy ("Nie odpowiadaj na pytania dotyczące produktów konkurencji")
Ten prompt pozostaje stały w zależności od zapytań. Zasady obowiązują uniwersalnie.
Twój prompt użytkownika powinien zawierać:
- Pobrano kontekst, wyraźnie wyznaczony
- Rzeczywiste zapytanie użytkownika
- Wszelkie instrukcje specyficzne dla zapytania
Wybór separatora wpływa na dokładność parsowania. Tagi XML przewyższają markdown i zwykły tekst dla Claude'a oraz modeli trenowanych na danych strukturalnych. Używam , , i jako granic.<context><document><query>
Struktura, która dobrze działa:
<documents>
<document source="policy-handbook.pdf" page="12">
[chunk content here]
</document>
<document source="faq-updated-2024.md">
[chunk content here]
</document>
</documents>
<query>
[user's question here]
</query>Metadane źródłowe umożliwiają cytowanie. Numery stron pozwalają użytkownikom na weryfikację. Przejrzysta struktura pomaga modelowi oddzielić treść od instrukcji.
Jakie metadane powinieneś uwzględnić? Tytuły źródłowe, tak. Znaczniki czasowe dla treści o dużej mierze czasu, tak. Numery stron, jeśli chcesz cytowania, tak.
Co powinieneś wykluczyć? Wewnętrzne oceny istotności, ścieżki systemu plików, informacje kodujące, dane debugujące. Zużywają one tokeny, nie pomagając w generowaniu. Jeśli metadane nie pomagają modelowi lepiej rozumować, pomiń je.
Awarie, które nie pojawiają się w logach
Dobre wskaźniki wyszukiwania nie gwarantują dobrych odpowiedzi. Obserwuj tryby awarii po udanym pobieraniu danych.
Halucynacja cytowana jest podstępna. Odpowiedź brzmi autorytatywnie, zawiera cytaty w nawiasach i całkowicie błędnie określa, które źródło co powiedziało. Fakty mogą być prawdziwe. Przypisania nie są.
Dzieje się tak, ponieważ dokładność cytowań i dokładność faktów są niezależne. Model może wydobyć poprawne informacje z kontekstu, błędnie pamiętając (jeśli to słowo w ogóle odnosi się do LLM), z którego fragmentu pochodzi. Systemy produkcyjne wymagają weryfikacji, która prowadzi do konkretnych źródeł.
Nazywam to zatruciem kontekstu, gdy bloki o niskiej aktualności wypierają dobre. Twój retriever zwraca dziesięć kawałków. Siedem jest przeciętnych. Trzy to dokładnie to, czego potrzebujesz. Te siedem rozcieńcza sygnał nie przez pozycję, lecz przez głośność. Model próbuje wykorzystać wszystko, zamiast znaleźć najlepsze części.
Bardziej zaciśnięte progi relewancji pomagają. Liczy się też ograniczenie chunku. Jeśli nie jesteś pewien, wybierz mniej dobrych kawałków zamiast przeciętnych.
Ciemność czasowa pojawia się, gdy twój indeks nie pokazuje sygnałów świeżości. Retriever zwraca dokument polisy z 2022 roku, ponieważ idealnie pasuje do terminów zapytania. Ta polisa została zastąpiona w 2024 roku. Ale Twój pipeline o tym nie wie.
Filtrowanie oparte na metadanych wykrywa to, ale tylko wtedy, gdy indeksujesz znaczniki czasu i zapytujesz z uwzględnieniem aktualności. W przypadku szybko zmieniających się domen warto uwzględnić świeżość jako czynnik rankingowy obok trafności.
Fragmentacja rozumowania zabija zapytania wieloskokowe. Użytkownik zadaje pytanie, które wymaga połączenia faktów z wielu bloków. Każdy fragment jest pomyślnie odzyskiwany. Model nie potrafi ich zsyntetyzować, ponieważ są semantycznie zdystansowane w promptie i brakuje im kontekstu łączącego.
Hierarchiczne podziały na części tutaj pomagają. Wyszukiwanie okien zdań w LlamaIndex osadza pojedyncze zdania dla precyzyjnego wyszukiwania, ale rozszerza się na otaczające akapity w czasie zapytania. Kontekst pomostowy jest dostępny za darmo.
Co faktycznie działa
Po zbudowaniu produkcyjnych systemów RAG, te wzorce się sprawdzają:
Wyszukiwanie: Wyszukiwanie hybrydowe łączące dopasowanie słów kluczowych BM25 z gęstymi osadzeniami, połączone za pomocą Reciprocal Rank Fusion. To wykrywa zapytania, które każde z tych metod by przeoczyło. Badania Anthropic dotyczące kontekstowego wyszukiwania wykazały, że podejścia hybrydowe zmniejszają liczbę niepowodzeń wyszukiwania o 67%.
Reranking: Cohere Reranking dla wdrożeń chmurowych, bge-reranker dla self-hosting. Zawsze zmieniaj ranking. Koszt opóźnień jest tego wart.
Deduplikacja: MMR z lambda około 0,6. Chcesz mieć istotę, ale też różnorodność.
Liczba bloków: 4–6 dla większości zapytań. Idź wyżej tylko wtedy, gdy naprawdę musisz syntetyzować z różnych źródeł.
Strategia pozycji: Najlepsze chunki zaczynają. Kontekst wspierający wypełnia środek. Podsumowanie na końcu, jeśli masz miejsce. Użyj obu końców promptu.
Struktura promptu: Tagi XML do wyznaczania kontekstu. Zawiera metadane źródłowe i czasowe. Dane wewnętrzne potoków wyłączone.
Ewaluacja: Oddziel metryki przywoływania (precyzja, przypominanie) od metryk generowania (wierność, ugruntowanie). Gdy odpowiedzi zawodzą, musisz wiedzieć, który komponent zawiódł.
To nie jest jedyne uzasadnione podejście. Ale to powstrzymuje przed debugowaniem niewłaściwej warstwy.
Warstwa, która zasługuje na swoje utrzymanie
Większość dyskusji o RAG skupia się na odzyskiwaniu informacji. Ma to sens. Jeśli nie odzyskasz odpowiednich chunków, nic dalej nie może cię uratować.
Ale odzyskiwanie to problem rozwiązany w porównaniu do augmentacji. Bazy wektorowe są dojrzałe. Modele osadzania są dobre i coraz lepsze. Zmiany rankingu stają się standardem. Warstwa wyszukiwania zawiera jasne najlepsze praktyki i mierzalne punkty odniesienia.
Warstwa augmentacji, czyli przestrzeń między odzyskiwaniem a generacją, jest młodsza i bardziej chaotyczna (messy). Efekty pozycji, strategie kompresji, architektura promptów, tryby awarii, które nie pojawiają się w metrykach pobierania. Systemy, które na papierze wyglądają identycznie, różnią się tutaj pod względem produkcji.
Gdy Twój system RAG zawodzi, powstrzymaj się od instynktu, by najpierw obwiniać retrievera. Sprawdź, gdzie umieszczasz fragmenty w swoim promptie. Sprawdź, czy nie podajesz zbyt dużo kontekstu. Sprawdź, czy struktura promptu pomaga, czy przeszkadza modelowi w analizie istotnych informacji.
Pobranie może być w porządku. To to, co dzieje się dalej, decyduje o tym, czy model faktycznie korzysta z tego, co znalazłeś.
Viz