Metadatenfilterung für Databricks RAG unter Verwendung von ai_extract und ai_prep_search
Verbinden Sie ai_parse_document, ai_extract v2.1 und ai_prep_search miteinander, sodass jeder Datenblock typisierte Metadaten enthält, wodurch die Vektorsuche vor der Rangfolge nach Ähnlichkeit filtern kann.
Quellen: Adding ai_extract to the mix: building a unified RAG pipeline with three Databricks SQL AI functions
Ein Databricks-Pipeline, der auf der SQL AI-Funktion ai_prep_search basiert, leistet bereits viel. Die ausgegebenen, analysierten Dokumente werden eingegeben, semantisch aufbereitete und kontextbezogene Teile kommen heraus – sie sind somit bereit für die Vektorsuche. Der Ablauf ist klar: Die Dokumente werden analysiert, die Teile vorbereitet, in eine Delta-Tabelle zusammengefasst und indiziert.
Doch etwas fehlt, wenn dieser Prozess auf einem Dokumentenkorpus ausgeführt wird – in diesem Fall auf einer Sammlung von Orion Robotics-Manualen. Die einzelnen Abschnitte an sich sind in Ordnung. Das Feld chunk_to_embed enthält den richtigen Dokumentkontext, und chunk_to_retrieve ist gut genug, um an ein LLM weitergegeben zu werden. Das Problem beginnt bei der Abfrage, da es keine zuverlässige Methode zum Filtern gibt. Eine Anfrage wie "Zeigen Sie mir den Firmware-Leitfaden für den Orion Motion Controller" kann nur mithilfe der semantischen Ähnlichkeit beantwortet werden, da der Index keine strukturierten Spalten wie document_type, product_name oder target_audience besitzt.
Für agentgesteuertes Abrufen besteht hier eine echte Lücke. Reine semantische Suche funktioniert in einer Demo gut. In der Produktion muss ein Agent den Suchraum vor dem Ausführen der Vektorähnlichkeitsprüfung verkleinern, und Metadatenfilter erledigen genau das. Die Lösung besteht darin, ai_extract v2.1 in den Pipeline einzufügen.
Warum Metadatenfilter wichtig sind
Hybride Suche in RAG bedeutet in der Regel eine Kombination aus Vektorsuche und Schlüsselwortabgleich wie BM25. Das ist nützlich, doch für strukturierte Dokumentensammlungen ist eine andere Art von Hybridmodell wichtiger: Vektorsuche in Kombination mit Metadatenfiltern.
Stellen Sie sich einen Agenten vor, der bereits weiß, dass der Benutzer ein Handbuch für den Orion Arm benötigt. Die Sortierung von 10.000 Datenblöcken aus allen Arten von Dokumenten wäre verschwenderisch. Ein besseres Vorgehen besteht darin, zunächst die Daten auf die relevanten Dokumente zu reduzieren und anschließend die vektorbasierte Ähnlichkeit dazu zu nutzen, die besten Ergebnisse aus diesem kleineren Pool zu ordnen. Databricks Vector Search ermöglicht dies durch Filterspalten, die bei der Erstellung des Index definiert werden.
Das Problem ist jedoch, dass die Metadaten sauber, konsistent und korrekt typisiert sein müssen. Wenn in einer Zeile document_type „User Manual“ lautet, in einer anderen „manual“ und in einer dritten gar nichts, funktionieren die Filter nicht richtig. Dadurch ist die Qualität der Ergebnisse schlecht, und es werden Stunden damit verbracht, das Problem zu beheben, obwohl die eigentliche Ursache in den zuvor erstellten inkonsistenten Metadaten liegt.
ai_extract v2.1 behebt dieses Problem. Es nutzt die von ai_parse_document erzeugte strukturierte VARIANT und gibt typisierte Metadaten zurück, die anhand eines Schemas überprüft werden. Enum-Felder legen feste Werte fest, String-Felder steuern die Extraktion freier Texte und Arrays umfassen mehrwertige Felder. Da die Extraktion einem Schema folgt, bleibt die Ausgabe unabhängig von den Layouts der Dokumente einheitlich.
Drei Funktionen, drei Aufgaben
Die Aufgaben der drei Funktionen überschneiden sich nicht, daher ist es hilfreich, sie vor dem Betrachten des Notebooks zu trennen.
- ai_parse_document v2.0 wandelt rohe Binärdateien (PDF, DOCX, Bilder) in eine strukturierte VARIANT um und identifiziert Textblöcke, Überschriften, Tabellen, Abbildungen, Rahmenboxen sowie die Abschnittshierarchie. Es kennt die strukturelle Gliederung eines Dokuments, hat aber keine Ahnung, worum es im Dokument geht.
Jede Funktion hat eine einzige Aufgabe, und der Pipeline-Workflow setzt sie zusammen.
Schritt für Schritt im Notebook
Das Notebook arbeitet mit dem Orion Robotics-Korpus, das sich in einem Unity Catalog Volume befindet und PDFs, DOCX-Dateien sowie Bilder enthält. Zuerst erfolgt die Parsung:
SELECT
path,
ai_parse_document(
content,
MAP('version', '2.0')
) AS parsed
FROM READ_FILES(
'/Volumes/<YOUR_CATALOG>/<YOUR_SCHEMA>/orion_docs/',
format => 'binaryFile'
)
Jedes Datei im Volume wird als Binärdaten gelesen und in eine strukturierte VARIANT umgewandelt. Es ist klug, explizit Version 2.0 zu verwenden, da dieses Schema die Koordinaten des Rahmenboxen sowie Beschreibungen der Diagramme enthält, auf die ai_prep_search zur Entscheidungsfindung bei der Aufteilung in Blöcke angewiesen ist. Wenn man bei der Standardversion bleibt, könnte Databricks das Verhalten im Hintergrund des Pipelines ändern.
Der Beispielaufruf ergab folgendes Ergebnis:
Die nächste Abfrage extrahiert Metadaten aus dem analysierten Ausgabeinhalt:
WITH parsed_documents AS (
SELECT path, ai_parse_document(content, MAP('version', '2.0')) AS parsed
FROM READ_FILES('/Volumes/<YOUR_CATALOG>/<YOUR_SCHEMA>/orion_docs/', format => 'binaryFile')
)
SELECT
path,
ai_extract(
parsed,
'{
"document_title": {"type": "string", "description": "Main title or heading of the document"},
"document_type": {"type": "enum", "labels": ["manual", "datasheet", "guide", "report", "specification", "policy", "other"]},
"product_name": {"type": "string", "description": "Primary product or system referenced"},
"version": {"type": "string", "description": "Document or product version if mentioned"},
"key_topics": {"type": "array", "items": {"type": "string"}},
"target_audience": {"type": "enum", "labels": ["engineer", "operator", "manager", "customer", "general"]}
}',
MAP(
'version', '2.1',
'instructions', 'These are technical documents from Orion Robotics. Extract metadata that would help users filter and find relevant documents.'
)
) AS metadata
FROM parsed_documents
Einige Auswahlmöglichkeiten erfordern eine Erklärung. Sowohl document_type als auch target_audience sind Enums, da Filter eine abgeschlossene Menge an Werten benötigen. Als Freitext könnten sie als „manual“, „User Manual“ oder „technical manual“ zurückkehren, was die Abgleichfunktion stört. Die Option instructions liefert Hintergrundinformationen – in diesem Fall, dass es sich um technische Dokumente von Orion Robotics handelt – wodurch die Extraktion verbessert wird. Zudem akzeptiert ai_extract den Wert von ai_parse_document in seinem ursprünglichen Zustand, ohne eine Umwandlung in Freitext.
Ausgabe des Beispiellaufs:
Danach folgt das Chunking:
WITH parsed_documents AS ( ... )
SELECT
path,
ai_prep_search(parsed) AS chunked
FROM parsed_documents
ai_prep_search verwendet dieselbe aufbereitete VARIANT. Die Dokumente werden in Blöcke aufgeteilt, die in einem document.contents-Array gespeichert werden, wobei jeder Block chunk_id, chunk_position, chunk_to_retrieve und chunk_to_embed enthält. Die letzten beiden enthalten absichtlich nicht denselben Text.
chunk_to_embed enthält zusätzlichen Kontext wie den Dokumententitel, die Abschnittsüberschrift und Seiteninformationen, damit das Einbettungsmodell den Block korrekt platzieren kann. chunk_to_retrieve ist reinerer Text, in der Regel die Version, die den Benutzern angezeigt wird oder an das LLM übergeben wird.
Die Aufteilung in solche Blöcke ist eine sinnvolle Gestaltung. Viele RAG-Pipelines verwenden denselben Text zum Einbetten und zur Beantwortung von Fragen, doch kontextreicher Text eignet sich besser für Einbettungen, während reiner Text zum Lesen geeignet ist – daher ist es besser, beides beizubehalten.
Beispielausgabe:
Alles zusammenfügen
Die kombinierte Abfrage ist der Punkt, an dem alle Teile zusammenkommen:
WITH parsed_documents AS (
SELECT path, ai_parse_document(content, MAP('version', '2.0')) AS parsed
FROM READ_FILES('/Volumes/<YOUR_CATALOG>/<YOUR_SCHEMA>/orion_docs/', format => 'binaryFile')
),
doc_metadata AS (
SELECT
path,
parsed,
ai_extract(
parsed,
'{ ... }',
MAP('version', '2.1', 'instructions', 'These are technical documents from Orion Robotics. Extract metadata for search filtering.')
) AS metadata
FROM parsed_documents
),
doc_chunks AS (
SELECT path, ai_prep_search(parsed) AS chunked
FROM doc_metadata
)
SELECT
chunk.value:chunk_id::STRING AS chunk_id,
chunk.value:chunk_position::INT AS chunk_position,
chunk.value:chunk_to_retrieve::STRING AS chunk_to_retrieve,
chunk.value:chunk_to_embed::STRING AS chunk_to_embed,
doc_chunks.chunked:document.source_uri::STRING AS source_uri,
dm.metadata:response:document_title::STRING AS document_title,
dm.metadata:response:document_type::STRING AS document_type,
dm.metadata:response:product_name::STRING AS product_name,
dm.metadata:response:version::STRING AS doc_version,
dm.metadata:response:target_audience::STRING AS target_audience
FROM
doc_chunks
JOIN doc_metadata dm ON doc_chunks.path = dm.path,
LATERAL variant_explode(doc_chunks.chunked:document.contents) AS chunk
Die Schritt LATERAL variant_explode erweitert das verschachtelte Chunk-Array in Zeilen. Die JOIN-Operation am Pfad fügt Metadaten auf Dokumentebene zu jedem Chunk dieses Dokuments hinzu. Das Ergebnis sind jeweils eine Zeile pro Chunk, die sowohl die Abruffelder als auch die filterbaren Metadatenspalten enthält. Das ist die Struktur, die in der Delta-Tabelle gespeichert wird und zur Versorgung des Vector-Search-Index verwendet wird.
Ergebnis der Beispielausführung:
Die Tabelle wird wie folgt materialisiert:
CREATE OR REPLACE TABLE <YOUR_CATALOG>.<YOUR_SCHEMA>.orion_docs_rag_chunks AS
-- [full CTE chain from Step 4]
Sobald die Tabelle vorhanden ist, wird ein Mosaic AI Vector Search Delta Sync-Index mit den folgenden Einstellungen erstellt:
- Spalte für die Einbettung: chunk_to_embed
- Primärschlüssel: chunk_id
- Felder, die gefiltert werden können: doc_version zusammen mit document_type, product_name und target_audience
Der chunk_id bleibt von einer Ausführung zur nächsten konstant und setzt sich aus der Dokument-ID sowie der Position des Chunks zusammen. Deshalb lassen inkrementelle MERGE-Änderungen an der Delta-Tabelle unveränderte Chunks unberührt, anstatt sie erneut zu indizieren – was bei regelmäßigen Aktualisierungen des Korpus erhebliche Einsparungen bringt.
Der Vorteil bei Abfragen
Der Nutzen zeigt sich, wenn ein Benutzer eine Anfrage stellt, und nicht bei der Ladezeit. Nehmen wir an, die Anfrage lautet „Finde den Firmware-Leitfaden für den Orion Motion Controller“. Die Suche kann nun in zwei Schritten durchgeführt werden:
- Ermitteln Sie die passenden Metadatenfilter, wie z. B. document_type = „Guide“ und product_name = „Orion Motion Controller“.
- Embedden Sie die Abfrage und suchen Sie ausschließlich unter den Dokumenten, die diese Filter erfüllen.
Fehlt ai_extract, ist ein Agent auf alleinige Verwendung von semantischer Ähnlichkeit angewiesen. Für einige ähnliche Dokumente mag das ausreichen. Bei Tausenden von Dateien, die viele Produkte, Arten und Zielgruppen umfassen, wird diese Methode jedoch unzuverlässig. Metadaten basierend auf Enumerationen beheben dieses Problem: Der Agent muss nicht zwischen einem Handbuch und einer Anleitung raten, sondern kann einfach nach document_type = „Manual“ filtern, um exakte Ergebnisse zu erhalten.
Beschränkungen und Kosten
ai_prep_search bietet keinerlei Kontrolle über das Chunking. Die Größe der Chunks, die Überschneidung sowie der Algorithmus lassen sich nicht einstellen. Für viele RAG-Fälle mit Dokumenten ist das akzeptabel, da semantisches Chunking mit zusätzlichem Kontext oft besser ist als einfache Aufteilung in feste Größen. Eltern-Kind-Strukturen oder detaillierte Kontrolle der Überschneidung erfordern weiterhin eigene Logik.
ai_extract verursacht zusätzliche Kosten und Verzögerungen, da für jedes Dokument ein weiterer Aufruf des LLM erforderlich ist. Delta Sync kann unveränderte Dokumente überspringen, doch die erste vollständige Verarbeitung kann dennoch kostspielig sein; daher ist es sinnvoll, vor der Verarbeitung des gesamten Korpus zunächst mit einem Beispiel zu testen.
Verwenden Sie Version 2.0 von ai_parse_document, wenn deren VARIANT-Ausgabe an ai_prep_search oder ai_extract weitergeleitet wird. Diese Version liefert Rahmenboxen, Elementtypen sowie Beschreibungen von Abbildungen. Rohtext kann zwar an ai_extract übergeben werden, doch die strukturellen Vorteile der Parsing-Logik gehen dabei verloren.
Fazit zum Design
Die Hinzufügung von ai_extract v2.1 erfordert nur wenig zusätzlichen Code, verändert aber die Architektur erheblich, da die Blöcke nun saubere, typisierte und filterbare Metadaten enthalten sowie die hybride Suchfunktion zuverlässiger wird. Der Prozess besteht weiterhin aus drei klaren Schritten:
- ai_parse_document erfasst die Struktur des Dokuments.
- ai_extract erfasst nützliche Metadaten.
- ai_prep_search erstellt semantische Blöcke.
Diese Trennung bringt Vorteile bei Dokumenten verschiedener Produkte, Versionen, Zielgruppen und Typen. Ohne Metadatenfilter stützt sich die Suche hauptsächlich auf eine Ähnlichkeitssuche. Mit diesen Filtern reduziert der Agent zunächst das Suchfeld und ordnet anschließend die Ergebnisse.
Der Ansatz eignet sich nicht für alles. Metadaten auf Blöckenebene, das Abrufen von Eltern-Kind-Beziehungen oder eine benutzerdefinierte Blöckung erfordern weiterhin speziellen Code. Für viele Databricks-Dokument-RAG-Pipelines ist es jedoch ein saubererer und einfacherer Ausgangspunkt. Zudem sollten die Blöcke weiterhin getestet und bewertet werden, niemals aber blind vertraut werden.