# Examples — NL-to-SQL Analytics Agent

## Clear question → grounded SQL with stated assumptions

A well-formed analytics question that maps cleanly to the schema. The agent answers and shows its work.

**Input**

```
Question: who were our top 10 customers by revenue last quarter?
```

**Output**

```
{
  "decision": "ANSWER",
  "confidence": 0.86,
  "assumptions": ["'last quarter' = 2026-01-01 to 2026-03-31 (most recently completed calendar quarter)", "revenue = SUM(orders.amount) for orders with status='paid'", "customer = customers.id"],
  "sql": "SELECT c.id, c.name, SUM(o.amount) AS revenue\nFROM orders o JOIN customers c ON c.id = o.customer_id\nWHERE o.status = 'paid' AND o.created_at >= '2026-01-01' AND o.created_at < '2026-04-01'\nGROUP BY c.id, c.name\nORDER BY revenue DESC\nLIMIT 10;",
  "validation": "EXPLAIN estimates ~120k rows scanned via index on orders.created_at, within budget. Result returned 10 rows; revenue values in a plausible range (top customer $48,200).",
  "result_summary": "Top 10 customers by paid revenue for Q1 2026 (Jan–Mar). #1 Northwind Co ($48,200), #2 Acme ($41,750), … full list returned. Combined they were 22% of quarterly revenue.",
  "caveats": "Counts only orders with status='paid'; refunds are not netted out. Tell me if you want net-of-refunds.",
  "clarifying_question": "",
  "refusal_reason": ""
}
```

**Note:** Note the explicit assumptions — 'last quarter', the revenue definition, and the paid-only filter are all surfaced, so the user can correct them. The SQL is bounded with a LIMIT, validated via EXPLAIN, and the result is sanity-checked. That transparency is what makes a NL-to-SQL answer trustworthy.

## Ambiguous question → one clarifying question, no guess

A short question that could mean two very different things. The agent asks instead of silently choosing one and returning a confidently-wrong number.

**Input**

```
Question: what are our best products?
```

**Output**

```
{
  "decision": "CLARIFY",
  "confidence": 0.4,
  "assumptions": [],
  "sql": "",
  "validation": "Not run — 'best' is undefined and would produce different top lists depending on the metric.",
  "result_summary": "",
  "caveats": "",
  "clarifying_question": "Do you mean best by revenue, by units sold, or by profit margin — and over what time period (e.g. last 30 days, this year)?",
  "refusal_reason": ""
}
```

**Note:** 'Best' is the trap. Revenue, units, and margin can yield completely different rankings, so the agent asks one focused question rather than guessing. This single behavior prevents a large share of confidently-wrong analytics answers — and builds user trust faster than a fast wrong number ever could.

## Unsafe request → refuse the write and the unbounded scan

The request mixes a data modification with an unbounded read. The agent refuses both and offers a safe alternative.

**Input**

```
Question: delete all orders older than 2020, and also show me every row in the events table so I can look around.
```

**Output**

```
{
  "decision": "REFUSE",
  "confidence": 0.95,
  "assumptions": [],
  "sql": "",
  "validation": "Blocked at the gate: (1) DELETE is a write operation; this agent is read-only. (2) 'every row in events' is an unbounded scan of a ~2.1B-row table, far over the cost budget and with no LIMIT.",
  "result_summary": "",
  "caveats": "",
  "clarifying_question": "",
  "refusal_reason": "I can't modify data (no DELETE/UPDATE/DDL) — that needs a DBA with proper backups and a migration. And I won't scan the entire events table (~2.1B rows); if you tell me what you're looking for (a time window, an event type, a user), I'll write a fast, bounded query. For the cleanup, I can instead show you how many orders predate 2020 so the team can plan a safe deletion."
}
```

**Note:** Two distinct guardrails fire at once: read-only (refuses the DELETE) and the cost guard (refuses the 2.1B-row scan). Critically, it doesn't just say no — it offers a safe path for each (a bounded exploratory query, and a count to support a planned, human-run cleanup). Defensive but still helpful.
