Wer eine RAG-Pipeline auf gemischten Quellen aufbaut — Audio-Mitschnitte, PDFs, Word-Dokumente, Notizen — kommt schnell an die Stelle, wo das Chunking versagt. Nicht weil die Chunks zu gross oder zu klein sind, sondern weil die Quelle voller Layout-Reste ist, die das Embedding verwirren.
Die Lösung ist langweilig: alles auf Markdown bringen, dann erst chunken.
Was schiefläuft, wenn man's nicht tut
Ein PDF-Extract liefert dir Spaltenumbrüche, Seitenzahlen und „Page 4 of 12"-Zeilen mitten im Text. Ein Word-Dokument bringt invisible Formatierungs-Tags. Ein Audio-Transkript hat Wiederholungen, Füllwörter, Sprecherwechsel ohne klare Struktur.
Wer das direkt embeddet, embedded die Layout-Artefakte mit. Das Resultat: Chunks, die syntaktisch ähnlich sind („Page 4 of 12"-Wiederholungen) ranken in der Vektorsuche hoch, semantisch relevante Inhalte verlieren.
Warum Markdown
Markdown ist genau strukturiert genug, um semantische Information zu erhalten — Headings, Listen, Code-Blöcke, Hervorhebungen — und genau plain genug, um nichts mitzuschleppen, was beim Embedden stört.
Konkret heisst das:
- Kein Layout, keine Spalten, keine Seitenumbrüche
- Hierarchie über
#-Levels statt Schriftgrössen - Listen als Listen, nicht als Pseudo-Tabellen
- Code als Code-Block, nicht als Bild
Das macht das anschliessende Chunking deterministisch. Du kannst entlang von Headings splitten, oder entlang von Absätzen, oder mit fixer Token-Länge — alles funktioniert, weil das Eingangsformat sauber ist.
Pipeline-Schritt
Der Konvertierungsschritt ist meist der unspannendste der ganzen Pipeline und gleichzeitig der wirkungsvollste:
def to_markdown(source: Path) -> str:
match source.suffix.lower():
case ".pdf":
return pdf_to_markdown(source)
case ".docx":
return docx_to_markdown(source)
case ".wav" | ".mp3" | ".m4a":
transcript = transcribe(source)
return transcript_to_markdown(transcript)
case ".md" | ".txt":
return source.read_text()
case _:
raise UnsupportedFormat(source.suffix)
Audio-Quellen bekommen einen zusätzlichen Schritt: das Transkript wird strukturiert (Sprecher, Themen, Zeitmarken), bevor es als Markdown gespeichert wird. Das ist kein Luxus — es ist der Unterschied zwischen einer durchsuchbaren Sitzung und einer Word-Wolke mit Zeitstempeln.
Was du gewinnst
- Deterministisches Chunking — gleicher Input, gleiche Chunks, immer
- Bessere Retrieval-Qualität — weil Embeddings auf Inhalt zielen, nicht auf Layout-Rauschen
- Debuggbare Pipeline — du kannst dir das Markdown anschauen und siehst, was das LLM sieht
- Reproduzierbarkeit — wenn du das Embedding-Modell wechselst, läuft der Re-Index auf den gleichen Chunks
Wer eine RAG-Pipeline plant: rechne Zeit für die Normalisierung ein. Sie ist nicht der Hot-Path, aber sie entscheidet, ob die ganze Pipeline brauchbare Antworten liefert.