home
diamond Go Premium
Data Engineering Path  ·  Data & AI

Capstone Project 1: Building a Self-Healing SQL Agent

In this hands-on capstone project, you will build an Autonomous Text-to-SQL Agent capable of inspecting database schemas, generating SQL queries, validating syntax using dry-run tests, and automatically self-correcting syntax errors before returning verified results to the user.


1. System Architecture

flowchart TD
    UserQ["User Business Question:<br/>'Which 5 enterprise customers spent the most in Q3?'"] --> Agent["SQL Agent Brain"]

    Agent --> CheckSchema["Action: get_schema('fct_orders', 'dim_customers')"]
    CheckSchema --> SchemaObs["Observation: Table DDLs & Column Names"]

    SchemaObs --> GenSQL["Thought: Draft SQL query with JOIN and GROUP BY"]
    GenSQL --> DryRun["Action: dry_run_explain(sql)"]

    DryRun --> Evaluation{Did EXPLAIN succeed?}
    Evaluation -->|Error: Column 'cust_tier' doesn't exist| SelfCorrect["Self-Correction Loop:<br/>Inspect error, rewrite query with 'customer_tier'"]
    SelfCorrect --> DryRun

    Evaluation -->|Success| Exec["Action: execute_query(sql)"]
    Exec --> Out["Final Formatted Table & Business Explanation"]

    style UserQ fill:#334155,color:#fff
    style Agent fill:#2563eb,color:#fff
    style DryRun fill:#d97706,color:#fff
    style SelfCorrect fill:#b91c1c,color:#fff
    style Exec fill:#059669,color:#fff
    style Out fill:#10b981,color:#fff

2. Complete Runnable Implementation

import re
from typing import Dict, Any

# Mock Database Engine
class MockWarehouseEngine:
    def __init__(self):
        self.tables = {
            "dim_customers": {"customer_id": "INT", "name": "VARCHAR", "customer_tier": "VARCHAR"},
            "fct_orders": {"order_id": "INT", "customer_id": "INT", "amount": "NUMERIC", "order_date": "DATE"}
        }

    def get_schema(self, table_name: str) -> str:
        if table_name in self.tables:
            cols = ", ".join([f"{col} {dtype}" for col, dtype in self.tables[table_name].items()])
            return f"Table {table_name}: ({cols})"
        return f"Error: Table '{table_name}' does not exist."

    def validate_sql(self, sql: str) -> Dict[str, Any]:
        # Simulated SQL compiler catching a deliberate schema bug
        if "cust_tier" in sql:
            return {
                "valid": False, 
                "error": "SQLCompilationError: Column 'cust_tier' does not exist in table 'dim_customers'. Did you mean 'customer_tier'?"
            }
        return {"valid": True, "error": None}

    def execute_sql(self, sql: str) -> str:
        return "[('Acme Corp', '$120,000'), ('GlobalTech', '$95,000'), ('Starlight Inc', '$82,000')]"

class SelfHealingSQLAgent:
    def __init__(self, db: MockWarehouseEngine):
        self.db = db

    def run(self, user_question: str) -> str:
        print(f"=== Starting SQL Agent for Goal: '{user_question}' ===")

        # Step 1: Discover schemas
        print("
[Step 1: Inspecting Schema]")
        schema_info = self.db.get_schema("dim_customers") + " | " + self.db.get_schema("fct_orders")
        print(f"Discovered: {schema_info}")

        # Step 2: First attempt (with deliberate typo to demonstrate self-healing)
        attempt_1_sql = (
            "SELECT c.name, SUM(o.amount) FROM fct_orders o "
            "JOIN dim_customers c ON o.customer_id = c.customer_id "
            "WHERE c.cust_tier = 'ENTERPRISE' GROUP BY 1 ORDER BY 2 DESC LIMIT 3;"
        )
        print(f"
[Step 2: Drafted SQL Query]:
{attempt_1_sql}")

        # Step 3: Dry-run validation
        print("
[Step 3: Running Validation / EXPLAIN]")
        val_result = self.db.validate_sql(attempt_1_sql)

        if not val_result["valid"]:
            print(f"⚠️ Validation Failed: {val_result['error']}")
            print("
🔄 [Self-Correction Loop Triggered]")
            # Autonomous correction: replace 'cust_tier' with 'customer_tier'
            corrected_sql = attempt_1_sql.replace("cust_tier", "customer_tier")
            print(f"Corrected Query:
{corrected_sql}")

            # Step 4: Re-validate
            val_retry = self.db.validate_sql(corrected_sql)
            if val_retry["valid"]:
                print("✅ Validation Succeeded!")
                data = self.db.execute_sql(corrected_sql)
                return f"Final Answer (Validated SQL Execution):
{data}"
            else:
                return f"Failed after self-correction: {val_retry['error']}"
        else:
            data = self.db.execute_sql(attempt_1_sql)
            return f"Executed Successfully:
{data}"

if __name__ == "__main__":
    db = MockWarehouseEngine()
    agent = SelfHealingSQLAgent(db)
    final_output = agent.run("Show top spending Enterprise tier customers")
    print(f"
{final_output}")

Key Takeaways

  • Dry-run validation (EXPLAIN) catches bugs before executing against production data.
  • The Self-Correction Loop turns syntax errors into learning feedback for the next generation turn.
  • Strict schema grounding prevents hallucinated column names.
Find this content helpful? ☕ Buy me a coffee

Entity Details

Create New Item

help

Submit Technical Query

Have a question or run into an issue? Describe it below, upload an optional screenshot, and our engineering team will answer it!

image Attach image (optional)

Submit Feedback

build Free Developer Utility Free Tool
gavel

Privacy & Legal Disclaimer

1. Client-Side Browser Processing

All utility tools on DeepEngineerHub (including Image to PDF, Text Formatters, JSON Converters, and Encryptors) execute 100% locally within your client browser using WebAssembly and JavaScript. No uploaded images, text, or documents are transmitted, collected, or stored on remote servers.

2. Limitation of Liability ("As-Is" Provision)

Tools and services are provided free of charge for convenience and educational purposes "as-is" without warranties of any kind. DeepEngineerHub shall not be held liable for any data loss, formatting inconsistencies, or indirect damages resulting from tool usage.

3. Open Source & Third-Party Software

Certain utilities utilize open-source client libraries (such as jsPDF, Mermaid.js, Pyodide) licensed under MIT, Apache, or BSD open licenses. All intellectual property remains with their respective copyright holders.