Startseite / Artikel

Qualität von RAG zur Indexzeit: Blöcktypen, Reinigung und Suchetiketten

Qualität von RAG zur Indexzeit: Erstellung von Text-, Tabelle- und Bildblöcken, Beseitigung von Parser-Störungen sowie Hinzufügen von von LLMs erstellten Suchetiketten, die die Anzahl der fehlgeschlagenen Abfragen etwa halbierten.

Verlag: REACTAPP.TOP12 Min. Lesezeit2701 Wörter

Quellen: The Part of RAG Where Quality Is Actually Won

Wo eine Demo zu einem Produktionsystem wird

Ein RAG-Parser liefert eine geordnete Liste von Knoten, wobei jeder Knoten kennt, aus welchem Abschnitt er stammt. Alles, was bis zum Zeitpunkt geschieht, an dem das Modell eine Anfrage verarbeitet, gehört zur Indexierungsphase – und genau diese Phase unterscheidet eine Demo von einer Lösung, die in der Produktion eingesetzt werden kann. Der Prozess gliedert sich in drei Schritte: Zunächst werden die Knoten in Blöcke umgewandelt, wobei es drei Arten von Blöcken mit jeweils eigenen Erstellungsmechanismen gibt. Anschließend werden aus diesen Blöcken die von dem Parser sorgfältig extrahierten, aber für das Modell unbrauchbaren Daten entfernt. Schließlich erhält jeder Block eine kurze, von einem Sprachmodell generierte Bezeichnung. Bei einer internen Bewertung anhand von Bankdokumenten verringerten diese Bezeichnungen die Anzahl der fehlgeschlagenen Abfragen um etwa die Hälfte und stellen somit den größten Nutzen in der gesamten Pipeline dar.

Es ist auch am wenigsten glamourös. Kaum jemand schreibt über die Lärmbeseitigung – genau deshalb gibt es so viele Erfolge in diesem Bereich. Die untenstehenden Zahlen stammen aus einer kleinen internen Bewertung und nicht aus einem strengen Benchmark, und man sollte sie entsprechend betrachten.

Drei Arten von Blöcken, drei Verarbeitungsmethoden

Der naive Ablauf verwendet einen einzigen Blocker: Der Text wird geteilt und weiterverarbeitet. Reale Dokumente enthalten mindestens drei Arten von Inhalten, die sich überhaupt nicht ähneln. Ein Prosaabsatz, eine Gebührentabelle und ein Screenshot benötigen jeweils eine eigene Verarbeitungsmethode; wenn man alles durch denselben Trenner leitet, entstehen halbe Tabellen und fehlende Schritte.

Textblöcke folgen einer Struktur

Die Aufteilung nach Zeichenzahl ist ausgeschlossen. Stattdessen wird jede Zeile zunächst eingegeben – als Überschrift, als nummerierter Schritt oder als gewöhnlicher Satz – und die Zusammenführungsregeln hängen von diesen Typen ab. Dadurch vermeidet man, dass ein nummerierter Schritt an einen unzusammenhängenden Absatz angehängt wird. Zu lange Texte werden an Satzenden abgeschnitten; das Durchtrennen mitten im Satz dient nur als letzte Lösung. Die benachbarten Abschnitte überschneiden sich leicht, sodass ein Verfahren, das eine Grenze überschreitet, von beiden Seiten noch verständlich bleibt.

Tabellen bleiben vollständig

Tabellen werden getrennt erstellt, weil ihre Bedeutung in ihrer Struktur liegt. Der Ersteller findet die Überschriftenzeile, wiederholt sie bei einer Aufteilung der Tabelle in jeder Teilstruktur und ordnet die Zeilen. Zudem werden Tabellen abgelehnt, die keinen eigentlichen Inhalt enthalten, meist die Inhaltsübersicht, die Parser als ordentliche Gitterstruktur aus Überschriften und Seitenzahlen ausgeben.

Die vorherige Version dieses Systems wies genau hier einen Fehler auf. Inhaltsverzeichnisse wurden mit einem groben Test erkannt, wobei viele Zeilen meist mit einer kleinen Zahl endeten. Auch Gebührentabellen entsprachen dieser Beschreibung, sodass ein tatsächlicher Gebührenplan ohne Vorwarnung aus dem Index verschwinden konnte. Der Ersatztest ist präziser: Auf einer echten Inhaltsseite steigen die Seitennummern stets an, während Gebührensätze wie 35 $, 30 $ und 7 $ sowohl sinken als auch steigen können. Eine Tabelle gilt nur dann als Index, wenn die Endzahlen niemals sinken. Nun bleiben die Gebührenpläne erhalten, die Inhaltsseiten erscheinen weiterhin, und eine einzige Bedingung beseitigte den Fehler.

Bilder werden nacheinander geprüft

Jedes Bild kann Kosten verursachen, daher versucht jedes von ihnen nacheinander drei Ansätze und stoppt beim ersten, der funktioniert.

  1. Der freie Pfad verwendet den Text, den der Parser bereits extrahiert hat. Wenn das Dokument eine verwendbare Beschriftung oder Anmerkung bereitstellt, die kein Platzhalter wie „Bild“ ist, wird dieser Text verwendet und kein Vision-Modell aufgerufen. In vielen Dokumenten folgen ein Drittel bis die Hälfte der Bilder diesem Weg, was eine echte Einsparung darstellt.
  2. Der bezahlte Weg ruft ein Vision-Modell auf. Dies gilt, wenn kein verwendbarer Text vorhanden ist und das Bild größer als ein kleiner Trenner ist. Ein präzise formulierter Prompt veranlasst das Modell, zunächst zu klassifizieren. Wenn das Bild ein Logo oder ein Ornament ist, antwortet es mit dem Wort „DECORATIVE“ und der entsprechende Teil wird weggeworfen. Andernfalls werden zwei Felder ausgefüllt. „SCREEN“ ist eine einzige Zeile, die angibt, wofür der Bildschirm dient – beispielsweise ein Eingabebildschirm für Ausgangsüberweisungen bei Geschäftskonten – und ist so formuliert, dass sie im Vektorraum nahe bei den Fragen der Benutzer liegt. „CONTENT“ listet alle sichtbaren Feldbezeichnungen, Schaltflächen und Codes in Lese-Reihenfolge auf, sodass Suchende genau die Zeichenketten erhalten, die sie eingeben könnten. Auf diese Weise kann ein Screenshot sowohl nach seiner Funktion als auch nach seinem gedruckten Text gefunden werden.
  3. Der Fallback-Pfad verwendet die Überschrift. Wenn beides fehlschlägt, aber der Abschnitt bekannt ist, wird ein Platzhalter gespeichert, der angibt, um welche Bildart es sich handelt und wo es sich befindet, sodass es zur Abfragezeit über seinen Abschnitt weiterhin erreichbar bleibt.

Bei der Größenprüfung gibt es einen kleinen Fehler. Sehr kleine Bilder werden aufgrund einer Breiten- und Höchstschwelle übersprungen, doch beide Dimensionen müssen klein sein; daher gelangt auch eine sehr dünne und sehr breite Trennlinie durch und wird dennoch an das Modell gesendet. Das verschwendet etwas Geld für Dekoration, ohne die Genauigkeit zu beeinträchtigen, und steht auf der To-Do-Liste.

Das Diagramm zeigt die drei in dieser Reihenfolge ausprobierten Bildpfade. Das Vision-Modell wird einmal zum Zeitpunkt der Indexierung ausgeführt, und jede nachfolgende Abfrage ist kostenlos.

Aufbereitung dessen, was der Parser richtig erkannt hat

Genau die Genauigkeit ist es, die Parser hier zu einem Ärgernis macht. Sie extrahieren den gesamten Inhalt einer Seite: ein „VERTRAULICH“-Stempel, der auf 200 Seiten wiederholt erscheint, Seitenzahlen, Urheberrechtsangaben, Sätze, die durch eine Seitenbruchstelle geteilt werden. Jeder dieser Elemente wird zu einem Teilabschnitt, und jeder davon ist Überfluss. Überfluss verfälscht außerdem die Ranking-Algorithmen, da eine Suchanfrage genauso leicht auf ein Werbebanner wie auf einen Verfahrensteil stoßen kann.

Sobald diese Teilabschnitte vorhanden sind, wird eine Reinigungsphase durchgeführt, bei der jeder Schritt auf eine bestimmte Art von Unrat abzielt, der tatsächlich in echten Dokumenten vorkommt. In folgender Reihenfolge:

  1. Durch das Filtern von Standardtexten werden reine Seitenzahlen, Überarbeitungsstempel sowie Links wie „Kontaktieren Sie uns“ oder „Mehr lesen“ am Anfang entfernt. Die Regeln variieren je nach Dateityp, da PDFs, Word-Dateien und Präsentationen jeweils ihre eigenen Arten von Überfluss aufweisen.
  2. Entfernen Sie wiederholte Inhalte. Text, der auf vielen Seiten identisch erscheint, gilt als überflüssig. Der Ausdruck „VERTRAULICH, NUR FÜR INNENVERWENDUNG“ auf fünfzehn Seiten wird mitgezählt und entfernt, während „Siehe Seite 3“, der zweimal auf derselben Seite vorkommt, erhalten bleibt, da er nicht mehrere Seiten umfasst.
  3. Bessern Sie fehlerhafte Trennungen aus. Ein Abschnitt, der mitten im Satz oder mit einem fehlenden Punkt beginnt, wurde an der falschen Stelle geteilt. Wenn er sich mit dem vorherigen Abschnitt im selben Abschnitt verbinden lässt, werden sie zusammengeführt. Andernfalls wird er als Fortsetzung gekennzeichnet, sodass seine Bezeichnung diesen Kontext enthält.
  4. Führen Sie verlorene Fragmente zusammen. Ein isolierter 40-Zeichen-Abschnitt an einer Abschnittsgrenze wird von einem benachbarten Abschnitt aufgenommen – allerdings nur, wenn beide genau denselben Abschnitt und dieselbe Überschrift teilen. Abschnitte werden nicht miteinander kombiniert, und kurze, eigenständige Hinweise bleiben unverändert.
  5. Durch die Deduplizierung wird ein unter einem Überschriftspunkt wiederholter Satz zu einem einzigen Abschnitt zusammengefasst. Wenn er unter zwei Überschriftspunkten erscheint, werden beide Kopien beibehalten, da sie durch den angehängten Abschnittspfad unterschiedliche Informationen enthalten. Bilder sind gänzlich ausgenommen, denn zwei ähnlich aussehende Bildschirme bleiben weiterhin zwei getrennte Screenshot-Aufnahmen.

Keine der Regeln ist kompliziert. Jede entstand aus der Beobachtung, wie ein echtes Dokument die einfachere Version übertrifft, und gemeinsam erklären sie, warum eine Anfrage an ein Verfahren und nicht an eine Urheberrechtsregelung weitergeleitet wird.

Die Suchetikette

Der Schritt mit dem größten Nutzen, den die meisten Systeme jedoch weglassen, kommt nun. Nehmen wir einen einfachen Abschnitt wie diesen:

Geben Sie die Menge ein und drücken Sie F4.

Es enthält seinen Abschnittspfad, Wire Transfer > Initiation > Amount Entry, was bereits hilft. Bevor es eingebettet wird, schreibt ein Sprachmodell außerdem eine kurze Suchbeschreibung von 18 bis 22 Wörtern, die zusammen mit dem Chunk gespeichert wird. Für diesen Chunk lautet die Beschreibung etwa so:

Verfahrensschritt: Geben Sie den Überweisungsbetrag ein und drücken Sie anschließend F4, um die Überweisung zu starten und zu bestätigen.

Betrachten Sie die Auswirkung. Ohne Etikett befindet sich die reine Anweisung im Embedding-Raum neben allen anderen Schritten, die vorschreiben, einen Wert einzugeben und eine Taste zu drücken. Mit dem Etikett liegt sie in der Nähe von Überweisungen und Bestätigungen – einem Vokabular, das ein echter Benutzer verwenden würde. Eine umformulierte Frage wie „Wie bestätige ich den Betrag bei einer Überweisung?“ findet dann diesen Abschnitt. Bei der internen Bewertung reduzierten die Etikette etwa die Hälfte der Fehlsuchvorfälle. Es handelte sich dabei um einen kleinen Test an einem einzigen Korpus, daher sollten Sie dies als starken Hinweis und nicht als universelle Konstante betrachten.

Die Technik leitet sich von Anthropics Contextual Retrieval ab, bei dem festgestellt wurde, dass das Hinzufügen eines kontextbezogenen Satzes vor jeden Chunk vor der Indexierung die Anzahl der Fehlerrückgaben erheblich verringert. Hier wurde sie für Inhalte angepasst, bei denen genaue Identifikatoren wichtig sind. Transaktionscodes und Funktions-Tasten müssen unverändert kopiert werden und dürfen niemals umformuliert werden. Zudem beginnt jede Bezeichnung mit einem Rollenwort wie „Vorgangsschritt“ oder „Referenztabelle“, sodass die Chunks mit den entsprechenden Arten von Fragen gruppiert werden.

Was der Label-Verfasser sieht

Eine Bezeichnung ist nur so gut wie der Kontext, der dem Modell zur Erstellung übergeben wird. Der reine Chunk allein reicht nicht aus, da gerade dieser seinen Sinn verloren hat, als er aus der Seite ausgeschnitten wurde. Daher enthält jede Anfrage für eine Bezeichnung mehrere Elemente:

  • den Text des Chunks selbst
  • den Abschnittspfad und die Überschrift
  • die Spaltenüberschriften, wenn der Datensatz eine Tabelle ist
  • einen vorherigen und einen nachfolgenden Datensatz, um zu zeigen, was ihn umgibt
  • eine kurze Zusammenfassung des gesamten Dokuments, um das Gesamtbild zu vermitteln

Die Dokumentzusammenfassung stammt von einem separaten Aufruf, der vor Beginn der Beschriftung erfolgt. Dabei werden Datensätze vom Anfang, Mittelteil und Ende sowie eine Tabelle und ein Bild ausgewählt, und es werden zwei oder drei Sätze zurückgegeben, in denen der Dokumenttyp, die Zielgruppe sowie die betroffenen Produkte und Codes genannt werden. Jeder Anfrage zur Beschriftung wird diese Zusammenfassung dann beigefügt. Der Ausdruck „Betrag eingeben“ sollte in einem Überweisungshandbuch anders beschriftet werden als in einer Kreditinformation, und die Zusammenfassung hilft dem Modell dabei, herauszufinden, welches Dokument es gerade liest.

Die Nachbarchunks verdienen eine genaue Betrachtung, denn „vorheriger und nachfolgender“ bedeutet nicht, dass es sich um physisch benachbarte Blöcke handelt. Der nächstgelegene Block kann zu einem völlig anderen Verfahren gehören. Daher werden die Nachbarn nach Relevanz in drei Stufen ausgewählt, wobei der erste passende Block berücksichtigt wird:

  1. der nächstgelegene Block im selben Abschnittspfad
  2. ansonsten der nächstgelegene Block im selben übergeordneten Abschnitt
  3. ansonsten der nächstgelegene Block unter demselben Überschriftspunkt

Die Kandidaten werden vom aktuellen Block ausgehend nach außen durchsucht, und alles außerhalb des richtigen Abschnitts wird übersprungen. Dadurch erkennt das Modell den nächsten Block, der tatsächlich zu diesem gehört. Jeder Nachbarchunk wird auf einige hundert Zeichen gekürzt, was ausreichend Kontext bietet, ohne die Kosten des Übertragens ganzer Blöcke.

Im F4-Beispiel erkennt das Modell, dass der vorherige Abschnitt in seiner Sektion sich auf die Auswahl eines Empfängers und der nächste auf die Bestätigung der Überweisung bezog. Dadurch kann es eine präzise Beschriftung für das Eingeben des Überweisungsbetrags erstellen, anstatt eine unklare Zusammenfassung. Gut gewählte Nachbarn sorgen dafür, dass die Beschriftungen genau sind.

Die Abbildung zeigt, wie das Beschriftungsmodell seine Nachbarn derselben Sektion liest und anschließend einen spezifischen Satz erstellt, der neben dem Abschnitt gespeichert wird und beiden Suchkanälen zur Verfügung gestellt wird.

Warum das kostengünstig ist

Drei Eigenschaften machen diesen Ansatz praktikabel und nicht teuer.

  • Einmalige Kosten. Die Beschriftung wird während der Indizierung erzeugt und gespeichert, sodass Abfragen nie dafür bezahlen müssen.
  • Niedrige Einzelkosten. Ein kleines, schnelles Modell beschriftet Abschnitte in Gruppen zu 25, wobei mehrere Gruppen gleichzeitig laufen, und die Kosten liegen deutlich unter einem Cent pro Abschnitt.
  • Sanfter Ausfall. Eine Batch-Datei, bei der die Beschriftungsfehler auftreten, wird einfach ohne Beschriftung indiziert, während der Vorgang weiterläuft. Diese Teile behalten ihren Text sowie den Abschnittspfad bei, erhalten jedoch keinen zusätzlichen Boost – dadurch schadet ein Ausfall zwar der Qualität, stoppt aber die Indizierung nie.
  • Die Beschriftung befindet sich in einem eigenen Feld, getrennt vom eigentlichen Inhalt des Teils. Diese Trennung ist später, bei Abfragen, wichtig, da der Label bei der Schlüsselwortsuche mehr Gewicht erhält. Im Moment zählt vor allem, dass die aufwändigsten Berechnungen bereits vor jeder Suche durchgeführt werden – das ist es auch, was paraphrasierte Abfragen funktionieren lässt.

    Determinismus und versionierter Prompt

    Die Erstellung von Labels ist deterministisch: Eine Temperatur von null sowie ein fester Seed bedeuten, dass das Neuindizieren eines Dokuments dieselben Labels erzeugt. Das untermauert die Aussage, dass die Kosten nur einmal anfallen. Beim Neuindizieren werden die Vektoren nicht umsortiert, sondern lediglich neu erstellt.

    Der Determinismus hängt auch von der Versionierung des Prompts ab. Eine Seed liefert nur für identische Eingaben identische Ausgaben, und der Anweisungstext ist Teil dieses Eingabes. Wenn der Enrichment-Prompt zwischen den Versionen geändert wird, ergeben sich bei jeder nachfolgenden Neindekxisierung unterschiedliche Labels, wodurch sich der Index stillschweigend in zwei Generationen aufteilt, ohne dass etwas davon gemeldet wird. Der Prompt wird daher mit einem Versionsschlüssel gespeichert, beim Indexieren mit diesem Schlüssel geladen und nur durch eine nachverfolgbare, bewusste Aktualisierung geändert. Die Reproduzierbarkeit zwischen den Versionen muss aktiv hergestellt werden, denn allein eine Seed reicht dafür nicht aus.

    Embedding und Speicherung

    Nach der Reinigung, Platzierung und Beschriftung ist die letzte Schritt im Indexierungsprozess die Erstellung des Vektors. Ein Embedding-Modell deckt alle Chunk-Typen ab: text-embedding-3-large, das auf 1536 Dimensionen reduziert wurde, sodass Texte, Tabellen und Screenshote alle im selben Raum gegenüber einer Abfrage bewertet werden können.

    In den Einbettungsmechanismus wird eine in einer festen Reihenfolge zusammengesetzte Zeichenkette eingegeben: ein strukturelles Präfix, eine Bezeichnung und anschließend der Textinhalt. Das Präfix bezieht sich auf bereits bekannte Fakten über den jeweiligen Abschnitt. Dazu gehören ein aus dem Abschnittspfad geschätzter Dokumenttyp wie beispielsweise Gebührenübersicht oder Überweisungsverfahren, zusammen mit dem Überschriftentext und dem Abschnittspfad; bei Bildern sind außerdem Seite und Position relevant. Wenn ein verfahrensähnlicher Abschnitt als solches markiert wird, wird er in Richtung von Fragen gelenkt, die mit „Wie“ beginnen.

    Zu diesem Zeitpunkt wird auch der Abschnittspfad in den Text eingefügt – genau hier lag beim älteren Version ein Fehler. Früher wurde das Präfix während der Bereinigung in den Datenblock geschrieben, und jede spätere Bearbeitung konnte es erneut voransetzen und so heimlich den Vektor beschädigen. Der aktuelle Code entfernt jedes gespeicherte Präfix und erstellt eines neu, wenn der Vektor eingebettet wird. Es existiert an nur einer Stelle und wird genau einmal hinzugefügt, unmittelbar bevor das Modell ihn liest, wodurch eine ganze Reihe von fehlerhaften Doppel-Präfix-Fällen beseitigt wurde.

    Der fertige Vektor wird zusammen mit dem Datenblockinhalt, der Beschriftung, den strukturellen Metadaten sowie einer Volltextspalte, aus der die Schlüsselwortsuche lädt, in der Datenbank gespeichert. Danach wird das Dokument aktiv. Wenn ein Schritt im Verarbeitungspfad fehlschlägt, wird das Dokument als abgelehnt markiert und der Grund wird zur Fehlerbehebung aufbewahrt, während eine frühere aktive Version die ganze Zeit über suchbar bleibt. Ein halb indiziertes Dokument ist niemals sichtbar.

    Wie eine abgeschlossene Zeile aussieht

    Um das konkret zu machen, hier ist der „Betrag eingeben und F4 drücken“-Abschnitt in seiner Endform im Store. Jedes Feld stammt von einem vorherigen Schritt.

    text              : "Enter amount and press F4."
                         (built + cleaned + section-prefixed)
    retrieval_context : "Procedure step entering the amount
                         and pressing F4 during wire transfer
                         initiation."          (the label)
    chunk_type        : TEXT
    section_path      : "Wire Transfer > Initiation >
                         Amount Entry"         (Part 2)
    preceding_heading : "Amount Entry"
    table_headers     : null                  (TABLE only)
    page_start / end  : 12 / 12
    image_object_key  : null                  (IMAGE only)
    embedding_model   : text-embedding-3-large
    embedding_dim     : 1536
    embedding         : [ ...1536 floats... ] (body+label)
    text_search_vector: label weighted high, body lower
    

    In einem Tabelle-Blöck füllen die Spaltenüberschriften die Stellen, an denen ein Text-Blöck Nullwerte enthält. Ein Bild-Blöck speichert einen Blob-Schlüssel, die MIME-Typ und die Seitenposition, wobei die CONTENT-Daten in text und die SCREEN-Zeile in retrieval_context abgelegt werden. Alle drei Arten teilen sich dasselbe Zeilenlayout. Die Abrufung stützt sich auf zwei Spalten: embedding für die Vektorsuche und text_search_vector für die Schlüsselwortsuche, wobei jede aus dem Inhalt plus dem Label abgeleitet wird. Die anderen Spalten lokalisieren den Blöck, zeigen ihn an oder beschränken ihn auf den richtigen Benutzer.

    Das ist die gesamte Ausgabe zur Indexierung: solche Zeilen, eine pro Blöck, gereinigt, beschriftet und vektorisiert, und wartend auf die erste Abfrage.

    Verwandte Literatur

  • Chunk-Größe und Überschneidung in RAG: Schrittweite, Token-Limits und Schnittstellen — Wie man ein langes Buch für RAG in Blöcke aufteilt: Größe und Überschneidung wählen, Schrittweite verstehen, innerhalb der Embedding-Token-Limits bleiben, Metadaten speichern und die Überschneidung in einer tatsächlichen Aufteilung erkennen. —