listSources
non esegue maiDiscovery. Sorgenti connesse con campi tipizzati, moduli di capability, firme di funzione — tre modalità da un unico input, così le spec usano nomi esatti.
Per gli agenti — lo strumento dati governato
SQAI offre a un modello con tool-calling accesso in sola lettura ai dati strutturati tramite tre strumenti governati. Il modello esprime intento tipizzato. Un contratto e la tua policy lo verificano. Un motore deterministico lo esegue. Ogni risposta torna con un hash che la riproduce.
IUn turno del ciclo
“Quale regione ha avuto il fatturato totale più alto?”Pianifica i passi. Esprime l'intento. Non tocca mai i dati.
{ version:"1", kind:"query", spec:{ metric:"revenue", aggregation:"sum", group_by:"region", source:"sales" } }contractcontratto di capability fissato con hashpolicyle tue allow-list, verificate prima dell'esecuzioneengineesecuzione deterministica, in sola letturastatus: ok · needs_clarification · rejected · erroreast — 2,130.50 · plan_hash f87610d8afeb…okEsce dal ciclo. Le righe o il valore — e l'hash che li riproduce.
needs_clarification ↺Rientra nel ciclo. Una domanda e i candidati trovati — il modello affina la spec e chiama di nuovo. Non indovina mai una colonna.
rifiuto ed errore percorrono lo stesso canale di ritorno — risultati tipizzati che il modello può leggere, non eccezioni che interrompono l'esecuzione.
IIGli strumenti
sqai.tools() restituisce esattamente tre strumenti — una superficie deliberatamente ridotta. Discovery e dry run non eseguono mai; l'esecuzione avviene in un solo punto.
listSourcesDiscovery. Sorgenti connesse con campi tipizzati, moduli di capability, firme di funzione — tre modalità da un unico input, così le spec usano nomi esatti.
queryDataUn unico input versionato — una discriminated union su kind: una query o un calcolo. Quattro esiti, sempre tipizzati, mai un'eccezione.
explainQueryRisolve e valida un'intenzione senza eseguirla. Il modello la usa per diagnosticare i chiarimenti; tu la usi per visualizzare un piano.
Il set si inserisce direttamente in generateText o streamText — tipizzato, assegnabile senza cast. Configurazione Vercel AI SDK →
IIIIl contratto d'errore
Un'eccezione interrompe un'esecuzione agente. Quindi nulla qui lancia: ambiguità, rifiuto e fallimento tornano come stati tipizzati che il modello può leggere — e correggere — nel suo budget di step.
retryable contrassegna quelli transitori — network_error · timeout · rate_limited · service_unavailable
queryData — i quattro esiti
okIl risultato: righe o valore, hash di provenienza, troncamento dichiarato.plan_hash · invocation_hash · computation_hash · truncatedneeds_clarificationL'intenzione era ambigua. Una domanda con i candidati — mai un'ipotesi.question · candidates · explanationrejectedIl resolver ha rifiutato l'intenzione. Nulla è stato eseguito.rejection_reason · candidates · explanationerrorUn fallimento strutturato con un codice stabile — il modello lo legge e si adegua.code · message · retryable · nearest_matches?Ogni ramo è dato. Il loop mantiene il suo turno.
Il principio
policy nel codice · nessun campo policy in alcun input tool · solo restrizione
IVIl principio, applicato
Il settore lo ha imparato in produzione — un agente di coding autonomo ha cancellato notoriamente un database live. La risposta di SQAI è strutturale, non comportamentale: la superficie esposta è in sola lettura per costruzione. Le categorie di scrittura e non deterministiche non vi vengono mai generate, e nessun flag, policy o input del modello le riattiva.
VIl collegamento
Un pacchetto avvolge il client in tool. Le sorgenti si connettono in modo lazy alla prima chiamata; il primo calcolo provisiona il runtime una volta, poi rimane attivo — 0,83 ms misurati.
import { generateText } from "ai";
import { createSQAI } from "@thyn-ai/sqai-ai-sdk";
const sqai = createSQAI({
sources: [{ data: "./data/sales.csv", name: "sales" }],
});
const { text, steps } = await generateText({
model, // any AI SDK model
tools: sqai.tools(), // listSources · queryData · explainQuery
prompt: "Which region had the highest total revenue?",
});Solo lato server — su Next.js, mantieni il runtime Node, non Edge.
Cosa raggiunge il modello
Il troncamento è sempre dichiarato — truncated: true, returned_rows < total_rows. Mai silenzioso. I risultati completi rimangono recuperabili nel codice applicativo tramite result_id.
VIAnalytics agentici
Gli analytics agentici falliscono in modi noti: chiamate tool malformate, modelli che fanno i propri calcoli, numeri plausibili che nessuno può verificare. Ognuno trova risposta strutturale, non in un prompt migliore.
Un unico input versionato, validato prima di qualsiasi esecuzione. Un'intenzione malformata o ambigua restituisce uno stato tipizzato — il loop continua invece di crashare.
Non qui. Calcola il motore. Su un set quantitativo di 16 domande, lo stesso modello è passato da 0/16 a 16/16 non appena il calcolo è stato affidato al motore.
Ogni risposta porta il suo hash. Ripeti la stessa intenzione sugli stessi dati e l'hash corrisponde — identico al byte in TypeScript e Python.
Un loop moltiplica la latenza. Il piano di query gira in-process a sub-millisecondo; il calcolo a caldo misurato a 0,83 ms.
VIIDomande
Non filtrare SQL — non generarlo. Connetti le sorgenti a createSQAI() e passa al modello sqai.tools(): la superficie che raggiunge è 4.574 capability in sola lettura più le tue sorgenti, senza percorso di scrittura per costruzione. Non esiste una categoria di scrittura da bloccare, né alcun input del modello che la riabiliti.
queryData restituisce needs_clarification con una domanda e i campi candidati individuati. Non ipotizza mai una colonna. Il modello risponde alla domanda e chiama di nuovo — un altro giro del ciclo.
No. Ogni errore è un risultato strutturato con un codice stabile, un messaggio e un flag retryable. Ambiguità, rifiuto, diniego di policy e guasti transitori tornano tutti come dati leggibili dal modello.
No. allowedSources, allowedFields e allowedFunctions vivono nel codice, impostati al momento di createSQAI() e verificati prima dell'esecuzione. Nessuno schema di input degli strumenti contiene un campo di policy; nominare una capability negata restituisce un errore strutturato policy_denied.
Sì — @thyn-ai/sqai-ai-sdk distribuisce i tre strumenti come typed tool set per generateText e streamText, con ai ^7.0.0 e zod ^4.0.0 come peer, Node 20 o superiore, lato server. Il client sottostante è lo stesso SDK collegabile a qualsiasi ciclo agente.