Die häufigsten Fehler und Stolpersteine
beim Aufbau und Betrieb von KI-RAG-Systemen
Juchu, Dein internes KI-System läuft, Du hast Deine ersten Dokumente eingebettet, die Vektordatenbank läuft, das Sprachmodell antwortet. Alles scheint zu funktionieren, alle im Unternehmen fangen fröhlich an, damit zu arbeiten.
"Können wir auch noch das Team-Laufwerk mit den Präsentationen reinnehmen?" Ja klar! "Und können wir auch die Forecasts der letzten 10 Jahre reinnehmen?" Sicher!
Aber irgendwann passiert etwas Seltsames: Die Antworten werden irgendwie ungenau, die Suche findet offensichtliche Treffer nicht, und nach drei Monaten weißt Du nicht mehr, welche Dokumente mit welchem Modell eingebettet wurden. Oh. Was ist passiert, es find doch so gut an!?
Die meisten RAG-Systeme scheitern nicht an der Technologie, sondern an vermeintlich kleinen, unbekannten oder vernachlässigten Entscheidungen, die am Anfang harmlos wirken und sich erst Wochen oder Monate später als nervige Sackgasse oder digitalisiertes Leiden erweisen.
Der Klassiker: Alles in die Vektordatenbank statt Drei-Schichten-Architektur
Du fängst an, Dein erstes RAG-System aufzubauen. Der naheliegende erste Schritt: Dokumente in Abschnitte zerlegen, einbetten, in die Vektordatenbank schreiben. Das funktioniert für einen Prototyp mit 50 Dokumenten. Aber sobald Du auch nur filtern willst, z.B. alle Dokumente von einem bestimmten Kunden, alle aus dem Jahr 2025, nur interne Richtlinien, fehlen die strukturierten Felder.
Eine Vektordatenbank speichert Bedeutung (das ist jetzt sehr vereinfacht ausgedrückt, reicht aber für den Moment), nicht Struktur. Sie kann sagen "dieses Dokument ähnelt Deiner Frage", aber nicht "dieses Dokument gehört zum Kunden Müller und wurde am 3. März erstellt". Dafür gibt es SQL-Datenbanken. Und wer wissen will, welche Themen miteinander zusammenhängen, kann eine Graph-Datenbank (en: Graph Database) einsetzen.
(Für alle in der KI-Gemeinschaft: hier gibt es dazu das Ebook inkl. der Quellcodes + Dokumentation für ein solches KI-RAG-System.)
Die Architektur, die sich für robuste RAG-Systeme bis zu vielen Millionen Dokumenten, E-Mails und Support-Tickets bewährt hat:
- SQL-Datenbank für Struktur und Metadaten.
- Vektordatenbank für semantische Suche.
- Graph-Datenbank für Beziehungen zwischen Begriffen und Konzepten (en: Entities).
Drei Schichten, jede für ihren Zweck.
Immer wieder gerne: Falsches Einbettungsmodell oder falsche Einstellungen
Irgendwie müssen die Daten von der SQL-Datenbank in die Vektor-Datenbank, zum Beispiel mit einem Einbettungsmodell (en: Embedding Model). Du wählst also ein Einbettungsmodell aus, folgst einem Tutorial oder einem rolextragenden KI-Guru auf Linkedin. Was passiert? Ein englisches Modell für deutsche Texte kommt zum Einsatz. Das passiert häufiger als man denkt, weil viele Tutorials englischsprachige Modelle verwenden. Das Modell versteht "Datenschutz-Folgenabschätzung" nicht als juristischen Fachbegriff, sondern als lose Wortfolge. Die Suchergebnisse sind bestenfalls ungefähr.
Oder die Einstellungen passen nicht zusammen. Jedes Einbettungsmodell erzeugt Vektoren mit einer bestimmten Länge, zum Beispiel 384 Dimensionen bei kleineren Modellen oder 1536 bei größeren. Diese Länge ist wie eine Sprache: Wenn Du Dokumente mit einem Modell einbettest das 384 Dimensionen erzeugt, und Suchanfragen mit einem Modell das 1536 Dimensionen erzeugt, reden beide aneinander vorbei. Die Suche liefert irgendwas, aber eben nicht das, was Du eigentlich möchtest.
Auch immer gut: Dokumente mit Modell A einbetten, aber Suchanfragen mit Modell B stellen. Selbst wenn beide Modelle Vektoren gleicher Länge erzeugen, ordnen sie Begriffe anders an. Es ist wie zwei Landkarten die denselben Maßstab haben, aber verschiedene Projektionen verwenden, die Koordinaten stimmen nicht überein.
Pro-Tipp: Ich habe im Beitrag Embedding Modelle Benchmark ausführliche Tests und Beschreibungen für die Eignung unterschiedlicher Modelle gemacht, inklusive einem interaktiven Tool für die bessere Einschätzung.
----8<---*snip*----
Zerlegung ohne Rücksicht auf Bedeutung
Bevor Texte in die Vektordatenbank können, müssen sie in kleinere Stücke zerlegt werden (en: Chunking). Der Grund: Ein Einbettungsmodell kann nicht ein ganzes Dokument mit 80 Seiten auf einmal verarbeiten. Also wird der Text vorher in Abschnitte aufgeteilt, die das Modell sinnvoll erfassen kann.
Der naheliegende erste Ansatz: Alle 500 Zeichen ein Schnitt. So steht es im Tutorial, also machen wir das. Was passiert? Ein Satz wird mitten im Gedanken durchgeschnitten. Der nächste Abschnitt beginnt ohne jeden Zusammenhang. Das Einbettungsmodell bettet dann nicht einen Gedanken ein, sondern den Anfang von einem und das Ende von einem anderen.
"Ja, aber es gibt doch Überlappung!" Stimmt, ein Überlappungsbereich (en: Overlap) zwischen den Abschnitten hilft ein wenig. Aber er löst nicht das eigentliche Problem: Die Grenzen der Abschnitte müssen sich nach der Bedeutung richten, nicht nach einer Zeichenzahl.
Was sind natürliche Grenzen?
- Ein Absatz, der einen Gedanken abschließt.
- Eine Zwischenüberschrift, die ein neues Thema einleitet.
- Ein thematischer Wechsel innerhalb des Textes.
Wenn ein Abschnitt dadurch zu lang wird für das Kontextfenster des Modells (die maximale Textmenge, die es auf einmal verarbeiten kann), teile ihn an der nächsten Satzgrenze.
Pro-Tipp: Viele Zerlegungsbibliotheken bieten neben dem festen Zeichenschnitt auch einen "recursive" Modus an, der genau das tut: Erst an Überschriften trennen, dann an Absätzen, dann an Sätzen. Das ist in den meisten Fällen ein guter Ausgangspunkt.
Wer mit PDFs oder anderen Layoutdokumenten arbeitet, kann sich mal Docling anschauen. Docling erkennt die Struktur eines Dokuments (Kapitel, Unterkapitel, Absätze) und zerlegt auf dieser Basis mit einem hierarchischen Chunker. Für bereits vorhandenes Markdown oder HTML leistet der MarkdownHeaderTextSplitter von LangChain gute Dienste, weil er an Überschriftenebenen trennt statt an Zeichenzahlen. Die stärkste Pipeline für maximale Qualität: Dokument zuerst mit Docling oder PyMuPDF4LLM in eine strukturierte Zwischenform bringen, dann hierarchisch zerteilen, und erst ganz am Ende bei Bedarf nach Tokenlänge nachbegrenzen.
Gleich und doch nicht gleich: Falsche Ähnlichkeitsberechnung
Die Dokumente sind eingebettet, die Datenbank ist gefüllt, die erste Suchanfrage geht raus. Die Vektordatenbank vergleicht jetzt den Suchvektor mit allen gespeicherten Vektoren und liefert die ähnlichsten zurück. Aber was "ähnlich" bedeutet, hängt vom Berechnungsverfahren ab (en: Distance Metric). Und da gibt es mehr als eines:
- Kosinusähnlichkeit (en: Cosine Similarity): Vergleicht die Richtung zweier Vektoren, ignoriert die Länge.
- Euklidischer Abstand (en: Euclidean Distance): Misst die direkte Entfernung im Raum.
- Skalarprodukt (en: Dot Product): Berücksichtigt Richtung und Länge zusammen.
Jedes Verfahren bewertet "Ähnlichkeit" anders und liefert andere Rangfolgen. Die meisten Einsteiger behalten einfach die Voreinstellung der Datenbank, ohne zu prüfen, ob sie zum verwendeten Einbettungsmodell passt.
Das ist das eigentliche Problem: Unterschiedliche Einbettungsmodelle sind für unterschiedliche Verfahren optimiert. Wenn das Modell für Kosinusähnlichkeit trainiert wurde, Du aber den euklidischen Abstand verwendest, sind die Ergebnisse verzerrt. Dokumente, die eigentlich gut passen, landen weiter hinten. Dokumente, die nur entfernt verwandt sind, rutschen nach oben.
Die gute Nachricht: Das lässt sich in ein paar Minuten klären. In der Modellbeschreibung auf Hugging Face steht unter dem Abschnitt "Usage", welches Verfahren das jeweilige Modell empfiehlt. Bei Qdrant und ChromaDB lässt sich das Verfahren beim Erstellen der Sammlung (en: Collection) festlegen, und dann passt es.
Läuft doch, oder? Kein Rückmeldekreislauf zwischen Suche und Antwortqualität
Das System läuft seit ein paar Wochen, Ergebnisse kommen, alle sind zufrieden. Aber niemand prüft, ob die gefundenen Abschnitte tatsächlich zur Frage passen (en: Retrieval Quality). Und genau da beginnt ein schleichendes Problem.
Die Antwortqualität verändert sich nicht, weil das Modell schlechter wird. Sie verändert sich, weil sich die Daten um es herum verändern. Neue Dokumente kommen dazu, alte werden irrelevant, Begriffe verschieben sich in ihrer Bedeutung. Das System läuft technisch einwandfrei, es gibt keine Fehlermeldung. Erst wenn jemand eine offensichtlich falsche Antwort bemerkt, wird das Problem sichtbar.
Eine Möglichkeit, das früh zu erkennen: Ein kleines Set bekannter Fragen regelmäßig stellen und die Antworten mit menschlichem Urteil prüfen. Zehn Fragen pro Woche reichen, um Veränderungen zu bemerken, bevor sie zum Problem werden. Das lässt sich auch automatisieren: Fragen mit bekannter richtiger Antwort durchlaufen lassen und den Ähnlichkeitswert der Treffer über die Zeit beobachten (en: Regression Testing). Wenn der Wert sinkt, hat sich etwas verändert.
Welches Modell war das nochmal? Keine Versionierung der Einbettungen
Drei Monate nach dem Start erscheint ein besseres Einbettungsmodell (en: Embedding Model). Vielleicht eine neue Version des bisherigen, vielleicht ein ganz anderer Anbieter. Du aktualisierst, bettest die neuen Dokumente ein, alles sieht gut aus. Aber die alten Dokumente liegen noch mit den Vektoren des vorherigen Modells in der Datenbank. Die neuen Vektoren sind nicht kompatibel mit den alten. Suchergebnisse werden unvorhersagbar.
Das lässt sich vermeiden, indem in der SQL-Datenbank pro Dokument gespeichert wird, mit welchem Modell und welcher Version es eingebettet wurde. Drei Felder reichen:
- Modellname (z.B. "mxbai-embed-large")
- Modellversion oder Datum der Einbettung
- Dimensionszahl des Vektors
Beim Modellwechsel lassen sich dann gezielt alle Dokumente mit der alten Version identifizieren und neu einbetten. Bei lokalen Modellen über Ollama oder vLLM kostet das Rechenzeit, aber kein Geld. Bei Cloud-Anbietern wie OpenAI liegt die Neueinbettung von 100.000 Dokumenten mit je 500 Wörtern aktuell bei etwa 1 bis 2 Euro. Das Geld ist also nicht das Problem, aber der Vorgang dauert Stunden und die Vektordatenbank ist währenddessen in einem Mischzustand. Ohne die Versionsinformation in der SQL-Datenbank lässt sich nicht feststellen, welche Dokumente schon neu eingebettet sind und welche noch nicht.
Alles rein, wird schon passen: Suchergebnisse ungefiltert ins Sprachmodell
Ein Nutzer stellt eine Frage. Die Vektordatenbank liefert die zehn ähnlichsten Abschnitte. Alle zehn werden als Kontext an das Sprachmodell (en: Large Language Model) geschickt. Darunter sind drei, die gut passen, vier, die entfernt verwandt sind, und drei, die nichts mit der Frage zu tun haben. Was passiert? Das Sprachmodell halluziniert auf Basis der irrelevanten Abschnitte und webt deren Inhalte in die Antwort ein.
Was "ähnlich genug" ist, hängt vom Einbettungsmodell und vom Datenbestand ab. Es gibt keinen universellen Schwellenwert (en: Similarity Threshold). Bei einem Modell bedeutet ein Ähnlichkeitswert von 0,7 einen guten Treffer, bei einem anderen erst 0,9. Der passende Wert lässt sich experimentell bestimmen: Suchergebnisse stichprobenartig prüfen und beobachten, ab welchem Wert die Treffer brauchbar sind.
Ein zweites Problem: Wenn zu viele Abschnitte als Kontext geschickt werden, kann das Kontextfenster (en: Context Window) des Sprachmodells überschritten werden. Teile des Kontexts gehen dann stillschweigend verloren. Das Modell antwortet trotzdem, nur eben auf Basis unvollständiger Informationen.
Für alle, die wirklich mehr wissen und umsetzen möchten ...
Zur KI-Gemeinschaft »Ab hier wird es speziell: Stolpersteine für Systeme mit SQL, Vektor und Graph
Die bisherigen Stolpersteine betreffen jedes RAG-System, egal wie es aufgebaut ist. Die folgenden Punkte tauchen auf, wenn Du mit der Drei-Schichten-Architektur (en: Three-Tier Architecture) arbeitest: SQL-Datenbank für Struktur, Vektordatenbank für Bedeutung, Graph-Datenbank für Beziehungen. Hier kommen eigene Herausforderungen dazu, die mit dem Zusammenspiel der drei Systeme zu tun haben.
Einmal befüllt, nie wieder angefasst: Graph-Beziehungen werden nicht gepflegt
Beim ersten Aufsetzen des Systems investierst Du viel Zeit in die Graph-Datenbank (en: Graph Database): Entitäten extrahieren, Beziehungen anlegen, alles sieht sauber aus. Dann geht das System live. Neue Dokumente werden eingebettet und in SQL registriert, aber die Beziehungen im Graphen aktualisiert niemand. Nach ein paar Monaten liefern Abfragen wie "Welche Themen hängen mit Datenschutz zusammen?" veraltete oder unvollständige Ergebnisse.
Der Graph ist nur so gut wie seine Pflege. Jedes Mal wenn ein neues Dokument verarbeitet wird, lässt sich automatisch prüfen, welche Entitäten (en: Entities) es enthält und wie sie mit bestehenden Entitäten zusammenhängen. Das kann ein einfaches Skript sein, das Schlüsselbegriffe extrahiert und als Knoten (en: Nodes) und Kanten (en: Edges) im Graphen anlegt. Auch ein Sprachmodell kann das übernehmen: Text übergeben und die wichtigsten Fachbegriffe samt ihrer Beziehungen zueinander extrahieren lassen.
Pro-Tipp: Wer die Graphpflege in die Dokumentenverarbeitung integriert, also direkt nach dem Einbetten auch die Entitäten und Beziehungen aktualisiert, hat das Problem dauerhaft gelöst. Das lässt sich als letzter Schritt in die bestehende Import-Pipeline einbauen.
Drei Datenbanken, drei Wahrheiten: SQL-Metadaten und Vektoreinträge laufen auseinander
Im laufenden Betrieb ändern sich Daten. Ein veraltetes Dokument wird aus der SQL-Datenbank gelöscht, aber der zugehörige Vektor bleibt in der Vektordatenbank. Oder ein neues Dokument wird eingebettet, aber nie in SQL registriert. Die drei Datenbanken sind nicht mehr konsistent (en: Data Consistency). Und niemand merkt es, weil keine Fehlermeldung kommt.
Ohne einen Abgleichmechanismus wächst diese Abweichung mit jedem Tag. Nach sechs Monaten finden sich Vektoren ohne Quelldokument, Dokumente ohne Einbettung und Graph-Knoten, die auf nichts mehr verweisen. Ein regelmäßiger Konsistenzcheck lässt sich mit wenig Aufwand einrichten:
- Alle Dokument-IDs in SQL mit den IDs in der Vektordatenbank abgleichen.
- Alle Entitäten im Graphen gegen die SQL-Quelldokumente prüfen.
- Verwaiste Einträge markieren oder automatisch bereinigen.
Beide Suchen laufen, aber wer hat Recht? Hybride Suche falsch gewichtet
Du hast beide Suchverfahren eingerichtet: Volltextsuche (en: Full-Text Search) über die SQL-Datenbank und semantische Suche über die Vektordatenbank. Beide liefern Ergebnisse, aber in unterschiedlicher Reihenfolge. Jetzt werden die Ergebnisse zusammengeführt (en: Hybrid Search), und die Standardgewichte der Zusammenführung passen nicht zum eigenen Datenbestand.
Ein konkretes Beispiel: Jemand sucht nach "DSGVO Art. 17". Die Volltextsuche findet den exakten Treffer sofort. Die Vektorsuche findet Dokumente über Datenschutz allgemein, weil sie nach Bedeutung sucht, nicht nach exakten Wörtern. Wenn die Vektorsuche zu stark gewichtet ist, verschwindet der exakte Treffer hinter allgemeineren Dokumenten.
Die passende Gewichtung hängt vom Datenbestand ab:
- Fachtexte mit präziser Terminologie: Volltextsuche verdient mehr Gewicht.
- Freitextanfragen in natürlicher Sprache: Vektorsuche verdient mehr Gewicht.
Der passende Wert lässt sich experimentell bestimmen: Zehn typische Suchanfragen mit verschiedenen Gewichtungen testen und beobachten, bei welcher Einstellung die relevantesten Treffer oben stehen.
Woher kommt das eigentlich? Abschnittreferenz zum Originaldokument verloren
Ein Nutzer stellt eine Frage und bekommt eine Antwort. Dann fragt er: "Woher stammt diese Information?" Der Vektor in der Datenbank zeigt auf einen Abschnitt, aber welcher Absatz in welchem Originaldokument das war, ist nicht mehr nachvollziehbar. Quellenangaben (en: Source Attribution) in der Antwort sind damit unmöglich.
In der SQL-Datenbank lässt sich die Zuordnung von Abschnitt zu Originaldokument speichern. Drei Informationen reichen pro Abschnitt:
- Referenz auf das Originaldokument (Dateiname, URL oder ID).
- Position innerhalb des Dokuments (Seitenzahl, Absatz oder Kapitel).
- Zeitpunkt der Einbettung.
Mit dieser Kette lässt sich jede Antwort bis zur Originalquelle zurückverfolgen. Ohne sie ist das System eine Blackbox, deren Antworten sich nicht überprüfen lassen.
Das kostet was? Einbettungskosten bei Neuindexierung nicht eingeplant
Du wechselst auf ein besseres Einbettungsmodell oder änderst die Art, wie Texte zerlegt werden. In beiden Fällen gilt: Alle Dokumente in der Vektordatenbank sind jetzt veraltet und lassen sich mit den neuen Einbettungen nicht mehr vergleichen. Eine Neuindexierung (en: Reindexing) steht an.
Wer lokale Modelle über Ollama oder vLLM nutzt, zahlt mit Rechenzeit, aber nicht mit Geld. Wer Cloud-Dienste nutzt, zahlt pro Anfrage. Die Beträge sind bei aktuellen Anbietern überschaubar: Bei OpenAI liegt die Neueinbettung von 100.000 Dokumenten mit je 500 Wörtern aktuell bei etwa 1 bis 2 Euro. Das Geld ist also nicht das eigentliche Problem.
Das eigentliche Problem ist die Übergangszeit. Der Vorgang dauert Stunden, und während dieser Zeit enthält die Vektordatenbank einen Mix aus alten und neuen Vektoren. Suchergebnisse sind in dieser Phase unzuverlässig. Ohne die Versionierungsinformation aus der SQL-Datenbank (siehe das Kapitel zur Versionierung weiter oben) lässt sich nicht feststellen, welche Dokumente schon neu eingebettet sind und welche noch auf den alten Vektoren liegen.
Finde mal alles zu allem: Graph-Abfragen ohne Begrenzung
Jemand stellt dem System eine breite Frage: "Was hängt alles mit Machine Learning zusammen?" Die Graph-Datenbank startet eine Beziehungsabfrage (en: Graph Traversal) und durchsucht den gesamten Graphen. Bei dicht vernetzten Entitäten kehrt sie mit Tausenden von Knoten zurück. Bei einem gut gepflegten Graphen ist das schnell die halbe Datenbank.
Ohne eine maximale Suchtiefe (en: Traversal Depth) und ein Ergebnislimit wird die Abfrage zum Speicherfresser und die Antwort unbrauchbar. Zwei bis drei Ebenen Tiefe reichen für die meisten Anwendungsfälle:
- 1 Ebene: Direkte Verbindungen ("Machine Learning" hat Verbindung zu "Neuronales Netz").
- 2 Ebenen: Verbindungen über einen Zwischenknoten ("Machine Learning" → "Neuronales Netz" → "Backpropagation").
- 3 Ebenen: Maximal, und auch nur wenn der Graph nicht zu dicht vernetzt ist.
Alles darüber hinaus liefert in der Regel mehr Rauschen als Signal. Die Suchtiefe lässt sich bei den meisten Graph-Datenbanken direkt in der Abfrage begrenzen.
Für alle, die wirklich mehr wissen und umsetzen möchten ...
Zur KI-Gemeinschaft »Eine fällt aus, alle stehen still: Keine getrennte Fehlerbehandlung pro Datenbanktyp
Mitten am Tag fällt einer der drei Datenbankserver aus. Oder einer antwortet langsam, weil gerade eine große Abfrage läuft. Viele Systeme behandeln alle drei Datenbanken als eine Einheit: Wenn die Vektordatenbank nicht erreichbar ist, bekommt der Nutzer gar kein Ergebnis, obwohl die SQL-Volltextsuche noch funktioniert.
Eine robustere Architektur behandelt jede Datenbankverbindung einzeln (en: Graceful Degradation). Die Idee: Die SQL-Datenbank liefert die Grundfunktion. Die Vektordatenbank ergänzt die semantische Suche. Der Graph ergänzt die Beziehungen. Wenn eine Ergänzung ausfällt, funktioniert das System eingeschränkt statt gar nicht.
Konkret lässt sich das so umsetzen:
- Jede Datenbankabfrage bekommt ein eigenes Zeitlimit (en: Timeout).
- Wenn die Vektordatenbank nicht innerhalb von n Sekunden antwortet, werden die Ergebnisse nur aus der SQL-Volltextsuche zusammengestellt.
- Wenn der Graph-Server nicht erreichbar ist, kommt die Antwort ohne Beziehungsinformationen.
- Der Mensch bekommt eine weniger präzise Antwort, aber er bekommt eine Antwort. (Wichtig: Das ist jedoch immer Abhängig von den Zielen und Prämissen des Systems, das Du betreibst.)
Aus so vielen Stolpersteinchen kann man ja auch was Schönes bauen :-)
Danke, dass Du Dir die Zeit genommen hast, das alles zu lesen. Das Schöne beim Stolpern über solche "Besonderheiten" ist aus meiner Sicht, dass wir mit jedem Mal etwas lernen: Wir arbeiten uns regelrecht an der Materie ab, staunen, fluchen, runzeln die Stirn, be-greifen, suchen bessere Wege, finden Lösungen, erweitern unser Wissen, unseren Erfahrungsschatz, unsere Fähigkeiten.
Wenn Du so etwas magst und Dich das Thema weiter interessiert, bist Du herzlich zur KI-Gemeinschaft eingeladen: Dort geht es genau um solche Fragen, mit echten Systemen, echtem Code und echten Menschen.
Liebe Grüße und noch einen schönen Tag,
Karl