Accroche
Tu as monté ton premier RAG sur la documentation interne. Tu as suivi RAG-01 à la lettre. Tu poses une question dont tu connais la réponse — elle est dans le wiki, page 3 du guide d’astreinte. Le retrieval ramène cinq chunks. Aucun ne contient la réponse. Tu cherches dans le store : la réponse est bien indexée, mais elle a été coupée juste après le mot « Procédure », et juste avant le paragraphe utile. Le chunk indexé contient un titre orphelin — rien d’autre.
Ce n’est pas la faute du modèle d’embedding. Ce n’est pas la faute du vector store. C’est la frontière qui a été posée au mauvais endroit, par un découpage aveugle au sens. Le chunking — l’opération qui transforme tes documents en unités cherchables — décide silencieusement de ce que ton système pourra retrouver, ou jamais. Cette leçon te donne quatre stratégies de découpage, leurs forces, leurs angles morts, et la grille pour décider laquelle correspond à ton corpus.
Pourquoi le découpage est la décision la plus discrète du pipeline
Rappel RAG-01 : un chunk est l’unité atomique du retrieval — le système ne sait ramener que des chunks, pas des fragments de chunks ni des unions de chunks. Si tu indexes un document de 60 pages en un seul morceau, ta recherche par similarité donne un score global au document entier — la page 3 (qui contient la réponse) et la page 47 (qui parle d’autre chose) fondues dans un seul vecteur. À l’autre extrême : des chunks d’une seule phrase sont précis mais coupés de leur contexte. « Le seuil est fixé à 80 % » n’a aucun sens si la phrase d’avant disait « pour les alarmes critiques » et la suivante « sauf en mode dégradé. »
Analogie infra : le chunking, c’est le MTU réseau des documents. Trop gros, tu fragmentes au mauvais endroit et tu perds des paquets entiers. Trop petit, tu paies de l’overhead pour rien. La bonne taille dépend du média que tu traverses — pas d’un chiffre universel.
Un chunk est l’unité atomique du retrieval : le système peut ramener un chunk, pas un demi-chunk ni la jointure de deux. Ce que tu chunks détermine ce que ton système pourra retrouver. Pas plus, pas moins.
Aucune stratégie n’est universellement bonne — chacune optimise un compromis (qualité du retrieval, coût d’indexation, complexité du code). La question est : quelle stratégie pour quel corpus.
Vue d’ensemble : quatre familles, du plus brut au plus astucieux
Le schéma en texte
- Document brut → Stratégie ?
- Stratégie ? → fixed-size coupe tous les N tokens
- Stratégie ? → recursive respecte la structure
- Stratégie ? → semantic coupe au changement de sens
- Stratégie ? → hierarchical 2 granularités stockées
Quatre familles dominent en 2026, ordonnées du plus mécanique au plus sophistiqué. Fixed-size coupe tous les N tokens, brut et aveugle. Recursive essaie d’abord les séparateurs naturels (paragraphes, lignes, phrases) avant de couper sec. Semantic mesure le changement de sens entre phrases consécutives et coupe aux ruptures. Hierarchical stocke deux niveaux : un fin pour la recherche, un gros pour le contexte.
Du plus naïf au plus malin. Fixed-size = vitesse pure, zéro intelligence. Recursive = la structure d’abord, défaut raisonnable. Semantic = coupe au sens, paie un embedding par phrase à l’indexation. Hierarchical = double indexation, retrieval fin + contexte gros. Le choix dépend du corpus, pas du goût.
Stratégie 1 — Fixed-size : couper tous les N tokens
Principe : on découpe le texte en blocs de taille fixe (par exemple 500 tokens), avec un overlap de 10 à 20 % entre blocs consécutifs pour qu’un argument coupé en plein milieu reste partiellement présent dans les deux chunks adjacents.
Définition : l’overlap, c’est le nombre de tokens partagés entre deux chunks consécutifs. Avec chunk_size=500 et overlap=50, le chunk 2 commence 450 tokens après le chunk 1. La zone de recouvrement absorbe les coupures malheureuses.
Le schéma en texte
- Texte continu ...phrase 1. phrase 2. phrase 3... → Chunk 1 tokens 0-500
- Texte continu ...phrase 1. phrase 2. phrase 3... → Chunk 2 tokens 450-950
- Texte continu ...phrase 1. phrase 2. phrase 3... → Chunk 3 tokens 900-1400
- Chunk 1 tokens 0-500 → Chunk 2 tokens 450-950 (overlap 50)
- Chunk 2 tokens 450-950 → Chunk 3 tokens 900-1400 (overlap 50)
Code minimal :
# Découpe un texte en chunks de taille fixe en caractères, avec overlap.
# `text` : le document en string ; `size` : caractères par chunk ; `overlap` : recouvrement.
def fixed_size_chunk(text: str, size: int = 500, overlap: int = 50) -> list[str]:
chunks, start = [], 0
while start < len(text):
# Tranche [start:start+size], puis avance de (size - overlap) pour la prochaine
chunks.append(text[start:start + size])
start += size - overlap
return chunks
Force : prédictible, rapide, zéro coût supplémentaire à l’indexation (juste un compteur). Idéal pour benchmarker une baseline avant de comparer à mieux.
Faiblesse : aveugle aux frontières naturelles du texte. Coupe en plein milieu d’une phrase, d’un tableau, d’un bloc de code. Sur un corpus structuré (markdown avec sections, code source), c’est de la destruction de signal.
Analogie infra : fixed-size, c’est un
dd if=/dev/disk bs=512— tu lis par blocs de taille fixe sans regarder ce qu’il y a dedans. Parfait pour un disque brut, désastreux pour un système de fichiers structuré.
Stratégie 2 — Recursive : respecter la structure d’abord
Principe : on définit une liste ordonnée de séparateurs, du plus structurant (\n\n paragraphe) au plus granulaire ( mot). On essaie de couper avec le plus structurant ; si un fragment dépasse la taille cible, on récurse sur le séparateur suivant.
Le schéma en texte
- Document → Coupe sur \\n\\n (paragraphes)
- Coupe sur \\n\\n (paragraphes) → Validé (chunk OK)
- Coupe sur \\n\\n (paragraphes) → Coupe sur \\n (lignes) (trop grand)
- Coupe sur \\n (lignes) → Validé (chunk OK)
- Coupe sur \\n (lignes) → Coupe sur '. ' (phrases) (trop grand)
- Coupe sur '. ' (phrases) → Validé (chunk OK)
- Coupe sur '. ' (phrases) → Découpe sec en mots (trop grand)
Liste typique pour de la prose : ["\n\n", "\n", ". ", " ", ""] — paragraphe, ligne, phrase, mot, caractère. Sur un markdown structuré, on enrichit avec les titres : ["\n## ", "\n### ", "\n\n", ...] pour préserver d’abord les frontières de sections.
Force : sur un markdown bien rédigé, les chunks tombent presque toujours sur des transitions naturelles. Aucun coût supplémentaire à l’indexation. C’est le défaut de la plupart des frameworks RAG du marché — LangChain et LlamaIndex sont les deux librairies Python dominantes en 2026 pour assembler un pipeline RAG, l’une et l’autre exposent une fonction de chunking recursive comme défaut prêt-à-l’emploi.
Faiblesse : agnostique au sens. Deux paragraphes adjacents qui parlent du même sujet sont coupés ; deux phrases qui changent radicalement de sujet à l’intérieur d’un même paragraphe restent ensemble. Sur de la prose dense (transcripts, dialogues) où les transitions ne suivent pas la structure typographique, recursive perd son avantage.
Analogie infra : recursive, c’est le parser HTML qui essaie d’abord de couper aux balises
<section>, puis aux<p>, puis aux<br/>, et seulement en dernier recours en plein texte. Tu respectes la grammaire du document tant que tu peux.
Recursive est ta baseline par défaut. Mesure ton recall avec ça. Tu ne dois changer de stratégie que si tu observes un plafond — pas par anticipation. C’est le ratio qualité/coût le plus défendable pour 80 % des corpus.
Stratégie 3 — Semantic : couper là où le sens bascule
Principe : la structure typographique n’est qu’un proxy du sens. Si on calcule directement la similarité sémantique (rappel RAG-01) entre phrases consécutives, on peut couper précisément là où le sens change.
L’algorithme : (1) découper en phrases, (2) embedder chaque phrase, (3) calculer la distance cosinus entre embeddings consécutifs, (4) couper là où la distance dépasse un seuil (typiquement le 95e percentile de toutes les distances mesurées).
Le schéma en texte
- Phrase 1 chunking RAG → embed
- Phrase 2 tailles 500 tok → embed
- Phrase 3 impact qualité → embed
- Phrase 4 passons aux LLM → embed
- embed → embed (cos=0.92)
- embed → embed (cos=0.88)
- embed → embed (cos=0.31)
- embed → Coupure ici (distance > seuil)
Sur ce schéma : entre les phrases 1, 2 et 3 le cosinus reste haut (0.88-0.92 = mêmes sujets). Entre la phrase 3 et la phrase 4, il chute à 0.31 (changement de sujet). On coupe là.
Force : qualité de retrieval mesurablement supérieure sur les corpus dont les transitions sémantiques ne s’alignent pas sur la typographie (transcripts, dialogues, longs essais sans sous-titrage).
Faiblesse : coût d’indexation non trivial. 10 000 documents × 50 phrases = 500 000 embeddings juste pour décider où couper, avant l’embedding final des chunks. Avec un modèle local rapide (cf. RAG-03), c’est faisable ; avec un modèle propriétaire facturé au token, le budget se voit.
Analogie infra : semantic chunking, c’est un split de transactions par sémantique métier au lieu de par taille de log. Tu ne coupes pas tous les 10 000 lignes, tu coupes quand le contexte applicatif change (changement de service, changement de session utilisateur). Plus précis, plus cher à calculer.
AP — Seuil percentile choisi à l’aveugle. Le 95e percentile produit en général ~5 % de coupures, soit des chunks de ~20 phrases. Si tu vises des chunks plus petits, descends le percentile (90, 85). Si tu vois trop de chunks d’une seule phrase, monte (97, 99). Toujours tester sur un échantillon avant de lancer sur le corpus entier.
Stratégie 4 — Hierarchical : deux granularités, deux usages
Principe : stocker deux niveaux de chunks. Une granularité fine (par exemple paragraphe ou phrase, 100-300 tokens) pour le retrieval précis ; une granularité grosse (par exemple section ou document, 2000 tokens) pour fournir le contexte au LLM. À la requête, tu cherches sur les chunks fins, puis tu remontes au parent pour la génération.
Le schéma en texte
- Document → Parent A ~2000 tok
- Document → Parent B ~2000 tok
- Parent A ~2000 tok → Child A1 ~300 tok
- Parent A ~2000 tok → Child A2 ~300 tok
- Parent B ~2000 tok → Child B1 ~300 tok
- Query → Child A2 ~300 tok (search precis)
- Child A2 ~300 tok → Parent A ~2000 tok
- Parent A ~2000 tok → Prompt enrichi
Le retrieval matche un child précis (ici C2). Au lieu de l’envoyer seul au LLM, on envoie son parent (P1) — qui contient C2 et son cadre argumentatif. La précision vient du child, le contexte vient du parent. Le pattern porte un nom : ParentDocumentRetriever dans LangChain — implémentation de référence si tu ne veux pas écrire la jointure parent-enfant à la main.
Force : combine précision retrieval (chunks fins, vecteurs pointus) et contexte génération (chunks gros, le LLM voit l’environnement complet). Résout le compromis taille/qualité par stratification au lieu de chercher LE bon chiffre unique.
Faiblesse : stockage doublé (child + parent). Code plus complexe (jointure parent-enfant). Latence un peu plus élevée. Cible : corpus longs et structurés (manuels techniques, documentation API, articles de fond).
Analogie infra : hierarchical, c’est une base avec index secondaire. L’index principal (B-tree fin) te donne la précision de recherche ; les pages parents te donnent les données contextuelles autour de la ligne trouvée. Tu paies de l’espace disque pour gagner en pertinence du résultat.
Hierarchical, c’est l’arme à sortir quand tu as épuisé recursive et que tu veux gagner sans payer le coût d’embedding du semantic. Particulièrement adapté aux corpus longs où une réponse a besoin de son cadre argumentatif pour être utilisable.
Le compromis taille de chunk
Quelle que soit la stratégie, la taille moyenne du chunk reste le paramètre dominant. La pratique converge autour de 300-500 tokens — au-dessus on dilue, en-dessous on perd le contexte.
| Taille chunk | Avantage | Inconvénient |
|---|---|---|
| 100-200 tokens | Précision haute (1 vecteur ≈ 1 idée) | Phrase utile sans cadre — le LLM répond à côté |
| 300-500 tokens | Défaut recommandé sur prose | — |
| 600-1000 tokens | Contexte préservé, synthèses mieux servies | Retrieval dilué (chunk pertinent à 30 %, bruit à 70 %) |
| > 1000 tokens | Approche document-as-chunk | Perd l’avantage du chunking, re-converge vers le « long contexte » |
Mesure empirique : sur un corpus typique d’articles techniques, le recall@5 plateau autour de 400-500 tokens. [NON VÉRIFIÉ — ordre de grandeur convergent dans plusieurs études publiques 2023-2024 ; valeur exacte varie de 20 à 30 % selon corpus et modèle d’embedding.]
Matrice de décision : quelle stratégie pour quel corpus
| Type de corpus | Stratégie | Pourquoi |
|---|---|---|
| Markdown structuré (wiki, articles) | Recursive sur ## puis \n\n | Les sections sont déjà des unités de sens. Recursive les respecte gratuitement. |
| Code source | AST splitter (frontières fonction/classe) | Couper au milieu d’une fonction casse la sémantique. |
| Juridique (contrats, lois) | Recursive enrichi (Article, §, Section) | Chaque clause est juridiquement autonome — préserver les marqueurs du domaine. |
| Transcripts, dialogues | Semantic | Pas de structure typographique fiable. La rupture de sens est le seul signal. |
| Manuels longs, doc API | Hierarchical (parent=section, child=paragraphe) | Le LLM a besoin du cadre, le retrieval a besoin de la précision. |
| Mixte (PDF + MD + code) | Routeur par type vers le splitter spécialisé | Une stratégie unique sur du contenu hétérogène = résultats hétérogènes. |
Règles heuristiques :
- Commence par recursive, mesure le recall, ne complexifie qu’en cas de plafond.
- Compte en tokens, pas en caractères — c’est l’unité du LLM final, et le ratio caractères/tokens varie avec la langue.
- Stocke la version de l’embedder en métadonnée : si tu re-chunks demain avec un autre modèle, tu dois détecter l’incohérence avant qu’elle pollue le retrieval (rappel RAG-01).
Pièges à connaître
AP-1 — Choisir une stratégie sans la mesurer. Le pire pattern : prendre semantic ou hierarchical pour faire « avancé » sans avoir mesuré recall@5 sur recursive. Toujours bench une baseline simple avant de complexifier — souvent tu n’as pas besoin du sophistiqué.
AP-2 — Overlap nul ou trop grand. Overlap = 0 → tu casses des arguments à la frontière sans filet. Overlap = 50 % → tu doubles le coût de stockage et la redondance dans les top_k retournés. Cible 10-20 % (chunk_size=500, overlap=50 à 100).
AP-3 — Avaler le frontmatter YAML dans le premier chunk. Si ton splitter ne pré-extrait pas le frontmatter, tu pollues l’embedding du chunk 1 avec date: 2026-04-26, tags: [...]. Solution : extraire le YAML en métadonnée, ne chunker que le body.
AP-4 — Couper en plein milieu d’un bloc atomique. Ton splitter recursive sectionne au milieu d’un bloc ``` ou d’un tableau markdown — le chunk se termine par ```python\ndef foo(): sans la fin. Solution : pre-pass pour identifier les blocs atomiques et les protéger pendant le découpage.
Ton corpus est fait de procédures d’exploitation : titre, contexte, puis une suite d’étapes numérotées. Tu découpes en fixed-size, 500 tokens, sans recouvrement. Les réponses sont systématiquement incomplètes. Qu’est-ce qui se passe, et quelle famille de découpage règle ça ?
Réponse
La coupe tombe au milieu d’une procédure. Le chunk rapporté contient les étapes 1 à 4 mais pas les étapes 5 à 8, ou un titre orphelin sans son contenu. Le modèle répond avec ce qu’il a — une procédure tronquée, qui a l’air complète.
Le fixed-size ignore la structure du document ; or ici la structure est l’information. Le recursive respecte les frontières naturelles (titres, paragraphes, listes) et coupe en dernier recours seulement. Le hierarchical va plus loin : il indexe finement pour trouver, et rend un contexte large pour répondre — exactement ce qu’il faut quand une réponse juste suppose la procédure entière.
Et si tu restes en fixed-size, mets au moins un recouvrement : il ne répare pas la coupe, mais il évite que l’information tombe pile dans la faille.
Ce que tu vas apprendre dans la suite
Tu sais maintenant choisir une stratégie de chunking en justifiant ton choix par le type de corpus. Mais le chunking n’est qu’un tiers du pipeline d’indexation. Reste à voir quel modèle d’embedding transformer tes chunks en vecteurs, et dans quel vector store les ranger.
Le schéma en texte
- RAG-02 Chunking (tu es ici) → RAG-03 Embeddings
- RAG-03 Embeddings → RAG-04 Vector stores
- RAG-03 — Embeddings : familles de modèles (BGE-M3, OpenAI, Voyage), multilinguisme, dimensionnalité, comment choisir.
- RAG-04 — Vector stores : sqlite-vec, FAISS, Qdrant, pgvector — quand utiliser quoi en fonction de l’échelle.
Glossaire
Format : TERME — définition courte + analogie infra quand pertinente.
- Chunking — Opération de découpage des documents en chunks. Quatre familles dominantes : fixed-size, recursive, semantic, hierarchical.
- Chunk (rappel RAG-01) — Morceau d’un document, unité atomique de retrieval. Le système ramène des chunks, pas des fragments.
- Fixed-size chunking — Découpage en blocs de taille fixe en tokens ou caractères, avec overlap. Aveugle au contenu. Analogie :
dd bs=512sur un disque brut. - Recursive chunking — Découpage qui essaie d’abord les séparateurs structurels (paragraphe, ligne, phrase) avant de couper sec. Défaut raisonnable. Analogie : parser HTML qui respecte d’abord les
<section>. - Semantic chunking — Découpage aux ruptures de sens, mesurées par chute de similarité cosinus entre phrases consécutives. Analogie : split de logs par changement de session applicative au lieu de par taille fixe.
- Hierarchical chunking — Stockage de deux niveaux : children fins pour le retrieval précis, parents gros pour le contexte LLM. Analogie : index secondaire B-tree pointant vers des pages parents.
- Overlap — Nombre de tokens partagés entre deux chunks consécutifs. Cible 10-20 % de chunk_size. Absorbe les coupures malheureuses à la frontière.
- Tokens (rappel RAG-00) — Unité élémentaire que digère le LLM (morceau de mot ; un mot français en consomme souvent plusieurs, selon le tokeniseur). À privilégier au comptage caractères pour aligner avec le coût final.
- Recall (retrieval) (rappel RAG-01) — Pourcentage de fois où la bonne réponse est dans les top_k chunks ramenés. Métrique clé pour évaluer une stratégie de chunking. Cible : ≥ 0.75 sur un jeu test avant de mettre en production.
- AST splitter — Splitter spécialisé code source qui découpe aux frontières syntaxiques (fonction, classe) en parsant l’arbre syntaxique du langage. Préserve la sémantique du code.
- ParentDocumentRetriever — Pattern d’implémentation du hierarchical chunking dans plusieurs frameworks RAG. Indexe les children, sert les parents au LLM.
- Similarité cosinus (rappel RAG-01) — Mesure d’angle entre deux vecteurs, entre -1 et 1. Proche de 1 = sens proches. Sert ici à détecter les ruptures sémantiques entre phrases.