Fonctionnement
Le modèle propose.
SQAI dispose.
Un agent n'écrit jamais de SQL et n'émet jamais de code à exécuter. Il formule une requête typée — et chaque requête emprunte le même chemin : validée contre un contrat épinglé, vérifiée selon votre politique, exécutée par un moteur déterministe, retournée avec les hachages qui permettent de la rejouer.
Chaque requête — ask(), compute(), ou un appel d'outil via un AI SDK — emprunte le même chemin. Rien n'atteint le moteur tant que chaque vérification n'a pas abouti.
01Intention typée
Une requête que le moteur peut analyser. Pas une chaîne à laquelle il doit faire confiance.
Le modèle émet un objet typé — jamais du SQL, jamais du code. Une intention de requête est un QuerySpec : une métrique à agréger sur une source nommée. Une intention de calcul est un ComputationSpec : une fonction nommée issue du contrat, avec ses arguments et ses liaisons. Une union discriminée, deux formes — et nulle part dans l'une ou l'autre où une chaîne exécutable pourrait se dissimuler.
Voilà toute la surface d'expression. Ce que la forme ne peut pas exprimer, le modèle ne peut pas le demander.
QuerySpecanatomie{"kind": "query",le tag d'union — query ou computation"version": "1",la version du spec, épinglée"source_name": "sales",une source connectée, par nom exact"metric": "revenue",la colonne à agréger"aggregation": "sum",l'une de sum · avg · count · min · max"group_by": "region"optionnel — ventiler le résultat par un champ}également optionnelfilter · limit · order
la forme de calculmodule · function · args · kwargs · bindings · seed?
02Vérif. du contrat
Un contrat, épinglé par hachage. L'inconnu est refusé avec des indications.
Le spec est validé contre le contrat de capacités — un fichier généré, épinglé par hachage, listant chaque opération, sa signature exacte et ses indicateurs de déterminisme. Une capacité n'est accessible que si elle est en lecture seule et déterministe, ou déterministe avec graine. Les opérations en écriture et non déterministes ne sont pas bloquées à l'exécution ; elles n'ont jamais été générées dans la surface.
Une fonction inconnue échoue en unsupported_operation et répond avec nearest_matches issus du même index — le modèle se corrige lui-même au lieu de boucler.
contract_hashsha256:79f1c5a6c7164e7e9e1750e70a5c03292fa87eb52d8148a740c06695924be9a1
- 4,778
- opérations listées dans le contrat
- 4,574
- exposées aux SDK, en lecture seule
- 4,564
- entièrement déterministes
- 10
- simulations à graine obligatoire
- 204
- exclues de la surface
éligibilitéread_only && (deterministic || deterministic_when_seeded)
sur un nom inconnuunsupported_operation + nearest_matches
03Vérif. de politique
Votre liste d'autorisation décide si — et comment — cela s'exécute.
La politique est fixée lors de la construction de l'instance par createSQAI() et appliquée en cours de processus avant toute exécution — sur la source, sur chaque métrique, groupe, filtre et colonne liée, ainsi que sur le nom de la fonction. Elle ne peut que restreindre le contrat : nommer une capacité hors de la surface éligible lève toujours unsupported_operation.
L'entrée d'outil du modèle ne comporte aucun champ allowed*. Rien dans la requête ne peut élargir l'accès — il n'y a donc rien qu'une injection de prompt puisse élargir.
allowedSourcessources que le modèle peut nommer
- "sales"
allowedFieldscolonnes qu'il peut lire, par source
- sales.region
- sales.revenue
- sales.order_date
allowedFunctionsle défaut — toutes les capacités en lecture seule, rien de plus
- "all-readonly"
un refus est précis, attribué, définitif
- policy_denied_source
- policy_denied_field
- policy_denied_function
source: "sqai" · non réessayable
04Moteur déterministe
Épinglé avant exécution : float64, un thread, un runtime.
D'abord, les liaisons se résolvent. Le modèle a nommé une source et un champ ; SQAI extrait les valeurs réelles via la primitive extractColumns du moteur, alignée par ligne, les nulls traités par paires. SQAI ne zippe jamais les tableaux lui-même — ainsi l'entrée hachée est exactement l'entrée qui a été exécutée.
Ce n'est qu'à ce stade que la requête atteint le moteur. Le runtime est signé, versionné et épinglé : précision float64, thread unique. Le premier compute() le provisionne une fois, en environ 110 secondes ; il reste ensuite résident — finance.npv mesure 0,83–0,93 ms à chaud.
0.1.04d64142e4c1ff63d299cfc8b172fdf97cb59169b545e1e02978e678a632ce6e1darwinarm64float641—2ea5ede72acd2912fe9e1230cef34afad4c9436bfa8a709bac2da02b478e3212Enregistrée avec chaque résultat — l'enveloppe énonce la portée de la garantie sans la surestimer.
05Réponse + provenance
Deux hachages. Un avant, un après.
invocation_hash est apposé avant l'exécution — sur le module, la fonction, les arguments, les liaisons résolues, la graine, le contrat et la portée. computation_hash est apposé après : le hachage d'invocation combiné avec le résultat canonique. Rejouer signifie exécuter à nouveau et comparer octet par octet.
Les deux SDK partagent un sérialiseur canonique unique, de sorte que le même calcul produit des hachages identiques octet par octet en TypeScript et en Python. Les réponses aux requêtes obéissent à la même rigueur : un plan_hash sur le plan résolu canonique, plus le chemin de décision et la portée exacte dans laquelle il s'est exécuté.
3188.1687606249325finance.npv · lié à une colonne active · 0,83 ms à chaud
apposé avant l'exécution
invocation_hash
8223a694250fc751fcf8a7777b8e1f524c44ff1f0e7e83451467f554a6123950
module · fonction · args · liaisons résolues · seed · contrat · portée
estampillé après exécution
computation_hash
ff5280ea128113f528dd1e9a28b9bc4a81469075ed7c981c7176fb172d98085d
le hash d'invocation, combiné avec le résultat canonique
Octet pour octet identiques en TypeScript et en Python — un résultat calculé dans un langage se rejoue et se vérifie dans l'autre.
tous deux calculés contrecontract_hash sha256:79f1c5a6…
sur le plan de requêteplan_hash f87610d8afeb…decision_path "exact_spec"deterministic_scope "local_registered_source"
06La boucle de découverte
Ne jamais supposer. Découvrir, simuler, puis exécuter.
Les noms de champs et les signatures de fonctions sont découverts, non hallusinés. Le SDK IA expose exactement trois outils — et un seul exécute.
- 01
listSources()Que puis-je toucher ?
Les sources connectées, avec les noms de champs exacts, les types et les opérations autorisées par champ. N'exécute jamais.
- 02
listSources({ capabilitySearch: "npv" })Que puis-je calculer ?
Le même outil, interrogeant le catalogue déterministe — modules correspondants, signatures exactes, et si un seed est requis.
- 03
explainQuery({ … })Cela fonctionnera-t-il ?
Une simulation. Retourne le plan résolu, la confiance et un invocation_hash de prévisualisation — une prévisualisation, jamais un hash exécuté.
- 04
queryData({ … })Exécuter.
Le seul outil qui exécute. Retourne le résultat avec sa provenance complète ; les résultats volumineux restent accessibles par result_id.
Les outils ne lèvent jamais d'exception — chaque résultat est typéok · needs_clarification · rejected · error
La mécanique, champ par champ.
Lire la documentation →- IA déterministel'enveloppe et les trois hashes, champ par champ
- Gouvernance des données IAlistes d'autorisation et règle de restriction stricte, en profondeur
- Connecter les donnéeschaque source produit la même forme typée