listSources
n'exécute jamaisDécouverte. Sources connectées avec champs typés, modules de capacités, signatures de fonctions — trois modes depuis une seule entrée, pour que les specs utilisent les noms exacts.
Pour les agents — l'outil de données gouverné
SQAI donne à un modèle à appel d'outils un accès en lecture seule aux données structurées via trois outils gouvernés. Le modèle formule une intention typée. Un contrat et votre politique la vérifient. Un moteur déterministe l'exécute. Chaque réponse est accompagnée d'un hachage qui permet de la rejouer.
IUn tour de boucle
“Quelle région a enregistré le chiffre d'affaires total le plus élevé ?”Planifie les étapes. Formule l'intention. Ne touche jamais aux données.
{ version:"1", kind:"query", spec:{ metric:"revenue", aggregation:"sum", group_by:"region", source:"sales" } }contractcontrat de capacité épinglé par hachagepolicyvos listes d'autorisation, vérifiées avant exécutionengineexécution déterministe en lecture seulestatus: ok · needs_clarification · rejected · erroreast — 2,130.50 · plan_hash f87610d8afeb…okSort de la boucle. Les lignes ou la valeur — et le hachage qui permet de les rejouer.
needs_clarification ↺Réintègre la boucle. Une question et les candidats trouvés — le modèle affine la spec et rappelle. Il ne devine jamais une colonne.
rejet et erreur empruntent le même chemin de retour — des résultats typés que le modèle peut lire, pas des exceptions qui interrompent l'exécution.
IILes outils
sqai.tools() retourne exactement trois outils — une surface délibérément réduite. La découverte et les simulations n'exécutent jamais ; l'exécution n'a lieu qu'en un seul endroit.
listSourcesDécouverte. Sources connectées avec champs typés, modules de capacités, signatures de fonctions — trois modes depuis une seule entrée, pour que les specs utilisent les noms exacts.
queryDataUne entrée versionnée — une union discriminée sur kind : une requête ou un calcul. Quatre résultats, toujours typés, jamais d'exception.
explainQueryRésout et valide une intention sans l'exécuter. Le modèle l'utilise pour déboguer des clarifications ; vous l'utilisez pour prévisualiser un plan.
L'ensemble s'intègre directement dans generateText ou streamText — typé, assignable sans cast. Configuration du Vercel AI SDK →
IIILe contrat d'erreur
Une exception interrompt l'exécution d'un agent. Rien ici ne lève donc d'exception : ambiguïté, refus et échec reviennent sous forme de statuts typés que le modèle peut lire — et corriger — dans son budget d'étapes.
retryable marque les transitoires — network_error · timeout · rate_limited · service_unavailable
queryData — les quatre résultats
okLe résultat : lignes ou valeur, hachages de provenance, troncature déclarée.plan_hash · invocation_hash · computation_hash · truncatedneeds_clarificationL'intention était ambiguë. Une question avec les candidats — jamais une supposition.question · candidates · explanationrejectedLe résolveur a refusé l'intention. Rien n'a été exécuté.rejection_reason · candidates · explanationerrorUn échec structuré avec un code stable — le modèle le lit et s'ajuste.code · message · retryable · nearest_matches?Chaque branche est une donnée. La boucle conserve son tour.
Le principe
politique dans le code · aucun champ de politique dans les entrées d'outil · restriction uniquement
IVLe principe, appliqué
L'industrie l'a appris en production — un agent de codage autonome a supprimé une base de données en production, fait désormais célèbre. La réponse de SQAI est structurelle, non comportementale : la surface exposée est en lecture seule par construction. Les catégories d'écriture et non déterministes n'y sont jamais générées, et aucun indicateur, politique ou entrée de modèle ne les réactive.
VLe câblage
Un package encapsule le client en outils. Les sources se connectent paresseusement au premier appel ; le premier calcul provisionne le runtime une fois, puis reste chaud — 0,83 ms mesuré.
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?",
});Côté serveur uniquement — sur Next.js, conservez le runtime Node, pas Edge.
Ce qui parvient au modèle
La troncature est toujours déclarée — truncated: true, returned_rows < total_rows. Jamais silencieuse. Les résultats complets restent accessibles dans le code applicatif via result_id.
VIAnalytique agentique
L'analytique agentique échoue de manières connues : appels d'outils malformés, modèles effectuant leur propre arithmétique, chiffres plausibles que personne ne peut vérifier. Chacun reçoit une réponse structurelle, non un meilleur prompt.
Une entrée versionnée, validée avant toute exécution. Une intention malformée ou ambiguë retourne un statut typé — la boucle continue au lieu de planter.
Pas ici. Le moteur calcule. Sur un ensemble de 16 questions quantitatives, le même modèle est passé de 0/16 à 16/16 dès que le moteur a pris en charge le calcul.
Chaque réponse porte son hash. Rejouez la même intention sur les mêmes données et le hash correspond — octet identique en TypeScript et en Python.
Une boucle multiplie la latence. Le plan de requête s'exécute en processus en sous-milliseconde ; le calcul chaud mesuré à 0,83 ms.
VIIQuestions
Ne filtrez pas le SQL — ne le générez pas. Connectez des sources à createSQAI() et transmettez au modèle sqai.tools() : la surface accessible est 4 574 capacités en lecture seule plus vos sources, sans chemin d'écriture par construction. Il n'existe aucune catégorie d'écriture à bloquer, et aucune entrée de modèle ne peut en réactiver une.
queryData retourne needs_clarification avec une question et les champs candidats identifiés. Il ne devine jamais une colonne. Le modèle répond à la question et rappelle — un tour de boucle supplémentaire.
Non. Chaque échec est un résultat structuré avec un code stable, un message et un indicateur de réessai. Ambiguïté, refus, blocage de politique et erreurs transitoires reviennent tous sous forme de données lisibles par le modèle.
Non. allowedSources, allowedFields et allowedFunctions sont définis dans le code, fixés à la création de createSQAI() et vérifiés avant toute exécution. Aucun schéma d'entrée d'outil ne contient de champ de politique ; nommer une capacité refusée retourne une erreur structurée policy_denied.
Oui — @thyn-ai/sqai-ai-sdk fournit les trois outils comme ensemble d'outils typés pour generateText et streamText, avec ai ^7.0.0 et zod ^4.0.0 en pairs, Node 20 ou supérieur, côté serveur. Le client sous-jacent est le même SDK que vous pouvez intégrer dans n'importe quelle boucle d'agent.