Warum schlechtes Chunking die Vektorsuche ruiniert, selbst mit guten Embeddings
Ein starrer 800-Zeichen-Splitter hat einen zentralen Satz einer Richtlinie halbiert und die Suche zerstört. Wie Überlappung und rekursives Chunking helfen und warum jede Stufe der Pipeline zählt.
Wenn die Vektorsuche Unsinn liefert, stehen zuerst die Datenbank und das Embedding-Modell unter Verdacht. Meist sind beide unschuldig. In einer Ingestion-Pipeline in TypeScript mit PDF-Parsing, Gemini-Embeddings und ChromaDB war der Übeltäter eine einzige Zeile Zeichenzähl-Logik im Chunker.
Anfangs sah alles gut aus. Der Code kompilierte, und die Vektoren lagen ordentlich im Store. Dann kam eine Anfrage zu den Regeln für die Studienanmeldung, gestellt gegen ein PDF mit der Hochschulordnung, als reiner Müll zurück.
Was der erste Chunker tat
Das ursprüngliche Denkmodell war peinlich simpel:
- Text aus dem PDF extrahieren.
- Alle 800 Zeichen schneiden.
- Für jedes Stück ein Embedding erzeugen.
- Die Vektoren in ChromaDB schreiben.
Statt an den Datenbankeinstellungen zu drehen, war es sinnvoller, die rohen Chunks auszugeben und sie einfach zu lesen. Ein zentraler Satz über die Exmatrikulation von Studierenden, die ihre Gebühren nicht bezahlt hatten, war mitten im Wort durchgeschnitten:
- Chunk 0 endete mit: "...the right to decline or d"
- Chunk 1 begann mit: "e-register students who have not completed fee payments."
Aus "de-register" wurde ein verirrtes "d" in dem einen Chunk und "e-register" im nächsten. Das zerrissene Wort war hässlich, aber der Verlust an Bedeutung war schlimmer. Chunk 0 enthielt die Handlung nicht mehr. In Chunk 1 fehlten das Subjekt und die Bedingung, die erklärt, warum die Handlung überhaupt passiert.
Die Vektordatenbank hat die relevante Passage also gar nicht übersehen. Die Bedeutung war zerstört, bevor das Embedding-Modell sie überhaupt zu sehen bekam.
Überlappung gibt eine zweite Chance
Die nächste Version bekam eine Überlappung von 100 Zeichen: Jeder Chunk nahm das Ende des vorherigen noch einmal mit, statt als isolierter Block von 800 Zeichen zu beginnen.
Die gängige Annahme lautet, dass Überlappung verhindert, dass ein Satz zerteilt wird. Das stimmt nicht. Eine feste Grenze kann immer noch an einer furchtbaren Stelle landen. Was Überlappung wirklich leistet: Text in der Nähe eines schlechten Schnitts bekommt eine weitere Gelegenheit, neben seinem Kontext aufzutauchen.
Man sollte es als Versicherung sehen, nicht als Vorbeugung: Die Überlappung verhindert den Schnitt nicht, sie gibt der Bedeutung nur eine zweite Chance, ihn zu überstehen.
Die Suche wurde sofort besser, aber das Design war im Kern immer noch falsch, denn es dachte in rohen Zeichen statt in Sprache. Es konnte nach wie vor ein Token oder ein Code-Snippet halbieren und so etwas wie function calculateT hinterlassen.
Also musste sich die Frage ändern, die der Chunker stellt:
- Alte Frage: Wo enden die nächsten 800 Zeichen?
- Neue Frage: Wo ist die natürlichste inhaltliche Grenze, um diesen Text zu schneiden?
Die Antwort war rekursives Chunking. Zuerst probiert man die größten strukturellen Trenner wie Abschnitte und Absätze und weicht nur dann auf kleinere wie Zeilen, Sätze und Wörter aus, wenn ein Stück noch über dem Limit liegt. Eine Regel, die man sich über den Code hängen sollte:
// The Golden Rule of Chunking:
// Preserve meaning at scale; cut aggressively only when forced to.
Jede Stufe erbt die vorherige
Rekursives Splitten ist nur ein Implementierungsdetail. Die größere Lehre ist, wie sich die Repräsentation durch ein KI-System fortpflanzt:
Doc ➔ Extraction ➔ Chunking ➔ Embedding ➔ Storage ➔ Retrieval ➔ LLM ➔ Answer
Jeder nachgelagerte Knoten ist durch das begrenzt, was ihm der vorgelagerte übergibt. Wenn die Ingestion den Kontext zerstört, holt ihn kein Prompt-Engineering und kein Index-Tuning zurück. Müll rein, semantischer Müll raus.
Ein Fix legt zwei weitere Probleme offen
Sobald der Chunker sich benahm, tauchten direkt dahinter zwei neue Probleme auf.
- Struktur: Absätze sind einfach, aber was ist mit einer Finanztabelle über drei Seiten oder einem mehrspaltigen Layout? Naiv gesplittet landen die Spaltenüberschriften getrennt von den Werten darunter, und die Daten bedeuten nichts mehr.
- Der Preis der Überlappung: Kontext ist nicht umsonst. Mehr Überlappung heißt mehr Chunks, also mehr Aufrufe der Embedding-API, mehr Speicher und langsamere Indexierung. Bei 10.000 Vektoren merkt man das nicht. Bei 1.000.000 ist es ein echter Engpass, technisch wie finanziell.
Wie sich das Denkmodell verändert hat
Früher hieß Chunking, Text in Stücke zu teilen, damit er in den Speicher passt. Heute heißt es zu entscheiden, welche Informationen zusammenbleiben müssen, damit nachgelagerte Modelle sie korrekt repräsentieren und wiederfinden können.
Den Code zu schreiben war der einfache Teil. Schwer war es, die versteckte Annahme zu finden, die alles kaputt gemacht hat, und genau diese Gewohnheit lohnt sich: bauen, kaputt machen, herausfinden warum und mit einem schärferen Bild vom System weitermachen.