
Vos documents sont trop longs pour être envoyés directement à un LLM ? Le chunking (découpage) est l’étape cruciale qui transforme un document de 50 pages en fragments cohérents que votre système RAG peut rechercher efficacement.
Ce guide vous montre 5 stratégies de chunking — du plus simple au plus intelligent — avec du code Python fonctionnel et des recommandations pour choisir la bonne approche selon votre cas.
Pourquoi le chunking est crucial pour le RAG
Section intitulée « Pourquoi le chunking est crucial pour le RAG »Un système RAG fonctionne en 3 étapes :
Un mauvais chunking produit des résultats médiocres, même avec un excellent modèle d’embeddings :
| Problème | Conséquence |
|---|---|
| Chunks trop grands (>1000 tokens) | Embeddings dilués, recherche imprécise |
| Chunks trop petits (<50 tokens) | Contexte insuffisant, réponses incomplètes |
| Coupures au milieu d’une phrase | Perte de sens, hallucinations |
| Séparation phrase-contexte | ”3.2” sans savoir que c’est une version |
Le bon chunking garantit que chaque fragment :
- Est suffisamment long pour avoir du sens (contexte)
- Est suffisamment court pour être précis (ciblage)
- Conserve les frontières naturelles du texte (cohérence)
Les 5 stratégies de chunking
Section intitulée « Les 5 stratégies de chunking »Voici un comparatif des approches, de la plus simple à la plus sophistiquée :
| Stratégie | Complexité | Avantages | Inconvénients |
|---|---|---|---|
| Fixe (caractères) | ⭐ | Simple, prévisible | Coupe au milieu des mots |
| Récursif | ⭐⭐ | Respecte les frontières | Tailles variables |
| Par phrases | ⭐⭐ | Cohérence sémantique | Phrases longues problématiques |
| Par sections | ⭐⭐⭐ | Structure préservée | Dépend du format (Markdown, HTML) |
| Sémantique | ⭐⭐⭐⭐ | Chunks thématiques | Plus lent, modèle requis |
Prérequis
Section intitulée « Prérequis »# Environnement Python 3.10+python3 -m venv chunking-testsource chunking-test/bin/activate
# Dépendances (optionnelles selon la stratégie)pip install sentence-transformers nltkStratégie 1 : Chunking fixe (caractères)
Section intitulée « Stratégie 1 : Chunking fixe (caractères) »La méthode la plus simple : découper tous les N caractères avec un chevauchement (overlap) pour éviter de perdre le contexte aux frontières.
def chunk_fixed(text: str, chunk_size: int = 500, overlap: int = 50) -> list[str]: """Découpage fixe avec overlap.
Args: text: Texte à découper chunk_size: Taille maximale de chaque chunk overlap: Nombre de caractères en commun entre chunks consécutifs
Returns: Liste de chunks """ chunks = [] start = 0
while start < len(text): end = start + chunk_size chunk = text[start:end]
if chunk.strip(): # Ignorer les chunks vides chunks.append(chunk)
# Avancer en tenant compte de l'overlap start = end - overlap
return chunksTest avec un texte réel :
texte = """Ansible est un outil d'automatisation IT open source. Il permet degérer la configuration des serveurs de manière déclarative.
Les playbooks Ansible sont écrits en YAML. Ils décrivent l'état souhaitéde l'infrastructure plutôt que les étapes pour y arriver.
L'inventaire définit les groupes de serveurs. Un groupe peut contenirdes serveurs de production, staging ou développement.
Les modules Ansible sont les unités d'exécution. Le module apt installedes paquets, le module copy copie des fichiers."""
chunks = chunk_fixed(texte, chunk_size=200, overlap=30)for i, chunk in enumerate(chunks): print(f"[{i+1}] ({len(chunk)} car.): {chunk[:50]}...")Résultat :
[1] (200 car.): Ansible est un outil d'automatisation IT open so...[2] (200 car.): re déclarative.
Les playbooks Ansible sont éc...[3] (200 car.): ure plutôt que les étapes pour y arriver.
L'inv...[4] (170 car.): r de production, staging ou développement.
Les ...Quand l’utiliser :
- Texte non structuré (logs, transcriptions)
- Prototypage rapide
- Quand vous avez besoin de tailles exactes
Stratégie 2 : Chunking récursif (LangChain style)
Section intitulée « Stratégie 2 : Chunking récursif (LangChain style) »Cette méthode utilise une hiérarchie de séparateurs : elle essaie d’abord de couper aux paragraphes, puis aux lignes, puis aux phrases, puis aux mots.
def chunk_recursive( text: str, chunk_size: int = 500, separators: list[str] = None) -> list[str]: """Chunking récursif avec hiérarchie de séparateurs.
Essaie de découper avec le premier séparateur. Si les chunks sont trop grands, applique récursivement avec le séparateur suivant. """ if separators is None: separators = ["\n\n", "\n", ". ", " "]
if not separators: # Plus de séparateurs : découpage brutal return [text[i:i+chunk_size] for i in range(0, len(text), chunk_size)]
sep = separators[0] parts = text.split(sep)
chunks = [] current_chunk = ""
for part in parts: # Si ajouter cette partie dépasse la limite if len(current_chunk) + len(part) + len(sep) > chunk_size: if current_chunk: chunks.append(current_chunk.strip())
# Si la partie seule est trop grande, récursion if len(part) > chunk_size: chunks.extend(chunk_recursive(part, chunk_size, separators[1:])) current_chunk = "" else: current_chunk = part else: current_chunk += (sep if current_chunk else "") + part
if current_chunk.strip(): chunks.append(current_chunk.strip())
return chunksTest :
chunks = chunk_recursive(texte, chunk_size=300)for i, chunk in enumerate(chunks): print(f"[{i+1}] ({len(chunk)} car.):") print(f" {chunk[:60]}...")Résultat :
[1] (193 car.): Ansible est un outil d'automatisation IT open source. Il p...[2] (187 car.): Les playbooks Ansible sont écrits en YAML. Ils décrivent l...[3] (193 car.): L'inventaire définit les groupes de serveurs. Un groupe pe...[4] (151 car.): Les modules Ansible sont les unités d'exécution. Le module...Quand l’utiliser :
- Documentation technique
- Articles de blog
- Texte avec paragraphes clairs
Stratégie 3 : Chunking par phrases
Section intitulée « Stratégie 3 : Chunking par phrases »Cette approche utilise NLTK pour identifier les frontières de phrases et regroupe plusieurs phrases jusqu’à atteindre la taille cible.
import nltkfrom nltk.tokenize import sent_tokenize
# Télécharger le modèle de tokenisation (une seule fois)nltk.download('punkt_tab', quiet=True)
def chunk_by_sentences( text: str, max_sentences: int = 3, max_chars: int = 500) -> list[str]: """Chunking par groupes de phrases.
Regroupe les phrases jusqu'à atteindre max_sentences ou max_chars, selon ce qui arrive en premier. """ sentences = sent_tokenize(text, language='french')
chunks = [] current_chunk = [] current_length = 0
for sentence in sentences: sentence = sentence.strip()
# Vérifier les limites if (len(current_chunk) >= max_sentences or current_length + len(sentence) > max_chars): if current_chunk: chunks.append(" ".join(current_chunk)) current_chunk = [sentence] current_length = len(sentence) else: current_chunk.append(sentence) current_length += len(sentence) + 1
if current_chunk: chunks.append(" ".join(current_chunk))
return chunksTest :
chunks = chunk_by_sentences(texte, max_sentences=2, max_chars=300)for i, chunk in enumerate(chunks): print(f"[{i+1}] ({len(chunk)} car.): {chunk[:50]}...")Résultat :
[1] (131 car.): Ansible est un outil d'automatisation IT open so...[2] (151 car.): Les playbooks Ansible sont écrits en YAML. Ils d...[3] (156 car.): L'inventaire définit les groupes de serveurs. Un...[4] (118 car.): Les modules Ansible sont les unités d'exécution....Quand l’utiliser :
- Texte narratif
- FAQ (question-réponse par chunk)
- Contenu où chaque phrase a une valeur autonome
Stratégie 4 : Chunking par sections (Markdown/HTML)
Section intitulée « Stratégie 4 : Chunking par sections (Markdown/HTML) »Pour les documents structurés (Markdown, HTML), respecter la structure originale produit des chunks thématiquement cohérents.
import re
def chunk_by_sections( text: str, header_pattern: str = r'^#{1,3}\s+.+$', max_chars: int = 1000) -> list[str]: """Chunking par sections Markdown.
Découpe aux titres (# ## ###) et retient le titre dans chaque chunk pour le contexte. """ lines = text.split('\n') chunks = [] current_chunk = [] current_header = "" current_length = 0
for line in lines: # Détection d'un titre if re.match(header_pattern, line, re.MULTILINE): # Sauvegarder le chunk précédent if current_chunk: chunk_text = '\n'.join(current_chunk).strip() if chunk_text: chunks.append(chunk_text)
current_header = line current_chunk = [line] current_length = len(line) else: # Vérifier la limite de taille if current_length + len(line) > max_chars and current_chunk: chunk_text = '\n'.join(current_chunk).strip() if chunk_text: chunks.append(chunk_text) # Nouveau chunk avec le header pour le contexte current_chunk = [current_header, line] if current_header else [line] current_length = len(current_header) + len(line) else: current_chunk.append(line) current_length += len(line) + 1
# Dernier chunk if current_chunk: chunk_text = '\n'.join(current_chunk).strip() if chunk_text: chunks.append(chunk_text)
return chunksTest avec un document Markdown :
doc_markdown = """# Guide Ansible
## Introduction
Ansible est un outil d'automatisation IT. Il gère la configuration des serveurs.
## Installation
Installez Ansible avec votre gestionnaire de paquets (apt, dnf, brew).
## Premiers pas
### Créer un inventaire
L'inventaire liste vos serveurs par groupe.
### Écrire un playbook
Un playbook est un fichier YAML qui décrit l'état souhaité."""
chunks = chunk_by_sections(doc_markdown, max_chars=300)for i, chunk in enumerate(chunks): print(f"[{i+1}] ({len(chunk)} car.):") print(chunk[:80] + "..." if len(chunk) > 80 else chunk) print()Résultat :
[1] (14 car.):# Guide Ansible
[2] (100 car.):## Introduction
Ansible est un outil d'automatisation IT. Il gère la configuration...
[3] (85 car.):## Installation
Installez Ansible avec votre gestionnaire de paquets (apt, dnf, brew).
[4] (47 car.):## Premiers pas
### Créer un inventaire...
[5] (70 car.):### Écrire un playbook
Un playbook est un fichier YAML qui décrit l'état...Quand l’utiliser :
- Documentation technique structurée
- README, wikis, guides
- Tout document avec des titres clairs
Stratégie 5 : Chunking sémantique
Section intitulée « Stratégie 5 : Chunking sémantique »L’approche la plus sophistiquée : utiliser les embeddings pour détecter les changements de thème et couper là où le sens change.
import numpy as npfrom sentence_transformers import SentenceTransformerfrom nltk.tokenize import sent_tokenize
def chunk_semantic( text: str, model_name: str = "intfloat/multilingual-e5-small", similarity_threshold: float = 0.75, min_chunk_sentences: int = 2) -> list[str]: """Chunking sémantique basé sur les embeddings.
Regroupe les phrases tant que leur similarité dépasse le seuil. Coupe quand le thème change. """ # Charger le modèle model = SentenceTransformer(model_name)
# Tokeniser en phrases sentences = sent_tokenize(text, language='french') if len(sentences) < 2: return [text]
# Encoder toutes les phrases (avec préfixe passage: pour E5) passages = [f"passage: {s}" for s in sentences] embeddings = model.encode(passages, show_progress_bar=False)
# Calculer la similarité entre phrases consécutives similarities = [] for i in range(len(embeddings) - 1): sim = np.dot(embeddings[i], embeddings[i+1]) sim /= (np.linalg.norm(embeddings[i]) * np.linalg.norm(embeddings[i+1])) similarities.append(sim)
# Créer les chunks aux points de faible similarité chunks = [] current_chunk = [sentences[0]]
for i, sim in enumerate(similarities): if sim < similarity_threshold and len(current_chunk) >= min_chunk_sentences: # Changement de thème détecté chunks.append(" ".join(current_chunk)) current_chunk = [sentences[i+1]] else: current_chunk.append(sentences[i+1])
if current_chunk: chunks.append(" ".join(current_chunk))
return chunksTest :
texte_mixte = """Ansible est un outil d'automatisation pour gérer des serveurs Linux.Il utilise SSH pour se connecter aux machines distantes.Les playbooks sont écrits en YAML avec une syntaxe déclarative.Docker permet de créer des conteneurs isolés pour les applications.Les images Docker sont construites à partir de Dockerfiles.Kubernetes orchestre les conteneurs à grande échelle dans un cluster."""
chunks = chunk_semantic(texte_mixte, similarity_threshold=0.80)for i, chunk in enumerate(chunks): print(f"[{i+1}] Thème {i+1}:") print(f" {chunk}") print()Résultat :
[1] Thème 1: Ansible est un outil d'automatisation pour gérer des serveurs Linux. Il utilise SSH pour se connecter aux machines distantes. Les playbooks sont écrits en YAML avec une syntaxe déclarative.
[2] Thème 2: Docker permet de créer des conteneurs isolés pour les applications. Les images Docker sont construites à partir de Dockerfiles.
[3] Thème 3: Kubernetes orchestre les conteneurs à grande échelle dans un cluster.Quand l’utiliser :
- Texte sans structure claire (transcriptions, HTML converti)
- Documents qui mélangent plusieurs sujets
- Quand la qualité prime sur la vitesse
Comparatif des stratégies
Section intitulée « Comparatif des stratégies »Voici un tableau récapitulatif pour vous aider à choisir :
| Critère | Fixe | Récursif | Phrases | Sections | Sémantique |
|---|---|---|---|---|---|
| Vitesse | ⚡⚡⚡ | ⚡⚡ | ⚡⚡ | ⚡⚡ | ⚡ |
| Dépendances | Aucune | Aucune | NLTK | Aucune | sentence-transformers |
| Cohérence | Faible | Moyenne | Bonne | Excellente | Excellente |
| Prévisibilité | Exacte | Variable | Variable | Variable | Variable |
| Documents structurés | ✗ | ✓ | ✓ | ✓✓✓ | ✓✓ |
| Texte libre | ✓ | ✓ | ✓✓ | ✗ | ✓✓✓ |
Recommandations par cas d’usage
Section intitulée « Recommandations par cas d’usage »Documentation Markdown
Stratégie : Sections (priorité) + Récursif (fallback)
Les docs techniques ont des titres — exploitez-les !
Articles de blog
Stratégie : Récursif
Les paragraphes sont des unités naturelles de sens.
FAQ / Q&A
Stratégie : Phrases (2-3 par chunk)
Question + réponse = 1 chunk cohérent.
Transcriptions
Stratégie : Sémantique
Pas de structure → laissez le modèle trouver les thèmes.
L’overlap : récupérer le contexte aux frontières
Section intitulée « L’overlap : récupérer le contexte aux frontières »Quelle que soit la stratégie, l’overlap (chevauchement) aide à conserver le contexte entre chunks.
def add_overlap(chunks: list[str], overlap_sentences: int = 1) -> list[str]: """Ajoute un overlap au début de chaque chunk.
Répète la dernière phrase du chunk précédent au début du chunk suivant. """ if len(chunks) < 2: return chunks
result = [chunks[0]]
for i in range(1, len(chunks)): # Extraire les dernières phrases du chunk précédent prev_sentences = sent_tokenize(chunks[i-1], language='french') overlap = " ".join(prev_sentences[-overlap_sentences:])
# Ajouter au début du chunk actuel result.append(f"{overlap} {chunks[i]}")
return resultIntégration avec votre pipeline RAG
Section intitulée « Intégration avec votre pipeline RAG »Une fois vos chunks créés, l’étape suivante est de les transformer en embeddings (vecteurs numériques) pour la recherche sémantique.
from sentence_transformers import SentenceTransformerimport chromadb
# 1. Chunker le documentchunks = chunk_by_sections(document_markdown)
# 2. Créer les embeddingsmodel = SentenceTransformer("intfloat/multilingual-e5-small")embeddings = model.encode([f"passage: {c}" for c in chunks])
# 3. Stocker dans ChromaDBclient = chromadb.PersistentClient(path="./ma_base")collection = client.get_or_create_collection("documentation")
collection.add( embeddings=embeddings.tolist(), documents=chunks, ids=[f"chunk_{i}" for i in range(len(chunks))])À retenir
Section intitulée « À retenir »Le chunking est une étape fondamentale qui impacte directement la qualité de votre RAG :
| Point clé | Recommandation |
|---|---|
| Taille cible | 200-500 tokens (800-2000 caractères) |
| Stratégie par défaut | Récursif pour texte général |
| Documents structurés | Sections (Markdown/HTML) |
| Texte non structuré | Sémantique si qualité critique |
| Overlap | 10-20% pour conserver le contexte |