Erstellung eines Agentic RAG für Text-to-SQL-Anwendungen

Die Kombination aus abrufgesteigerter Generierung (RAG) und generativen KI-Modellen hat Veränderungen in der natürlichen Sprachverarbeitung durch Verbesserung der Antworten auf Anfragen gebracht. Im Bereich des agenziellen RAG wurde diese herkömmliche Methode, die auf einem monolithischen Modell für Aufgaben beruht, durch die Einführung von Modularität und Autonomie verbessert. Indem der Problemlösungsprozess in Werkzeuge aufgeteilt wird, die in einem Agenten integriert sind, bietet der agenzielle RAG Vorteile wie Genauigkeit, Transparenz, Skalierbarkeit und Debugging-Funktionen.

Die Vision hinter dem agenziellen RAG für Text-to-SQL

Traditionelle RAG-Systeme rufen häufig relevante Dokumente ab und verlassen sich auf ein einzelnes monolithisches Modell zur Generierung von Antworten. Obwohl dies in einigen Fällen eine effektive Methode ist, ist dieser Ansatz bei strukturellen Ausgaben wie der Generierung von SQL möglicherweise nicht am effektivsten. Hier können wir die Kraft des agenziellen RAG-Rahmens nutzen, indem wir:

  1. Die Aufgaben in kleinere, handhabbarere Werkzeuge innerhalb eines Agenten aufteilen
  2. Die Genauigkeit verbessern, indem Aufgaben spezialisierten Werkzeugen zugewiesen werden
  3. Die Transparenz erhöhen, indem die Argumentation und den Arbeitsablauf jedes Werkzeugs verfolgt werden
  4. Die Skalierung und das Debugging durch modulares Design vereinfachen

Lassen Sie uns darüber sprechen, wie dieses Werkzeug funktioniert und welche Rolle jedes einzelne Komponente dabei spielt, Benutzerfragen in genaue SQL-Abfragen zu verwandeln.

Architekturüberblick

Die Struktur umfasst einen Agenten, der Werkzeuge im Text-to-SQL-Arbeitsablauf nutzt. Der Prozess kann wie folgt zusammengefasst werden:

Benutzerabfrage → Abfrage-Transformationstool → Few-Shot-Prompting-Tool → Hybrid-Suchwerkzeug → Re-Ranking-Tool → Tabellenabrufwerkzeug → Prompt-Building-Tool → LLM-Ausführungswerkzeug → SQL-Ausführungswerkzeug → Endausgabe

1. Benutzerabfrage-Transformationstool

Dieses Tool beinhaltet die Verarbeitung der Benutzerabfrage zur besseren Verständnis des LLM. Es behandelt Unklarheiten, umformuliert Benutzerfragen, übersetzt Abkürzungen in ihre Formen und liefert bei Bedarf Kontext.

Verbesserungen

  • Behandlung zeitlicher Bezugnahmen. Begriffe wie „Stand heute“ oder „bisher“ auf konkrete Daten abbilden.
  • Ersetzen mehrdeutiger Wörter. Zum Beispiel könnte „aktuell“ durch „letzte 7 Tage“ ersetzt werden.
  • Verknüpfung von Kurzformen oder Abkürzungen mit ihren Bezeichnungen.

Beispiel

Eingabe: „Zeige aktuelle Verkäufe MTD.“

Transformierte Abfrage: „Rufe Verkaufsdaten der letzten 7 Tage (Monat bis heute) ab.“

Python

 

from datetime import date, timedelta

def transform_query(user_query):
    # Behandlung offener zeitlicher Bezugnahmen
    today = date.today()
    transformations = {
        "as of today": f"up to {today}",
        "till now": f"up to {today}",
        "recent": "last 7 days",
        "last week": f"from {today - timedelta(days=7)} to {today}",
    }
    
    for key, value in transformations.items():
        user_query = user_query.replace(key, value)

    # Zuordnung gängiger Abkürzungen
    abbreviations = {
        "MTD": "Month to Date",
        "YTD": "Year to Date",
    }
    for abbr, full_form in abbreviations.items():
        user_query = user_query.replace(abbr, full_form)

    return user_query

query_transform_tool = Tool(
    name="Query Transformer",
    func=transform_query,
    description="Refines user queries for clarity and specificity, handles abbreviations and open-ended terms."
)

2. Few-Shot-Prompting-Tool

Dieses Tool ruft das LLM auf, um die Frage einer Art aus einem Set zu identifizieren (wir können auch sagen, dass die Vorlage übereinstimmt). Die übereinstimmende Frage verbessert den Prompt mit einem Beispiel-SQL-Query.

Beispiel-Arbeitsablauf

1. Eingabe-Frage: „Zeigen Sie mir die Gesamtverkäufe nach Produkt für die letzten 7 Tage.“

2. Vordefinierte Vorlagen:

  • „Verkauf nach Region gruppiert anzeigen.“ → Beispiel-SQL; SELECT region, SUM(sales) …
  • „Zeigen Sie die Gesamtverkäufe nach Produkt.“ → Beispiel SQL: SELECT Produktname, SUM(Verkäufe) …

3. Ähnlichste Frage: „Zeigen Sie die Gesamtverkäufe nach Produkt.“

4. Ausgabe Beispiel SQL: SELECT Produktname, SUM(Verkäufe) FROM …

Python

 

from langchain.chat_models import ChatOpenAI

llm = ChatOpenAI(model="gpt-4")

predefined_examples = {
    "Show sales grouped by region": "SELECT region, SUM(sales) FROM sales_data GROUP BY region;",
    "Show total sales by product": "SELECT product_name, SUM(sales) FROM sales_data GROUP BY product_name;",
}

def find_similar_question(user_query):
    prompt = "Find the most similar question type for the following user query:\n"
    prompt += f"User Query: {user_query}\n\nOptions:\n"
    for example in predefined_examples.keys():
        prompt += f"- {example}\n"
    prompt += "\nRespond with the closest match."

    response = llm.call_as_function(prompt)
    most_similar = response['content']
    return predefined_examples.get(most_similar, "")

few_shot_tool = Tool(
    name="Few-Shot Prompting",
    func=find_similar_question,
    description="Finds the most similar question type using an additional LLM call and retrieves the corresponding example SQL."
)

3. Hybrid-Suchwerkzeug

Für eine robuste Abfrage kombiniert dieses Werkzeug semantische Suche, Schlüsselwortsuche basierend auf BM25 und schlüsselwortbasiertes Mapping. Die Suchergebnisse dieser Suchmethoden werden durch Reziprok-Rang-Fusion zusammengeführt.

Wie kommt das alles zusammen?

Schlüsselworttabellenzuordnung

Dieser Ansatz ordnet die Tabellen den Schlüsselwörtern zu, die in der Abfrage enthalten sind. Zum Beispiel:

  • Das Vorhandensein von „Verkäufe“ führt dazu, dass die Verkäufe-Tabelle in die engere Auswahl kommt.
  • Das Vorhandensein von „Produkt“ führt dazu, dass die Produkte-Tabelle in die engere Auswahl kommt. 

Schlüsselwortüberlappungszuordnung (BM25)

Dies ist eine Suchmethode, die auf der Schlüsselwortüberlappung basiert und Tabellen gemäß Relevanz in die engere Auswahl nimmt. Dafür wenden wir die BM25-Technik an. Diese sortiert die Dokumente nach Relevanz für eine Benutzersuche. Diese Suchtechnik berücksichtigt die Termhäufigkeit im Blick sowie TF-IDF (Termfrequenz-Inverse Dokumentenfrequenz). 

Die Termfrequenz (TF) hilft dabei, die Häufigkeit eines Begriffs in einem gegebenen Dokument zu messen. Der Ansatz der inversen Dokumentenfrequenz (IDF) unterstreicht Wörter, die in jedem Dokument auftauchen, um ihre Bedeutung zu verringern.

Die Normalisierung berücksichtigt die Dokumentenlänge, um jegliche Voreingenommenheit gegenüber längeren Dokumenten zu verhindern.

Gegeben:

  • sales_data: Enthält Begriffe wie „Verkäufe“, „Datum“, „Produkt.“
  • products: Enthält Begriffe wie „Produkt“, „Kategorie.“
  • orders: Enthält Begriffe wie „Bestellung“, „Datum“, „Kunde.“
  • financials: Enthält Begriffe wie „Umsatz“, „Gewinn“, „Kosten.“

Benutzerabfrage: „Zeige Gesamtverkäufe nach Produkt.“

  • Identifiziere Begriffe in der Benutzerabfrage: [„Verkäufe“, „Produkt“].
  • Sortiere jedes Dokument (basierend auf Häufigkeit und Relevanz dieser Begriffe) in der Datenbanktabelle.

Relevanz der Dokumente:

  • sales: Hohe Relevanz aufgrund von „Verkäufe“ und „Produkt“
  • products: Hohe Relevanz aufgrund von „Produkt.“
  • orders: Geringere Relevanz aufgrund des Vorhandenseins von nur „Verkäufe.“
  • financials: Nicht relevant.

Ausgabe:

Rangliste: [products, sales_data, orders, financials]

Semantische Suche

In dieser Suchmethode finden wir semantisch ähnliche Tabellen mithilfe von Vektor-Einbettungen. Dies erreichen wir, indem wir einen Ähnlichkeitswert wie den Kosinus-Ähnlichkeitswert zwischen dem Dokument (Tabellenvektoren) und den Benutzerabfragevektoren berechnen.

Reziproker Rangfusion

Kombiniert BM25 und semantische Suchergebnisse mithilfe der Reziprokrangfusionstrategie, die unten etwas genauer erläutert wird:

Reziprokrangfusion (RRF) kombiniert BM25 und semantische Suche:

RRF ist eine Methode, um Ergebnisse aus mehreren Ranking-Algorithmen (z. B. BM25 und semantische Suche) zu kombinieren. Es weist jedem Dokument basierend auf seinem Rang in den einzelnen Methoden eine Punktzahl zu und vergibt höhere Punktzahlen an Dokumente, die in mehreren Methoden höher gerankt sind.

RRF-Formel:

RRF(d) = Σ(r ∈ R) 1 / (k + r(d))

Wo:

  • d ist ein Dokument
  • R ist die Menge der Rangierer (Suchmethoden)
  • k ist eine Konstante (typischerweise 60)
  • r(d) ist der Rang des Dokuments d in der Suchmethode r

Schritt-für-Schritt-Beispiel

Eingabedaten.

1. BM25-Ranking-Ergebnisse:

  • Produkte (Rang 1)
  • Verkaufsdaten (Rang 2)
  • Aufträge (Rang 3)

2. Semantische Such-Ranking-Ergebnisse:

  • Verkaufsdaten (Rang 1)
  • Finanzdaten (Rang 2)
  • Produkte (Rang 3)

Schritt-für-Schritt-Fusion

Berechnen Sie für jede Tabelle die Punktzahl:

1. Verkaufsdaten

  • BM25-Rang = 2, Semantischer Rang = 1
  • RRF-Punktzahl = (1/60+2 ) + (1/60+1) = 0.03252

2. Produkte

  • BM25-Rang = 1, Semantischer Rang = 3
  • RRF-Punktzahl = (1/60+1) + (1/60+3)= 0.03226

3. Aufträge

  • BM25-Rang = 3, Semantischer Rang = Nicht gerankt
  • RRF-Wert = (1/60+3)= 0.01587

4. Finanzdaten

  • BM25-Rang = Nicht gerankt, Semantischer Rang = 2
  • RRF-Wert = (1/60+2)=0.01613

5. Sortieren nach RRF-Wert

  • Verkaufsdaten (höchster Wert aufgrund der Top-Platzierung in der semantischen Suche).
  • Produkte (hoher Wert von BM25).
  • Bestellungen (insgesamt geringere Relevanz).
  • Finanzdaten (begrenzte Überschneidung).

Endausgabe: [‚Verkaufsdaten‘, ‚Produkte,‘ ‚Finanzdaten,‘ ‚Bestellungen‘]

Tische, die mithilfe der Schlüsselworttabelle-Mapping abgerufen wurden, sind immer enthalten.

Python

 

from rank_bm25 import BM25Okapi

def hybrid_search(query):
    # Schlüsselwortbasiertes Mapping
    keyword_to_table = {
        "sales": "sales_data",
        "product": "products",
    }
    keyword_results = [table for keyword, table in keyword_to_table.items() if keyword in query.lower()]

    # BM25-Suche
    bm25 = BM25Okapi(["sales_data", "products", "orders", "financials"])
    bm25_results = bm25.get_top_n(query.split(), bm25.corpus, n=5)

    # Semantische Suche
    semantic_results = vector_store.similarity_search(query, k=5)

    # Gegenseitige Rangfusion
    def reciprocal_rank_fusion(results):
        rank_map = {}
        for rank, table in enumerate(results):
            rank_map[table] = rank_map.get(table, 0) + 1 / (1 + rank)
        return sorted(rank_map, key=rank_map.get, reverse=True)

    combined_results = reciprocal_rank_fusion(bm25_results + semantic_results)

    return list(set(keyword_results + combined_results))

hybrid_search_tool = Tool(
    name="Hybrid Search",
    func=hybrid_search,
    description="Combines keyword mapping, BM25, and semantic search with RRF for table retrieval."
)

4. Re-Ranking-Tool

Dieses Tool gewährleistet, dass die relevantesten Tabellen priorisiert werden.

Beispiel

  • Eingabetabellen: [„Verkaufsdaten,“ „Produkte,“ „Finanzdaten“]
  • Neuranglogik
    • Für jede Tabelle wird ein Relevanzwert berechnet, indem die Abfrage und die Tabellenbeschreibung konkateniert werden.
    • Nach Relevanzwert sortieren.
  • Ausgabe: [„Verkaufsdaten,“ „Produkte“]

Ein wenig mehr über die Re-Ranking-Logik:

Der CrossEncoder berechnet einen Relevanzwert, indem er die verknüpfte Abfrage und die Tabellenbeschreibung als ein einzelnes Eingabepaar analysiert. Dieser Prozess umfasst:

  • Eingabepaar. Die Abfrage und jede Tabellenbeschreibung werden gepaart und als Eingabe an den Cross-Encoder übergeben.
  • Gemeinsame Kodierung. Im Gegensatz zu separaten Encodern (z. B. Bi-Encodern) kodiert der Cross-Encoder das Paar gemeinsam, wodurch er den Kontext und die Abhängigkeiten zwischen der Abfrage und der Tabellenbeschreibung besser erfassen kann.
  • Bewertung. Das Modell gibt für jedes Paar einen Relevanzwert aus, der angibt, wie gut die Tabelle zur Abfrage passt.
Python

 

from transformers import pipeline

reranker = pipeline("text-classification", model="cross-encoder/ms-marco-TinyBERT-L-2")

def re_rank_context(query, results):
    scores = [(doc, reranker(query + " " + doc)[0]['score']) for doc in results]
    return [doc for doc, score in sorted(scores, key=lambda x: x[1], reverse=True)]

re_rank_tool = Tool(
    name="Re-Ranker",
    func=re_rank_context,
    description="Re-ranks the retrieved tables based on relevance to the query."
)

5. Prompt-Bauwerkzeug

Dieses Werkzeug erstellt einen detaillierten Prompt für das Sprachmodell, der die verfeinerte Abfrage des Benutzers, das abgerufene Schema und Beispiele aus dem Few-Shot-Prompting-Werkzeug integriert.

Angenommen, Sie sind jemand, der im Generieren von SQL-Abfragen versiert ist. Generieren Sie eine SQL-Abfrage, um: Die Gesamte Verkäufe nach Produkt für die letzten 7 Tage abzurufen.

Relevante Tabellen:

  1. sales_data: Enthält die Spalten [sales, date, product_id].
  2. products: Enthält die Spalten [product_id, product_name].

Beispiel SQL:

Plain Text

 

SELECT product_name, SUM(sales) FROM sales_data JOIN products ON sales_data.product_id = products.product_id GROUP BY product_name;

Zukünftiger Umfang

Während dieses System einen einzelnen Agenten mit mehreren Werkzeugen verwendet, um die Modularität zu vereinfachen und die Komplexität zu reduzieren, könnte in Zukunft ein Multi-Agenten-Rahmenwerk erkundet werden. Wir könnten möglicherweise Folgendes erkunden:

  1. Gewidmete Agenten für Kontextabruf. Separate Agenten für semantische und Stichwortsuchen.
  2. Aufgabenbezogene Agenten. Agenten spezialisiert auf SQL-Validierung oder -Optimierung.
  3. Zusammenarbeit zwischen Agenten. Verwendung eines Koordinationsagenten zur Verwaltung der Aufgabendelegation.

Dieser Ansatz könnte die Skalierbarkeit verbessern und ermöglicht komplexere Workflows, insbesondere bei Unternehmenseinsätzen.

Schlussfolgerung

Agentic RAG für Text-zu-SQL-Anwendungen bietet einen skalierbaren, modularen Ansatz zur Lösung strukturierter Abfrageaufgaben. Durch die Integration von Hybrid-Suche, Neu-Ranking, Few-Shot-Prompting und dynamischer Prompt-Erstellung innerhalb eines einzigen Agentenrahmens gewährleistet dieses System Genauigkeit, Transparenz und Erweiterbarkeit. Dieser verbesserte Workflow zeigt einen leistungsstarken Bauplan auf, um natürliche Sprachfragen in handlungsfähige SQL-Abfragen zu verwandeln.

Source:
https://dzone.com/articles/creating-an-agentic-rag-for-text-to-sql-applications