Aller au contenu principal
Version: 1.0.0

Tutoriel - Utiliser les appels natifs sous autorité Touba

Ce tutoriel montre comment laisser un modèle proposer un appel de fonction natif sans lui confier l’exécution. Le programme Touba conserve ses contrats ; seule la représentation utilisée par le provider change.

À la fin, vous aurez un agent qui peut choisir et réutiliser ses skills avec l’interface native du SDK, tandis que le runtime continue de valider chaque décision.

Ce qui change, et ce qui ne change pas​

ÉlémentComportement
source .tbinchangée ; aucune syntaxe spéciale n’est nécessaire
AIR et protocole runtimeinchangés
provider SDKdecisionInterface: "native" active les appels de fonctions du modèle
exécutionreste exclusivement réalisée par le runtime Touba
mode par défautstructured tant que l’hôte ne choisit pas explicitement native

L’interface native est utile lorsque le modèle choisi sait produire des appels de fonctions stricts et des sorties structurées. Elle ne transforme pas une skill, une function, une strategy ou un agent en tool Touba : elle offre seulement une représentation commune pendant la décision.

Prérequis​

  • la commande touba ;
  • le SDK JavaScript @touba/sdk ;
  • Node.js pour charger le module provider ;
  • un modèle qui prend en charge les appels de fonctions et les sorties structurées ;
  • une clé fournisseur conservée dans l’environnement ou dans un secret manager hôte.

Installer l’archive SDK​

Utiliser Node.js 24 et npm. Dans un terminal macOS/Linux :

Terminal
mkdir -p native-calculator-demo
cd native-calculator-demo
mkdir -p providers vendor
npm init -y
node --input-type=module -e 'import {execFileSync} from "node:child_process"; import {copyFileSync} from "node:fs"; const v=JSON.parse(execFileSync("touba",["version","--json"],{encoding:"utf8"})); copyFileSync(v.distribution.sdk_archive,"vendor/touba-sdk.tgz");'
npm install --offline --ignore-scripts --save-exact ./vendor/touba-sdk.tgz

Étape 1 - Écrire le programme Touba​

Créez native-calculator.tb :

native-calculator.tb
module examples.native_calculator

skill Double
description "Doubles one integer and returns the result."
takes item as Int
gives Int
do
give item * 2

skill AddTen
description "Adds ten to one integer and returns the result."
takes item as Int
gives Int
do
give item + 10

agent Calculator
description "Selects declared arithmetic skills for the requested calculation."
takes start as Int
gives Int
responsibility "Starting from start, invoke Double on the initial value, invoke Double again on the observed result, then invoke AddTen on that result. Return the final observed integer."
uses Double
uses AddTen
callable_by strategy RunCalculation

strategy RunCalculation
description "Runs one governed calculation."
takes start as Int
gives Int
uses Calculator
do
Calculator
with start as start
into result
give result

Le périmètre de l’agent est déclaré par uses. Le provider ne pourra proposer que Double ou AddTen. La règle callable_by continue de réserver l’entrée dans Calculator à la strategy déclarée.

Compilez le programme :

Terminal
touba compile native-calculator.tb --out native-calculator.air.json

Étape 2 - Activer l’interface native dans le provider​

Créez providers/calculator.provider.mjs :

providers/calculator.provider.mjs
import { createOpenAiDecisionProvider } from "@touba/sdk";

export const provider_metadata = Object.freeze({
provider_kind: "sdk_ai",
provider_name: "calculator-provider",
provider_version: "1.0.0",
model_family: "openai",
prompt_template_id: "calculator-native-v1",
decision_schema_version: "touba-agent-decision-v1",
});

export const decideAgentCapability = createOpenAiDecisionProvider({
model: "gpt-4.1-nano",
decisionInterface: "native",
instructions: [
"Complete the requested calculation using observed data.",
"Use only capabilities authorized by the Touba runtime.",
"Return a final answer only after the required calculation is complete.",
].join(" "),
maxOutputTokens: 1000,
});

Les instructions contrôlées par l’application sont en anglais pour garder un protocole provider cohérent. Les entrées métier et les réponses peuvent rester dans la langue de l’utilisateur.

Pour un autre fournisseur compatible AI SDK, utilisez la même option avec createAiSdkDecisionProvider :

Fournir un véritable objet modèle issu du provider installé à createAiSdkDecisionProvider({ model, decisionInterface: "native" }) ; voir le guide SDK.

Si le modèle refuse la température ou les appels forcés, ajouter respectivement temperature: null ou nativeToolChoice: "auto" à cette configuration. Ce sont des options de transport, pas des permissions : Touba refuse toujours une sélection sans appel valide. Qualifier aussi les réglages de raisonnement du connecteur et la limite de sortie avant d'exécuter un parcours payant.

Étape 3 - Lancer l’agent​

Exposez la clé dans l’environnement, sans la placer dans le source, les inputs ou le fichier provider :

Terminal
: "${TOUBA_AGENT_DECISION_OPENAI_API_KEY:=${OPENAI_API_KEY:?Set OPENAI_API_KEY in your shell or secret manager}}"
export TOUBA_AGENT_DECISION_OPENAI_API_KEY

Exécutez ensuite la strategy :

Terminal
touba run native-calculator.air.json \
--entry strategy:examples.native_calculator/RunCalculation \
--agent-decision-provider providers/calculator.provider.mjs \
--agent-decision-provider-initial-mode capabilities \
--max-agent-steps 3 \
--max-provider-calls 8 \
--inputs-json '{"start":6}' \
--output json \
--trace-out native-calculator.trace.json > result.json
node --input-type=module -e 'import assert from "node:assert/strict"; import {readFileSync} from "node:fs"; const run=JSON.parse(readFileSync("result.json","utf8")); assert.equal(run.status,"success"); assert.equal(run.result,34); assert.deepEqual(run.trace.events.filter(e=>e.kind==="agent_capability_selected").map(e=>e.payload.selected_ref),["skill:examples.native_calculator/Double","skill:examples.native_calculator/Double","skill:examples.native_calculator/AddTen"]); console.log("NATIVE_CALCULATION_OK");'

Le mode initial capabilities évite un aller-retour quand vous savez que la mission exige une capacité. Gardez le mode summary, qui est le défaut, si le modèle doit pouvoir répondre sans consulter le catalogue.

Cette commande effectue des appels facturables au vrai fournisseur. Calcul attendu : 6 → 12 → 24 → 34. La même capacité est réutilisée. Une panne fournisseur ou un échec du modèle reste un échec de test ; ne pas substituer une réponse précalculée.

Cette séquence nécessite six appels fournisseur sans retry : trois sélections, deux orientations intermédiaires et une finalisation. La limite de huit laisse une marge de retry ; elle ne correspond pas à huit appels de skills.

Étape 4 - Comprendre le cycle réel​

Cycle de décision
agent Touba
-> orientation structurée, ou catalogue initial explicitement demandé
-> catalogue autorisé construit par le runtime
-> fonctions natives dérivées des signatures
-> une opération ou un résultat typé proposé par le modèle
-> normalisation par le SDK, sans exécution
-> validation, puis exécution ou retour du résultat par le runtime
-> observation associée aux arguments de l’appel
-> décision suivante ou finalisation structurée

En phase catalogue, le SDK expose une fonction native par capacité autorisée, ainsi que touba_finish pour un résultat final conforme au gives. Le modèle doit proposer exactement un appel. Le SDK ne fournit pas de fonction execute au fournisseur : un appel reçu n’a donc encore produit aucun effet.

Le runtime reçoit ensuite une décision Touba normale et vérifie la cible, les arguments et l’autorité avant d’entrer dans la capacité. touba_finish devient une décision answer_directly, sans effet métier ; la réponse doit respecter le gives de l’agent. Une prose libre sans ce contrôle reste refusée.

Étape 5 - Réutiliser une capacité​

Une capacité autorisée n’est pas consommée après son premier appel. L’agent peut sélectionner Double plusieurs fois, avec les mêmes arguments ou avec des arguments différents. Chaque invocation est validée et comptée séparément.

La réutilisation s’arrête lorsque la mission est terminée ou lorsqu’une limite s’applique, par exemple :

  • maxAgentSteps pour les étapes agentiques ;
  • max_provider_calls pour les appels au provider ;
  • max_tool_calls pour les tools réellement dispatchés ;
  • les budgets de durée et d’exécution déclarés par le projet.

Le champ historique des capacités utilisées sert à décrire ce qui s’est passé. Il ne constitue pas une liste de capacités interdites pour la suite.

Étape 6 - Vérifier la frontière d’autorité​

L'historique conversationnel est actif par défaut : le SDK conserve les messages du connecteur entre les étapes de cette invocation. Les deux appels à Double restent deux opérations distinctes, chacun avec son résultat runtime. Pour observer sa taille, ajoutez history: { onEvent: event => console.info(event) } à la configuration du provider. Ces événements ne contiennent pas les messages. Pour borner la taille ou reprendre après redémarrage avec SQLite, consultez le guide de l'historique conversationnel. Le stockage ne remplace ni les checkpoints ni le journal des effets.

Proposition du modèleContrôle appliqué par Touba
fonction native choisiela cible doit appartenir à uses
arguments produitsla signature et les types sont revérifiés
appel d’un autre agentcallable_by est revérifié à chaque invocation
accès mémoirescopes source, grants et resource bindings restent requis
usage d’un secretseul le handle autorisé peut traverser ; la valeur résolue reste hors prompt
demande de validationla décision appartient à l’humain, pas au modèle
effet externemanifest, grants, identité d’effet et journal runtime restent applicables
repriseles observations conservées ne redéclenchent pas les effets passés

Un nom de fonction inconnu, plusieurs appels dans une même proposition, des arguments invalides ou une réponse fournisseur incomplète sont refusés avant l’exécution de la capacité.

Étape 7 - Inspecter le résultat​

Terminal
touba inspect native-calculator.trace.json

La trace permet de distinguer la décision, la capacité sélectionnée, la consommation des budgets, l’exécution et la réponse terminale. Elle ne doit pas contenir la clé fournisseur ni une valeur secrète résolue.

Choisir entre structured et native​

Choisir structuredChoisir native
comportement par défaut, portablemodèle fiable sur les appels de fonctions stricts
provider ne prenant pas en charge les fonctions nativesmeilleure adéquation avec l’interface native du fournisseur
intégration existante fondée sur generateObjectcatalogue riche avec signatures directement exposées

Les deux interfaces utilisent le même protocole runtime et la même frontière d’autorité. Changer d’interface ne demande pas de modifier le programme .tb.

Erreurs fréquentes​

SymptômeVérification
aucun appel natif n’est produitvérifier le support des appels de fonctions par le modèle et le transport
décision refuséevérifier uses, callable_by, les descriptions et les types d’arguments
réponse IncompleteModelDecisionvérifier que la génération fournisseur est réellement terminée
limite atteinte avant la réponseajuster les budgets sans supprimer les contrôles nécessaires
observation indisponible en clairvérifier si opaque_refs a été choisi volontairement

Pour approfondir, lire le guide du SDK Touba et le concept de décision agentique gouvernée.