Aller au contenu principal
Version: 1.0.0

Validation humaine

Touba réserve le namespace human.* à deux statements, distincts d’un tool :

  • human.approve, une barrière imposée par le programme (mode required_approval) ;
  • human.review, un review initié par la logique d’une skill ou strategy (mode agent_review).

Dans les deux cas, le runtime suspend l’exécution et ne reprend qu’après une décision humaine explicite. Une décision approved reste une décision runtime : elle ne devient pas une preuve Verified.

Forme​

source.tb
human.approve
with context as reviewed_change
into approval
source.tb
human.review
with context as analysis
into decision

Règles communes :

  • context est l’unique argument obligatoire ;
  • into est obligatoire ;
  • ces statements sont admis dans skill et strategy ;
  • ils sont interdits dans function, pour préserver la pureté des fonctions.

Cas simple​

validation_simple.tb
module examples.validation_simple

type ChangeRequest
summary is Text

type AppliedChange
summary is Text

tool ApplyChange
takes request as ChangeRequest
gives AppliedChange
requires filesystem_write

skill ReviewAndApply
takes request as ChangeRequest
gives AppliedChange
uses ApplyChange
do
human.approve
with context as request
into approved

ApplyChange
with request as request
into applied

show
with message as "Change applied"

give applied

human.approve bloque l’exécution avant ApplyChange. Sans décision approved, le tool à effet de bord (requires filesystem_write) n’est jamais atteint.

Décisions observables​

Une décision humaine peut être fournie par fichier ou inline avec --human-decisions-json. Les valeurs utiles pour la validation V1 sont approved et rejected.

Avec approved, le runtime reprend après le point human.approve ou human.review, puis continue le bloc do.

Avec rejected, le runtime arrête le scope courant avant l’effet externe suivant. La trace contient la décision, l’événement de rejet, puis la fin du run avec un statut arrêté. Le résultat est null si aucun autre résultat n’a été produit :

Trace
human_approval_decided decision=rejected
human_approval_rejected
scope_exited status=stopped
run_completed status=stopped

L’identifiant d’approbation est produit depuis le graphe AIR. Pour un scénario de test déterministe, vous pouvez donc fournir la décision au lancement :

Terminal
touba run program.air.json \
--human-decisions-json '{"approval:graph:skill:examples.review/ReviewAndApply:node:graph:skill:examples.review/ReviewAndApply:1":"rejected"}' \
--output trace

Cas avancé​

validation_advanced.tb
module examples.validation_advanced

type Analysis
confidence is Float
summary is Text

skill ReviewWhenNeeded
takes analysis as Analysis
gives Analysis
do
if analysis.confidence < 0.8
human.review
with context as analysis
into decision
give analysis
else
give analysis

Ici, human.review n’est déclenché que dans une branche : la décision de demander un review humain dépend d’une condition calculée par la skill, pas d’une barrière inconditionnelle.

human.approve vs human.review vs request_human_review​

  • human.approve : barrière statique, écrite dans le programme. La condition d’arrêt ne dépend pas d’une décision de modèle.
  • human.review : statement source, mais la décision de l’atteindre peut dépendre d’une logique conditionnelle dans skill/strategy (comme ci-dessus).
  • request_human_review : une action que peut choisir un provider de décision agentique pendant la boucle d’un agent, sans que ce soit un statement écrit dans le source. Voir Décision agentique gouvernée pour ce mécanisme, qui est différent de human.review.

Rôle, délai et séparation des signataires​

Le programme peut exiger un rôle, une échéance et un signataire distinct. L'application hôte authentifie les personnes et atteste leurs droits actuels ; le modèle ne fournit jamais cette attestation.

Approbation contrainte
human.approve
with context as invoice
with role as "finance"
with timeout_ms as 3600000
with distinct_from as "initiator_and_previous"
into approval

Ces arguments optionnels sont des littéraux : role est un identifiant non vide (128 octets UTF-8 maximum), timeout_ms va de 1 à 2 592 000 000 ms (30 jours), et distinct_from vaut "initiator", "previous" ou "initiator_and_previous". Les signataires précédents sont ceux dont une approbation a déjà été acceptée dans ce run, y compris dans les sous-appels et les branches parallèles.

Sans décision à temps, on expired peut diriger le programme vers une autre approbation. Cette branche doit terminer explicitement par give ou stop sur tous ses chemins : elle ne rejoint pas implicitement la suite protégée. Sans branche, l'expiration arrête le run. À l'échéance exacte, l'expiration gagne.

Une décision acceptée à temps reste valable pour sa seule occurrence lors d'une reprise ultérieure. Une nouvelle demande exige une nouvelle vérification des droits. Les identités opaques restent dans le checkpoint privé, pas dans la trace publique. Voir le tutoriel d'approbations avec rôles et délais.

Contrat runtime​

  • human.approve et human.review ne sont pas des tool : ils ne passent pas par AirTool.id et n’ont pas de handler.
  • La pause et la reprise sont explicites : Touba ne lit pas stdin implicitement et n’invente pas de prompt interactif.
  • Une décision humaine acceptée ne transforme pas automatiquement le résultat en preuve formelle (Verified).
  • Les implémentations inline, noms human.* arbitraires et workflows humains enrichis sont traités par des surfaces d’interface dédiées.

Reprise et effets externes​

La référence AIR d'une approbation reste statique, mais une décision n'autorise qu'un passage. Dans une boucle, chaque nouvelle rencontre du statement exige une nouvelle décision pour le checkpoint courant ; une approbation n'autorise pas les itérations futures. Les contrôles d'autorité restent actifs à chaque appel.

Lors de la reprise d'un agent typé appelé par une chaîne source, ses entrées et les valeurs de son appelant restent séparées. Un paramètre homonyme ne remplace pas la valeur de l'appelant ; seul le résultat retourné est lié par into. Cette séparation est conservée après plusieurs pauses. Un checkpoint d'appel d'agent qui ne contient pas les deux portées est refusé avant le provider. Les checkpoints peuvent contenir des données métier : protégez leur stockage ; ils ne sont pas des traces publiques.

--human-decisions injecte des décisions au démarrage d’un run. Ce chemin sert aux scénarios déterministes. Pour reprendre après un effet externe déjà produit, utilisez un checkpoint de reprise.

Pour un nœud human_approval ou une revue request_human_review dans une pile séquentielle d’appels source et de sélections agentiques, Touba peut écrire un checkpoint de reprise :

Terminal
touba run program.air.json \
--human-checkpoint-out human.checkpoint.json \
--output trace

La reprise consomme ensuite ce checkpoint et une décision humaine explicite :

Terminal
touba run program.air.json \
--human-resume-checkpoint human.checkpoint.json \
--human-decisions-json '{"approval:example":"approved"}' \
--output trace

Ce checkpoint évite de redispatcher les effets déjà produits dans ce cas borné. Sans checkpoint, une pause humaine après dispatch externe est refusée par HumanApprovalPauseRequiresDurableResume.

Le checkpoint humain conserve aussi la pile des appels source de skills et stratégies, avec leurs valeurs, types et curseurs de boucle distincts. Une validation dans un sous-workflow, ou une revue demandée par un agent appelé depuis cette chaîne, reprend à son point d’arrêt puis revient à ses callers. Les appels achevés ne sont pas rejoués ; budgets, temps actif déjà consommé et identifiants d’effets restent communs au run. Les erreurs après reprise suivent les chemins d’échec déclarés. La profondeur est bornée à 64 appels.

La pile entière est vérifiée avant un nouvel effet. Seul le format V1 courant est admis, avec call_stack et toutes ses portées : aucun ancien état incomplet n’est converti. Les secrets résolus sont interdits jusque dans les callers. Un checkpoint local reste un état hôte de confiance, pas une preuve signée.

La pile conserve aussi les capacités choisies par un agent, y compris un sous-agent qui demande une revue ou appelle un sous-workflow. Après la décision humaine, Touba termine l’appel en attente, ajoute son résultat aux observations du caller et reprend sa boucle à l’étape suivante. Il ne redemande pas au modèle une sélection déjà faite. Une décision hôte unique reste un appel/retour, sans boucle de modèle ajoutée. Les limites d’étapes antérieures sont conservées ; les décisions humaines ne sont pas réutilisées entre invocations.

Dans un groupe parallel, les validations humaines sont également prises en charge : chaque attente appartient à une occurrence de branche. Le groupe suspend les nouveaux départs et attend la stabilisation des appels engagés ; le checkpoint conserve les branches terminées, les continuations, les budgets et les décisions consommées. Une décision n'autorise pas les autres branches. Après reprise, les branches terminées ne sont pas redispatchées.

Le guide de reprise distingue cette continuation humaine du checkpoint de progression parallèle et du profil de reprise durable séquentiel. Aucun de ces mécanismes ne garantit une reprise après crash à n'importe quelle instruction ou un effet externe exactement une fois.