Natural-language → SQL against a real PostgreSQL database, with a safety layer that blocks 100% of destructive operations, a hallucination detector that verifies the generated SQL actually answers the question asked, and a confidence score on every result.
Eval headline (fill in after running
python -m evals.run_evals): X% execution accuracy · Y% hallucination/behavior detection · 6/6 dangerous queries blocked, zero unsafe queries executed.
question ─► schema filter ─► Claude (structured output) ─► guardrails ─► read-only executor
│ │
blocked+logged ◄┘ ▼
back-translation judge + sanity checks + multi-query agreement
▼
confidence score ─► API / Streamlit UI
| Layer | Implementation |
|---|---|
| LLM | Claude (claude-opus-4-8) via the Anthropic SDK, Pydantic structured outputs (messages.parse) |
| Schema | SQLAlchemy introspection + FK graph + sample values + lexical relevance filter |
| Guardrails | sqlglot AST: blocks DDL/DML, multi-statements, deep subqueries; injects LIMIT; EXPLAIN-based scan cap; every block logged |
| Sandbox | READ ONLY transaction, always rolled back, executed as a SELECT-only DB user (defense in depth) |
| Hallucination detection | SQL→question back-translation + LLM alignment judge, result sanity checks (NULL-heavy joins, implausible magnitudes), optional independent multi-query agreement |
| Confidence | Weighted blend: alignment 35%, multi-query 25%, sanity 20%, model self-confidence 15%, syntax 5% |
| Serving | FastAPI (/v1/query, /v1/schema, /v1/history, /v1/feedback) + Streamlit UI |
cp .env.example .env # set ANTHROPIC_API_KEY
docker compose up --build
# UI: http://localhost:8501
# API: http://localhost:8000/docsLocal dev without Docker (Postgres from compose only):
docker compose up -d db
pip install -r requirements.txt
uvicorn app.api:app --reload
streamlit run frontend/streamlit_app.pypytest tests/ # guardrail unit tests (no DB / API key needed)
python -m evals.run_evals # golden-query suite: execution match, exact match,
# ambiguity/unanswerable behavior, guardrail effectiveness- Ambiguity handling — "What is our revenue?" returns structured interpretations (gross vs. collected) instead of a guess.
- Unanswerable detection — questions the schema can't answer are refused with a reason.
- Feedback flywheel — 👍 results become few-shot candidates; 👎 results become eval cases.
- Auditability — every executed and blocked query is logged with EXPLAIN plan and reason.