Come funziona
Il modello propone.
SQAI dispone.
Un agente non scrive mai SQL e non emette mai codice da eseguire. Scrive una richiesta tipizzata — e ogni richiesta percorre la stessa linea: validata contro un contratto fissato, verificata rispetto alla tua policy, eseguita su un motore deterministico, restituita con gli hash che la riproducono.
Ogni richiesta — ask(), compute(), o una tool call dell'AI SDK — percorre la stessa linea. Nulla raggiunge il motore finché ogni controllo non è superato.
01Intento tipizzato
Una richiesta che il motore può analizzare. Non una stringa di cui fidarsi.
Il modello emette un oggetto tipizzato — mai SQL, mai codice. Un intento di query è un QuerySpec: una metrica da aggregare su una sorgente nominata. Un intento di calcolo è un ComputationSpec: una funzione nominata dal contratto, con argomenti e binding. Un'unione discriminata, due forme — e in nessuna delle due c'è spazio per nascondere una stringa eseguibile.
Questa è l'intera superficie di authoring. Ciò che la forma non può esprimere, il modello non può richiedere.
QuerySpecanatomia{"kind": "query",il tag dell'unione — query o computation"version": "1",la versione dello spec, fissata"source_name": "sales",una sorgente connessa, per nome esatto"metric": "revenue",la colonna da aggregare"aggregation": "sum",uno tra sum · avg · count · min · max"group_by": "region"opzionale — suddivide il risultato per un campo}anch'esso opzionalefilter · limit · order
la forma computationmodule · function · args · kwargs · bindings · seed?
02Verifica contratto
Un contratto, hash fissato. L'ignoto viene rifiutato con indicazioni.
Lo spec viene validato contro il contratto di capability — un file generato e hash-fissato che elenca ogni operazione, la sua firma esatta e i suoi flag di determinismo. Una capability è raggiungibile solo se è in sola lettura e deterministica, o deterministica se inizializzata con un seed. Le operazioni con scrittura e non deterministiche non vengono bloccate a runtime; non sono mai state generate nella superficie.
Una funzione sconosciuta fallisce come unsupported_operation e risponde con nearest_matches dallo stesso indice — il modello si corregge invece di ciclare.
contract_hashsha256:79f1c5a6c7164e7e9e1750e70a5c03292fa87eb52d8148a740c06695924be9a1
- 4,778
- operazioni elencate nel contratto
- 4,574
- esposte agli SDK, sola lettura
- 4,564
- completamente deterministiche
- 10
- simulazioni con seed obbligatorio
- 204
- escluse dalla superficie
idoneitàread_only && (deterministic || deterministic_when_seeded)
su un nome sconosciutounsupported_operation + nearest_matches
03Verifica policy
La tua allow-list decide se — e come — viene eseguito.
La policy viene fissata quando createSQAI() costruisce l'istanza e applicata in-process prima di qualsiasi esecuzione — sulla sorgente, su ogni metrica, raggruppamento, filtro e colonna vincolata, e sul nome della funzione. Può solo restringere il contratto: nominare una capability al di fuori della superficie idonea genera comunque unsupported_operation.
L'input dello strumento del modello non contiene alcun campo allowed*. Nulla nella richiesta può ampliare l'accesso — quindi non c'è nulla che un prompt injection possa allargare.
allowedSourcessorgenti che il modello può nominare
- "sales"
allowedFieldscolonne che può leggere, per sorgente
- sales.region
- sales.revenue
- sales.order_date
allowedFunctionsil default — ogni capability in sola lettura, niente di più
- "all-readonly"
un rifiuto è preciso, attribuito, definitivo
- policy_denied_source
- policy_denied_field
- policy_denied_function
source: "sqai" · non ripetibile
04Motore deterministico
Fissato prima dell'esecuzione: float64, un thread, un runtime.
Prima si risolvono i binding. Il modello ha nominato una sorgente e un campo; SQAI recupera i valori effettivi tramite la primitiva extractColumns del motore, allineata per riga, con i null gestiti a coppie. SQAI non concatena mai array autonomamente — così l'input che viene sottoposto a hash è esattamente l'input che è stato eseguito.
Solo ora la richiesta raggiunge il motore. Il runtime è firmato, versionato e fissato: precisione float64, un singolo thread. Il primo compute() lo inizializza una volta sola, in circa 110 secondi; dopodiché rimane residente — finance.npv misura 0,83–0,93 ms a caldo.
0.1.04d64142e4c1ff63d299cfc8b172fdf97cb59169b545e1e02978e678a632ce6e1darwinarm64float641—2ea5ede72acd2912fe9e1230cef34afad4c9436bfa8a709bac2da02b478e3212Registrato con ogni risultato — l'envelope dichiara l'ambito della garanzia invece di sovrastimarla.
05Risposta + provenienza
Due hash. Uno prima, uno dopo.
invocation_hash viene apposto prima dell'esecuzione — sul modulo, la funzione, gli argomenti, i binding risolti, il seed, il contratto e lo scope. computation_hash viene apposto dopo: l'invocation hash combinato con il risultato canonico. Riprodurre significa rieseguirlo e confrontare i byte.
Entrambi gli SDK condividono un unico serializzatore canonico, così lo stesso calcolo produce hash byte-identici in TypeScript e Python. Le risposte alle query seguono la stessa disciplina: un plan_hash sul piano risolto canonico, più il percorso decisionale e lo scope esatto in cui è stato eseguito.
3188.1687606249325finance.npv · vincolato a una colonna live · 0,83 ms a caldo
apposto prima dell'esecuzione
invocation_hash
8223a694250fc751fcf8a7777b8e1f524c44ff1f0e7e83451467f554a6123950
modulo · funzione · argomenti · binding risolti · seed · contratto · scope
apposto dopo l'esecuzione
computation_hash
ff5280ea128113f528dd1e9a28b9bc4a81469075ed7c981c7176fb172d98085d
l'hash di invocazione, combinato con il risultato canonico
Byte per byte identici in TypeScript e Python — un risultato calcolato in un linguaggio si riproduce e si verifica nell'altro.
entrambi calcolati rispetto acontract_hash sha256:79f1c5a6…
sul piano di queryplan_hash f87610d8afeb…decision_path "exact_spec"deterministic_scope "local_registered_source"
06Il ciclo di scoperta
Mai supporre. Scopri, simula, poi esegui.
I nomi dei campi e le firme delle funzioni vengono scoperti, non allucinati. L'AI SDK espone esattamente tre strumenti — e solo uno di essi esegue.
- 01
listSources()Cosa posso toccare?
Sorgenti connesse, con nomi di campo esatti, tipi e operazioni consentite per campo. Non esegue mai.
- 02
listSources({ capabilitySearch: "npv" })Cosa posso calcolare?
Lo stesso strumento, che interroga il catalogo deterministico — moduli corrispondenti, firme esatte e se è richiesto un seed.
- 03
explainQuery({ … })Funzionerà?
Una simulazione a secco. Restituisce il piano risolto, la confidenza e un invocation_hash di anteprima — un'anteprima, mai un hash eseguito.
- 04
queryData({ … })Esegui.
L'unico strumento che esegue. Restituisce il risultato con la sua provenienza completa; i risultati sovradimensionati rimangono recuperabili tramite result_id.
Gli strumenti non generano mai eccezioni — ogni esito è tipizzatook · needs_clarification · rejected · error
La meccanica, campo per campo.
Leggi la documentazione →- AI deterministical'envelope e i tre hash, campo per campo
- Governance dei dati AIallow-list e la regola narrow-only, in profondità
- Connetti i datiogni sorgente assume la stessa forma tipizzata