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

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.

Auteur
Équipe de Recherche Ailog
Date de publication
Temps de lecture
9 min de lecture
Niveau
intermediate
Étape du pipeline RAG
Retrieval

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 Indexer : Petits chunks (200 tokens) Rechercher : Trouver les petits chunks pertinents Récupérer : Retourner le document parent (2000 tokens)

Implémentation de base

``python 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

`python 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

`python 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

`python 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 :

`python 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

`python 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ération
  • découpage
  • context
  • parent document
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 !