La combinación de generación aumentada por recuperación (RAG) y modelos de inteligencia artificial generativa ha traído cambios al procesamiento del lenguaje natural al mejorar las respuestas a las consultas. En el ámbito de RAG Agentic, este método convencional de depender de un modelo monolítico para tareas ha sido mejorado mediante la introducción de modularidad y autonomía. Al descomponer el proceso de resolución de problemas en herramientas integradas dentro de un agente, RAG Agentic proporciona beneficios como precisión, transparencia, escalabilidad y capacidades de depuración.
La Visión Detrás de RAG Agentic para Texto a SQL
Los sistemas RAG tradicionales a menudo recuperan documentos relevantes y dependen de un solo modelo monolítico para generar respuestas. Aunque este es un método efectivo en algunos casos, cuando se trata de salidas estructuradas como en el caso de generar SQL, este enfoque puede no ser el más efectivo. Ahí es donde podemos aprovechar el poder del marco RAG Agentic, donde:
- Dividimos las tareas en herramientas más pequeñas y manejables dentro de un agente
- Mejoramos la precisión asignando tareas a herramientas especializadas
- Mejoramos la transparencia rastreando el razonamiento y flujo de trabajo de cada herramienta
- Simplificamos la escalabilidad y depuración a través del diseño modular
Hablemos sobre cómo funciona esta herramienta y el papel que desempeña cada componente en transformar las preguntas de los usuarios en consultas SQL precisas.
Descripción General de la Arquitectura
La estructura comprende un agente que utiliza herramientas dentro del flujo de trabajo de texto a SQL. El proceso se puede resumir de la siguiente manera:
Consulta del usuario → Herramienta de Transformación de Consultas → Herramienta de Estímulo de Pocas Muestras → Herramienta de Búsqueda Híbrida → Herramienta de Reclasificación → Herramienta de Recuperación de Tablas → Herramienta de Construcción de Estímulos → Herramienta de Ejecución LLM → Herramienta de Ejecución SQL → Resultado Final
1. Herramienta de Transformación de Consultas de Usuario
Esta herramienta implicaría procesar la consulta del usuario para una mejor comprensión del LLM. Aborda ambigüedades, reformula preguntas de usuario, traduce abreviaturas a sus formas y proporciona contexto cuando es necesario.
Mejoras
- Manejar referencias temporales. Mapear términos como “hasta la fecha de hoy” o “hasta ahora” a fechas explícitas.
- Reemplazar palabras ambiguas. Por ejemplo, “reciente” podría ser reemplazado por “últimos 7 días.”
- Conectar abreviaturas o siglas con sus nombres.
Ejemplo
Entrada: “Mostrar ventas recientes MTD.”
Consulta transformada: “Recuperar datos de ventas de los últimos 7 días (Mes hasta la Fecha).”
from datetime import date, timedelta
def transform_query(user_query):
# Manejar referencias temporales abiertas
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)
# Mapear abreviaturas comunes
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. Herramienta de Estímulo de Pocas Muestras
Esta herramienta hace una llamada al LLM para identificar la pregunta de un tipo de un conjunto (también podemos decir que coincide con la plantilla). La pregunta coincidente mejora el estímulo con una consulta SQL de ejemplo.
Flujo de trabajo de ejemplo
1. Pregunta de entrada: “Muéstrame las ventas totales por producto de los últimos 7 días.”
2. Plantillas predefinidas:
- “Mostrar ventas agrupadas por región.” → Ejemplo SQL; SELECT región, SUM(ventas) …
- “Mostrar ventas totales por producto.” → Ejemplo SQL; SELECT nombre_producto, SUM(ventas) …
3. Pregunta más similar: “Mostrar ventas totales por producto.”
4. Ejemplo de salida SQL: SELECT nombre_producto, SUM(ventas) 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. Herramienta de Búsqueda Híbrida
Para una recuperación sólida, esta herramienta combina la búsqueda semántica, la búsqueda de palabras clave basada en BM25 y el mapeo basado en palabras clave. Los resultados de búsqueda de estos métodos de búsqueda se combinan utilizando fusión de rango recíproco.
¿Cómo se unen todos estos elementos?
Mapeo de Tabla de Palabras Clave
Este enfoque asigna las tablas a las palabras clave que se encuentran en la consulta. Por ejemplo:
- La presencia de “ventas” resulta en la tabla de ventas siendo preseleccionada.
- La presencia de “producto” resulta en la tabla de productos siendo preseleccionada.
Mapeo de Superposición de Palabras Clave (BM25)
Este es un método de búsqueda basado en la superposición de palabras clave que preselecciona tablas en función de la relevancia. Para esto, aplicaremos la técnica BM25. Esto ordena los documentos en función de su relevancia para una búsqueda del usuario. Esta técnica de búsqueda considera la saturación de términos en vista, así como TF-IDF (Frecuencia de Término-Inversa de Frecuencia de Documento).
La Frecuencia de Término (TF) ayuda a medir la frecuencia de un término en un documento dado. El enfoque de Frecuencia Inversa de Documento (IDF) subraya las palabras que aparecen en cada documento disminuyendo su importancia.
La normalización tiene en cuenta la longitud del documento para evitar cualquier sesgo hacia documentos más largos.
Dado:
- datos_de_ventas: Contiene términos como “ventas,” “fecha,” “producto.”
- productos: Contiene términos como “producto,” “categoría.”
- pedidos: Contiene términos como “pedido,” “fecha,” “cliente.”
- finanzas: Contiene términos como “ingresos,” “beneficio,” “gasto.”
Consulta del usuario: “Mostrar ventas totales por producto.”
- Identificar términos en la consulta del usuario: [“ventas,” “producto”].
- Ordenar cada documento (basado en la frecuencia y relevancia de estos términos) en DataBaseTable.
Relevancia de los documentos:
- ventas: Alta relevancia debido a “ventas” y “producto.”
- productos: Alta relevancia debido a “producto.”
- pedidos: Baja relevancia debido a la presencia solo de “ventas.”
- finanzas: No relevante.
Resultado:
Lista clasificada: [productos, datos_de_ventas, pedidos, finanzas]
Búsqueda Semántica
En este método de búsqueda, como su nombre indica, encontramos tablas semánticamente similares utilizando incrustaciones vectoriales. Logramos esto calculando un puntaje de similitud, como la similitud coseno, entre el documento (vectores de tabla) y los vectores de consulta del usuario.
Fusión de Rango Recíproco
Combina los resultados de búsqueda BM25 y semántica utilizando la estrategia de fusión de rango recíproco, que se explica un poco más en detalle a continuación:
Fusión de rango recíproco (RRF) combinando BM25 y búsqueda semántica:
RRF es un método para combinar resultados de múltiples algoritmos de clasificación (por ejemplo, BM25 y búsqueda semántica). Asigna una puntuación a cada documento basada en su rango en los métodos individuales, otorgando puntuaciones más altas a los documentos clasificados más alto en múltiples métodos.
Fórmula RRF:
RRF(d) = Σ(r ∈ R) 1 / (k + r(d))
Donde:
- d es un documento
- R es el conjunto de clasificadores (métodos de búsqueda)
- k es una constante (típicamente 60)
- r(d) es el rango del documento d en el método de búsqueda r
Ejemplo Paso a Paso
Datos de entrada.
1. Resultados de clasificación de BM25:
- productos (Rango 1)
- datos de ventas (Rango 2)
- órdenes (Rango 3)
2. Resultados de clasificación de búsqueda semántica:
- datos de ventas (Rango 1)
- finanzas (Rango 2)
- productos (Rango 3)
Fusión Paso a Paso
Para cada tabla, calcular la puntuación:
1. datos de ventas
- Rango BM25 = 2, Rango Semántico = 1
- Puntuación RRF = (1/60+2) + (1/60+1) = 0.03252
2. productos
- Rango BM25 = 1, Rango Semántico = 3
- Puntuación RRF = (1/60+1) + (1/60+3) = 0.03226
3. órdenes
- BM25 Rank = 3, Rango Semántico = No Clasificado
- Puntaje RRF = (1/60+3)= 0.01587
4. financieros
- BM25 Rank = No Clasificado, Rango Semántico = 2
- Puntaje RRF = (1/60+2)=0.01613
5. Ordenar por puntaje RRF
- datos_ventas (puntaje más alto debido al rango superior en la búsqueda semántica).
- productos (puntaje alto de BM25).
- órdenes (relevancia menor en general).
- financieros (superposición limitada).
Resultado final: [‘datos_ventas’, ‘productos,’ ‘financieros,’ ‘órdenes’]
Tablas recuperadas utilizando el mapeo de Tabla de Palabras Clave siempre se incluyen.
from rank_bm25 import BM25Okapi
def hybrid_search(query):
# Mapeo basado en palabras clave
keyword_to_table = {
"sales": "sales_data",
"product": "products",
}
keyword_results = [table for keyword, table in keyword_to_table.items() if keyword in query.lower()]
# Búsqueda BM25
bm25 = BM25Okapi(["sales_data", "products", "orders", "financials"])
bm25_results = bm25.get_top_n(query.split(), bm25.corpus, n=5)
# Búsqueda Semántica
semantic_results = vector_store.similarity_search(query, k=5)
# Fusión de Rango Recíproco
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. Herramienta de Reclasificación
Esta herramienta garantiza que las tablas más relevantes tengan prioridad.
Ejemplo
- Tablas de entrada: [“datos_ventas,” “productos,” “financieros”]
- Lógica de reordenamiento
- Para cada tabla, calcular un puntaje de relevancia concatenando la consulta y la descripción de la tabla.
- Ordenar por puntaje de relevancia.
- Salida: [“datos_ventas,” “productos”]
Un poco más sobre la lógica de reordenación:
El codificador–enlazador calcula una puntuación de relevancia analizando la consulta concatenada y la descripción de la tabla como un par de entradas único. Este proceso implica:
- Entrada de pares. La consulta y cada descripción de tabla se emparejan y se pasan como entrada al codificador enlazado.
- Codificación conjunta. A diferencia de los codificadores separados (por ejemplo, bi-codificadores), el codificador enlazado codifica conjuntamente el par, lo que le permite capturar mejor el contexto y las dependencias entre la consulta y la descripción de la tabla.
- Puntuación. El modelo genera una puntuación de relevancia para cada par, indicando qué tan bien se ajusta la tabla a la consulta.
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. Herramienta de Construcción de Instrucciones
Esta herramienta construye una instrucción detallada para el modelo de lenguaje, incorporando la consulta refinada del usuario, el esquema recuperado y ejemplos de la Herramienta de Instrucciones de Pocas Muestras.
Supongamos que eres alguien que es competente en generar consultas SQL. Genera una consulta SQL para: Recuperar las ventas totales agrupadas por producto de los últimos 7 días.
Tablas relevantes:
- datos_ventas: Contiene las columnas [ventas, fecha, id_producto].
- productos: Contiene las columnas [id_producto, nombre_producto].
Ejemplo de SQL:
SELECT product_name, SUM(sales) FROM sales_data JOIN products ON sales_data.product_id = products.product_id GROUP BY product_name;
Alcance Futuro
Aunque este sistema utiliza un único agente con múltiples herramientas para simplificar la modularidad y reducir la complejidad, en el futuro se podría explorar un marco de trabajo multiagente. Posiblemente podríamos explorar lo siguiente:
- Agentes dedicados para la recuperación de contexto. Agentes separados para búsquedas semánticas y por palabras.
- Agentes específicos de tareas. Agentes especializados en validación u optimización de SQL.
- Colaboración entre agentes. Utilizando un agente de coordinación para gestionar la delegación de tareas.
Este enfoque podría mejorar la escalabilidad y permitir flujos de trabajo más sofisticados, especialmente en implementaciones a nivel empresarial.
Conclusión
RAG Agentic para aplicaciones de texto a SQL ofrece un enfoque escalable y modular para resolver tareas de consultas estructuradas. Al incorporar búsqueda híbrida, reordenamiento, indicaciones de poca muestra y construcción dinámica de indicaciones dentro de un marco de trabajo de un solo agente, este sistema garantiza precisión, transparencia y extensibilidad. Este flujo de trabajo mejorado demuestra un poderoso modelo para convertir preguntas en lenguaje natural en consultas SQL ejecutables.
Source:
https://dzone.com/articles/creating-an-agentic-rag-for-text-to-sql-applications