De combinatie van retrieval-augmented generation (RAG) en generatieve AI-modellen heeft veranderingen teweeggebracht in de natuurlijke taalverwerking door de reacties op vragen te verbeteren. In het domein van Agentic RAG is deze conventionele methode van vertrouwen op een monolithisch model voor taken verbeterd door modulariteit en autonomie te introduceren. Door het probleemoplossingsproces op te splitsen in tools geïntegreerd binnen een agent, biedt Agentic RAG voordelen zoals nauwkeurigheid, transparantie, schaalbaarheid en debugmogelijkheden.
De Visie Achter Agentic RAG voor Tekst-naar-SQL
Traditionele RAG-systemen halen vaak relevante documenten op en vertrouwen op een enkel monolithisch model om reacties te genereren. Hoewel dit in sommige gevallen een effectieve methode is, is dit voor structurele uitvoer zoals bij het genereren van SQL mogelijk niet de meest effectieve aanpak. Hier kunnen we de kracht van het Agentic RAG-framework benutten, waar we:
- De taken opdelen in kleinere, meer beheersbare tools binnen een agent
- Nauwkeurigheid verbeteren door taken toe te wijzen aan gespecialiseerde tools
- Transparantie verbeteren door het redeneren en workflow van elke tool te traceren
- Schalen en debuggen vereenvoudigen door modulaire ontwerp
Laten we bespreken hoe deze tool werkt en de rol die elk component speelt bij het omzetten van gebruikersvragen in nauwkeurige SQL-query’s.
Architectuuroverzicht
De structuur omvat een agent die tools gebruikt binnen de tekst-naar-SQL-workflow. Het proces kan als volgt worden samengevat:
Gebruikersquery → Query-transformatietool → Few Shot Prompting Tool → Hybride zoektool → Opnieuw rangschikkingstool → Tabelhersteltool → Promptbouwtool → LLM-uitvoeringstool → SQL-uitvoeringstool → Definitieve output
1. Gebruikersquery-transformatietool
Deze tool omvat het verwerken van de gebruikersquery voor een beter begrip van de LLM. Het lost ambiguïteiten op, herschrijft gebruikersvragen, vertaalt afkortingen naar hun vormen en biedt context wanneer nodig.
Verbeteringen
- Omgaan met tijdsreferenties. Wijs termen zoals “vanaf vandaag” of “tot nu toe” toe aan expliciete data.
- Vervang dubbelzinnige woorden. Bijvoorbeeld kan “recent” worden vervangen door “laatste 7 dagen.”
- Koppel afkortingen of verkortingen aan hun namen.
Voorbeeld
Invoer: “Toon recente verkopen MTD.”
Getransformeerde query: “Haal verkoopgegevens op voor de laatste 7 dagen (Maand tot Datum).”
from datetime import date, timedelta
def transform_query(user_query):
# Omgaan met open tijdsreferenties
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)
# Koppel veelvoorkomende afkortingen
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
Deze tool doet een oproep naar de LLM om de vraag van een soort te identificeren uit een set (we kunnen ook zeggen dat het overeenkomt met het sjabloon). De overeenkomstige vraag verbetert de prompt met een voorbeeld SQL-query.
Voorbeeldworkflow
1. Invoervraag: “Toon me totale verkopen per product voor de 7 dagen.”
2. Vooraf gedefinieerde sjablonen:
- “Toon verkoop gegroepeerd per regio.” → Voorbeeld SQL; SELECT regio, SUM(verkoop) …
- “Toon totale verkoop per product.” → Voorbeeld SQL; SELECT product_name, SUM(sales) …
3. Meest soortgelijke vraag: “Toon totale verkoop per product.”
4. Uitvoervoorbeeld SQL: SELECT product_name, SUM(sales) FROM …
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. Hybride Zoektool
Voor een robuuste opvraging combineert deze tool semantisch zoeken, trefwoordzoeken op basis van BM25, en trefwoord-gebaseerde mapping. De zoekresultaten van deze zoekmethoden worden samengevoegd met behulp van wederzijdse rangschikkingsfusie.
Hoe komt dit allemaal samen?
Trefwoord Tabel Mapping
Deze aanpak koppelt de tabellen aan de trefwoorden die in de query voorkomen. Bijvoorbeeld:
- De aanwezigheid van “verkoop” resulteert in de selectie van de verkoopstabel.
- De aanwezigheid van “product” resulteert in de selectie van de productentabel.
Trefwoord Overlap Mapping (BM25)
Dit is een zoekmethode op basis van trefwoordoverlap die tabellen selecteert op basis van relevantie. Hiervoor passen we de BM25-techniek toe. Dit sorteert de documenten op relevantie voor een gebruikerszoekopdracht. Deze zoektechniek houdt rekening met termverzadiging in beeld en TF-IDF (Termfrequentie-Inverse Documentfrequentie).
Termfrequentie (TF) helpt bij het meten van de frequentie van een term in een gegeven document. De Inverse Document Frequency (IDF) benadering benadrukt woorden die in elk document voorkomen en vermindert de relevantie.
Normalisatie houdt rekening met de lengte van documenten om enige vooringenomenheid ten opzichte van langere documenten te voorkomen.
Gegeven:
- sales_data: Bevat termen zoals “verkoop,” “datum,” “product.”
- producten: Bevat termen zoals “product,” “categorie.”
- bestellingen: Bevat termen zoals “bestelling,” “datum,” “klant.”
- financiën: Bevat termen zoals “omzet,” “winst,” “kosten.”
Gebruikersquery: “Toon totale verkoop per product.”
- Identificeer termen in de gebruikersquery: [“verkoop,” “product”].
- Sorteer elk document (op basis van frequentie en relevantie van deze termen) in DataBaseTable.
Relevantie van documenten:
- verkoop: Hoge relevantie vanwege zowel “verkoop” als “product.”
- producten: Hoge relevantie vanwege “product.”
- bestellingen: Lagere relevantie vanwege alleen de aanwezigheid van “verkoop.”
- financiën: Niet relevant.
Output:
Gesorteerde lijst: [producten, sales_data, bestellingen, financiën]
Semantisch Zoeken
In deze zoekmethode vinden we semantisch vergelijkbare tabellen door gebruik te maken van vector embeddings. We bereiken dit door het berekenen van een overeenkomstscores, zoals cosine similarity, tussen het document (tabelvectoren) en gebruikersqueryvectoren.
Reciprocal Rank Fusion
Combineert BM25- en semantische zoekresultaten met behulp van de reciproke rangfusiestrategie, die hieronder wat meer in detail wordt uitgelegd:
Reciproke Rangfusie (RRF) combineert BM25- en semantische zoekresultaten:
RRF is een methode om resultaten van meerdere rangschikkingsalgoritmes (bijv. BM25 en semantische zoekopdracht) te combineren. Het kent een score toe aan elk document op basis van zijn rangorde in de individuele methoden en geeft hogere scores aan documenten die hoger gerangschikt zijn over meerdere methoden.
RRF-formule:
RRF(d) = Σ(r ∈ R) 1 / (k + r(d))
Waar:
- d is een document
- R is de set van rangschikkers (zoekmethoden)
- k is een constante (typisch 60)
- r(d) is de rangorde van document d in zoekmethode r
Stapsgewijs Voorbeeld
Invoergegevens.
1. BM25-rangschikresultaten:
- producten (Rang 1)
- verkoopgegevens (Rang 2)
- bestellingen (Rang 3)
2. Semantische zoekrangschikkingsresultaten:
- verkoopgegevens (Rang 1)
- financiën (Rang 2)
- producten (Rang 3)
Stapsgewijze Fusie
Bereken voor elke tabel de score:
1. verkoopgegevens
- BM25-rang = 2, Semantische rang = 1
- RRF-score = (1/60+2 ) + (1/60+1) = 0,03252
2. producten
- BM25-rang = 1, Semantische rang = 3
- RRF-score = (1/60+1) + (1/60+3)= 0,03226
3. bestellingen
- BM25-rang = 3, Semantische rang = Niet gerangschikt
- RRF-score = (1/60+3) = 0,01587
4. financieel
- BM25-rang = Niet gerangschikt, Semantische rang = 2
- RRF-score = (1/60+2) = 0,01613
5. Sorteren op RRF-score
- sales_data (hoogste score vanwege hoogste rang in semantische zoekopdracht).
- producten (hoge score van BM25).
- bestellingen (lagere relevantie over het algemeen).
- financieel (beperkte overlap).
Definitieve output: [‘sales_data’, ‘producten,’ ‘financieel,’ ‘bestellingen’]
Tabellen die zijn opgehaald met behulp van Tabel met trefwoorden mapping zijn altijd inbegrepen.
from rank_bm25 import BM25Okapi
def hybrid_search(query):
# Trefwoord-gebaseerde 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 Zoeken
bm25 = BM25Okapi(["sales_data", "products", "orders", "financials"])
bm25_results = bm25.get_top_n(query.split(), bm25.corpus, n=5)
# Semantisch Zoeken
semantic_results = vector_store.similarity_search(query, k=5)
# Reciprocal Rank Fusion
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. Hertikkingstool
Deze tool zorgt ervoor dat de meest relevante tabellen prioriteit krijgen.
Voorbeeld
- Invoertabellen: [“sales_data,” “producten,” “financieel”]
- Hertikking logica
- Voor elke tabel een relevantiescore berekenen door de zoekopdracht en de tabelbeschrijving samen te voegen.
- Sorteren op relevantiescore.
- Output: [“sales_data,” “producten”]
Een beetje meer over de herclassificatielogica:
De cross–encoder berekent een relevantiescore door de geconcateneerde query en tabelbeschrijving te analyseren als een enkel invoerpaar. Dit proces omvat:
- Paar invoer. De query en elke tabelbeschrijving worden gekoppeld en als invoer naar de cross-encoder gestuurd.
- Gezamenlijke codering. In tegenstelling tot afzonderlijke encoders (bijv. bi-encoders) codeert de cross-encoder het paar gezamenlijk, waardoor het beter context en afhankelijkheden tussen de query en de tabelbeschrijving kan vastleggen.
- Scoren. Het model geeft een relevantiescore voor elk paar, wat aangeeft hoe goed de tabel overeenkomt met de query.
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. Tool voor prompt-opbouw
Deze tool construeert een gedetailleerde prompt voor het taalmodel, waarin de verfijnde query van de gebruiker, opgehaald schema, en voorbeelden van de Few-Shot Prompting Tool zijn opgenomen.
Stel dat u iemand bent die bedreven is in het genereren van SQL-query’s. Genereer een SQL-query om: Totale verkoop gegroepeerd per product van de afgelopen 7 dagen op te halen.
Relevante tabellen:
- sales_data: Bevat kolommen [sales, datum, product_id].
- producten: Bevat kolommen [product_id, productnaam].
Voorbeeld SQL:
SELECT product_name, SUM(sales) FROM sales_data JOIN products ON sales_data.product_id = products.product_id GROUP BY product_name;
Toekomstperspectief
Hoewel dit systeem een enkele agent met meerdere tools gebruikt om modulariteit te vereenvoudigen en complexiteit te verminderen, zou in de toekomst een multi-agent framework kunnen worden onderzocht. We zouden mogelijk het volgende kunnen verkennen:
- Toegewijde agenten voor contextuele opvraging. Aparte agenten voor semantisch en trefwoord zoeken.
- Taakspecifieke agenten. Agenten gespecialiseerd in SQL-validatie of -optimalisatie.
- Samenwerking tussen agenten. Gebruik van een coördinatieagent om taakdelegatie te beheren.
Deze aanpak kan de schaalbaarheid verbeteren en meer geavanceerde workflows mogelijk maken, vooral bij implementaties op ondernemingsniveau.
Conclusie
Agentic RAG voor tekst-naar-SQL-toepassingen biedt een schaalbare, modulaire aanpak om gestructureerde querytaken op te lossen. Door hybride zoekopdrachten, herrangschikking, weinig-schot prompting en dynamische promptconstructie binnen een enkelvoudig agentenkader te incorporeren, zorgt dit systeem voor nauwkeurigheid, transparantie en uitbreidbaarheid. Deze verbeterde workflow toont een krachtige blauwdruk voor het omzetten van natuurlijke taalvragen in bruikbare SQL-query’s.
Source:
https://dzone.com/articles/creating-an-agentic-rag-for-text-to-sql-applications