5. RetrievalIntermédiaire

Récupération de Document Parent : Contexte Sans Bruit

13 novembre 2025
9 min de lecture
Équipe de Recherche Ailog

Recherchez dans de petits fragments, récupérez les documents complets : le meilleur de la précision et du contexte pour les systèmes RAG.

Le problème

Petits chunks :

  • ✅ Récupération précise
  • ❌ Contexte manquant

Grands chunks :

  • ✅ Contexte complet
  • ❌ Récupération bruyante

Solution : Rechercher petit, retourner grand.

Comment ça fonctionne

  1. Indexer : Petits chunks (200 tokens)
  2. Rechercher : Trouver les petits chunks pertinents
  3. Récupérer : Retourner le document parent (2000 tokens)

Implémentation de base

DEVELOPERpython
import uuid # Stocker les chunks avec référence parent chunks = [] documents = [] for doc in raw_documents: parent_id = str(uuid.uuid4()) # Stocker le document complet documents.append({ "id": parent_id, "content": doc, "embedding": embed(doc) }) # Créer de petits chunks for chunk in split_into_chunks(doc, size=200): chunks.append({ "id": str(uuid.uuid4()), "content": chunk, "embedding": embed(chunk), "parent_id": parent_id # Lien vers le parent }) # Indexer uniquement les chunks vector_db.upsert(collection="chunks", documents=chunks)

Récupération

DEVELOPERpython
def parent_document_retrieval(query, k=5): # Rechercher les petits chunks chunk_results = vector_db.search( collection="chunks", query_vector=embed(query), limit=k ) # Obtenir les IDs des documents parents parent_ids = [chunk["parent_id"] for chunk in chunk_results] # Récupérer les documents parents parent_docs = [ doc for doc in documents if doc["id"] in parent_ids ] return parent_docs

Implémentation Langchain

DEVELOPERpython
from langchain.retrievers import ParentDocumentRetriever from langchain.storage import InMemoryStore from langchain.vectorstores import Chroma from langchain.text_splitter import RecursiveCharacterTextSplitter # Store pour les documents parents docstore = InMemoryStore() # Vector store pour les chunks vectorstore = Chroma(embedding_function=embeddings) # Splitters child_splitter = RecursiveCharacterTextSplitter(chunk_size=200) parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000) # Créer le retriever retriever = ParentDocumentRetriever( vectorstore=vectorstore, docstore=docstore, child_splitter=child_splitter, parent_splitter=parent_splitter ) # Ajouter des documents retriever.add_documents(documents) # Récupérer (retourne les docs parents complets) results = retriever.get_relevant_documents("machine learning")

Hiérarchie multi-niveaux

DEVELOPERpython
# Structure Livre → Chapitre → Paragraphe def create_hierarchy(book): book_id = str(uuid.uuid4()) for chapter in book.chapters: chapter_id = str(uuid.uuid4()) # Indexer les paragraphes (petits) for paragraph in chapter.paragraphs: vector_db.upsert({ "id": str(uuid.uuid4()), "content": paragraph, "embedding": embed(paragraph), "parent_id": chapter_id, # Chapitre "grandparent_id": book_id # Livre }) # Stocker le chapitre chapters[chapter_id] = chapter # Stocker le livre books[book_id] = book def retrieve_with_context(query): # Trouver les paragraphes pertinents paragraphs = vector_db.search(embed(query), limit=3) # Obtenir le contexte environnant results = [] for p in paragraphs: chapter = chapters[p["parent_id"]] book = books[p["grandparent_id"]] results.append({ "match": p["content"], "chapter": chapter, "book_title": book.title }) return results

Récupération par fenêtre

Retourner le chunk + contexte environnant :

DEVELOPERpython
def windowed_retrieval(query, window_size=2): # Trouver le chunk pertinent chunk_results = vector_db.search(embed(query), limit=5) # Obtenir les chunks avant et après expanded_results = [] for chunk in chunk_results: parent_doc = get_document(chunk["parent_id"]) chunk_index = find_chunk_index(parent_doc, chunk["content"]) # Obtenir la fenêtre start = max(0, chunk_index - window_size) end = min(len(parent_doc.chunks), chunk_index + window_size + 1) expanded_chunk = "".join(parent_doc.chunks[start:end]) expanded_results.append(expanded_chunk) return expanded_results

Implémentation Qdrant

DEVELOPERpython
from qdrant_client import QdrantClient from qdrant_client.models import PointStruct client = QdrantClient("localhost", port=6333) # Créer une collection avec ID parent dans le payload client.create_collection( collection_name="chunks", vectors_config={"size": 1536, "distance": "Cosine"} ) # Insérer les chunks avec référence parent points = [] for i, chunk in enumerate(chunks): points.append(PointStruct( id=i, vector=chunk["embedding"], payload={ "content": chunk["content"], "parent_id": chunk["parent_id"] } )) client.upsert(collection_name="chunks", points=points) # Récupérer def retrieve_parents(query): results = client.search( collection_name="chunks", query_vector=embed(query), limit=5 ) # Obtenir les IDs parents uniques parent_ids = list(set([r.payload["parent_id"] for r in results])) # Récupérer les parents depuis le document store parents = [get_document(pid) for pid in parent_ids] return parents

Quand utiliser

Utiliser la récupération de document parent quand :

  • Les documents ont une structure claire
  • Vous avez besoin du contexte complet pour le LLM
  • La précision est importante

Ne pas utiliser quand :

  • Les documents sont déjà petits (< 500 tokens)
  • Vous voulez minimiser l'usage de tokens
  • Le contexte n'est pas important

La récupération de document parent vous donne la précision sans sacrifier le contexte. Le meilleur des deux mondes.

Tags

récupérationdécoupagecontextparent document

Articles connexes

Ailog Assistant

Ici pour vous aider

Salut ! Pose-moi des questions sur Ailog et comment intégrer votre RAG dans vos projets !