Day 5 of 5
⏱ ~60 minutes
Build a Data Dashboard — Day 5

AI Natural Language Queries: Ask Your Data Anything

The final piece: an AI layer that lets users type questions in plain English and get SQL queries, results, and charts back. You'll use the Claude API for text-to-SQL, add safety validation, and wire it all into your Streamlit dashboard.

How Text-to-SQL Works

The idea is simple: give an AI model your database schema, give it the user's question, and ask it to produce a valid SQL query. Then run that query and display the results. You're using the LLM as a query translator, not to analyze data directly.

This approach is more reliable than asking the AI to analyze a full CSV, because the AI never sees your raw data — only the schema and the result of the query it generates.

Setting Up the Claude API

bash
pip install anthropic
.streamlit/secrets.toml
[anthropic]
api_key = "sk-ant-..."

[database]
url = "sqlite:///sales.db"

Building the Text-to-SQL Function

python
import anthropic

client = anthropic.Anthropic(api_key=st.secrets["anthropic"]["api_key"])

SCHEMA = """
Table: sales
Columns:
  - date TEXT (format: YYYY-MM-DD)
  - region TEXT (values: North, South, East, West)
  - product TEXT (values: Widget A, Widget B, Widget C)
  - revenue INTEGER
  - units INTEGER
"""

def question_to_sql(question: str) -> str:
    """Convert a natural language question to a SQL query."""
    response = client.messages.create(
        model="claude-3-haiku-20240307",
        max_tokens=500,
        messages=[{
            "role": "user",
            "content": f"""You are a SQL expert. Given this database schema:

{SCHEMA}

Write a SQLite SELECT query to answer this question:
"{question}"

Rules:
- Return ONLY the SQL query, no explanation
- Use SQLite syntax
- Never use INSERT, UPDATE, DELETE, DROP, or CREATE
- Always include a LIMIT 1000 unless the user asks for all data

SQL query:"""
        }]
    )
    return response.content[0].text.strip()

Safety Validation

Before executing any AI-generated SQL, validate it. You don't want a user typing "drop all tables" and having that executed.

python
import re

DANGEROUS = ["insert", "update", "delete", "drop", "create", "alter", "truncate", "exec"]

def is_safe_query(sql: str) -> bool:
    """Return True if the query is a safe SELECT statement."""
    sql_lower = sql.lower().strip()
    # Must start with SELECT
    if not sql_lower.startswith("select"):
        return False
    # Must not contain dangerous keywords
    for keyword in DANGEROUS:
        if re.search(r'\b' + keyword + r'\b', sql_lower):
            return False
    return True

The Full AI Query Page

python — pages/4_Ask_AI.py
import streamlit as st
import pandas as pd
import plotly.express as px
from sqlalchemy import create_engine, text
import anthropic
import re

st.set_page_config(page_title="Ask AI — Dashboard", layout="wide")

engine = create_engine(st.secrets["database"]["url"])
client = anthropic.Anthropic(api_key=st.secrets["anthropic"]["api_key"])

SCHEMA = """
Table: sales — columns: date (TEXT), region (TEXT), product (TEXT), revenue (INTEGER), units (INTEGER)
"""

DANGEROUS = ["insert", "update", "delete", "drop", "create", "alter", "truncate"]

def is_safe(sql):
    s = sql.lower().strip()
    if not s.startswith("select"):
        return False
    return not any(re.search(r'\b' + k + r'\b', s) for k in DANGEROUS)

def ask_claude(question):
    r = client.messages.create(
        model="claude-3-haiku-20240307",
        max_tokens=500,
        messages=[{"role": "user", "content":
            f"Schema: {SCHEMA}\n\nWrite a SQLite SELECT query for: \"{question}\"\n\n"
            "Return ONLY the SQL query. Include LIMIT 500."}]
    )
    return r.content[0].text.strip()

# ---- UI ----
st.title("Ask Your Data Anything")
st.caption("Powered by Claude — type a question in plain English")

examples = [
    "What is total revenue by region?",
    "Which product sold the most units in January?",
    "Show me monthly revenue for the North region",
    "What are the top 10 highest revenue days?"
]

with st.expander("Example questions"):
    for ex in examples:
        if st.button(ex, key=ex):
            st.session_state.question = ex

question = st.text_input(
    "Your question",
    value=st.session_state.get("question", ""),
    placeholder="e.g. What is total revenue by region?"
)

if question:
    with st.spinner("Generating SQL..."):
        try:
            sql = ask_claude(question)
        except Exception as e:
            st.error(f"API error: {e}")
            st.stop()

    with st.expander("Generated SQL", expanded=False):
        st.code(sql, language="sql")

    if not is_safe(sql):
        st.error("Unsafe query detected. Only SELECT queries are allowed.")
        st.stop()

    try:
        df = pd.read_sql(text(sql), engine)
        st.success(f"{len(df):,} rows returned")
        st.dataframe(df, use_container_width=True)

        # Auto-chart: if there's a numeric column, try to visualize
        numeric_cols = df.select_dtypes("number").columns.tolist()
        text_cols = df.select_dtypes("object").columns.tolist()

        if numeric_cols and text_cols:
            fig = px.bar(df, x=text_cols[0], y=numeric_cols[0],
                         title=question)
            st.plotly_chart(fig, use_container_width=True)

    except Exception as e:
        st.error(f"Query error: {e}")
        st.info("Try rephrasing your question.")
💡
Cost note: claude-3-haiku-20240307 is the cheapest Claude model and more than capable for text-to-SQL. Each query costs roughly $0.0002 — essentially free for a personal or small team dashboard.
📝 Day 5 Exercise
Ship Your Complete AI Dashboard
  1. Add the pages/4_Ask_AI.py file to your existing multi-page app.
  2. Configure your Anthropic API key in .streamlit/secrets.toml.
  3. Test with at least 5 different natural language questions.
  4. Verify that dangerous queries like "drop table sales" are blocked.
  5. Deploy to Streamlit Community Cloud: push to GitHub, connect at share.streamlit.io.
  6. Add your Anthropic key as a secret in the Streamlit Cloud dashboard (not in the code).

Day 5 Summary — Course Complete

  • Text-to-SQL: give Claude your schema + the question, get a SQL query back, run it safely.
  • Always validate AI-generated SQL before executing. Only allow SELECT statements.
  • Auto-detect numeric and text columns to generate charts automatically from query results.
  • Deploy to Streamlit Community Cloud for free — push to GitHub and connect.

Take This Further in the Live Bootcamp

In the 3-day bootcamp you'll build a production-grade AI dashboard with authentication, real-time data, and deploy it with a custom domain. Five cities, October 2026.

Reserve Your Seat — $1,490
Finished this lesson?