cd /news/large-language-models/ai-agents-that-speak-sql-text-to-sql… · home topics large-language-models article
[ARTICLE · art-53446] src=dev.to ↗ pub= topic=large-language-models verified=true sentiment=↑ positive

AI Agents That Speak SQL: Text-to-SQL with Hugging Face smolagents

Hugging Face's smolagents library enables AI agents to generate and execute SQL queries against databases, using a code-based reasoning approach that allows error correction and verification. The agent writes Python snippets to interact with the database, observe results, and refine its actions, overcoming the fragility of single-pass text-to-SQL pipelines.

read4 min views1 publishedJul 9, 2026

The simplest way to connect an LLM to a database is a single-pass pipeline: the user writes a question in natural language, the model generates a SQL query, and that query is executed directly against the database. This is, for example, the approach followed in freeCodeCamp's tutorial "How to Talk to Any Database Using AI", where a Python script builds a prompt with the table schema and asks a model to return raw SQL.

The problem is that this pipeline is fragile. If the model generates a query with a syntax error, or worse, a syntactically valid but semantically incorrect query, there's nobody reviewing the result before showing it to the user. There's no error correction, no verification that the answer actually makes sense.

That's where agents come in. Instead of a model that just "translates" text into SQL, an agent can execute the query, observe the result, and decide whether it needs to fix something before responding. That's exactly what smolagents

, Hugging Face's library for building agents with very little code, does.

smolagents

is an open source library from Hugging Face designed so an agent "thinks in code": instead of the LLM returning a JSON blob describing the action to take, it writes actual Python snippets that the agent executes step by step, observing the result before continuing. This pattern is called CodeAgent

and follows the ReAct framework (reason → act → observe).

Official repository: https://github.com/huggingface/smolagents

from sqlalchemy import (
    create_engine, MetaData, Table, Column,
    String, Integer, Float, insert, inspect, text
)

engine = create_engine("sqlite:///:memory:")
metadata_obj = MetaData()

table_name = "receipts"
receipts = Table(
    table_name,
    metadata_obj,
    Column("receipt_id", Integer, primary_key=True),
    Column("customer_name", String(16), primary_key=True),
    Column("price", Float),
    Column("tip", Float),
)
metadata_obj.create_all(engine)

rows = [
    {"receipt_id": 1, "customer_name": "Alan Payne", "price": 12.06, "tip": 1.20},
    {"receipt_id": 2, "customer_name": "Alex Mason", "price": 23.86, "tip": 0.24},
    {"receipt_id": 3, "customer_name": "Woodrow Wilson", "price": 53.43, "tip": 5.43},
    {"receipt_id": 4, "customer_name": "Margaret James", "price": 21.11, "tip": 1.00},
]
for row in rows:
    stmt = insert(receipts).values(**row)
    with engine.begin() as connection:
        connection.execute(stmt)
inspector = inspect(engine)
columns_info = [(col["name"], col["type"]) for col in inspector.get_columns("receipts")]

table_description = "Columns:\n" + "\n".join(
    [f"  - {name}: {col_type}" for name, col_type in columns_info]
)
print(table_description)

This gives us something like:

Columns:
  - receipt_id: INTEGER
  - customer_name: VARCHAR(16)
  - price: FLOAT
  - tip: FLOAT

In smolagents, a tool is just a Python function decorated with @tool

. The docstring is key: the agent reads it to know when and how to use the tool.

from smolagents import tool

@tool
def sql_engine(query: str) -> str:
    """
    Allows you to perform SQL queries on the table 'receipts'.
    Returns a string representation of the result.
    The table has the following columns:
    Columns:
      - receipt_id: INTEGER
      - customer_name: VARCHAR(16)
      - price: FLOAT
      - tip: FLOAT

    Args:
        query: the SQL query to execute. Must be valid SQL.
    """
    output = ""
    with engine.connect() as con:
        rows = con.execute(text(query))
        for row in rows:
            output += "\n" + str(row)
    return output
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?")

The agent doesn't just generate the SQL: it executes it, reads the result, and if something doesn't add up (say, a column that doesn't exist), it can retry with a corrected query before giving you the final answer. That's the real difference compared to the single-pass pipeline.

The same pattern holds as the schema grows. We add a second table for waiters and update the tool's description:

table_name = "waiters"
waiters = Table(
    table_name,
    metadata_obj,
    Column("receipt_id", Integer, primary_key=True),
    Column("waiter_name", String(16), primary_key=True),
)
metadata_obj.create_all(engine)

rows = [
    {"receipt_id": 1, "waiter_name": "Corey Johnson"},
    {"receipt_id": 2, "waiter_name": "Michael Watts"},
    {"receipt_id": 3, "waiter_name": "Michael Watts"},
    {"receipt_id": 4, "waiter_name": "Margaret James"},
]
for row in rows:
    stmt = insert(waiters).values(**row)
    with engine.begin() as connection:
        connection.execute(stmt)

agent.run("Which waiter got the most tips in total?")

The agent now has to reason about a JOIN between receipts

and waiters

, and it does so without us changing a single line of the agent's logic — only the tool's description was updated.

If you want to see this pattern taken into a project with production-style structure (REST API, result validation, explicit self-correction), this repository implements exactly that idea on top of smolagents:

https://github.com/Sakeeb91/text2sql-agent

There, the flow is: the agent inspects the schema, generates the SQL, validates that the result makes sense, and if it detects a problem, self-corrects before answering — all exposed behind an API with JWT or API key authentication.

The "classic" Text-to-SQL pipeline (like freeCodeCamp's) is a great starting point for understanding the problem, but it falls short in production as soon as questions get more complex. Wrapping the SQL query as a tool inside a smolagents CodeAgent

gives the system the ability to review its own work, which translates into fewer silently wrong answers — which is, in the end, the worst kind of error a system that talks to your database can make.

── more in #large-language-models 4 stories · sorted by recency
── more on @hugging face 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/ai-agents-that-speak…] indexed:0 read:4min 2026-07-09 ·