Chapitre 2 : Stratégies de division de texte
Introduction
La division de texte (chunking) est cruciale pour la performance de RAG. La bonne taille de segment et la bonne stratégie impactent significativement la qualité de la récupération et la précision de la génération.
Pourquoi le Chunking est Important
Problèmes sans chunking :
- Les documents entiers sont trop volumineux
- La récupération renvoie du contenu non pertinent
- Gaspille la fenêtre de contexte LLM
Paramètres Clés
Taille du Segment
Le nombre de caractères/tokens par segment :
| Taille du Segment | Avantages | Inconvénients |
|---|---|---|
| Petit (256) | Récupération précise | Peut perdre le contexte |
| Moyen (512-1024) | Équilibré | Bon pour la plupart des cas |
| Grand (2000+) | Plus de contexte | Récupération moins précise |
Chevauchement des Segments
Texte partagé entre les segments consécutifs :
Segment 1: [_______________]
Segment 2: [_______________]
^chevauchement^
Pourquoi le chevauchement ?
- Empêche de couper les phrases en plein milieu
- Préserve le contexte à travers les limites des segments
- Typique : 10-20% de la taille du segment
Stratégies de Division
1. Division Récursive par Caractères
L'approche la plus courante - divise par hiérarchie de séparateurs :
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200,
separators=["\n\n", "\n", ". ", " ", ""]
)
chunks = splitter.split_documents(documents)
Hiérarchie des séparateurs :
- Doubles sauts de ligne (paragraphes)
- Sauts de ligne simples
- Points (phrases)
- Espaces (mots)
- Caractères (dernier recours)
2. Division Basée sur les Tokens
Utilise le comptage réel des tokens (important pour les limites LLM) :
from langchain.text_splitter import TokenTextSplitter
splitter = TokenTextSplitter(
chunk_size=500, # tokens, pas caractères
chunk_overlap=50,
encoding_name="cl100k_base" # Tokenizer GPT-4
)
3. Division par Phrase
Préserve les limites des phrases :
from langchain.text_splitter import SentenceTransformersTokenTextSplitter
splitter = SentenceTransformersTokenTextSplitter(
chunk_overlap=50,
tokens_per_chunk=256
)
4. Division Sémantique
Divise en fonction de la similarité sémantique :
from langchain_experimental.text_splitter import SemanticChunker
from langchain.embeddings import OpenAIEmbeddings
splitter = SemanticChunker(
OpenAIEmbeddings(),
breakpoint_threshold_type="percentile"
)
# Segments basés sur la similarité sémantique
semantic_chunks = splitter.split_documents(documents)
5. Division par En-têtes Markdown
Respecte la structure du document :
from langchain.text_splitter import MarkdownHeaderTextSplitter
headers_to_split_on = [
("#", "En-tête 1"),
("##", "En-tête 2"),
("###", "En-tête 3"),
]
splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=headers_to_split_on
)
Choisir la Taille du Segment
def optimal_chunk_size(embedding_model, llm_context_window, top_k):
"""
Calculer la taille optimale du segment
"""
# Laisser de la place pour le modèle de prompt et la réponse
available_tokens = llm_context_window * 0.6
# Diviser par le nombre de segments à récupérer
chunk_tokens = available_tokens / top_k
# Convertir en caractères (environ 4 caractères par token)
return int(chunk_tokens * 4)
# Exemple : GPT-4 avec 8K de contexte, récupérant 5 segments
optimal = optimal_chunk_size(
embedding_model="text-embedding-3-small",
llm_context_window=8192,
top_k=5
)
# Résultat : ~3900 caractères par segment
Tableau de Comparaison
| Stratégie | Idéal Pour | Qualité du Segment |
|---|---|---|
| Caractère Récursif | Usage général | Bon |
| Basée sur les Tokens | Gestion du contexte LLM | Excellent |
| Phrase | Systèmes de Q&R | Bon |
| Sémantique | Contenu varié | Excellent |
| En-têtes Markdown | Documents structurés | Excellent |
Meilleures Pratiques
- Commencez avec les valeurs par défaut - 1000 caractères, 200 de chevauchement
- Testez différentes tailles - Évaluez la qualité de la récupération
- Adaptez au contenu - Les documents techniques peuvent nécessiter des segments plus grands
- Considérez les embeddings - Certains modèles ont des limites de tokens max
- Préservez les métadonnées - Suivez la source et la position du segment
# Pipeline de segmentation en production
def chunk_documents(documents, chunk_size=1000, overlap=200):
splitter = RecursiveCharacterTextSplitter(
chunk_size=chunk_size,
chunk_overlap=overlap,
length_function=len,
add_start_index=True # Suivre la position
)
chunks = splitter.split_documents(documents)
# Ajouter l'index du segment aux métadonnées
for i, chunk in enumerate(chunks):
chunk.metadata["chunk_index"] = i
chunk.metadata["total_chunks"] = len(chunks)
return chunks
Résumé
- Taille du segment affecte la précision de la récupération vs le contexte
- Chevauchement prévient la perte d'information aux limites
- Stratégie doit correspondre à votre type de document
- Testez et itérez pour trouver les paramètres optimaux
Prochaines Étapes
Continuez vers Chapitre 3 : Types de fichiers pour apprendre à gérer des documents complexes.