Valider à la main une action d'un agent IA avec guard()
Publié le
Pour qu'une personne valide une action d'un agent IA avant qu'elle ait lieu, on enveloppe cette action dans un bloc guard() du SDK Python d'ORCHESTRA. Le bloc interroge ORCHESTRA : si aucune politique ne s'applique, l'action s'exécute ; si une politique exige une approbation, le code attend la décision d'un relecteur ; si une politique l'interdit, une exception est levée et le bloc ne démarre jamais. Cela ne fonctionne que pour les actions que le code de l'agent soumet lui-même à guard().
Ce que guard() contrôle, et ce qu'il ne contrôle pas
ORCHESTRA n'exécute pas vos agents et ne peut pas en arrêter un de l'extérieur. Il ne contrôle que les actions que le code de l'agent lui soumet. C'est le rôle de guard() : une demande d'autorisation placée juste avant l'effet de bord (envoyer un message, créer un devis, déclencher un paiement).
Cette précision a une conséquence pratique. Une action qui n'est pas enveloppée dans guard() n'est ni retenue ni refusée, même si une politique la décrit. De même, les données de télémétrie (OpenTelemetry, webhooks) arrivent après coup : si une politique les reconnaît, l'alerte est marquée « observée après exécution ». Elles servent à voir, pas à retenir. La documentation des intégrations le dit ainsi : pour retenir une action, il faut l'envelopper.
L'exemple minimal
Dans le SDK Python (orchestra-workforce-sdk), guard() se demande depuis une exécution en cours, c'est-à-dire dans un bloc agent.run() :
with execution.guard("send_email", tool="smtp", params={"recipients_count": 500}):
send_the_email()Le premier argument est le type d'action, un nom que vous choisissez et que vos politiques reconnaîtront. tool nomme l'outil concerné. params contient les valeurs que les politiques pourront comparer. Le bloc with ne s'exécute que si l'action est autorisée ; dans le cas contraire, une exception de la famille ActionBlocked est levée avant la première ligne du bloc.
Une précision sur l'installation : le SDK n'est pas encore publié sur PyPI, il s'installe depuis le serveur ORCHESTRA que vous utilisez. Et guard() est synchrone : votre code attend sur la ligne du with.
Écrire la politique qui déclenche la validation
Les décisions viennent de politiques déterministes : aucun modèle n'intervient dans une décision. Une condition est une feuille (un champ, un opérateur, une valeur) ou une combinaison avec all, any et not. Les opérateurs disponibles sont eq, neq, gt, gte, lt, lte, in, not_in, contains et exists.
Les champs sont lus dans un contexte qui contient les paramètres de l'action (action.recipients_count dans l'exemple), le type d'action (action.type), le nom de l'outil (tool.name), l'agent (agent.id, agent.name et ses indicateurs) et l'environnement. Pour valider à la main tout envoi à plus de 100 destinataires, la condition est :
{"field": "action.recipients_count", "op": "gt", "value": 100}Chaque politique a ensuite un effet, parmi trois : require_approval (l'action attend une personne), deny (l'action est refusée tout de suite) et alert (l'action passe, mais une alerte est enregistrée). Quand plusieurs politiques s'appliquent à la même action, la plus stricte l'emporte : deny avant require_approval, require_approval avant alert.
Avant d'activer une règle, un test à blanc évalue une condition sur un contexte que vous fournissez, sans rien modifier, et renvoie le résultat de chaque règle. C'est le moyen de vérifier qu'un seuil se déclenche là où vous le pensez.
Ce qui se passe pendant l'attente
Quand l'effet est require_approval, ORCHESTRA crée une demande d'approbation et le code de l'agent interroge son état toutes les 2 secondes (poll_interval) pendant 300 secondes au plus (wait). Ces deux valeurs se règlent dans l'appel à guard().
La demande a aussi sa propre durée de vie, fixée par la politique : approval_ttl_seconds, 3 600 secondes par défaut, de 30 secondes à 7 jours. Passé ce délai, la demande expire. Les deux durées sont indépendantes : si wait est plus court que la durée de la demande, votre code abandonne avant que la demande n'expire.
Le relecteur peut aussi poser une question avant de trancher. Si vous passez une fonction on_info_request à guard(), elle reçoit la question et renvoie la réponse ; l'action continue d'attendre dans les deux cas.
Les erreurs à prévoir dans votre code
Tout refus passe par une exception qui hérite d'ActionBlocked. Vous pouvez les attraper ensemble ou une par une :
- ActionDenied : une politique interdit l'action, ou l'approbation n'est plus utilisable (déjà consommée, ou paramètres modifiés).
- ApprovalRejected : un relecteur a refusé.
- ApprovalExpired : aucune décision dans le délai, ou la demande a expiré ou a été annulée.
- GatewayUnavailable : ORCHESTRA n'a pas pu être joint, et l'action n'a pas été exécutée.
Le plus simple est d'attraper ActionBlocked autour du bloc, de consigner la raison et de poursuivre le reste du travail de l'agent sans l'action refusée.
Les garanties qui comptent pour une validation
Une approbation donnée ne sert qu'une fois, et seulement pour les paramètres qui ont été examinés. Au moment d'agir, le SDK consomme l'approbation en envoyant l'empreinte des paramètres. Si l'approbation a déjà servi, ou si les paramètres ont changé depuis la demande, l'action est refusée avec ActionDenied. Un relecteur qui a validé un envoi à 500 destinataires ne valide donc pas un envoi à 5 000.
Par défaut, guard() échoue en mode fermé (fail_closed=True) : si ORCHESTRA est injoignable, l'action n'a pas lieu et GatewayUnavailable est levée. Mettre fail_closed=False ne convient qu'aux actions qui peuvent s'exécuter sans vérification ; la décision est alors notée allow_unverified. Une exception subsiste : si une décision humaine est déjà en attente et que le serveur disparaît, le code n'invente pas de réponse et échoue quand même.
À la sortie du bloc, le SDK déclare le résultat à ORCHESTRA : exécutée, ou échouée si une exception a été levée dans le bloc. Cette information complète le journal d'audit de la décision.
Une clé d'idempotence peut être fournie avec idempotency_key, pour qu'une même demande répétée retrouve l'approbation existante au lieu d'en créer une nouvelle ; la même clé avec une autre action est refusée.
Quelques conseils de mise en place
- Envoyez dans params ce dont les politiques ont besoin, pas davantage : un nombre de destinataires plutôt que leurs adresses, un montant plutôt qu'un dossier entier.
- Commencez par un effet alert pour observer à quelle fréquence une règle se déclenche, puis passez à require_approval quand le seuil vous convient.
- Prévoyez qui relit, et quand. Une demande que personne ne traite expire, et l'agent, lui, a attendu.
- Pensez à la suspension : suspendre un agent fait refuser ses appels à guard(), mais le code qui n'interroge pas ORCHESTRA continue de tourner.
État de test et limites
Le SDK Python et la passerelle de contrôle sont couverts par des tests automatisés, dont un avec un processus externe. Les connecteurs Anthropic, OpenAI, LangChain et LangGraph ont été testés avec de vrais clients logiciels mais des services simulés, pas contre un service en production. Ceux de LangChain et LangGraph surveillent seulement, sauf si vous y ajoutez guard(). Rien ici ne remplace un contrôle propre à l'outil que votre agent appelle : ORCHESTRA décide si votre code peut continuer, pas ce que l'outil fera ensuite.
La validation humaine est incluse dans le plan Free, avec 2 agents connectés et sans limite de durée. Les plans payants ne sont pas encore en vente. Le détail est sur la page Tarifs.
Pour aller plus loin
La page Aide répond aux questions de connexion d'un agent et à celle de la suspension. Pour voir comment votre propre flotte d'agents se comporterait, vous pouvez créer un compte gratuit ou demander une démonstration. Les autres guides sont dans la liste des guides.