Wyszukiwanie nadrzędny–podrzędny i od małego do dużego
Przechowuj małe fragmenty podrzędne na potrzeby precyzyjnego wyszukiwania, ale zwracaj ich większe fragmenty nadrzędne do LLM, aby zapewnić bogatszy kontekst i równoważyć precyzję wyszukiwania z jakością generowania.
Wyszukiwanie nadrzędny–podrzędny i od małego do dużego to bezpłatna lekcja AI Engineering Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej AI Engineering Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Dylemat: precyzja a kontekst
Systemy RAG muszą równoważyć dwie potrzeby: małe fragmenty są pobierane z dużą precyzją, ponieważ każdy z nich koncentruje się na jednej idei, ale brakuje im otaczającego kontekstu potrzebnego LLM-owi do wygenerowania pełnej odpowiedzi. Duże fragmenty zapewniają bogaty kontekst, ale zmniejszają precyzję wyszukiwania, ponieważ słabo pasują do wielu zapytań zamiast silnie pasować do jednego. Dzielenie na fragmenty nadrzędne i podrzędne rozwiązuje ten dylemat.
Architektura nadrzędny–podrzędny
W przypadku dzielenia na fragmenty nadrzędne i podrzędne tworzysz z tego samego dokumentu dwie warstwy fragmentów. Fragmenty podrzędne są małe (np. 1–3 zdania) i są osadzane oraz indeksowane na potrzeby wyszukiwania. Fragmenty nadrzędne to większe sekcje (np. całe akapity lub strony), przechowywane osobno. Gdy zostanie pobrany fragment podrzędny, zamiast niego przekazujesz LLM-owi jego fragment nadrzędny.
Budowanie hierarchii fragmentów
Pierwszym krokiem jest podzielenie dokumentu na duże fragmenty nadrzędne, a następnie podzielenie każdego fragmentu nadrzędnego na mniejsze fragmenty podrzędne. Każdy fragment podrzędny przechowuje odwołanie — zazwyczaj w polu metadanych parent_id — wskazujące jego fragment nadrzędny. To mapowanie pozwala znaleźć pełny fragment nadrzędny na podstawie dowolnego pobranego fragmentu podrzędnego.
from langchain.text_splitter import RecursiveCharacterTextSplitter
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=1500, chunk_overlap=0)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
parent_chunks = parent_splitter.split_documents(docs)
child_chunks = []
for i, parent in enumerate(parent_chunks):
children = child_splitter.split_documents([parent])
for child in children:
child.metadata['parent_id'] = i
child_chunks.extend(children)Indeksowanie wyłącznie fragmentów podrzędnych
Tylko fragmenty podrzędne są osadzane i przechowywane w bazie wektorowej. Fragmenty nadrzędne są przechowywane w osobnym magazynie klucz–wartość (słowniku w pamięci, Redisie lub bazie dokumentowej). Dzięki temu indeks wektorowy pozostaje gęsty i precyzyjny, a bogaty kontekst znajduje się poza nim.
# Store parents in a docstore
parent_store = {i: chunk.page_content for i, chunk in enumerate(parent_chunks)}
# Embed and index only children
vectorstore = Chroma.from_documents(
child_chunks,
embedding=OpenAIEmbeddings()
)Wyszukiwanie: fragment podrzędny na wejściu, nadrzędny na wyjściu
Podczas wyszukiwania zapytanie użytkownika jest osadzane i porównywane z fragmentami podrzędnymi. Znajdowane są k najlepszych fragmentów podrzędnych, a zawarte w nich odwołania parent_id są rozwiązywane przez wyszukanie danych w magazynie fragmentów nadrzędnych. Następnie do promptu LLM-a wstawiane są fragmenty nadrzędne, a nie podrzędne. LLM otrzymuje szeroki kontekst, a wyszukiwanie zachowuje precyzję.
def retrieve_with_parents(query, vectorstore, parent_store, k=5):
child_results = vectorstore.similarity_search(query, k=k)
seen_parent_ids = set()
parent_contexts = []
for child in child_results:
pid = child.metadata['parent_id']
if pid not in seen_parent_ids:
parent_contexts.append(parent_store[pid])
seen_parent_ids.add(pid)
return parent_contextsLangChain ParentDocumentRetriever
LangChain udostępnia klasę ParentDocumentRetriever, która implementuje ten wzorzec od razu po wyjęciu z pudełka. Należy podać splitter dokumentów nadrzędnych, splitter dokumentów podrzędnych, bazę wektorową dla embeddingów dokumentów podrzędnych oraz magazyn dokumentów dla dokumentów nadrzędnych. Klasa konfiguruje hierarchię i w przejrzysty sposób obsługuje pobieranie danych.
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
store = InMemoryStore()
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
retriever.add_documents(docs)
results = retriever.invoke('What is the refund policy?')Wyjaśnienie pobierania od małych do dużych fragmentów
Pobieranie od małych do dużych fragmentów to inna nazwa tej samej koncepcji: pobierane są małe, precyzyjne fragmenty, a następnie przed wysłaniem ich do LLM rozszerza się je o otaczający kontekst. Niektóre implementacje rozszerzają fragment nie do ustalonego dokumentu nadrzędnego, lecz do okna sąsiednich zdań — przekazując modelowi zdania znajdujące się przed dopasowanym fragmentem i po nim, aby zachować ciągłość kontekstu.
def retrieve_with_window(query, vectorstore, sentences, window=2, k=5):
results = vectorstore.similarity_search(query, k=k)
expanded = []
for r in results:
idx = r.metadata['sentence_index']
start = max(0, idx - window)
end = min(len(sentences), idx + window + 1)
expanded.append(' '.join(sentences[start:end]))
return expandedUsuwanie duplikatów fragmentów nadrzędnych
W odpowiedzi na jedno zapytanie może zostać pobranych wiele fragmentów podrzędnych należących do tego samego dokumentu nadrzędnego. Bez usuwania duplikatów ten sam fragment dokumentu nadrzędnego pojawiłby się w promptcie wielokrotnie, niepotrzebnie zużywając tokeny. Przed złożeniem kontekstu zawsze usuwaj duplikaty na podstawie identyfikatora dokumentu nadrzędnego. Przykład kodu w przedstawionej wyżej funkcji retrieve obsługuje to za pomocą zbioru seen_parent_ids.
Kiedy stosować dzielenie na fragmenty nadrzędne i podrzędne
Pobieranie w układzie nadrzędny–podrzędny sprawdza się najlepiej, gdy dokumenty mają wyraźną strukturę hierarchiczną: rozdziały z sekcjami, artykuły z akapitami lub wiki z podsekcjami. Jest szczególnie skuteczne w przypadku długiej dokumentacji technicznej, w której precyzyjne pytania wymagają odpowiedzi dotyczących konkretnego miejsca, ale odpowiedzi te mają sens dopiero w szerszym kontekście sekcji.
Dobór rozmiarów fragmentów podrzędnych i nadrzędnych
Typowa konfiguracja obejmuje: fragmenty podrzędne o rozmiarze 200–400 tokenów (skupione na pojedynczych zagadnieniach) oraz fragmenty nadrzędne o rozmiarze 1000–2000 tokenów (pełne sekcje). Jeśli fragmenty podrzędne są zbyt małe, stają się pojedynczymi zdaniami, które same w sobie nie mają wystarczającego znaczenia. Jeśli fragmenty nadrzędne są zbyt duże, ponownie pojawia się problem rozmycia kontekstu, którego próbowano uniknąć.
Porównanie podejść: podsumowanie
Podsumowując dotychczasowe strategie dzielenia: stały rozmiar jest szybki, ale rozbija kontekst; dzielenie semantyczne zachowuje spójność tematyczną; układ nadrzędny–podrzędny optymalizuje zarówno precyzję pobierania, jak i bogactwo kontekstu LLM. W większości produkcyjnych systemów RAG przetwarzających długie dokumenty dzielenie na fragmenty nadrzędne i podrzędne zapewnia najlepszą jakość pobierania przy akceptowalnej złożoności.
Szybki sprawdzian
Sprawdź swoją wiedzę na temat dzielenia na fragmenty nadrzędne i podrzędne z tego modułu.
Podsumowanie modułu
W tym module nauczysz się, że: fragmenty podrzędne zapewniają precyzyjne pobieranie, a fragmenty nadrzędne — bogaty kontekst, ParentDocumentRetriever w LangChain automatycznie realizuje ten proces, a usuwanie duplikatów na podstawie identyfikatora dokumentu nadrzędnego zapobiega powtarzaniu kontekstu. Następnie omówimy strategie dzielenia zależne od rodzaju dokumentu, stosowane w plikach z kodem i dokumentach HTML.
Ucz się Python dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 30
- Lekcje
- 120
Często zadawane pytania
Czy lekcja „Wyszukiwanie nadrzędny–podrzędny i od małego do dużego” jest bezpłatna?
Tak — pełny tekst „Wyszukiwanie nadrzędny–podrzędny i od małego do dużego” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu AI Engineering Academy, przejdź na CoddyKit PRO. Kurs AI Engineering Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Wyszukiwanie nadrzędny–podrzędny i od małego do dużego”?
Przechowuj małe fragmenty podrzędne na potrzeby precyzyjnego wyszukiwania, ale zwracaj ich większe fragmenty nadrzędne do LLM, aby zapewnić bogatszy kontekst i równoważyć precyzję wyszukiwania z jako… Ćwiczysz AI Engineering Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć AI Engineering Academy?
Nie wymagamy żadnego doświadczenia. AI Engineering Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Wyszukiwanie nadrzędny–podrzędny i od małego do dużego”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji AI Engineering Academy?
Tak. Każda lekcja AI Engineering Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Dlaczego naiwne dzielenie na fragmenty pogarsza wyszukiwanie
- Dzielenie semantyczne na podstawie podobieństwa embeddingów
- Wyszukiwanie nadrzędny–podrzędny i od małego do dużego
- Strategie dopasowane do dokumentów: kod i HTML