Phase 05: NLP: Foundations to Advanced

Stratégies de déchiquetage pour les RAG

La configuration de l'emballage influence la qualité de la récupération autant que le choix du modèle d'emballage (Vectara NAACL 2025).

Type: Build

Languages: Python

Prerequisites: Phase 5 · 14 (Information Retrieval), Phase 5 · 22 (Embedding Models)

Time: ~60 minutes

Le problème

Vous mettez un contrat de 50 pages dans un système RAG. L'utilisateur demande: "Quelle est la clause de résiliation?" Le récupérateur renvoie la page d'accueil. Pourquoi? Parce que le modèle a été formé sur 512 jetons et la clause de résiliation est de 20 pages, divisée sur une page, sans mots clés locaux liant à la requête.

Le problème n'est pas "acheter un meilleur modèle d'intégration". Le problème est de se déchiffrer.

Les critères de référence de février 2026 montrent des résultats surprenants:

  • L'étude de Vectara de 2026: le déchiquetage récursif de 512 jetons a dépassé le déchiquetage sémantique avec une précision de 69% → 54%.
  • SPLADE + Mistral-8B sur les questions naturelles: la chevauchement a fourni un bénéfice mesurable zéro.
  • Cliff de contexte: la qualité de la réponse diminue fortement autour de 2500 jetons de contexte.

La réponse " évidente " (chunking sémantique, 20% de chevauchement, 1000 jetons) est souvent fausse.

Le concept

!Six chunking strategies visualized on one passage

Fixed chunking.Divisez tous les caractères N ou les jetons, la ligne de base la plus simple, rompt le milieu de la phrase, bonne compression, mauvaise cohérence.

Recursive.La chaîne de langage RecursiveCharacterTextSplitter- Essayez de vous séparer .\n\nD'abord, puis\nAlors ....Il est en train de tomber, le 2026 est en défaut.

Semantic.Embed chaque phrase. Compute la similitude cosine entre les phrases adjacentes. Divisez où la similitude tombe en dessous d'un seuil. Préserve la cohérence du sujet. Plus lent; produit parfois de minuscules fragments de 40 jetons qui nuisent à la récupération.

Sentence.Partagez-les en limites de phrases. Une phrase par morceau ou une fenêtre de N phrases.

Parent-document.Remplissez les petits morceaux d'enfants pour les récupérer et la plus grande partie parent pour le contexte. Remplissez par enfant; retournez parent. Dégrade gracieusement: les mauvais morceaux d'enfants retournent toujours des parents raisonnables.

Late chunking (2024).Embed l'ensemble du document au niveau des jetons d'abord, puis pool jetons intégrations dans des emblèmes de pièces. préserve le contexte de pièces croisées. Fonctionne avec les emblèmes de long-context (BGE-M3, Jina v3).

Contextual retrieval (Anthropic, 2024).Préparez chaque pièce avec un résumé de sa position dans le document généré par le LLM ("Cette pièce est la section 3.2 des clauses de résiliation...").

La règle qui vaille tous les défauts

Correspondre la taille de la pièce au type de requête:

Query typeChunk size
Factoid ("what is the CEO's name?")256-512 tokens
Analytical / multi-hop512-1024 tokens
Whole-section comprehension1024-2048 tokens

Le résultat de la recherche devrait être suffisamment grand pour contenir la réponse plus le contexte local, suffisamment petit pour que le retour de K du retriever se concentre sur la réponse plutôt que sur le bruit de contexte.

Faites-le

Étape 1: déchiquetage fixe et récursif

pythondef chunk_fixed(text, size=512, overlap=0):
    step = size - overlap
    return [text[i:i + size] for i in range(0, len(text), step)]


def chunk_recursive(text, size=512, seps=("\n\n", "\n", ". ", " ")):
    if len(text) <= size:
        return [text]
    for sep in seps:
        if sep not in text:
            continue
        parts = text.split(sep)
        chunks = []
        buf = ""
        for p in parts:
            if len(p) > size:
                if buf:
                    chunks.append(buf)
                    buf = ""
                chunks.extend(chunk_recursive(p, size=size, seps=seps[1:] or (" ",)))
                continue
            candidate = buf + sep + p if buf else p
            if len(candidate) <= size:
                buf = candidate
            else:
                if buf:
                    chunks.append(buf)
                buf = p
        if buf:
            chunks.append(buf)
        return [c for c in chunks if c.strip()]
    return chunk_fixed(text, size)

Étape 2: décomposition sémantique

pythondef chunk_semantic(text, encoder, threshold=0.6, min_chars=200, max_chars=2048):
    sentences = split_sentences(text)
    if not sentences:
        return []
    embs = encoder.encode(sentences, normalize_embeddings=True)
    chunks = [[sentences[0]]]
    for i in range(1, len(sentences)):
        sim = float(embs[i] @ embs[i - 1])
        current_len = sum(len(s) for s in chunks[-1])
        if sim < threshold and current_len >= min_chars:
            chunks.append([sentences[i]])
        else:
            chunks[-1].append(sentences[i])

    result = []
    for group in chunks:
        text_group = " ".join(group)
        if len(text_group) > max_chars:
            result.extend(chunk_recursive(text_group, size=max_chars))
        else:
            result.append(text_group)
    return result

La musiquethresholdtrop haut → fragments trop bas → une pièce géante

Étape 3: document parental

pythondef chunk_parent_child(text, parent_size=2048, child_size=256):
    parents = chunk_recursive(text, size=parent_size)
    mapping = []
    for p_idx, parent in enumerate(parents):
        children = chunk_recursive(parent, size=child_size)
        for child in children:
            mapping.append({"child": child, "parent_idx": p_idx, "parent": parent})
    return mapping


def retrieve_parent(child_query, mapping, encoder, top_k=3):
    child_embs = encoder.encode([m["child"] for m in mapping], normalize_embeddings=True)
    q_emb = encoder.encode([child_query], normalize_embeddings=True)[0]
    scores = child_embs @ q_emb
    top = np.argsort(-scores)[:top_k]
    seen, parents = set(), []
    for i in top:
        if mapping[i]["parent_idx"] not in seen:
            parents.append(mapping[i]["parent"])
            seen.add(mapping[i]["parent_idx"])
    return parents

Les parents dédupeurs: plusieurs enfants peuvent se rendre au même parent; tout retourner serait une perte de contexte.

Étape 4: récupération contextuelle (motifs anthropiques)

pythondef contextualize_chunks(document, chunks, llm):
    context_prompts = [
        f"""<document>{document}</document>
Here is the chunk to situate: <chunk>{c}</chunk>
Write 50-100 words placing this chunk in the document's context."""
        for c in chunks
    ]
    contexts = llm.batch(context_prompts)
    return [f"{ctx}\n\n{c}" for ctx, c in zip(contexts, chunks)]

L'indexation des éléments contextualisés. Au moment de la requête, la récupération bénéficie du signal supplémentaire environnant.

Étape 5: évaluer

pythondef recall_at_k(queries, corpus_chunks, encoder, k=5):
    chunk_embs = encoder.encode(corpus_chunks, normalize_embeddings=True)
    hits = 0
    for q_text, gold_idxs in queries:
        q_emb = encoder.encode([q_text], normalize_embeddings=True)[0]
        top = np.argsort(-(chunk_embs @ q_emb))[:k]
        if any(i in gold_idxs for i in top):
            hits += 1
    return hits / len(queries)

La meilleure stratégie pour votre corps peut ne pas correspondre à un article de blog.

Les pièges

  • Chunking evaluated only on factoid queries.Les requêtes multi-hop révèlent des gagnants très différents.
  • Semantic chunking without a minimum size.Produit 40 fragments de jetons qui nuisent à la récupération.min_tokens- Je suis désolé .
  • Overlap as cargo cult.Les études de 2026 révèlent que le chevauchement offre souvent un bénéfice nul et un coût double.
  • No min/max enforcement.Les morceaux de 5 jetons ou 5000 jetons sont tous les deux en panne.
  • Cross-doc chunking.Ne laissez jamais une pièce couvrir deux documents, toujours par pièce, puis fusionnez.

Utilisez-le

La pile de 2026:

SituationStrategy
First build, unknown corpusRecursive, 512 tokens, no overlap
Factoid QARecursive, 256-512 tokens
Analytical / multi-hopRecursive, 512-1024 tokens + parent-document
Heavy cross-reference (contracts, papers)Late chunking or contextual retrieval
Conversational / dialog corpusTurn-level chunks + speaker metadata
Short utterances (tweets, reviews)One document = one chunk

Commencez par le récursif 512. Mesurer le rappel@5 sur un ensemble d'évaluation de 50 requêtes.

La faire partir

  • Je ne sais pas .outputs/skill-chunker.md- Le numéro de la liste:
markdown---
name: chunker
description: Pick a chunking strategy, size, and overlap for a given corpus and query distribution.
version: 1.0.0
phase: 5
lesson: 23
tags: [nlp, rag, chunking]
---

Given a corpus (document types, avg length, domain) and query distribution (factoid / analytical / multi-hop), output:

1. Strategy. Recursive / sentence / semantic / parent-document / late / contextual. Reason.
2. Chunk size. Token count. Reason tied to query type.
3. Overlap. Default 0; justify if >0.
4. Min/max enforcement. `min_tokens`, `max_tokens` guards.
5. Evaluation plan. Recall@5 on 50-query stratified eval set (factoid, analytical, multi-hop).

Refuse any chunking strategy without min/max chunk size enforcement. Refuse overlap above 20% without an ablation showing it helps. Flag semantic chunking recommendations without a min-token floor.

Exercices

  1. Easy.Comparer le nombre de pièces et la qualité des limites.
  2. Medium.Construire un ensemble d'évaluation de 30 requêtes sur 5 documents. Mesurer le rappel@5 pour le document récursif, sémantique et parent. Lequel gagne?
  3. Hard.Mettre en œuvre la récupération contextuelle. Mesurer l'amélioration du MRR par rapport à la référence récursive. Rapporter le coût de l'index (appels LLM) par rapport à l'augmentation de la précision.

Les termes clés

TermWhat people sayWhat it actually means
ChunkA piece of a docSub-document unit that gets embedded, indexed, and retrieved.
OverlapSafety marginN tokens shared between adjacent chunks; often useless in 2026 benchmarks.
Semantic chunkingSmart chunkingSplit where adjacent-sentence embedding similarity drops.
Parent-documentTwo-level retrievalRetrieve small children, return larger parents.
Late chunkingChunk after embeddingEmbed full doc at token level, pool into chunk vectors.
Contextual retrievalAnthropic's trickLLM-generated summary prepended to each chunk before indexing.
Context cliff2500-token wallQuality drop observed around 2.5k context tokens in RAG (Jan 2026).

Pour en savoir plus

This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.

Browse the complete course catalog or open this lesson on GitHub.