Chat With Your Data: a natural-language layer on top of BudgetDB.
Ask a plain-English finance or operations question, run read-only SQL against a trusted warehouse through tool calling, and answer from the actual rows queried.
chat_with_data.case_study
/chat-with-data
Business question
Can leadership query BudgetDB without writing SQL, while keeping the AI read-only?
This project continues BudgetDB: the warehouse provides trusted raw-to-fact data, and Chat With Your Data adds a conversational layer with schema-grounded tool use, SQL safety checks, and auditable database reads.
1defis_safe_select(sql: str) -> bool:2stripped = sql.strip().rstrip(";")3ifnotre.match(r"^\s*(SELECT|WITH)\b", stripped, re.I):4returnFalse5if_FORBIDDEN.search(stripped):6returnFalse7if";"instripped:8returnFalse9returnTrue101112defrun_sql_query(sql: str) -> dict:13ifnotis_safe_select(sql):14return {"error": "Refused: only a single read-only SELECT/WITH statement is permitted."}1516conn = sqlite3.connect(f"file:{DB_PATH}?mode=ro", uri=True)17conn.row_factory = sqlite3.Row18rows = conn.execute(sql).fetchall()19conn.close()20return {"rows": [dict(r) forrinrows][:200]}
How it works
A three-step agentic loop, not a static prompt-to-SQL demo.
The system gives the AI controlled context and one controlled capability: query the warehouse safely.
agent_loop.workflow
schema.tool.answer
Schema-grounded context
Claude receives the warehouse schema, table meanings, and guidance on which tables to prefer for finance and workforce questions.
One controlled tool
The model has exactly one tool, run_sql_query. It chooses SQL, receives real rows, and can run follow-up queries inside a capped agentic loop.
Numbers-backed answer
The final response is written in plain English and grounded in the actual rows returned from the database, not a guessed summary.
Safety model
Read-only enforcement is the senior design decision.
For finance and operations data, the system cannot rely on prompt instructions alone. The tool uses application-level allowlisting and database-level read-only enforcement.
safety_layers.checks
allowlist.mode=ro.audit
Allowlisted SQL
Generated SQL must start with SELECT or WITH, cannot contain mutating statements, and cannot stack multiple statements.
Read-only connection
The demo opens SQLite in mode=ro. The production path is a dedicated read-only Postgres role with SELECT grants only.
Visible audit trail
Every query is printed before execution so the system leaves a clear record of what the AI asked from the warehouse.
Over-cautious by design
The allowlist accepts false positives, such as rejecting a literal string that contains DROP, because finance workflows should favor safety.
Source notes
What is real, what is next, and what is intentionally disclosed.
This page keeps the same source-note pattern as the rest of the portfolio: anonymized data, real project files, and no softened claims.
Built and tested
Safety allowlist, read-only database execution, seeded demo warehouse, and tests against the real seeded database.
Written but not live-tested here
The Claude API tool-calling loop follows the Anthropic SDK pattern, but the live API round trip was not tested in the build environment because no API key was available.
Production path
Swap SQLite for Postgres through a dedicated read-only role, add a lightweight web UI, and log question, generated SQL, and answer for auditability.