Aller au contenu
Développement medium

Chunking : découper vos documents pour le RAG

10 min de lecture

logo python

Un document de cinquante pages ne s'indexe pas d'un bloc : son embedding se dilue, et la recherche remonte un pavé entier là où il fallait un paragraphe. Le chunking découpe chaque document en morceaux — assez petits pour un embedding net, assez grands pour rester compréhensibles. Ce guide présente trois stratégies — taille fixe, par phrases, par sections —, explique le rôle décisif du recouvrement, et donne le critère pour choisir selon votre corpus. C'est l'étape qui pèse le plus sur la qualité d'un RAG : un mauvais découpage plombe tout ce qui suit. Public visé : développeur qui prépare un corpus nettoyé pour l'indexation.

  • Pourquoi le chunking conditionne la qualité d'un RAG.
  • Le découpage par taille fixe et le rôle du recouvrement.
  • Le découpage par phrases, qui ne casse jamais une phrase.
  • Le découpage par sections, qui respecte la structure du document.
  • Choisir la stratégie adaptée à votre corpus.
  • Python 3.10+ — le chunking de ce guide tient en bibliothèque standard.
  • Avoir un corpus extrait et nettoyé à découper.

Le RAG cherche par similarité : il compare le vecteur d'une question aux vecteurs des morceaux indexés. Or un vecteur résume tout le texte qu'il représente en un point unique.

Un chunk trop gros mélange plusieurs idées : son vecteur est une moyenne floue qui ne ressemble fortement à aucune question précise. Un chunk trop petit perd son contexte : « il faut redémarrer le service » ne dit pas lequel. Entre les deux, il existe une taille où le vecteur capte une idée nette — et c'est tout l'enjeu du chunking.

Cette étape n'a rien d'intelligent : c'est du découpage de chaîne de caractères. Mais elle décide de ce que la recherche pourra, ou non, retrouver. Un mauvais chunking ne se rattrape pas en aval.

La stratégie la plus simple : une fenêtre glissante d'un nombre fixe de mots. On avance dans le texte, on prend N mots, on recommence.

def chunk_taille_fixe(texte, taille=60, recouvrement=12):
"""Découpe par fenêtre fixe de N mots, avec recouvrement entre voisins."""
if recouvrement >= taille:
raise ValueError("le recouvrement doit être inférieur à la taille")
mots = texte.split()
pas = taille - recouvrement
chunks, debut = [], 0
while debut < len(mots):
chunks.append(" ".join(mots[debut:debut + taille]))
if debut + taille >= len(mots):
break
debut += pas
return chunks

Son avantage est sa robustesse : elle marche sur n'importe quel texte, structuré ou non. Son défaut : elle coupe à l'aveugle, parfois en plein milieu d'une phrase. C'est là qu'intervient le recouvrement.

Sans recouvrement, une idée à cheval sur deux chunks est perdue : sa première moitié finit un chunk, sa seconde ouvre le suivant, et aucun des deux vecteurs ne la capte entièrement.

Le recouvrement (overlap) règle cela : les derniers mots d'un chunk réapparaissent au début du suivant. Une idée coupée par la frontière se retrouve intacte dans au moins un chunk.

chunks = chunk_taille_fixe(texte, taille=25, recouvrement=5)
# Les 5 derniers mots du chunk 1 sont aussi les 5 premiers du chunk 2.

Le recouvrement a un coût — du texte dupliqué, donc des chunks en plus — mais il est presque toujours rentable. Un recouvrement de l'ordre de 10 à 20 % de la taille du chunk est un bon point de départ. Une seule contrainte : il doit rester inférieur à la taille, sinon le découpage n'avance plus.

Couper en plein milieu d'une phrase produit des chunks bancals. Le découpage par phrases l'interdit : il regroupe des phrases entières sans jamais en casser une, jusqu'à un budget de mots.

def chunk_par_phrases(texte, max_mots=60):
"""Regroupe des phrases entières sans dépasser un budget de mots."""
phrases = segmenter_phrases(texte) # voir le guide sur le nettoyage
chunks, courant, compte = [], [], 0
for phrase in phrases:
n = len(phrase.split())
if courant and compte + n > max_mots:
chunks.append(" ".join(courant))
courant, compte = [], 0
courant.append(phrase)
compte += n
if courant:
chunks.append(" ".join(courant))
return chunks

Un chunk se ferme dès que la phrase suivante ferait dépasser le budget. Chaque chunk est donc une suite de phrases complètes — toujours lisible. La contrepartie : une phrase très longue peut, à elle seule, dépasser le budget. C'est rare et acceptable ; mieux vaut une phrase entière trop longue qu'une phrase tronquée.

Quand le document est déjà structuré — du Markdown avec des titres —, le meilleur découpage est celui que l'auteur a déjà fait. Le chunking par sections coupe sur les titres : une section, un chunk.

def chunk_par_sections(markdown):
"""Découpe un Markdown sur ses titres : un chunk par section."""
sections, titre, corps = [], "(préambule)", []
for ligne in markdown.splitlines():
if ligne.lstrip().startswith("#"):
if any(l.strip() for l in corps):
sections.append({"titre": titre, "texte": "\n".join(corps).strip()})
titre, corps = ligne.lstrip("#").strip(), []
else:
corps.append(ligne)
if any(l.strip() for l in corps):
sections.append({"titre": titre, "texte": "\n".join(corps).strip()})
return sections

Chaque chunk garde le titre de sa section — un contexte précieux, qu'on peut préfixer au texte avant de l'indexer. C'est la stratégie la plus qualitative quand elle s'applique : elle suit le sens du document. Sa limite est évidente : elle exige un document structuré. Sur de la prose continue, sans titres, elle n'a rien sur quoi s'appuyer.

Aucune stratégie n'est universellement meilleure : le bon choix dépend de la nature du corpus.

CorpusStratégie adaptéePourquoi
Markdown, HTML structuré, codePar sectionsSuit le découpage voulu par l'auteur
Prose continue, articlesPar phrasesRespecte l'unité de sens minimale
Texte hétérogène, sans structureTaille fixe + recouvrementRobuste, marche partout

La règle de décision est simple. Le document a une structure exploitable ? Découpez par sections. Sinon, c'est de la prose ? Découpez par phrases. Et dans le doute, ou face à un corpus mélangé, la taille fixe avec recouvrement est le repli fiable. Beaucoup de pipelines combinent les trois selon le type de document rencontré.

SymptômeCause probableSolution
La recherche remonte des pavésChunks trop grosRéduire la taille, viser une idée par chunk
Chunks incompréhensibles isolésChunks trop petits, contexte perduAugmenter la taille, préfixer le titre
Idées coupées aux frontièresPas de recouvrementAjouter 10–20 % de recouvrement
ValueError sur le recouvrementRecouvrement ≥ tailleLe rendre strictement inférieur
Chunking par sections videDocument sans titresBasculer sur phrases ou taille fixe
  • Le chunking décide de ce que la recherche pourra retrouver — il ne se rattrape pas en aval.
  • La taille fixe est robuste mais coupe à l'aveugle ; le recouvrement corrige les coupures.
  • Le découpage par phrases ne casse jamais une phrase — idéal pour la prose.
  • Le découpage par sections suit la structure de l'auteur — le plus qualitatif quand il s'applique.
  • Le bon choix dépend du corpus : structuré, prose, ou hétérogène.
  • Préfixer le titre de section à un chunk enrichit son vecteur à peu de frais.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn