Skip to content

6 · Self-Correction — how it corrects errors

packages/dql-agent/src/answer-loop.ts (query-plan + repair) · agent-run-engine.ts (replan / escalate) · agent-run-gates.ts

ZLens corrects itself at three layers: prevention (a query-plan before SQL), execution-guided repair (fix the SQL from the real error), and escalation (switch to a deeper route). Crucially, the recovery reasoning stays in the trace — it never leaks into the answer the user reads.

Prevention — query-plan before SQL (CoT)

The SQL-generation prompt requires the model to state its plan before writing SQL: the grain (one row per what), the measures + aggregation, the dimensions/filters, and the exact join path + keys. Reasoning about grain and joins up front is the single cheapest accuracy lever in the text-to-SQL literature — it prevents wrong-grain answers and fan-out joins at the source.

flowchart LR
    Q["Question + grounded schema"] --> PLAN["1 · Query plan<br/>grain · measures · dims · join path + keys"]
    PLAN --> SQL["2 · SQL that matches the plan"]
    SQL --> EXEC["3 · Bounded preview execution"]

Execution-guided repair loop

sequenceDiagram
    participant EX as Executor
    participant WH as Warehouse preview
    participant GT as Gate
    participant LP as Loop / replan

    EX->>WH: run generated SQL
    WH-->>EX: error — column customer_name not found
    Note over EX: 1. local lexical repair — cheap, deterministic
    EX->>WH: retry repaired SQL
    WH-->>EX: still failing
    Note over EX: 2. LLM SQL repair — corrected SQL + prose
    EX->>EX: adopt corrected SQL · keep prose in TRACE only
    EX->>WH: retry corrected SQL
    WH-->>EX: rows returned
    EX->>GT: result to gate
    GT-->>LP: passed, or semantic flag then one more retry
    Note over LP: budgeted repairs, then accept as needs_review

The critical invariant — recovery reasoning never becomes the answer. When the LLM repairs the SQL it also emits error-recovery prose ("the column X was not recognized… I updated the query to use Y"). That prose is captured as a repairNarrative and surfaced in the trace / analysis plan, not as parsed.text. The answer the user sees stays a clean, data-first summary of the corrected result.

flowchart LR
    RP["LLM repair response"] --> SQLc["corrected SQL → adopt ✅"]
    RP --> NAR["recovery prose"]
    NAR -->|"trace / assumptions"| TRACE["🔍 'Auto-corrected the query…'"]
    NAR -.->|"❌ never"| ANS["the answer text"]
    SQLc --> RESULT["execute → clean data summary → answer"]

This fixed the exact repro where "who are the top customers?" showed the error narrative instead of the ranked customers. Now: clean summary + the real table.

Escalation — switch route when repair can't help

If a step's failure isn't SQL-repairable (e.g. no answer could be grounded at all), the gate emits a blocking eval with an escalate action. The loop switches to a deeper route.

flowchart LR
    G1["generated_answer<br/>(no grounded answer)"] -->|escalate| R["research"]
    C1["certified_answer<br/>(no certified block)"] -->|escalate| R
    A1["app_build<br/>(no certified tiles)"] -->|escalate| B["dql_block_draft"]

The full correction ladder

flowchart TD
    START["Executor produces result"] --> E{"gate: passed?"}
    E -->|yes| DONE["accept → trust state"]
    E -->|"no · SQL error"| R1["local lexical repair"]
    R1 --> R2["LLM SQL repair (prose → trace)"]
    R2 --> E
    E -->|"no · semantic (fan-out)"| R3["retry: 'aggregate to one value'"]
    R3 --> E
    E -->|"no · can't ground"| ESC["escalate to deeper route"]
    E -->|"budget exhausted"| ACC["accept as needs_review<br/>(surface honestly, don't loop)"]
    ESC --> START

What makes this governed self-correction

Property Why it matters
Recovery prose → trace, not answer The user reads results, not the agent's debugging
Execution-guided (real error → fix) Corrections are grounded in what the warehouse actually said
Bounded repairs No infinite loops or runaway cost
Semantic gate drives a repair Catches wrong-but-runnable SQL, not just crashes
Escalation is mapped, not random Predictable "answer → research" deepening

→ Next: Memory & learning