Startseite / Artikel

RAG ohne Vektordatenbank: Ein Pipeline in BigQuery SQL und wo es versagt

Erstellen Sie eine retrieval-augmented Generation vollständig mit BigQuery SQL – von Embeddings bis zu fundierten Antworten – und sehen Sie sich anschließend die Kosten, Quoten sowie die Limits für Prompt-Versionierung an.

Verlag: REACTAPP.TOP11 Min. Lesezeit2592 Wörter

Quellen: SQL Is All You Need: A Complete RAG Pipeline in SQL with BigQuery

Eine Vektordatenbank, ein Orchesterierer, ein Embedding-Dienst, API-Schlüssel, die überall verteilt sind, sowie drei Container – nur um Fragen zu Ihren eigenen Dokumenten beantworten zu können. So sah die retrieval-augmented Generation in den meisten Anleitungen aus, und für eine große Anzahl von Anwendungsfällen ist sie möglicherweise nicht mehr notwendig.

Die KI-Funktionen von BigQuery ermöglichen es dem LLM, sich wie jeder andere SQL-Operator zu verhalten, genauso wie JOIN oder WHERE. Embedding, Vektorabfrage und strukturierte Generierung finden alle dort statt, wo sich die Daten bereits befinden – unter den IAM-, Audit-Logging- und Kostenschutzmechanismen, die Sie bereits haben. Es handelt sich dabei auch nicht um einen geschlossenen Umfeld um das Modell eines einzelnen Anbieters; Claude, Llama, Mistral sowie Open-Source-Modelle von Hugging Face können alle verwendet werden.

Die folgenden Abschnitte bauen denselben Pipeline auf zwei Arten anhand eines öffentlichen Datensatzes auf, den jeder abfragen kann – dem BBC-News-Korpus unter bigquery-public-data.bbc_news.fulltext. Die erste Variante ist eine einmalige Batch-Version, die allgemein verfügbare Funktionen verwendet. Die zweite ist eine laufende Version im Vorab-Modus, bei der das Korpus ständig erweitert wird, sobald neue Datensätze eingehen. Anschließend wird ehrlich dargestellt, an welchen Stellen dieser Ansatz versagt und warum Python weiterhin die bessere Lösung ist.

Der Stack, den die meisten Tutorials voraussetzen

Eine Standard-RAG-Einrichtung beinhaltet in der Regel:

  1. Etwas zur Erstellung von Embeddings: eine gehostete API oder ein Modell-Endpunkt, den man selbst einrichtet und wartet
  2. Eine Vektordatenbank, was einen weiteren Anbieter, eine weitere Rechnung sowie einen weiteren Synchronisierungs-Pipeline bedeutet, um mit der Quelle der Wahrheit in Einklang zu bleiben
  3. Einen Orchestrierer wie LangChain, LlamaIndex oder selbstgeschriebene Verbindungsmechanismen
  4. Eine LLM-API mit eigenen Geschwindigkeitsbeschränkungen und Quoten
  5. Hosting für all das, in der Regel einige Container, die jemand verpacken und stets aktualisieren muss

Zählen Sie die Komponenten: fünf Systeme und mindestens drei API-Schlüssel. Dann gibt es noch die Kosten, die niemand erwähnt – nämlich dass Ihre Daten das System verlassen. Jede in BigQuery integrierte Verwaltungsvorschrift, jede Sicherheitsrichtlinie auf Zeilenebene sowie jedes Prüfanforderung hört auf zu gelten, sobald Datenblöcke in einen externen Vektor-Speicher exportiert werden.

Diese Architektur existierte, weil es keine Alternative gab. Die Voraussetzungen haben sich jedoch geändert.

Das LLM als SQL-Operator

Die AI.*-Funktionsfamilie in BigQuery ist nun allgemein verfügbar. Für RAG sind drei Funktionen besonders wichtig:

  • AI.GENERATE_EMBEDDING erzeugt in großen Mengen Embeddings aus jeder Tabelle.
  • VECTOR_SEARCH führt semantische Suche als eingebaute GoogleSQL-Funktion durch. Sie können einen IVF- oder TreeAH-Index hinzufügen, wobei TreeAH auf Googles ScaNN-Algorithmus basiert.
  • AI.GENERATE ist eine zeilenweise skalare Funktion, die über ein output_schema strukturierte Ausgaben zurückgeben kann, und sie passt an jeden Ort, an dem ein Wert verwendet wird – einschließlich SELECT-, WHERE- und sogar JOIN-Bedingungen.

Kein Schlüssel erscheint in Ihrem Code. Die Authentifizierung erfolgt über eine BigQuery-Verbindung, ein verwaltetes Servicekonto, dem Sie einmal eine Rolle zuweisen. Es gibt bereits eine Standardverbindung, was den größten Teil der Einrichtung überflüssig macht.

Sie sind nicht an Gemini gebunden

Eine häufige Sorge ist, dass dies Sie an Gemini bindet. Das ist jedoch nicht der Fall, obwohl die Details oft falsch verstanden werden. Die Funktionen gibt es in zwei Arten.

Allgemeine Funktionen wie AI.GENERATE_TEXT, AI.GENERATE_TABLE und AI.GENERATE_EMBEDDING mit dem Argument MODEL geben Ihnen volle Kontrolle darüber, welches Modell ausgeführt wird. Unterstützte entfernte Modelle umfassen die Gemini-Familie sowie Anthropic Claude, Meta Llama und Mistral AI als verwaltete Endpunkte, sowie Open-Source-Modelle von Hugging Face oder dem Model Garden auf Googles Agentenplattform (früher Vertex AI). Für offene Modelle kann CREATE MODEL das Modell sogar automatisch bereitstellen.

Verwaltete Funktionen (AI.IF, AI.CLASSIFY, AI.SCORE sowie das skalare AI.GENERATE bei Angabe eines endpoint) sind abgespeckte Versionen, die auf Gemini basieren. BigQuery wählt das Modell aus und kümmert sich um die Anfrageformulierung.

Es wird behauptet, dass die Konformität Embeddings aus einem selbst gehosteten mehrsprachigen E5- oder Qwen-Modell erfordert, wobei Claude die Antworten generiert. Beides beginnt mit einem einzigen CREATE MODEL-Befehl. Bei den Embeddings handelt es sich um die vollständige Änderung, da sich nichts danach weiter verändert. Die Generierung weist jedoch eine Einschränkung auf.

Weg eins: Einmal-Pipeline (allgemein verfügbar)

Dieses Muster eignet sich für Batch- und Analysearbeiten, bei denen ein Korpus einmal oder nach einem Zeitplan eingebettet und anschließend abgefragt wird.

Schritt 0: Einmalige Einrichtung

Die Verbindung ist der Schritt, den die meisten für Warehouse konzipierten Tutorials weglassen. Es handelt sich um das verwaltete Servicekonto, über das BigQuery auf die Modelle zugreift, und dieses wird nur einmal erstellt.

Falls Sie die BigQuery-Admin-Rolle haben, ist der schnellste Weg die Standardverbindung. Geben Sie WITH CONNECTION DEFAULT in den Befehl ein, und BigQuery stellt sie bereit sowie weist bei erster Nutzung die entsprechenden Rollen zu.

Falls Sie die Verbindung selbst verwalten möchten – wofür im zweiten Schritt der Pfad us.my_connection benötigt wird – sind zwei Befehle nötig: Zuerst wird eine Cloud-Ressourcenverbindung erstellt, anschließend erhält deren Service-Konto die Rolle „Agent Platform User“ (roles/aiplatform.user, früher Vertex AI User).

PROJECT_ID=my-poject-id

# 1. Create a Cloud resource connection (its location must match your dataset)
bq mk --connection --location=US --project_id=$PROJECT_ID \
  --connection_type=CLOUD_RESOURCE my_connection

# 2. Grant the connection's service account the Agent Platform User role
gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="serviceAccount:$(bq show --format=prettyjson \
    --connection $PROJECT_ID.US.my_connection | jq -r .cloudResource.serviceAccountId)" \
  --role=roles/aiplatform.user

Die Entscheidung, welche Rolle vergeben wird, hängt davon ab, wie auf das Modell verwiesen wird: im Projekt, das das Modell besitzt, wenn ein Name übergeben wird, oder im Projekt aus der Endpunkt-URL, wenn eine vollständige URL übergeben wird. Die Authentifizierung endet hier mit einer Rolle und ohne Schlüssel.

Danach wird das Embedding-Modell verwendet, wobei die Standardverbindung wiederverwendet wird:

CREATE OR REPLACE MODEL `rag_demo_us.emb_model`
  REMOTE WITH CONNECTION DEFAULT
  OPTIONS (ENDPOINT = 'gemini-embedding-001');

Diese Aussage stellt die gesamte Bereitstellung des Embedding-Dienstes dar. Es gibt keinen Container, keinen Endpunkt zum Skalieren und keinen Schlüssel zum Ersetzen. Wenn Sie ein Open-Source-Modell bereitgestellt haben, ersetzen Sie einfach dessen Endpunkt-URL.

Schritt 1: Das Korpus einbetten

Eine Aussage einbettet die BBC-Artikel:

CREATE OR REPLACE TABLE `rag_demo_us.news_emb` AS
SELECT *
FROM AI.GENERATE_EMBEDDING(
  MODEL `rag_demo_us.emb_model`,
  (
    SELECT title, body AS content
    FROM `bigquery-public-data.bbc_news.fulltext`
    WHERE category = 'tech'
  ),
  STRUCT('RETRIEVAL_DOCUMENT' AS task_type)
);

Zwei Details können leicht übersehen werden. Die Eingabeanfrage muss eine Spalte mit dem Namen content enthalten, gegebenenfalls mit einem Alias versehen, und die Hinzufügung einer title-Spalte verbessert die Qualität der Embeddings für RETRIEVAL_DOCUMENT. Zudem ist task_type wichtig: Verwenden Sie RETRIEVAL_DOCUMENT für das Korpus und RETRIEVAL_QUERY für die Frage des Benutzers, da asymmetrische Embeddings die Suchqualität deutlich verbessern.

Die resultierende Tabelle enthält Ihre ursprünglichen Spalten sowie drei neu erstellte Spalten. embedding enthält den Vektor, statistics ist ein JSON-Wert mit token_count und truncated, und status gibt Fehler an. Beachten Sie, dass die neuere Version AI.GENERATE_EMBEDDING embedding zurückgibt, und nicht ml_generate_embedding_result wie in den älteren ML.GENERATE_EMBEDDING-Anleitungen. Die Verwendung beider Strukturen gleichzeitig führt typischerweise zu Fehlern durch falsches Kopieren und Einfügen.

Erster Fehlerpunkt: stilles Teilversagen. Eine Batch-Aufgabe kann insgesamt erfolgreich sein, während einzelne Zeilen aufgrund von Quotenbeschränkungen oder Dienststörungen fehlschlagen. BigQuery gibt dies pro Zeile im Feld status an, wobei ein leeres String-Wert einen Erfolg bedeutet; filtern Sie daher immer mit WHERE status = ''. Wenn parallele Batch-Aufgaben auf Ressourcenengpässe stoßen, stellt Google Wiederholungs-Skripte für Remote-Inferenz sowie ein Dataform-Paket bereit, die so lange wiederholt werden, bis jede Zeile verarbeitet ist.

Zweiter Fehlerpunkt: stilles Trunkieren. Die statistics jeder Zeile zeigen die Anzahl der Token sowie ein Trunkierungsflag. Ein Nachrichtenartikel umfasst ein paar hundert Token, doch Dokumente, die die Eingabegrenze des Modells überschreiten, werden ohne Fehler in gekürzter Form eingebettet. Bei langen Dokumenten sollten Sie das Trunkierungsflag überwachen und diese im Voraus in Teile aufteilen.

Schritt 2: Optional einen Index hinzufügen

Kleine Korpora können darauf verzichten, da VECTOR_SEARCH auf eine exakte Brute-Force-Suche zurückgreift. In größeren Skalen sollte man eines erstellen:

CREATE OR REPLACE VECTOR INDEX news_idx
ON `rag_demo_us.news_emb`(embedding)
OPTIONS (index_type = 'IVF', distance_type = 'COSINE');

Zu erwartende Verhaltensweisen: Die Pflege des Index wird im Hintergrund für Sie übernommen, später hinzugefügte Zeilen können sofort gesucht werden (BigQuery durchsucht den unindizierten Rest mittels Brute-Force), Tabellen unter 10 MB erhalten keinen indizierten Index, und die Standard-Edition unterstützt keine Indizes.

Schritt 3: Abruf und Erstellung in einer Abfrage

Die Frage einbetten, die fünf besten Artikel abrufen, die Anfrage verarbeiten und eine getippte Antwort erstellen:

SELECT
  AI.GENERATE(
    (
      'Answer using only this context:\n',
      STRING_AGG(base.content, '\n---\n'),
      '\nQuestion: what were the major browser security stories?'
    ),
    endpoint      => 'gemini-2.5-flash',
    output_schema => 'answer STRING, confidence FLOAT64, sources ARRAY<STRING>'
  ).*
FROM VECTOR_SEARCH(
  TABLE `rag_demo_us.news_emb`, 'embedding',
  (
    SELECT embedding
    FROM AI.GENERATE_EMBEDDING(
      MODEL `rag_demo_us.emb_model`,
      (SELECT 'major browser security stories' AS content),
      STRUCT('RETRIEVAL_QUERY' AS task_type)
    )
  ),
  top_k => 5
);

Der Kern des Pipelines umfasst etwa ein Dutzend Zeilen – ohne LangChain, ohne externe Vektordatenbank und ohne etwas, was bereitgestellt werden müsste. output_schema liefert typisierte Spalten für die Antwort, einen Zuverlässigkeitswert sowie Quellen, anstatt eines JSON-Objekts, das man erst parsen und dessen Inhalt man dann interpretieren muss.

Die Einschränkung der One-Shot-Version ist jedoch ernsthaft: Die Daten bleiben nicht konstant. Neue Artikel erscheinen, Beschreibungen ändern sich und Tickets werden aktualisiert. Jede Eingabe oder Bearbeitung bedeutet, den Embedding-Vorgang erneut auszuführen, Änderungen zu erkennen, Wiederholungsversuche zu handhaben und Veraltungen zu überwachen.

Der zweite Weg: ein sich selbst wartendes Korpus (Vorabversion)

Autonome Erstellung von Embeddings ist die Lösung. Deklarieren Sie den Embedding als generierte Spalte – dann übernimmt BigQuery die Aktualisierung davon:

CREATE TABLE `rag_demo_us.news_live` (
  title STRING,
  body  STRING,
  body_embedding STRUCT<result ARRAY<FLOAT64>, status STRING>
    GENERATED ALWAYS AS (
      AI.EMBED(
        body,
        connection_id => 'us.my_connection',
        endpoint      => 'gemini-embedding-001')
    ) STORED OPTIONS (asynchronous = TRUE)
);

Dann fügen Sie Daten von überall ein:

INSERT INTO `rag_demo_us.news_live` (title, body)
SELECT title, body
FROM `bigquery-public-data.bbc_news.fulltext`
WHERE category = 'tech';

Das Design lohnt es, zweimal gelesen zu werden. Mit GENERATED ALWAYS AS AI.EMBED(...) wird die Embedding-Datenstruktur zu Metadaten der Tabelle und nicht zu Code des Pipelines. Wenn die Quellspalte eingefügt oder aktualisiert wird, erstellt BigQuery im Hintergrund den Vektor oder aktualisiert ihn. Mit asynchronous = TRUE warten die Einfügevorgänge nicht auf Aufrufe des Modells, und die Embedding-Arbeit wird in Hintergrundprozessen ausgeführt, deren Aufgaben im Aufgabenverlauf ein gc_-Präfix aufweisen. Die Spalte hat die Struktur STRUCT<result, status>, sodass Fehler pro Zeile angezeigt werden und nicht unterdrückt werden.

Bei der Auswahl des Modells wird im Beispiel gemini-embedding-001 verwendet, weil es zu dieser Zeit allgemein verfügbar war und auf der multilingualen MTEB-Leaderboard-Liste gute Leistungen zeigte. Das kanonische Beispiel in der Dokumentation nutzt hingegen text-embedding-005, das genauso gut funktioniert. Man sollte sich einer Folge bewusst sein: Die Standardausgabe hat 3072 Dimensionen, während die text-embedding-Modelle 768 Dimensionen haben, was die Speicher- und Indexkosten erhöht. Um dies zu vermeiden, sollte man über model_params bei AI.EMBED eine kleinere Größe angeben. Das Modell wird im Matryoschka-Stil trainiert, wodurch es sauber auf 768 oder 256 Dimensionen abgeschnitten werden kann.

Auch die Abfragen werden einfacher. AI.SEARCH prüft, mit welchem Modell die Tabelle erstellt wurde, und embeddet den Suchstring für den Benutzer automatisch:

SELECT base.title, base.body, distance
FROM AI.SEARCH(
  TABLE `rag_demo_us.news_live`, 'body',
  'major browser security stories',
  top_k => 5
);

Der Text wird in einer Textspalte durchsucht, wobei das Lager im Hintergrund Vektoren verarbeitet. Auch Vektorindizes werden hier unterstützt: Erstellen Sie einen auf der Quellspalte, und das Training beginnt, sobald 80 % der Zeilen Embeddings enthalten.

Für alles, was in die Produktion geht, ist diese Version entscheidend. Der One-Shot-Ansatz dient nur zur Demonstration, während ein selbstwartendes Korpus ein Design ist, das jahrelang genutzt werden kann.

Doch es gilt eine Einschränkung: Dieser Teil befindet sich im Vorschauzustand und unter vorläufigen Bedingungen, die sich ändern können. Berücksichtigen Sie außerdem, dass jede Tabelle eine automatisch generierte Einbettungskolonne erhält, dass Einfügevorgänge in Batches durchgeführt werden sollten und nicht als häufige DML-Aktionen, da gleichzeitige DML-Vorgänge die Generierung verzögern können, sowie dass Kopien, Klone und Snapshots die Daten ohne die Generierungs-Einrichtung enthalten. In den Veröffentlichungsnotizen steht, dass Sie dies für neue oder bestehende Tabellen über CREATE TABLE oder ALTER TABLE aktivieren können. Die Veröffentlichungsnotizen von BigQuery sind die zuverlässige Quelle bezüglich des Status der Funktionen, daher sollten Sie diese vor der Entwicklung prüfen.

Was Sie ohne zusätzliche Arbeit erhalten

Das ist der Aspekt, um den sich Daten-Teams am meisten kümmern sollten – und dem am wenigsten Aufmerksamkeit geschenkt wird.

  • Die Sicherheit auf Zeilen- und Spaltenebene gilt auch für VECTOR_SEARCH. Eine Zeile, die ein Benutzer nicht sehen darf, erreicht niemals als RAG-Kontext die Abfrage. Diese Garantie durch eine synchronisierte externe Vektordatenbank zu gewährleisten, ist schwierig.
  • IAM ist das einzige Authentifizierungsmodell. Es gibt keine Schlüssel in Umgebungsvariablen und keine Vorgehensanleitungen für die Rotation im Pipeline.
  • Auditsprotokolle, Kostenzuordnung und Quoten stammen alle aus der Infrastruktur, die Ihr Plattformteam bereits betreibt. Über das Label bigquery_job_id_prefix können Sie sogar die Kosten pro Auftrag in Cloud Billing nachverfolgen.
  • Es muss kein Synchronisierungsjob ausgeführt werden. Vektoren befinden sich direkt neben den Zeilen, die sie beschreiben, sodass ein Vektorlager nicht vom Lager abweichen kann, wenn es nur ein einziges Lager gibt. Bei autonomen Embeddings benötigt auch kein Aktualisierungsjob einen Betreuer.

Die beste Infrastruktur ist die, die man niemals einsetzt.

Wo es versagt

  1. Die Abrechnung erfolgt nach Token, und Gemini 2.5 berechnet auch das Denken. Diese Modelle verlangen Gebühren für Reasoning-Token zusätzlich zur Ausgabe. Bei Klassifizierungsaufgaben verringert das Setzen des Denk-Budgets auf Null in model_params sowohl die Latenz als auch die Kosten. Überspringt man diesen Schritt, steigen die Batch-Kosten unbemerkt an. Verwenden Sie zunächst AI.COUNT_TOKENS, um eine Schätzung zu erhalten.
  2. Komplexe Abfragen können dem Modell eine andere Anzahl an Zeilen liefern, als man erwartet. Häufige Ursachen sind JOINs sowie ORDER BY mit LIMIT. Google empfiehlt, die Eingabedaten zunächst in eine Tabelle zu schreiben und anschließend die Inferenz auf dieser Tabelle durchzuführen.
  • Quoten sind real. Die Einbettungsleistung ist durch Quoten in Form von Tokens pro Minute begrenzt, standardmäßig einige Millionen. Das eignet sich für die meisten Arbeitslasten, doch ein Plan, einen ganzen Data Lake über Nacht einzubetten, erfordert Vorbereitungen – oder die Verwendung von Provisioned Throughput mit request_type => 'dedicated'.
  • VECTOR_SEARCH wird nach gescannten Bytes abgerechnet. Bei On-Demand-Preisen werden die Basistabelle, der Index sowie die Abfrage berücksichtigt. Platzieren Sie STORING-Spalten im Index, damit Sie nicht wieder zur Basistabelle zurückgehen müssen, und filtern Sie frühzeitig mit WHERE an diesen gespeicherten Spalten.
  • Die Prompts werden nicht versioniert. Ein Prompt ist einfach nur ein String innerhalb von SQL, daher gibt es weder ein Registry-System noch A/B-Tests oder Bewertungstools. Teams, die die Prompts eines Produktionsassistenten wöchentlich überarbeiten, spüren das, und genau hier kommt eine kleine Python-Schicht oder dbt zum Tragen, das die Prompts als versionierte Konfigurationen speichert.
  • Überprüfen Sie das Status-Label

    Der Status der Funktionen ändert sich, daher sollten Sie ihn überprüfen. Wie ursprünglich berichtet, standen AI.GENERATE und AI.GENERATE_TABLE Ende Januar 2026 zur allgemeinen Nutzung zur Verfügung, während die Partnermodelle (Claude, Llama, Mistral) in der ersten Hälfte des Jahres 2025 verfügbar wurden. Vorab-Funktionen wie autonome Embeddings könnten seitdem weiterentwickelt worden sein – die Veröffentlichungsnotizen sind der richtige Ort, um dies zu bestätigen.

    Brauchen Sie wirklich nur SQL?

    Daraus ergeben sich drei Schlussfolgerungen.

    Für batchbasierte und analytische RAG-Anwendungen lohnt es sich, dies ernsthaft in Betracht zu ziehen. Dokumentklassifizierung, semantische Deduplizierung, Bereichserweiterungspipelines sowie interne Q&A-Funktionen an gemanagten Daten sprechen alles für den auf dem Datenlager basierenden Ansatz hinsichtlich Governance, Einfachheit und Gesamtkosten – außerdem behält man die Wahl des Modells bei.

    Ein ständig wechselndes Korpus ist ein anderes Szenario, und autonome Embeddings verschieben das Gleichgewicht. Die Suche war nie der schwierige Teil bei produktiven RAG-Anwendungen; vielmehr war es die Aufrechterhaltung der Vektoren in Einklang mit den aktuellen Daten – und eine generierte Spalte beseitigt diese gesamte Arbeitskategorie.

    Die entscheidende Fähigkeit besteht darin, die Grenzen zu kennen. Eine untersekundenschnelle Benutzerschnittstelle erfordert weiterhin eine dedizierte Serviceschicht, das Prompt-Lebenszyklus-Management weiterhin Versionierung und die Kosten pro Token weiterhin Überwachung. Diese Einschränkungen sind nicht verschwunden – sie haben sich nur verlagert.

    Zur weiteren Lektüre bietet die Google BigQuery-Dokumentation einen Überblick über generative KI sowie eine Matrix zur Modellunterstützung, CREATE MODEL für Partner- und offene Modelle, AI.GENERATE, AI.GENERATE_EMBEDDING, AI.SEARCH, autonome Erstellung von Embeddings, Suchfunktionen, Verwaltung von Vektorindizes sowie Release-Notes.