Blogs / Chunking-Strategien für RAG: Benchmark an echten Dokumenten

Chunking-Strategien für RAG: Benchmark an echten Dokumenten

Veröffentlicht
18. September 2026
Autor
Faizan Nadeem
Tags
RAG AI Engineering Backend Development LLM
Ein ordentlich gestapelter Stoß bedruckter Papierblätter
Foto von Ron Dyar auf Unsplash

Die meisten RAG-Post-Mortems setzen am falschen Ende an. Jemand tauscht das Embedding-Modell aus, fügt einen Reranker hinzu, feilt am Prompt — und die Retrieval-Qualität bewegt sich kaum, weil der eigentliche Schaden viel früher entstanden ist, beim Chunking, bevor irgendeine dieser Komponenten den Text überhaupt zu Gesicht bekommen hat. Chunking ist meist das Erste, was implementiert wird, und das Letzte, was man sich noch einmal ansieht — dabei ist es genau andersherum: Es ist die eine Entscheidung, die festlegt, was am Ende überhaupt abrufbar ist. Trennt eine Chunk-Grenze eine Tabelle von ihrer Beschriftung, oder eine Definition vom Satz, der sie verwendet, kann kein Embedding-Modell und kein Reranker das zur Abfragezeit wieder zusammensetzen.

Der Grund, warum Chunking übersprungen wird, ist, dass die meisten Tutorials es als gelösten, langweiligen Schritt behandeln — alle 500 Token splitten, 50 Token Overlap und weiter zu den interessanten Teilen. Aber Chunk-Größe, Overlap und Grenzlogik haben messbare Auswirkungen sowohl auf Recall (wurde der Chunk mit der Antwort überhaupt abgerufen?) als auch auf Precision (wie viel von dem Abgerufenen war tatsächlich relevant?). Dieser Beitrag testet vier Chunking-Strategien — fixed-size, recursive, semantic und hierarchical — am selben kleinen Korpus, mit einer Methodik, die sich auf die eigenen Dokumente übertragen lässt, plus ausgearbeiteten Beispielzahlen aus diesem Testset, um zu zeigen, wie sich die Unterschiede in der Praxis auswirken.

Das lernst du:

  • Den tatsächlichen mechanischen Unterschied zwischen fixed-size, recursive, semantic und hierarchical Chunking, mit lauffähigem Python für jede Strategie
  • Wie Chunk-Größe und Overlap Recall gegen Precision abwägen, mit ausgearbeiteten Zahlen aus einem kleinen Testkorpus
  • Warum semantic Chunking bei manchen Dokumenten spürbar hilft und bei anderen fast nichts bringt
  • Wie hierarchical Chunking Kontext bewahrt, den flaches Chunking wegwirft, und wann sich der zusätzliche Aufwand lohnt
  • Eine reproduzierbare Methodik, um Chunker am eigenen Korpus und den eigenen Queries zu benchmarken
  • Wie man für eine neue RAG-Pipeline eine sinnvolle Standard-Chunking-Strategie wählt, ohne die erste Version zu übertechnisieren

Inhaltsverzeichnis

  1. Warum Chunking die Retrieval-Entscheidung ist, die alle überspringen
  2. Vier Chunking-Strategien, definiert
  3. Einen kleinen, ehrlichen Benchmark aufbauen
  4. Ergebnisse: Recall und Precision nach Strategie
  5. Chunk-Größe und Overlap: Was wirklich etwas bewegt
  6. Wann sich semantic Chunking lohnt
  7. Hierarchical Chunking für lange, strukturierte Dokumente
  8. Einen Standard für den eigenen Korpus wählen
  9. Wie es von hier aus weitergeht

Warum Chunking die Retrieval-Entscheidung ist, die alle überspringen

Die Retrieval-Qualität wird durch Chunking begrenzt, bevor sie durch irgendetwas anderes begrenzt wird. Ein Embedding-Modell kann nur codieren, was innerhalb der Grenzen eines Chunks liegt, und ein Reranker kann nur Chunks neu bewerten, die es überhaupt in die Kandidatenmenge geschafft haben. Zerschneidet eine Chunk-Grenze einen Absatz so, dass der Satz mit der Antwort allein steht, ohne umgebenden Kontext, repräsentiert das Embedding dieses Chunks ein Fragment, nicht die Idee — und es wird bei einer Query, die eigentlich nach der gesamten Idee fragt, niedriger ranken.

Es gibt zwei Fehlerarten, die unter „schlechtes Retrieval" oft vermischt werden, und sie brauchen unterschiedliche Lösungen:

  • Recall-Fehler: Der Chunk mit der Antwort wurde gar nicht erst abgerufen, weil er nicht als zusammenhängende Einheit herausgetrennt wurde, oder er steckt in einem so großen Chunk, dass das Embedding durch unzusammenhängenden Inhalt verwässert wird.
  • Precision-Fehler: Der Chunk wurde abgerufen, ist aber verrauscht — aufgefüllt mit unzusammenhängendem Boilerplate aus benachbarten Abschnitten, sodass das LLM zusätzliche Arbeit leisten muss, um Signal von Füllmaterial zu trennen, was das Halluzinationsrisiko erhöht, selbst wenn die richtige Information technisch vorhanden ist.

Fixed-size Chunking ist anfällig für beides. Rein nach Token-Anzahl zu splitten, ohne Rücksicht auf die Dokumentstruktur, führt auf einem ausreichend langen Korpus manchmal dazu, dass eine Tabellenzeile von ihrer Kopfzeile getrennt wird, manchmal dazu, dass ein Codeblock mitten in einer Funktion aufgetrennt wird, und manchmal landet eine Grenze mitten in einem Satz. Jeder dieser Fälle ist ein Chunk, der schwerer korrekt abzurufen und schwerer zu verarbeiten ist, sobald er abgerufen wurde. Die folgenden Strategien sind, in dieser Reihenfolge, zunehmend bewusster gegenüber Dokumentstruktur und semantischem Inhalt — und zunehmend teurer zu berechnen.

Vier Chunking-Strategien, definiert

Fixed-Size Chunking

Den Rohtext in Fenster von N Token (oder Zeichen) splitten, meist mit etwas Overlap, damit eine Idee, die eine Grenze überspannt, mindestens in einem Chunk vollständig erscheint.

def fixed_size_chunks(text: str, chunk_size: int = 400, overlap: int = 50) -> list[str]:
    words = text.split()
    chunks = []
    start = 0
    while start < len(words):
        end = start + chunk_size
        chunks.append(" ".join(words[start:end]))
        start += chunk_size - overlap
    return chunks

Das ist schnell, deterministisch und völlig blind gegenüber Struktur. Es ist die richtige Baseline, an der man alles andere misst — nicht die Strategie, die man ohne Vergleich ausliefert.

Recursive Chunking

Recursive Splitter (der Default in LangChains RecursiveCharacterTextSplitter) probieren eine Liste von Separatoren in Prioritätsreihenfolge durch — Absatzumbrüche, dann Satzgrenzen, dann Wortgrenzen — und fallen erst auf einen härteren Split zurück, wenn ein Stück nach dem sanfteren Versuch immer noch zu groß ist.

def recursive_chunks(text: str, chunk_size: int = 400, separators=None) -> list[str]:
    separators = separators or ["\n\n", "\n", ". ", " "]
    if len(text.split()) <= chunk_size or not separators:
        return [text]
    sep, rest = separators[0], separators[1:]
    parts = text.split(sep)
    chunks, buffer = [], ""
    for part in parts:
        candidate = (buffer + sep + part) if buffer else part
        if len(candidate.split()) <= chunk_size:
            buffer = candidate
        else:
            if buffer:
                chunks.append(buffer)
            buffer = part if len(part.split()) <= chunk_size else None
            if buffer is None:
                chunks.extend(recursive_chunks(part, chunk_size, rest))
                buffer = ""
    if buffer:
        chunks.append(buffer)
    return chunks

Das respektiert Absatz- und Satzgrenzen, wo immer möglich, was allein schon die meisten Mid-Sentence- und Mid-Table-Splits beseitigt, die fixed-size Chunking plagen — bei praktisch keinen zusätzlichen Rechenkosten.

Semantic Chunking

Statt an strukturellen Zeichen zu splitten, splittet semantic Chunking nach Bedeutung. Jeden Satz embedden, durch das Dokument gehen und die Embedding-Ähnlichkeit zwischen aufeinanderfolgenden Sätzen vergleichen, und eine neue Chunk-Grenze setzen, wo immer die Ähnlichkeit unter einen Schwellenwert fällt — das Signal, dass sich das Thema gerade geändert hat.

import numpy as np

def semantic_chunks(sentences: list[str], embed_fn, threshold: float = 0.75) -> list[list[str]]:
    embeddings = [embed_fn(s) for s in sentences]
    chunks, current = [], [sentences[0]]
    for i in range(1, len(sentences)):
        sim = cosine_sim(embeddings[i - 1], embeddings[i])
        if sim < threshold:
            chunks.append(current)
            current = []
        current.append(sentences[i])
    if current:
        chunks.append(current)
    return chunks

def cosine_sim(a: np.ndarray, b: np.ndarray) -> float:
    return float(np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)))

Semantic Chunking erzeugt Chunks variabler Länge, die tatsächlichen Themengrenzen folgen statt willkürlicher Token-Zahlen — auf Kosten eines Embedding-Aufrufs pro Satz während der Ingestion.

Hierarchical Chunking

Hierarchical (oder „parent-child") Chunking hält zwei Granularitäten gleichzeitig vor: kleine Child-Chunks für präzises Embedding und Retrieval, verknüpft mit einem größeren Parent-Chunk (ein ganzer Abschnitt, oder ein ganzes Dokument), der an die Generierung weitergegeben wird, sobald sein Child abgerufen wurde. Dafür werden strukturelle Signale genutzt — Markdown-Überschriften, Abschnittsnummerierung — um den Baum aufzubauen.

def hierarchical_chunks(sections: dict[str, str], child_size: int = 200) -> list[dict]:
    records = []
    for heading, body in sections.items():
        parent_id = heading
        for child in recursive_chunks(body, chunk_size=child_size):
            records.append({"parent_id": parent_id, "parent_text": body, "child_text": child})
    return records

Zur Abfragezeit wird über die kleinen child_text-Embeddings gesucht, für Präzision, und dann der zugehörige parent_text an den Generierungsschritt übergeben, sodass das Modell einen vollständigen Kontext sieht statt ein isoliertes Fragment.

Einen kleinen, ehrlichen Benchmark aufbauen

Um die Trade-offs konkret zu machen, habe ich alle vier Strategien an einem kleinen, eigens dafür gebauten Testset laufen lassen — zwanzig Markdown-Dokumente aus internen Engineering-Runbooks und API-Referenzseiten, im Schnitt rund 1.800 Wörter lang, mit einer Mischung aus Prosa, Tabellen und Codeblöcken. Das ist bewusst ein Spielzeug-Korpus, keine groß angelegte, veröffentlichte Studie: Die Zahlen unten sind illustrative Beispielrechnungen, die die Methodik an einem Korpus demonstrieren sollen, der klein genug ist, um ihn von Hand zu prüfen — keine Aussage über Chunking-Performance im Allgemeinen. Der Punkt dieses Abschnitts ist, dass sich diese exakte Pipeline an eigenen Dokumenten an einem Nachmittag nachbauen lässt.

Das Eval-Set aufbauen. Für jedes Dokument habe ich zwei bis drei natürlichsprachliche Fragen formuliert, deren Antwort in einer bestimmten Passage enthalten war, und dann festgehalten, welcher Satz (oder welche Sätze) im Quelldokument die Frage tatsächlich beantwortet — die „Gold"-Textstelle. Das ergab 47 Query/Gold-Span-Paare über die 20 Dokumente hinweg.

Indexierung. Jede Strategie hat alle 20 Dokumente gechunkt, und jeder resultierende Chunk wurde mit demselben Embedding-Modell embedded (das Modell konstant zu halten isoliert die Variable Chunking — ein anderes Embedding-Modell pro Strategie zu mischen würde die Ergebnisse verfälschen).

Metrik-Definitionen:

  • Recall@5: Für jede Query die Top-5-Chunks nach Cosine-Similarity abrufen; als Treffer zählen, wenn irgendein abgerufener Chunk den Gold-Span enthält (oder eine strikte Ober-/Teilmenge, die ihn überlappt).
  • Precision@5: Von den 5 abgerufenen Chunks — welcher Anteil enthält Inhalt, der tatsächlich relevant für die Query ist, beurteilt anhand des Gold-Spans und seines unmittelbaren Kontexts.
def recall_at_k(retrieved: list[str], gold_span: str, k: int = 5) -> int:
    return int(any(gold_span in chunk or chunk in gold_span for chunk in retrieved[:k]))

def precision_at_k(retrieved: list[str], relevant_ids: set[str], k: int = 5) -> float:
    top_k = retrieved[:k]
    hits = sum(1 for c in top_k if c in relevant_ids)
    return hits / k

Das ist dasselbe Grundgerüst, das auch für die vollständige Pipeline-Evaluation genutzt wird, nur ohne den Generierungsschritt, damit man ausschließlich die Retrieval-Qualität isoliert.

Ergebnisse: Recall und Precision nach Strategie

Ausgearbeitete Zahlen aus dem oben beschriebenen Testset mit 47 Queries und 20 Dokumenten, jeweils bei Chunk-Größen, die pro Strategie auf ihren natürlichen Default eingestellt sind (400 Token, ~50 Token Overlap für fixed und recursive; ein Ähnlichkeits-Schwellenwert von 0,75 für semantic; 200-Token-Children für hierarchical):

StrategieRecall@5Precision@5Ø Chunks/Dok.Ingestion-Kosten
Fixed-size68 %0,525,1Am niedrigsten
Recursive79 %0,615,4Niedrig
Semantic85 %0,716,8Hoch (1 Embed-Aufruf/Satz)
Hierarchical87 %0,749,2 ChildrenMittel-hoch

Zwei Muster fielen bei diesem Testset auf, und beide entsprachen dem, was der Mechanismus vorhersagt, statt im Nachhinein überraschend zu sein. Erstens war der Sprung von fixed-size zu recursive größer als der Sprung von recursive zu semantic — das Respektieren von Absatz- und Satzgrenzen fing die meisten leichten Gewinne ab, und die Zusatzkosten von semantic Chunking kauften eine kleinere, zweitrangige Verbesserung obendrauf. Zweitens kam der Recall-Vorteil von hierarchical Chunking fast ausschließlich von Queries, deren Gold-Span Kontext außerhalb des unmittelbar abgerufenen Satzes benötigte — etwa wenn die Bedeutung einer Tabelle vom einleitenden Absatz abhing —, wo der Parent-Chunk Kontext lieferte, den das Child allein nicht bieten konnte.

Precision bewegte sich in jedem Fall hier zusammen mit Recall, was im Allgemeinen nicht garantiert ist — es ist eine Eigenschaft dieses Korpus, dessen Abschnitte vernünftig eigenständige Themen haben. Ein Korpus mit dichterer Querverweisung zwischen Abschnitten könnte plausibel zeigen, dass Precision und Recall auseinanderlaufen, auf eine Art, die dieses kleine Testset nicht zutage gefördert hat.

Chunk-Größe und Overlap: Was wirklich etwas bewegt

Recursive Chunking konstant gehalten und die Chunk-Größe über dieselben 47 Queries hinweg variiert:

Chunk-Größe (Token)OverlapRecall@5Precision@5
150071 %0,58
4005079 %0,61
8005081 %0,49
80015083 %0,51

Das Muster hier ist der übliche Chunk-Größen-Trade-off, nur konkret gemacht: Kleinere Chunks treiben die Precision nach oben (weniger irrelevanter Inhalt pro Chunk), begrenzen aber den Recall bei Queries, deren Antwort mehr als einen Chunk umfasst; größere Chunks gewinnen etwas Recall zurück, indem sie mehr Kontext zusammenhalten, aber die Precision sinkt, weil jeder abgerufene Chunk mehr unzusammenhängenden Umgebungstext mitschleppt. Overlap ist eine günstige Teillösung speziell für das Grenzproblem — er macht es weniger wahrscheinlich, dass eine Idee, die über zwei Chunks verteilt ist, in beiden vollständig verloren geht, auf Kosten von redundantem Speicherplatz und, bei hohen Overlap-Anteilen, nahezu identischen Chunks, die um dieselben Retrieval-Plätze konkurrieren.

Der praktische Schluss aus diesem Sweep: Es gibt selten eine universell „richtige" Chunk-Größe — es gibt eine Größe, die dazu passt, wie eigenständig die Ideen der eigenen Quelldokumente typischerweise sind. Kurze, listenartige Referenzdokumente wollen kleine Chunks; narrative oder erklärende Prosa mit abhängigem Kontext will größere Chunks oder einen hierarchical Fallback.

Wann sich semantic Chunking lohnt

Der Gewinn von semantic Chunking gegenüber recursive Chunking war bei diesem Testset nicht gleichmäßig über die Dokumente verteilt — er konzentrierte sich fast vollständig auf prosalastige Dokumente mit unregelmäßigen Absatzlängen (ein Design-Dokument mit einem Drei-Satz-Absatz gefolgt von einem Vierzehn-Satz-Absatz), wo die absatzbasierten Splits von recursive Chunking entweder einen langen Absatz ungünstig zerschnitten oder mehrere kurze, thematisch verschiedene Absätze in einem Chunk bündelten. Bei den API-Referenzseiten, die von ihren Autoren bereits in kurze, einthemige Absätze gegliedert waren, landeten die Grenzen von semantic Chunking fast an denselben Stellen wie die von recursive Chunking — die zusätzlichen Kosten pro Satz-Embedding brachten fast nichts, weil das Dokument bereits strukturell mit seinen eigenen semantischen Grenzen übereinstimmte.

Das ist die eigentliche Entscheidungsregel: Semantic Chunking lohnt sich proportional dazu, wie schlecht strukturiert die eigenen Quelldokumente relativ zu ihren tatsächlichen Themengrenzen sind. Ist der Korpus bereits gut organisiertes Markdown oder gut formatierte Dokumentation, fängt recursive Chunking das meiste vom selben Signal ein — für einen Bruchteil der Ingestion-Kosten. Ist der Korpus gescraptes HTML, OCR-Output oder frei formulierter, von Nutzern eingereichter Text mit inkonsistenter Formatierung, wird die Unabhängigkeit von semantic Chunking von der Dokumentstruktur zu einem echten Vorteil statt zu einem überflüssigen.

Hierarchical Chunking für lange, strukturierte Dokumente

Der Vorteil von hierarchical Chunking liegt eigentlich nicht so sehr an der Retrieval-Präzision beim Embedding-Schritt — Child-Chunks in diesem Benchmark verwendeten dasselbe zugrunde liegende recursive Splitting wie die reine recursive-Strategie. Der Vorteil zeigt sich weiter unten im Prozess, bei der Generierung: Weil jeder abgerufene Child einen Verweis auf seinen vollständigen Parent-Abschnitt trägt, ist der Kontext, der dem LLM nach dem Retrieval übergeben wird, ein zusammenhängender Abschnitt statt ein isoliertes 200-Token-Fragment. Das ist genau die Art von Fix, die hilft, wenn Retrieval immer wieder technisch passende, aber inhaltlich unvollständige Chunks zurückgibt — der Embedding-Schritt war nie das Problem; das Problem war die Chunk-Grenze, die umgebenden Kontext wegwarf.

Der Trade-off ist Speicherplatz und Pipeline-Komplexität: Man pflegt jetzt zwei Chunk-Granularitäten, ein Parent-Child-Mapping, und meist ein zweistufiges Retrieval, bei dem der Child-Index durchsucht wird, aber der Parent-Text das ist, was tatsächlich zurückgegeben wird. Für Dokumente unter ein paar Tausend Token ist das oft unnötig — dann einfach das ganze Ding als einen Chunk embedden. Hierarchical Chunking rechtfertigt seine Komplexität bei langen, strukturierten Dokumenten (Spezifikationen, Runbooks, Verträge, mehrteilige Reports), bei denen die Antwort einer Query plausibel von Kontext abhängt, der mehrere Absätze vom Satz entfernt liegt, der sie am direktesten beantwortet.

Einen Standard für den eigenen Korpus wählen

Angesichts der obigen Ergebnisse eine sinnvolle Entscheidungsreihenfolge für eine neue Pipeline:

  1. Mit recursive Chunking bei einer Größe starten, die zur natürlichen Ideenlänge der eigenen Dokumente passt. Für prosalastigen Inhalt sind 300–500 Token mit 10–15 % Overlap ein solider Ausgangspunkt; erst messen, bevor man annimmt, weiter nachjustieren zu müssen.
  2. Semantic Chunking nur hinzufügen, wenn der Korpus strukturell unordentlich ist — inkonsistente Formatierung, gescrapter Inhalt, oder Dokumente, bei denen Absatzgrenzen nicht zuverlässig den Themengrenzen folgen. Den Recall-Gewinn am eigenen Eval-Set messen, bevor man sich auf die zusätzlichen Ingestion-Kosten festlegt — er könnte kleiner ausfallen als erwartet, wie hier bei den bereits sauberen API-Dokumenten.
  3. Hierarchical Chunking gezielt für lange, strukturierte Dokumente hinzufügen, bei denen Kontext über den unmittelbaren Chunk hinaus wichtig ist — nicht als Standard für jeden Korpus. Die Komplexitätskosten sind real und werden bei Dokumenten, die kurz genug sind, um sie einfach im Ganzen abzurufen, nicht erstattet.
  4. Immer den Recall@k-/Precision@k-Vergleich an den eigenen Dokumenten und Queries laufen lassen, bevor man irgendeinem allgemeinen Benchmark vertraut, auch diesem hier — das 20-Dokumente-Testset demonstriert die Methodik und die Richtung der Effekte, kein universelles Ranking für jeden Korpus.

Keine dieser Strategien repariert eine Chunking-Entscheidung, die auf einer anderen Ebene schlecht getroffen wurde — Chunk-Größe und Grenzlogik helfen nichts, wenn die Metadaten, die man braucht, um Ergebnisse korrekt zu filtern (Tenant, Dokumentversion, Zugriffsebene), nie an den Chunk angehängt wurden — das ist eine eigene, separate Fehlerklasse.

Wie es von hier aus weitergeht

Chunking setzt die Obergrenze dessen, was Retrieval finden kann; der Rest einer RAG-Pipeline entscheidet, wie gut sie nutzt, was unter dieser Obergrenze liegt. Wenn dieser Beitrag das Problem „haben wir das Dokument sinnvoll aufgeteilt" gelöst hat, decken diese ab, was mit den Chunks zu tun ist, sobald sie existieren:

Fazit

Chunking verdient so viel Engineering-Aufmerksamkeit, wie Embedding-Modelle und Reranker bekommen, und bekommt normalerweise nur einen Bruchteil davon. Die vier Strategien hier stehen in keiner absoluten Rangfolge — fixed-size ist eine vernünftige Prototyp-Baseline, recursive ein starker allgemeiner Standard, semantic Chunking zahlt sich bei strukturell unordentlichen Korpora aus, und hierarchical Chunking rechtfertigt seine Komplexität bei langen Dokumenten, bei denen Antworten von umgebendem Kontext abhängen. Die Zahl, die die eigene Meinung darüber ändern sollte, welche Strategie zu verwenden ist, ist kein Benchmark, den man in einem Blogbeitrag liest — auch nicht in diesem —, sondern Recall@k und Precision@k, gemessen an den eigenen Dokumenten, mit den eigenen Queries, denn das ist das einzige Testset, dem sich das eigene Produktivsystem tatsächlich stellen muss.

Faizan Nadeem
Autor
Faizan Nadeem

AI/GenAI engineer specialising in agentic RAG, LangGraph, and production LLM systems with Python and FastAPI. 2 years building AI agents, RAG pipelines, and the backends behind them.

Weitere Artikel