DEV Community

Mauricio Choqueña Choque
Mauricio Choqueña Choque

Posted on

Agentes que se auto-corrigen: Text-to-SQL con smolagents (Hugging Face)

El problema de los pipelines "texto → SQL" tradicionales

La mayoría de las soluciones de IA para bases de datos siguen el mismo patrón: el usuario escribe una pregunta en lenguaje natural, un modelo la traduce a una consulta SQL, y esa consulta se ejecuta directamente contra la base de datos. Es simple, pero frágil: si el modelo genera una consulta incorrecta, esta puede ejecutarse sin errores y devolver un resultado que parece válido pero no lo es. Nadie se entera de que la respuesta está mal, porque no hubo ningún fallo visible que lo delate.

La documentación oficial de smolagents, el framework de agentes de Hugging Face, propone una alternativa: en vez de un pipeline de un solo paso, construir un agente capaz de revisar el resultado de su propia consulta y decidir si necesita corregirla. Este patrón —conocido como ReAct (Reasoning + Acting)— convierte una traducción ciega en un proceso iterativo de prueba y verificación.

El stack utilizado

  • smolagents — framework de agentes de código de Hugging Face.
  • CodeAgent — la clase principal del framework: un agente que razona escribiendo y ejecutando código, iterando sobre sus propios resultados anteriores.
  • SQLAlchemy — para crear una base de datos SQLite en memoria y ejecutar las consultas.
  • InferenceClientModel — conecta el agente con modelos alojados en la Inference API de Hugging Face (Serverless o dedicada).
  • Repositorio del framework: github.com/huggingface/smolagents
  • Documentación del ejemplo: Self-correcting Text-to-SQL

Paso a paso: cómo se construye el agente

1. Preparar la base de datos

Se crea una tabla receipts (recibos) en SQLite, con columnas receipt_id, customer_name, price y tip, y se insertan algunas filas de ejemplo (nombres de clientes, precios y propinas).

2. Convertir la tabla en una herramienta (tool) para el agente

Aquí está el detalle más importante del diseño: la descripción de la tabla no se le "explica" al modelo en un prompt suelto, sino que se incrusta directamente en el docstring de la función sql_engine, decorada con @tool:

\`python
@tool
def sql_engine(query: str) -> str:
"""
Allows you to perform SQL queries on the table. Returns a string representation of the result.
The table is named 'receipts'. Its description is as follows:
Columns:
- receipt_id: INTEGER
- customer_name: VARCHAR(16)
- price: FLOAT
- tip: FLOAT

Args:
    query: The query to perform. This should be correct SQL.
"""
output = ""
with engine.connect() as con:
    rows = con.execute(text(query))
    for row in rows:
        output += "\\n" + str(row)
return output
Enter fullscreen mode Exit fullscreen mode

`\

Ese docstring es lo que el CodeAgent "lee" para entender qué esquema tiene disponible y cómo debe formar sus consultas.

3. Crear el agente

\`python
from smolagents import CodeAgent, InferenceClientModel

agent = CodeAgent(
tools=[sql_engine],
model=InferenceClientModel(model_id="meta-llama/Llama-3.1-8B-Instruct"),
)
agent.run("Can you give me the name of the client who got the most expensive receipt?")
`\

Con solo esto, el agente ya es capaz de generar la consulta SQL correspondiente, ejecutarla mediante la herramienta sql_engine, y devolver el nombre del cliente con el recibo más caro.

Nivel 2: cuando el esquema se complica (joins)

La prueba de fuego llega cuando se agrega una segunda tabla, waiters (meseros), que relaciona cada receipt_id con el nombre del mesero que lo atendió. Esto obliga al agente a razonar sobre un join entre dos tablas para responder algo como "¿qué mesero recibió más propinas en total?".

Como el esquema cambió, la descripción de la herramienta sql_engine se actualiza dinámicamente, incluyendo ahora las columnas de ambas tablas:

\`python
sql_engine.description = updated_description

agent = CodeAgent(
tools=[sql_engine],
model=InferenceClientModel(model_id="Qwen/Qwen3-Next-80B-A3B-Thinking"),
)
agent.run("Which waiter got more total money from tips?")
`\

Dos cosas clave suceden aquí:

  1. El agente actualiza su comprensión del entorno simplemente porque la descripción de su herramienta cambió — no hace falta reentrenar nada ni reescribir la lógica del agente.
  2. El modelo se cambia por uno más potente (Qwen3-Next-80B-A3B-Thinking en lugar de Llama-3.1-8B-Instruct) porque la tarea de razonar sobre un join es más difícil que una consulta simple sobre una sola tabla. La documentación confirma que este cambio de modelo mejora notablemente el resultado frente a consultas más complejas.

Por qué este enfoque es superior a un pipeline directo

Pipeline directo texto→SQL Agente con smolagents (ReAct)
Genera y ejecuta la consulta en un solo paso Genera, ejecuta, observa el resultado y puede corregir
Un error puede pasar desapercibido El agente puede detectar resultados sospechosos o vacíos y reintentar
El esquema debe estar "hardcodeado" en el prompt El esquema vive en la descripción de la herramienta y se puede actualizar en caliente
Rígido ante consultas complejas (joins, subconsultas) Escala mejor cambiando de modelo o ajustando la herramienta

Conclusión

El ejemplo de smolagents demuestra que el salto de calidad en text-to-SQL no viene solo de tener un modelo de lenguaje más grande, sino de darle al modelo la capacidad de actuar, observar y corregir, en lugar de generar una única respuesta "a ciegas". Esto es especialmente valioso en bases de datos reales, donde los esquemas cambian, las consultas se vuelven complejas (joins, agregaciones, filtros anidados), y un error silencioso puede llevar a decisiones basadas en datos incorrectos.

Para quien quiera experimentar: el repositorio huggingface/smolagents incluye este ejemplo listo para correr en Google Colab o SageMaker Studio Lab, y es un excelente punto de partida para adaptar el patrón a una base de datos propia.

Top comments (0)