IIl caso contro il SQL generato

Il modello non dovrebbe
scrivere il SQL.

Il text-to-SQL è il modo più ovvio per puntare un agente su un database, e la demo è genuinamente impressionante. È in produzione che le cose si complicano: la stessa domanda compila in SQL diverso, un errore silenzioso di join sposta il fatturato del 15%, e l'output del modello diventa una superficie d'attacco. Esiste un'altra strada.

IIPrima il merito

Text-to-SQL ha meritato l'hype. Nel suo dominio.

Dove è lo strumento giusto

  • Prototipi su dati usa e getta
  • Esplorazione di uno schema sconosciuto, con supervisione umana
  • Domande una tantum che qualcuno verificherà
  • La demo — funziona davvero bene in demo

Dove si rompe

  • Numeri su cui qualcuno agirà
  • Testo non attendibile in qualsiasi punto del contesto
  • Credenziali di produzione sulla connessione
  • Risposte che devono essere riproducibili o verificabili
  • Agenti che operano senza supervisione

Il pattern non è sbagliato. Il raggio d'azione lo è. Ogni rischio in questa pagina risale a una sola scelta progettuale: l'output del modello viene eseguito come codice.

IIILe due strade

Stessa domanda. Due architetture.

Segui una domanda lungo entrambe le strade — una stringa SQL libera a sinistra, un piano tipizzato a destra. I numeri rimandano al registro dei rischi qui sotto.

la domanda

Quale regione ha il fatturato totale più alto?

Percorso A

Il modello scrive SQL

  1. il modello improvvisa una stringa 010304

    Qualsiasi cosa il modello abbia letto — un messaggio utente, una riga recuperata — può influenzare questa stringa.

    esecuzione 1SELECT region, SUM(total) FROM orders GROUP BY region;esecuzione 2 · stessa domandaSELECT o.region, SUM(i.amount) FROM orders o LEFT JOIN order_items i ON i.order_id = o.id GROUP BY 1;
  2. la connessione la esegue 05

    Con la piena autorità della connessione. Lettura, join — e, salvo che qualcuno si sia ricordato del flag, anche scrittura.

la risposta

run 1 → east · 2130.50

run 2 → east · 2450.08+15% — fan-out del join 02

Due esecuzioni, due numeri. Entrambi plausibili. Nessun segnale su quale — se uno — sia corretto.

Percorso B

Il modello invia un piano tipizzato

  1. intento tipizzato

    Non una stringa — un valore con uno schema. Può nominare solo le operazioni definite dal contratto.

    { "kind": "query", "version": "1", "source": "revenue", "op": "sum", "group_by": "region" }
  2. verifica del contratto

    Ancorato tramite hash. Un'operazione sconosciuta restituisce unsupported_operation con le corrispondenze più vicine — mai un'ipotesi.

  3. verifica della policy

    La tua allow-list a livello di codice. Una richiesta può restringere la superficie, mai ampliarla.

  4. esecuzione deterministica

    float64, thread singolo, runtime fisso. Stesso piano, stessi byte, stessa risposta.

la risposta

east · 2130.50

plan_hash f87610d8afeb…

decision_path "exact_spec"

Una risposta, con gli hash per riprodurla — la settimana prossima, il trimestre prossimo, in entrambi i linguaggi.

IVIl registro dei rischi

Cinque modi in cui SQL generato va storto

  1. 01

    P2SQLarXiv 2308.01990

    L'iniezione si sposta sul canale di output

    La sanitizzazione degli input ispeziona ciò che entra nel modello. Gli attacchi P2SQL arrivano in ciò che esce: SQL ben formato e malevolo, assemblato da istruzioni nascoste in un messaggio utente o in una riga recuperata. Nessun filtro di input lo vede mai — l'attacco è l'output.

  2. 02

    −15%la dashboard che mentiva

    I join errati falliscono in silenzio

    Un join con fan-out conta le righe due volte e il fatturato risulta sfalsato del 15%. Nessuna eccezione, nessun avviso — SQL errato non va in crash, riporta. Il risultato è sempre un numero, e un numero plausibile non porta alcun segnale che sia sbagliato.

  3. 03

    1 → nuna domanda, n query

    Stessa domanda, SQL diverso

    Chiedi due volte e il modello può compilare la domanda in due modi diversi — a volte con due risposte diverse. Non esiste una query canonica da revisionare, mettere in cache o riprodurre. Il numero di ieri non può essere riprodotto, nemmeno solo per verificarlo.

  4. 04

    91.2 → 21.3% corretto · benchmark → schema enterprise

    Il precipizio dell'accuratezza

    Su schemi benchmark puliti, un modello frontier ha scritto SQL corretto nel 91,2% dei casi. Su schemi enterprise reali: 21,3%. Nella stessa ondata di ricerca, circa il 40% delle esecuzioni di agenti text-to-SQL ha fallito del tutto o restituito risultati errati. I benchmark sono ordinati. Il tuo schema no.

  5. 05

    1 DB di produzioneeliminato da un agente — l'incidente Replit

    Il percorso di scrittura era sempre lì

    L'agente di coding di Replit ha eliminato un database di produzione nonostante istruzioni esplicite di non toccarlo. Questa è la lezione: un'istruzione di sola lettura è una richiesta. Se la connessione può scrivere, il percorso di scrittura esiste, e prima o poi un completamento errato lo trova. La sola lettura deve essere una proprietà dello strumento, non una riga nel prompt.

Non filtrare l'output.
Non generarlo.

Nessuna stringa SQL, nessuna injection · nessuna supposizione, nessuna deriva

VIl certificato

Cinque dimensioni, a confronto

Non è un generatore LLM-to-SQLREADME del motore

Certificato di differenza

superficie di authoringSQL generatouna stringa SQL liberapiano tipizzatoun'intenzione tipizzata e versionata
superficie di esecuzioneSQL generatotutto ciò che la connessione consentepiano tipizzato4.574 capability di sola lettura
percorso di scritturaSQL generatopresente salvo blocco esplicitopiano tipizzatoassente, per costruzione
governanceSQL generatoa livello di prompt, best-effortpiano tipizzatoallow-list a livello di codice, non ampliabile dal modello
riproducibilitàSQL generatonessunapiano tipizzatoun hash di replay su ogni risultato

colonna destra ancorata al contrattosha256:79f1c5a6…924be9a1

Le clausole reggono: l'unica via d'uscita del motore — getUnsafeRuntime — è assente da ogni superficie esposta al modello, che non può quindi raggiungerla. E il README enuncia la dottrina senza ambiguità: questo non è un generatore LLM-to-SQL.

VIDomande

Poste in produzione

Come blocco DELETE e DROP nel SQL generato da un LLM?

Non filtrare il SQL — smetti di generarlo. Le denylist ispezionano stringhe, e i modelli sono inesauribilmente creativi nel produrne di nuove. SQAI elimina la classe alla radice: il modello presenta un piano tipizzato contro 4.574 capability di sola lettura, e nessuna capability di scrittura esiste per alcun piano.

Cos'è la P2SQL injection?

Prompt-to-SQL injection: SQL malevolo che compare nell'output del modello, assemblato da istruzioni nascoste in ciò che il modello ha letto — un messaggio utente, un documento, una riga recuperata. La sanitizzazione dell'input non la intercetta, perché l'attacco vive nel canale di output. È documentata in arXiv 2308.01990.

Il text-to-SQL è mai la scelta giusta?

Sì — per prototipi ed esplorazione supervisionata su dati non di produzione è rapido e genuinamente utile. Diventa la scelta sbagliata quando il risultato guiderà decisioni, il contesto contiene testo non attendibile, o la risposta deve essere riproducibile.

Come concedo a un agente AI accesso in sola lettura a un database?

Rendi la sola lettura strutturale, non configurata. Un flag di sola lettura su una connessione è un'impostazione che qualcuno può modificare. La superficie di esecuzione di SQAI contiene solo capability di lettura — il percorso di scrittura è assente per costruzione, e persino la via d'uscita del motore è irraggiungibile da qualsiasi strumento esposto al modello.

In cosa differisce un piano tipizzato dal SQL generato?

Una stringa SQL può esprimere qualsiasi cosa la grammatica consenta. Un piano tipizzato può esprimere solo ciò che il contratto definisce. Ogni piano è validato contro un contratto ancorato a un hash, verificato rispetto alla tua policy di allow-list ed eseguito su un motore deterministico — con un hash di replay su ogni risultato.

Prendi l'altra strada.

Nessun account. Nessuna chiave. I dati locali restano locali.