recon + sqlglot validator + drill_down package; guard ReAct dimension picks against candidate list

This commit is contained in:
2026-06-03 07:15:02 -03:00
parent e124a8a7d9
commit 2dad62f7e7
38 changed files with 1954 additions and 596 deletions

View File

@@ -0,0 +1,7 @@
Task: write a short interpretation of an iterative drill-down across a metric.
You're given the analyst's question, the metric, and the slices tried (each: a dimension and its top rows). Identify the dimension(s) that show the strongest signal — biggest variance, most concentrated outliers, clearest pattern — and write a short, concrete summary.
Output: two to four sentences. Lead with the dimension(s) that mattered most. Cite specific values from the slices (e.g. "districts with unemployment above 5% accounted for 73% of defaults"). No preamble.
If no slice produced a meaningful signal, say so plainly.

View File

@@ -0,0 +1,7 @@
Question:
{question}
Metric: {metric}
Slices (in order tried):
{slices_block}

View File

@@ -0,0 +1,16 @@
Task: pick the next dimension to slice by, in an iterative drill-down investigation.
You're given the analyst's question, the candidate dimensions, the dimensions already tried (with their slice rows), and a remaining iteration budget. Decide which dimension to try next, or stop if the investigation has converged or budget is exhausted.
Output: a JSON object in one fenced ```json block:
{
"action": "drill" | "stop",
"dimension": "<dimension name from the candidate list>", // required when action is "drill"
"reason": "<one sentence>"
}
Rules:
- Pick a dimension that hasn't been tried yet and that the previous slices suggest might explain variance in the metric.
- If every candidate has been tried, or the previous slice already shows a clear story, return action="stop".
- Don't repeat dimensions.
- Never invent a dimension that isn't in the candidate list.

View File

@@ -0,0 +1,13 @@
Question:
{question}
Metric we're slicing:
{metric}
Candidate dimensions:
{dimensions}
Already tried (with row previews):
{history}
Remaining iterations: {budget}

View File

@@ -6,7 +6,16 @@ Output: a JSON object with exactly this shape, in one fenced ```json block:
{
"rationale": "<one sentence on why this plan>",
"steps": [
{"analysis": "<analysis_name>", "args": { ... }, "why": "<one sentence>"}
{
"analysis": "<analysis_name>",
"args": { ... },
"why": "<one sentence>",
"fallback": { // OPTIONAL — see rules
"analysis": "<analysis_name>",
"args": { ... },
"why": "<one sentence>"
}
}
]
}
@@ -15,3 +24,4 @@ Rules:
- The `args` keys must match the analysis's args_schema; additional keys are ignored, missing optional ones are fine.
- Most questions are single-step. Only chain analyses when the second genuinely needs the first's output.
- Comparing two periods is ONE `compare_periods` step, not two `direct_answer` steps.
- A step's `fallback` is optional. Include one when the primary analysis is speculative (e.g. an exploratory `drill_down` that might find nothing useful) and there's a safer Analysis that can still produce an answer. The fallback runs only if the primary errors or yields no finding. Fallbacks must not themselves have fallbacks.

View File

@@ -4,8 +4,9 @@ Output: exactly one fenced ```sql block, nothing else — no prose, no second bl
Constraints:
- search_path is set to `financial`; don't qualify table names with the schema.
- **Postgres folds unquoted identifiers to lowercase.** Always double-quote any column or table name that contains an uppercase letter — including all the `district` columns: `"A2"`, `"A3"`, `"A4"`, `"A5"`, `"A6"`, `"A7"`, `"A8"`, `"A9"`, `"A10"`, `"A11"`, `"A12"`, `"A13"`, `"A14"`, `"A15"`, `"A16"`. Mixing quoted and unquoted forms of the same column (`"A3"` and `A13`) will fail at runtime — be consistent.
- Quote identifiers that collide with SQL keywords (notably `"order"`).
- Dates in the source are YYMMDD integers (e.g. 950315 → 1995-03-15). Convert with TO_DATE(LPAD(date::text, 6, '0'), 'YYMMDD') whenever you need a real date for filtering, comparison, or display.
- Dates in the source are YYMMDD integers (e.g. 950315 → 1995-03-15). Convert with `TO_DATE(LPAD(date::text, 6, '0'), 'YYMMDD')` whenever you need a real date for filtering, comparison, or display.
- Loan `status` values: 'A' finished OK, 'B' finished defaulted, 'C' running OK, 'D' running in debt.
- Transaction `type` values: 'PRIJEM' credit, 'VYDAJ' debit.
- Read-only: never emit DDL or DML.