IntermédiaireRAG
20 min de lecture11 vues

Stratégies de Chunking et Indexation pour le RAG

La qualité d'un RAG repose à 80% sur le chunking. Découvrez comment découper vos documents pour maximiser la pertinence des réponses de votre agent IA.

Pourquoi le Chunking est Critique

En RAG, il y a un adage implacable : garbage in, garbage out. Si vos documents sont mal découpés, même le meilleur LLM du monde ne pourra pas générer de bonnes réponses : il travaillera avec du contexte incohérent, tronqué ou hors sujet.

Le chunking est l'étape où vous découpez vos documents en morceaux (chunks) avant de les indexer dans une vector database. Cette étape influence directement deux métriques fondamentales :

  • Faithfulness : la réponse générée est-elle fidèle à ce que contiennent réellement les documents ? Un mauvais chunking coupe les phrases au milieu et prive le LLM d'information contextuelle.
  • Context Relevancy : les chunks récupérés par la recherche vectorielle sont-ils réellement utiles ? Des chunks trop larges noient l'information utile dans du bruit. Des chunks trop petits perdent le contexte nécessaire à la compréhension.

L'Analogie du Découpage d'un Livre

Imaginez que vous vouliez indexer un manuel technique de 500 pages :

  • Trop petit (50 caractères par chunk) : vous indexez des demi-phrases sans sens. La recherche retourne des fragments inexploitables.
  • Trop grand (10 000 caractères par chunk) : vous indexez des chapitres entiers. La recherche retourne du contexte pertinent noyé dans 95% d'information hors sujet.
  • Bien calibré (500-1000 tokens) : chaque chunk contient une idée complète, lisible hors contexte.

La taille optimale dépend de votre cas d'usage. Il n'existe pas de valeur universelle. Une documentation API se chunke différemment d'un roman ou d'un contrat juridique. L'expérimentation est indispensable.


Les 5 Stratégies de Chunking

Tableau Comparatif

StratégiePrincipeVitesseQualitéCas d'usage idéal
FixedSizeChunkingDécoupe à N caractères fixesTrès rapideMoyenneTexte homogène, logs
RecursiveChunkingParagraphe → phrase → motRapideBonneTexte généraliste structuré
DocumentChunkingRespecte la structure du docRapideTrès bonnePDFs structurés, HTML
AgenticChunkingLLM décide où couperTrès lenteMaximaleDocuments critiques
Semantic ChunkingRegroupe par similarité de sensLenteExcellenteQualité maximale sans LLM

1. FixedSizeChunking

La stratégie la plus simple : on découpe le texte tous les N caractères, avec un overlap optionnel.

from agno.knowledge.chunking.fixed import FixedSizeChunking
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.knowledge.knowledge import Knowledge
from agno.vectordb.pgvector import PgVector

chunker = FixedSizeChunking(
    chunk_size=1000,    # Taille du chunk en caractères
    overlap=100,        # Chevauchement entre chunks
)

knowledge = Knowledge(
    vector_db=PgVector(
        table_name="my_docs",
        db_url="postgresql+psycopg://user:pass@localhost:5432/mydb",
    ),
)

knowledge.insert(
    url="https://example.com/doc.pdf",
    reader=PDFReader(chunking_strategy=chunker),
)

Avantages : rapide, prédictible, facile à déboguer. Inconvénients : coupe souvent en plein milieu d'une phrase ou d'un paragraphe, ce qui dégrade la cohérence sémantique des chunks.

2. RecursiveChunking

Essaie de couper d'abord aux frontières naturelles (double saut de ligne, puis saut de ligne simple, puis point, puis espace). C'est le meilleur compromis vitesse/qualité pour la plupart des textes.

from agno.knowledge.chunking.recursive import RecursiveChunking
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.knowledge.knowledge import Knowledge
from agno.vectordb.pgvector import PgVector

chunker = RecursiveChunking(
    chunk_size=800,
    overlap=80,
)

knowledge = Knowledge(
    vector_db=PgVector(
        table_name="my_docs",
        db_url="postgresql+psycopg://user:pass@localhost:5432/mydb",
    ),
)

knowledge.insert(
    url="https://example.com/doc.pdf",
    reader=PDFReader(chunking_strategy=chunker),
)

Avantages : respecte mieux la structure naturelle du texte, chunks plus cohérents. Inconvénients : taille de chunk variable, peut générer des chunks très courts sur du texte mal formaté.

3. DocumentChunking

Analyse la structure du document (titres, paragraphes, listes) et découpe aux frontières logiques de contenu.

from agno.knowledge.chunking.document import DocumentChunking
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.knowledge.knowledge import Knowledge
from agno.vectordb.pgvector import PgVector

chunker = DocumentChunking()

knowledge = Knowledge(
    vector_db=PgVector(
        table_name="my_docs",
        db_url="postgresql+psycopg://user:pass@localhost:5432/mydb",
    ),
)

knowledge.insert(
    url="https://example.com/rapport.pdf",
    reader=PDFReader(chunking_strategy=chunker),
)

Avantages : chaque chunk correspond à une unité logique du document (section, sous-section). Excellent pour la documentation technique, les rapports, les articles. Inconvénients : dépend de la qualité du formatage du document source.

4. AgenticChunking

Utilise un LLM pour décider intelligemment où couper. Le modèle analyse le texte et identifie les frontières sémantiques optimales.

from agno.knowledge.chunking.agentic import AgenticChunking
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.knowledge.knowledge import Knowledge
from agno.vectordb.pgvector import PgVector

# AgenticChunking utilise un LLM : définissez votre clé API
chunker = AgenticChunking()

knowledge = Knowledge(
    vector_db=PgVector(
        table_name="critical_docs",
        db_url="postgresql+psycopg://user:pass@localhost:5432/mydb",
    ),
)

knowledge.insert(
    url="https://example.com/contrat-complexe.pdf",
    reader=PDFReader(chunking_strategy=chunker),
)

AgenticChunking consomme des tokens LLM pour chaque document. Sur un corpus de 1000 PDF, le coût peut être significatif. Utilisez-le uniquement pour des documents à haute valeur ajoutée (contrats, documents réglementaires, manuels critiques).

5. Semantic Chunking (Concept)

Le semantic chunking regroupe les phrases par similarité d'embedding : si deux phrases consécutives ont des embeddings proches, elles restent dans le même chunk. Quand la similarité chute brusquement, on coupe.

Phrase 1: "Le moteur thermique convertit l'énergie chimique en énergie mécanique."
Phrase 2: "Cette conversion se fait via la combustion du carburant."
Phrase 3: "Le rendement d'un moteur diesel est d'environ 40%."
          ↓ Faible similarité avec la phrase suivante → coupure
Phrase 4: "Les freins à disque offrent une meilleure dissipation thermique."

Avantages : chunks sémantiquement cohérents, pas de paramètre de taille à régler. Inconvénients : nécessite de calculer des embeddings pendant l'indexation (plus lent et plus cher).


L'Overlap : Ne Jamais Perdre le Contexte

L'overlap (chevauchement) est la quantité de texte répétée entre deux chunks consécutifs. Son rôle est de préserver le contexte aux frontières des chunks.

Sans overlap :
Chunk 1 : "...Le modèle XGBoost utilise le gradient boosting pour optimiser"
Chunk 2 : "la fonction de perte sur plusieurs itérations successives..."
→ La phrase est coupée en deux chunks. Aucun chunk n'est compréhensible seul.

Avec overlap de 100 caractères :
Chunk 1 : "...Le modèle XGBoost utilise le gradient boosting pour optimiser"
Chunk 2 : "...pour optimiser la fonction de perte sur plusieurs itérations successives..."
→ Le contexte est préservé dans les deux chunks.

La Règle des 10-20%

Un overlap sain représente 10 à 20% de la taille du chunk :

Taille du chunkOverlap recommandé
500 caractères50 à 100 caractères
1000 caractères100 à 200 caractères
2000 caractères200 à 400 caractères

Un overlap trop grand dilue les embeddings : le chunk 2 ressemble trop au chunk 1, et la recherche retourne des doublons. Un overlap trop petit (ou nul) crée des discontinuités qui perturbent la compréhension. La règle des 10-20% est un excellent point de départ.


Micro-exercice : Comparer FixedSize vs Recursive

L'objectif est de comparer objectivement les deux stratégies sur un même document.

from agno.knowledge.chunking.fixed import FixedSizeChunking
from agno.knowledge.chunking.recursive import RecursiveChunking
from agno.knowledge.embedder.openai import OpenAIEmbedder
from agno.knowledge.reader.pdf_reader import PDFReader
from agno.knowledge.knowledge import Knowledge
from agno.vectordb.lancedb import LanceDb, SearchType

PDF_URL = "https://agno-public.s3.amazonaws.com/recipes/ThaiRecipes.pdf"
EMBEDDER = OpenAIEmbedder(id="text-embedding-3-small")

def build_knowledge(chunker, table_name: str) -> Knowledge:
    return Knowledge(
        vector_db=LanceDb(
            uri="tmp/lancedb",
            table_name=table_name,
            search_type=SearchType.hybrid,
            embedder=EMBEDDER,
        ),
    )

# Indexation avec FixedSizeChunking
knowledge_fixed = build_knowledge(
    FixedSizeChunking(chunk_size=500, overlap=50),
    table_name="docs_fixed",
)
knowledge_fixed.insert(url=PDF_URL, reader=PDFReader(
    chunking_strategy=FixedSizeChunking(chunk_size=500, overlap=50)
))

# Indexation avec RecursiveChunking
knowledge_recursive = build_knowledge(
    RecursiveChunking(chunk_size=500, overlap=50),
    table_name="docs_recursive",
)
knowledge_recursive.insert(url=PDF_URL, reader=PDFReader(
    chunking_strategy=RecursiveChunking(chunk_size=500, overlap=50)
))

# Comparaison des résultats pour une même question
question = "Comment préparer une soupe Tom Kha ?"

results_fixed = knowledge_fixed.search(question, num_documents=3)
results_recursive = knowledge_recursive.search(question, num_documents=3)

print("=== FixedSizeChunking ===")
for i, doc in enumerate(results_fixed, 1):
    print(f"Chunk {i} ({len(doc.content)} chars): {doc.content[:200]}...")
    print()

print("=== RecursiveChunking ===")
for i, doc in enumerate(results_recursive, 1):
    print(f"Chunk {i} ({len(doc.content)} chars): {doc.content[:200]}...")
    print()

Ce que vous observerez : les chunks du RecursiveChunking se terminent à des fins de phrase ou de paragraphe, tandis que le FixedSizeChunking coupe brutalement. La lisibilité et la cohérence des chunks récursifs est nettement supérieure.


Comment Choisir : Arbre de Décision

Recommandation par défaut : commencez avec RecursiveChunking(chunk_size=800, overlap=100). C'est le meilleur compromis pour 80% des cas d'usage. Ajustez ensuite en mesurant vos métriques RAG (faithfulness, context relevancy).


Bonnes Pratiques

  1. Mesurez avant d'optimiser : implémentez un dataset de test (questions + réponses attendues) et comparez les stratégies objectivement.
  2. Adaptez à la source : PDFs scannés → OCR d'abord, puis chunking. Code source → chunking par fonction/classe. Emails → chunking par message.
  3. Testez différentes tailles : 256, 512, 1024 tokens. La taille optimale varie selon le modèle d'embedding et la nature du contenu.
  4. Pensez au reranking : un bon reranker peut compenser un chunking imparfait en réordonnant les résultats par pertinence réelle.

Specialiste IA — Master Intelligence Artificielle

Diplome d'un Master en Intelligence Artificielle, je travaille au quotidien sur des projets IA en entreprise. J'ai cree IwanttolearnAI pour rendre l'apprentissage de l'IA accessible a tous, gratuitement.