Prompt-Engineering für eine bessere SQL-Codegenerierung mit LLMs
29.05.2024
•
Raghid Bsat
Stellen Sie sich vor, Sie sind ein Marketing-Manager, der damit beauftragt ist, Werbestrategien zu optimieren, um verschiedene Kundensegmente effektiv anzusprechen…
Stellen Sie sich vor, Sie sind ein Marketing-Manager, der damit beauftragt ist, Werbestrategien zu optimieren, um verschiedene Kunden-Segmente effektiv anzusprechen. Sie sind gefordert, die folgende Frage zu beantworten: „Welche Kunden-Segmente erzeugen den höchsten Return on Investment für unsere Werbekampagne?“. Sie möchten vielleicht die Rentabilität der Kunden-Segmente betrachten und sie mit dem Customer Lifetime Value (CLV) vergleichen, um solche Fragen zu beantworten. Die folgende SQL-Abfrage könnte die Antwort sein:
Das Erstellen einer solchen komplexen Abfrage erfordert Fachwissen und Kontext, die über den Umfang vieler Geschäftsprofis hinausgehen, und würde mehr Zeit in Anspruch nehmen, um erstellt zu werden. Oft gibt es eine Sprachbarriere zwischen Geschäftsführern und den datengestützten Entscheidungen, die sie treffen müssen. Dies wird typischerweise durch SQL kodiert, das viele Geschäftsprofis als einschüchternd oder kaum praktikabel empfinden. Der Aufstieg von GenAI hat die Tür für KI-Assistenten geöffnet, solche Barrieren zu überwinden, wobei große Sprachmodelle (LLMs) die Fähigkeit gezeigt haben, natürliche Sprache zu verstehen und zu generieren. Die Verwendung von LLMs zur Erstellung von SQL-Anweisungen aus natürlicher Sprache kann das Problem des Datenzugriffs für nicht-technische Profile erleichtern.
Bis heute gab es viele Versuche, einen fortschrittlichen LLM SQL-Abfragegenerator zu erstellen. Ein Beispiel dafür ist MotherDuck’s „AI that Quacks, DUCKDB NSQL-7B“. Dieser Blogbeitrag erklärt die Bemühungen von MotherDuck und Numbers Station, ein Text-zu-SQL LLM zu erstellen. Sie haben ein Open Source LLM-Modell unter Verwendung des Basis-Modells von CodeLLama erstellt und es mit ihrem benutzerdefinierten Trainingssatz verfeinert, der eine modifizierte Version von NSText2SQL ist. Sie haben eine außergewöhnlich gute Leistung bei den verwendeten SQL-Benchmarks berichtet. Während dieses Tool erstaunlich klingt und das Trainieren eines eigenen Open-Source-Modells auf einem benutzerdefinierten SQL-Trainingsdatensatz einen bedeutenden Fortschritt im Vergleich zur bloßen Verwendung vorhandener Modelle darstellen kann, ist es wichtig, dem Modell zusätzlichen Kontext zu liefern. Dies beinhaltet das Erstellen präziser und kontextuell angemessener Eingabeaufforderungen, um die gewünschten Antworten von Sprachmodellen zu erhalten. Mit einer geeigneten Eingabeaufforderung wird dem LLM ein besserer Kontext gegeben, um fehlerfreie Abfragen zu erstellen. In diesem Blogbeitrag werde ich folgendes ansprechen: die Herausforderung der Eingabeaufforderungsgestaltung und die Szenarien, in denen die Nutzung einer Retrieval Augmented Generation (RAG)-Lösung vorteilhaft ist, im Vergleich dazu, alle Informationen direkt in die Eingabeaufforderung aufzunehmen.
…
Wie erstellt man eine effektive Eingabeaufforderung?
Datenbankschema einbeziehen
Die größte Herausforderung bei der Verwendung eines LLM zur Generierung von SQL-Code besteht darin, das Modell über das Schema der Tabellen, die wir abfragen möchten, zu informieren. Solche Informationen können in einem, was wir als „Kontextfenster“ bezeichnen, bereitgestellt werden, das die Anzahl der Tokens (~Wörter) ist, die das Modell als Eingabe verwenden kann, wenn es Antworten generiert. Im Fall der Generierung von SQL-Code können wir unser Datenbankschema in den Kontext einbeziehen, bevor wir unsere Frage stellen. Der Haken ist, dass LLMs mit einem begrenzten Kontextfenster kommen. Zum Beispiel hat GPT-4 ein Fenster von 128.000 Tokens (~96.000 Wörter). Wenn Ihre Datenbank klein genug ist, können Sie das vollständige Schema als Teil der Eingabeaufforderung bereitstellen.
Dies ist ein Beispiel für eine Eingabeaufforderung mit dem einbezogenen Datenbankschema:
Als ich dies mit dem Snowflake-Beispieldatensatz (TPCH_SF1) ausprobierte, bemerkte ich, dass im Allgemeinen die einfachen Abfragen korrekt beantwortet wurden. Diese Abfragen zeichnen sich durch einfache Filter und mehrere Bedingungen aus:
Benutzerfeedback einbeziehen (Sitzungshistorie)
Im Fall von komplexen Abfragen mit mehreren Joins und Aggregationen war das LLM nicht in der Lage, genaue Ergebnisse zu reproduzieren. Es gab zwei Hauptprobleme, mit denen das LLM konfrontiert war:
Semantische Komplexität zwischen Tabellen: Komplexe Abfragen beinhalten oft spezifische Beziehungen zwischen Tabellen, geschachtelte Unterabfragen und bedingte Logik. LLMs könnten Schwierigkeiten haben, die volle semantische Bedeutung dieser komplexen Strukturen zu erfassen, was zu Ungenauigkeiten beim Verständnis und der Reproduktion der beabsichtigten Abfragesemantik führt.
Unklarheiten im Geschäft: Es kann bestimmte mehrdeutige Verweise geben, wie z.B. Spaltenalias oder mehrdeutige Tabellenreferenzen. Das Lösen dieser Mehrdeutigkeiten erfordert kontextuelles Verständnis und Inferenzfähigkeiten, die für LLMs eine Herausforderung darstellen können. Im Fall von Abfrage 2 (unten) nahm das LLM an, dass einige Spalten bereits im Schema existieren, was eine ungültige SQL-Anweisung erzeugte.
Benutzerfeedback kann das LLM in die richtige Richtung „drücken“, was nach einem fehlgeschlagenen Versuch zu einer korrekten Abfrage führt. Dies ist der nächste Schritt in der Eingabeaufforderungsgestaltung: Das Hinzufügen weiterer relevanter kontextueller Informationen neben den Datenbankschemas kann entscheidend für die optimierte SQL-Code-Generierung sein. Sie können die Sitzungshistorie als Teil der Eingabeaufforderung wie folgt einbeziehen:
Geschäftsinformationen einbeziehen
Für tiefergehende Geschäftskontexte neigen große Organisationen dazu, relevante Daten überall in verschiedenen Formaten und Quellen verstreut zu haben. Ich schlage vor, die Daten in drei verschiedene Kategorien zu unterteilen:
Fachspezifisches Wissen: Dies ist die Information, die für die Frage relevant sein kann. Zum Beispiel können wir Aussagen wie die folgenden einbeziehen:
| Die belgische Niederlassung heißt die „Schokoladen-Haven“-Niederlassung
| Die Benelux-Region besteht aus den Niederlanden, Luxemburg und Belgien;
| Marketingkampagnen in der Region „Ardennen“ haben historisch gesehen höhere Konversionsraten gezeigt.
Historisch verwendete Abfragen: Dies sind Abfragen, die häufig von Geschäftsanalysten verwendet werden und die Standardabfragen für gängige analytische Bedürfnisse sind: Monatlicher Verkaufsbericht nach Region, Analyse der Kundenabwanderung nach Produktkategorie, meistverkaufte Produkte nach Quartal, Rentabilitätsanalyse nach Kundensegment.
Datenumwandlungs- und Anreicherungslogik: Diese Kategorie umfasst die Logik und Regeln, die angewendet werden, um Rohdaten in sinnvolle Geschäftseinblicke zu transformieren und anzureichern. Einige Beispiele sind: Berechnungen für abgeleitete Kennzahlen wie Customer Lifetime Value (CLV), durchschnittlicher Bestellwert (AOV) oder Konversionsraten; Zeitbasierte Berechnungen für Trendanalysen, Saisonalitätsanpassungen oder Kohortenanalysen.
Bonus-Kategorie: Geschäftsregeln und -richtlinien: Während dies möglicherweise noch experimentell ist und es keine festgelegte Lösung für die Handhabung von Datenverwaltung mit GenAI gibt, würde ich dies im Allgemeinen mit Vorsicht behandeln. Diese Kategorie umfasst Regeln wie Zugriffssteuerungsrichtlinien, die bestimmen, wer innerhalb der Organisation Berechtigungen hat, um bestimmte Tabellen oder Richtlinien zur Datenaufbewahrung anzusehen oder zu ändern. Ich würde dies stattdessen in ein SQL-säuberungslogikbasiertes Modul verschieben, nachdem das LLM eine Antwort zurückgegeben hat.
Die endgültige Eingabeaufforderung in Aktion:
…
Bringen Sie Ihren LLM-SQL-Bot mit Datenengineering auf die nächste Stufe:
Für einen potenziellen zukünftigen Kunden haben wir ein Proof-of-Concept erstellt, das die Möglichkeiten von LLMs mit der Generierung von SQL-Code auf der Basis ihres Data Warehouses zeigt. Wir haben eine einfache Benutzeroberfläche erstellt, mit der Geschäftsprofis mit einem Chatbot interagieren, SQL-Anweisungen generieren und die resultierende Tabelle ausdrucken können. Im Fall der Demo, an der ich gearbeitet habe, enthielt die Datenbank nur 8 Tabellen; mit einer variierenden Schema-Länge zwischen 3 und 18 Spalten. Das Erstellen der Data Definition Language (DDL) SQL-Befehle (CREATE TABLE …) summiert sich auf 1k Tokens. In diesem kleinen Beispiel hatte ich genügend Raum mit dem Token-Limit, um alles im selben Kontext bereitzustellen. Dies mag jedoch nicht der Fall sein für große Organisationen, die tendenziell mehrere hundert Tabellen in ihren Datenbanken haben. Hier kommt die Macht der Retrieval-Augmented Generation (RAG) ins Spiel. Ich habe mich auf die Architektur verlassen, die von Kaarthik in seinem Blogbeitrag vorgeschlagen wurde. Seine Hauptidee war die Verwendung eines RAG, das Informationen aus einer Vektordatenbank abruft, die mit den Tabellenschemas erstellt wurde. Das Ergebnis der Abfrage würde später in die Eingabeaufforderung gegeben werden, die an das LLM über eine API weitergegeben wird. Zusammenfassend ist dies die verfolgte Architektur:

Durch die Verwendung eines RAG müssten Sie nicht den gesamten Kontext in die Eingabeaufforderung einfügen, sondern nur die relevantesten Teile. Die Idee eines RAG besteht darin, auf vorhandenes Wissen oder Daten zuzugreifen, um genaue Antworten zu geben. Die relevanten Informationen werden abgerufen und in die Eingabeaufforderung des LLM eingefügt, um bessere Antworten zu generieren. Im Fall dieser Demo waren es die Tabellenschemata (Datenbankschema-Abfragen), die als readme-Dateien mit zusätzlichem Kontext für jede Tabelle in die Vektordatenbank eingefügt wurden. Relevante Schemata werden mit einer Ähnlichkeitssuche abgerufen, bevor sie in die Eingabeaufforderung eingearbeitet und an das LLM als API-Aufruf weitergegeben werden. Vektoren werden in der Vektordatenbank erstellt, indem die Eingabeabfrage als numerischer Vektor dargestellt wird und mit dem gespeicherten numerischen Vektor verglichen wird. Die Abruffunktion würde N-ähnliche Datensätze zurückgeben. Hier können Sie mehr darüber lesen, wie Sie Ähnlichkeiten zwischen Vektor-Embeddings hier speichern und abrufen können.
Das Speichern und Indizieren von Geschäftskontextkategorien, Datenbankschemas und Benutzerfeedback effizient in einer Vektordatenbank ist, wo die Magie des Datenengineerings geschehen sollte: Ob die Datenquellen PDFs, Word-Dokumente, Textdateien oder Datenbanken sind, sie können in eine zentrale Datenbank aufgenommen werden, in der sie strukturiert, indiziert und durchsuchbar gemacht werden.
Fachspezifisches Wissen kann in einem Wissensgraphen gespeichert werden, in dem Entitäten und ihre Beziehungen als Knoten und Kanten dargestellt werden. Historisch verwendete Abfragen können in einer Abfragetabelle gespeichert werden, in der sie kategorisiert und nach ihrem Zweck und ihrer Nutzung gekennzeichnet werden. Die Logik der Datenumwandlung und -anreicherung kann in einer Regelbuchdatenbank gespeichert werden. Als Dateningenieur streben wir danach, Systeme zu bauen, die nicht nur effizient, sondern auch flexibel und reaktionsschnell auf Veränderungen sind. Durch die Nutzung der Power von ETL (Extract, Transform, Load)-Pipelines können wir dynamische Prozesse erstellen, die unsere Geschäftswissensdatenbanken automatisch aktualisieren, wenn neue Einsichten entstehen und sich Geschäftskontexte weiterentwickeln. Dies kann ein Überwachungs-ETL-Pipeline umfassen, die kontinuierlich diese verschiedenen Datenquellen beobachtet, die notwendigen Transformationen anwendet und die verbundenen Datenbanken aktualisiert.
Große Kontextfenster
Google’s Gemini 1.5 hat eine Kontextfenstergröße von 128k Tokens integriert. Jedoch können bereits eine begrenzte Anzahl von Entwicklern ihre verfeinerte Version ausprobieren, die ein Fenster von bis zu 1 Million Tokens enthält. Dies wirft die Frage auf, ob zu diesem Zeitpunkt wirklich ein RAG benötigt wird? Mit einer so großen Fenstergröße können diese Modelle 10-mal mehr Informationen innerhalb einer Eingabeaufforderung festhalten, ohne die Notwendigkeit, relevante Informationen in einer Vektordatenbank abzurufen. Dies kann zu einem effizienteren Inferenz- und Trainingsprozess führen, da der Mittelsmann eines RAG entfällt und das Modell direkten Zugriff auf alle wichtigen Kontexte hat.
Ich würde jedoch argumentieren, dass ein RAG in diesem Fall weiterhin relevant wäre: Das Ziel besteht darin, gründlich zu verstehen, welchen Kontext das LLM-Modell als „relevant“ betrachtete, als es eine Abfrage generierte. Es könnte nicht ausreichen, eine Abfrage für einen Geschäftsanalysten magisch zu generieren. Das Abrufen des relevanten Kontexts kann zurückgegeben werden, um zu erklären, wie diese bestimmte Abfrage generiert wurde, damit der Mensch sie bewerten kann.
…
Schlussfolgerung:
Zusammenfassend empfehle ich für einen LLM-SQL-Bot, Ihr eigenes Open Source-Modell (ähnlich dem von DuckDB NSQL) zu trainieren und auch die in diesem Blogbeitrag erwähnten Geschäftskategorien einzubeziehen. Dies stellt sicher, dass das Modell über wichtige Kontexte beim Generieren von Antworten gut informiert ist. Motherduck’s NSQL-7B macht einen guten Job darin, zu validieren, ob die generierte SQL-Anweisung im bereitgestellten Schema funktioniert oder nicht. Dieser Validierungsprozess fungiert im Wesentlichen als Sanitärschritt für Ihre Abfrageanweisungen und hilft, mögliche Fehler oder Unstimmigkeiten zu erkennen, bevor sie Probleme in Ihrer Datenbank verursachen. Abhängig von dem Datenvolumen in der Organisation könnte man entweder entscheiden, alle Informationen direkt in die Eingabeaufforderung aufzunehmen oder eine RAG-Lösung zu erstellen.
Es ist ebenso wichtig, die Bedeutung von sauberen und bedeutungsvollen Daten sowie eine gut gestaltete Eingabeaufforderung zu betonen, die den relevanten Geschäftskontext erfasst. Wenn Organisationen dazu bereit sind, ihren eigenen LLM-SQL-Bot zu erstellen, empfehle ich, die Datenqualität, die Datenerfassung und die Datenbereinigung zu priorisieren. Das Normalisieren der Daten in ein strukturiertes Format hat sich ebenfalls als besseres Format für das LLM erwiesen, um es zu verstehen.
Latest
Portable by design: Rethinking data platforms in the age of digital sovereignty
Build a portable, EU-compliant data platform and avoid vendor lock-in—discover our cloud-neutral stack in this deep-dive blog.
Cloud-Unabhängigkeit: Test eines europäischen Cloud-Anbieters gegen die Giganten
Kann ein europäischer Cloud-Anbieter wie Ionos AWS oder Azure ersetzen? Wir testen es – und finden überraschende Vorteile in Bezug auf Kosten, Kontrolle und Unabhängigkeit.
Vermeide schlechte Daten von Anfang an
Das Erfassen aller Daten ohne Qualitätsprüfungen führt zu wiederkehrenden Problemen. Priorisieren Sie die Datenqualität von Anfang an, um nachgelagerte Probleme zu vermeiden.