{"slug": "ai-agents-that-speak-sql-text-to-sql-with-hugging-face-smolagents", "title": "AI Agents That Speak SQL: Text-to-SQL with Hugging Face smolagents", "summary": "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.", "body_md": "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.\n\nThe 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.\n\nThat'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`\n\n, Hugging Face's library for building agents with very little code, does.\n\n`smolagents`\n\nis 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`\n\nand follows the ReAct framework (reason → act → observe).\n\nOfficial repository: [https://github.com/huggingface/smolagents](https://github.com/huggingface/smolagents)\n\n``` python\nfrom sqlalchemy import (\n    create_engine, MetaData, Table, Column,\n    String, Integer, Float, insert, inspect, text\n)\n\nengine = create_engine(\"sqlite:///:memory:\")\nmetadata_obj = MetaData()\n\n# Define the \"receipts\" table\ntable_name = \"receipts\"\nreceipts = Table(\n    table_name,\n    metadata_obj,\n    Column(\"receipt_id\", Integer, primary_key=True),\n    Column(\"customer_name\", String(16), primary_key=True),\n    Column(\"price\", Float),\n    Column(\"tip\", Float),\n)\nmetadata_obj.create_all(engine)\n\n# Insert sample data\nrows = [\n    {\"receipt_id\": 1, \"customer_name\": \"Alan Payne\", \"price\": 12.06, \"tip\": 1.20},\n    {\"receipt_id\": 2, \"customer_name\": \"Alex Mason\", \"price\": 23.86, \"tip\": 0.24},\n    {\"receipt_id\": 3, \"customer_name\": \"Woodrow Wilson\", \"price\": 53.43, \"tip\": 5.43},\n    {\"receipt_id\": 4, \"customer_name\": \"Margaret James\", \"price\": 21.11, \"tip\": 1.00},\n]\nfor row in rows:\n    stmt = insert(receipts).values(**row)\n    with engine.begin() as connection:\n        connection.execute(stmt)\ninspector = inspect(engine)\ncolumns_info = [(col[\"name\"], col[\"type\"]) for col in inspector.get_columns(\"receipts\")]\n\ntable_description = \"Columns:\\n\" + \"\\n\".join(\n    [f\"  - {name}: {col_type}\" for name, col_type in columns_info]\n)\nprint(table_description)\n```\n\nThis gives us something like:\n\n```\nColumns:\n  - receipt_id: INTEGER\n  - customer_name: VARCHAR(16)\n  - price: FLOAT\n  - tip: FLOAT\n```\n\nIn smolagents, a tool is just a Python function decorated with `@tool`\n\n. The docstring is key: the agent reads it to know when and how to use the tool.\n\n``` php\nfrom smolagents import tool\n\n@tool\ndef sql_engine(query: str) -> str:\n    \"\"\"\n    Allows you to perform SQL queries on the table 'receipts'.\n    Returns a string representation of the result.\n    The table has the following columns:\n    Columns:\n      - receipt_id: INTEGER\n      - customer_name: VARCHAR(16)\n      - price: FLOAT\n      - tip: FLOAT\n\n    Args:\n        query: the SQL query to execute. Must be valid SQL.\n    \"\"\"\n    output = \"\"\n    with engine.connect() as con:\n        rows = con.execute(text(query))\n        for row in rows:\n            output += \"\\n\" + str(row)\n    return output\npython\nfrom smolagents import CodeAgent, InferenceClientModel\n\nagent = CodeAgent(\n    tools=[sql_engine],\n    model=InferenceClientModel(model_id=\"meta-llama/Llama-3.1-8B-Instruct\"),\n)\n\nagent.run(\"Can you give me the name of the client who got the most expensive receipt?\")\n```\n\nThe 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.\n\nThe same pattern holds as the schema grows. We add a second table for waiters and update the tool's description:\n\n```\ntable_name = \"waiters\"\nwaiters = Table(\n    table_name,\n    metadata_obj,\n    Column(\"receipt_id\", Integer, primary_key=True),\n    Column(\"waiter_name\", String(16), primary_key=True),\n)\nmetadata_obj.create_all(engine)\n\nrows = [\n    {\"receipt_id\": 1, \"waiter_name\": \"Corey Johnson\"},\n    {\"receipt_id\": 2, \"waiter_name\": \"Michael Watts\"},\n    {\"receipt_id\": 3, \"waiter_name\": \"Michael Watts\"},\n    {\"receipt_id\": 4, \"waiter_name\": \"Margaret James\"},\n]\nfor row in rows:\n    stmt = insert(waiters).values(**row)\n    with engine.begin() as connection:\n        connection.execute(stmt)\n\nagent.run(\"Which waiter got the most tips in total?\")\n```\n\nThe agent now has to reason about a JOIN between `receipts`\n\nand `waiters`\n\n, and it does so without us changing a single line of the agent's logic — only the tool's description was updated.\n\nIf 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:\n\n[https://github.com/Sakeeb91/text2sql-agent](https://github.com/Sakeeb91/text2sql-agent)\n\nThere, 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.\n\nThe \"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`\n\ngives 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.", "url": "https://wpnews.pro/news/ai-agents-that-speak-sql-text-to-sql-with-hugging-face-smolagents", "canonical_source": "https://dev.to/marymar_danytzacallotico/agentes-de-ia-que-hablan-sql-text-to-sql-con-hugging-face-smolagents-5gmd", "published_at": "2026-07-09 23:19:35+00:00", "updated_at": "2026-07-09 23:35:37.583315+00:00", "lang": "en", "topics": ["large-language-models", "ai-agents", "natural-language-processing", "developer-tools"], "entities": ["Hugging Face", "smolagents", "CodeAgent", "ReAct", "SQLAlchemy"], "alternates": {"html": "https://wpnews.pro/news/ai-agents-that-speak-sql-text-to-sql-with-hugging-face-smolagents", "markdown": "https://wpnews.pro/news/ai-agents-that-speak-sql-text-to-sql-with-hugging-face-smolagents.md", "text": "https://wpnews.pro/news/ai-agents-that-speak-sql-text-to-sql-with-hugging-face-smolagents.txt", "jsonld": "https://wpnews.pro/news/ai-agents-that-speak-sql-text-to-sql-with-hugging-face-smolagents.jsonld"}}