Aller au contenu

Le cycle d'une session agentique

Les étapes d'une session d'agent, de la consigne à la différence validée, à quel moment vous êtes consulté, et ce qui borne la session.

Poser une question à un modèle de langage donne une réponse. Confier une tâche à un agent ouvre une session : le modèle demande une action, l’action s’exécute dans un bac à sable, son résultat lui revient, il demande la suivante. Une tâche peut prendre plusieurs dizaines d’allers-retours.

Cette page décrit cette session : ses étapes, ce qui se passe dans la boucle, à quel moment vous êtes consulté, et ce qui la borne. Il n’existe pas d’agent permanent : une session naît d’une consigne et se termine avec son bac à sable.

Une session traverse cinq étapes, chacune tenue par un composant classique et non par le modèle (livre blanc sécurité V3, section 3.2).

  1. Authentification. Vous êtes authentifié auprès de l’annuaire de votre organisation, et la session est liée à un projet.
  2. Politique du projet. La passerelle charge la politique de ce projet : outils actifs, commandes libres, commandes soumises à validation, budgets.
  3. Ouverture du bac à sable. La passerelle ouvre un bac à sable sur l’hôte d’exécution, avec une copie de travail du dépôt.
  4. Travail de l’agent. L’agent explore le code, modifie la copie et exécute les commandes autorisées. Chaque échange avec le modèle passe par la passerelle.
  5. Différence, validation, journal. Le résultat vous revient sous forme de différence, que vous acceptez ou refusez. L’ensemble est journalisé, puis le bac à sable est détruit.

Le code est lu par exploration du dépôt, sans index préalable.

Pendant la quatrième étape, l’agent travaille par appels d’outils. Un « outil » est une action que le modèle peut demander : lire un fichier, en écrire un, lancer une commande, chercher dans le code. Chaque demande d’outil passe par six états possibles, que le produit nomme.

le modèle écrit lesargumentsarguments complets etanalysablesarguments inanalysablesvotre accord, ou outil déjàautorisévotre refusoutil disabled par lapolitiquel'action a aboutil'action a échouérésultat rendu au modèleerreur rendue au modèle

generating

generated

errored

calling

canceled

done

Trois propriétés se lisent sur ce dessin.

Un outil ne s’exécute pas depuis l’état où le modèle écrit. Il y a un état intermédiaire, generated, où la demande est complète et rien n’est encore fait. La décision d’exécuter se prend dans cet état. Ce que le modèle produit est une proposition, pas une action.

Une demande mal formée est refusée. Les arguments arrivent du modèle en morceaux ; l’IDE les analyse une fois, complets, et strictement. Un texte inanalysable devient une erreur rendue au modèle, pas un objet vide exécuté en devinant l’intention. Le terminal ne suit pas cette règle : il analyse les morceaux au fur et à mesure.

Une erreur n’arrête pas la session. errored rend l’erreur au modèle comme done lui rend le résultat : dans les deux cas la boucle reprend, et le modèle peut corriger. Ce qui arrête la session est décrit dans Ce qui borne la boucle.

  • Où l’action s’exécute. Le dessin décrit le cycle d’une demande, pas le lieu de son exécution ; voir la section suivante.
  • Plusieurs outils à la fois. Un même tour peut porter plusieurs demandes ; la boucle ne reprend que lorsque toutes ont quitté leurs états intermédiaires.
  • L’annulation qui vient d’ailleurs. Interrompre une réponse en cours passe les demandes encore en generating ou generated à l’état canceled, sans qu’un refus ait été exprimé sur chacune.
  • La reprise après un refus. Le réglage continueAfterToolRejection, inactif par défaut, décide si votre refus relance la boucle (le modèle reçoit un message de refus et continue) ou l’arrête.
  • La compaction. Quand l’historique devient trop long, il est résumé au milieu de la boucle. Cela ne change pas l’état d’un appel d’outil. Dans le terminal, une compaction consomme une action du budget de session ; dans l’IDE, non.
  • Le terminal passe à canceled, et non à errored, un outil que la politique interdit.

Chaque action s’exécute dans un bac à sable, créé pour la session et détruit à sa fin (livre blanc sécurité V3, section 4.2). Le bac à sable n’a pas d’interface réseau, s’exécute sans privilège, sur un système de fichiers racine en lecture seule, et ne partage aucun état avec une autre session ou un autre projet. Les échanges avec le modèle sont relayés par la passerelle.

Le bac à sable s’exécute en deux lieux possibles (livre blanc sécurité V3, section 4.4) :

  • sur un hôte d’exécution dédié, le mode de référence. Le bac à sable travaille sur une copie du dépôt, le poste ne porte que l’extension, et l’agent n’a pas les droits de l’utilisateur sur son poste ;
  • sur le poste, la variante. Le conteneur est sans privilège et sans réseau, le dépôt local y est monté comme seul volume en écriture, et les chaînes de compilation du poste sont disponibles. Elle convient aux socles de poste qui admettent déjà un moteur de conteneurs.

Le lieu se choisit par la variable LEMNISCATE_AGENT_EXECUTION, qui accepte deux valeurs : server (le bac à sable sur l’hôte d’exécution) et workstation (le conteneur sur le poste, valeur retenue quand la variable est absente). Une valeur inconnue est refusée au lieu d’être ramenée au défaut. Le choix se fait par périmètre, avec le responsable de la sécurité.

Ce que l’agent voit dans le bac à sable, et ce qu’il ne porte pas, est décrit dans Les garde-fous d’exécution.

Entre generated et calling, la politique du projet tranche. Elle est écrite par l’administrateur, évaluée par la passerelle, et n’est pas interprétée par le modèle (livre blanc sécurité V3, section 5.1). Pour chaque outil, elle a trois valeurs, de la plus restrictive à la moins restrictive :

PolitiqueComportement
disabledl’outil n’est pas exécuté, et le modèle est informé
allowedWithPermissionvous êtes consulté avant chaque exécution
allowedWithoutPermissionl’outil s’exécute sans vous consulter

Le défaut est allowedWithPermission : sans décision de l’administrateur, aucune commande n’est libre, et le produit vous demande. Le terminal nomme les mêmes trois valeurs exclude, ask et allow, avec ask par défaut.

Deux propriétés de ce mécanisme comptent autant que la liste.

Une évaluation faite au moment de l’exécution ne peut pas assouplir la politique de base. Certains outils sont jugés une seconde fois selon ce qu’on leur demande précisément, une commande de terminal par exemple. Ce second jugement peut durcir le verdict, pas l’adoucir. Un outil mis en disabled ne se rouvre pas parce que la commande demandée paraît anodine. Une évaluation qui échoue donne disabled.

La décision est attribuée. Chaque exécution retient si l’accord venait d’un humain ou d’une automatisation. Il n’y a pas de troisième valeur, et pas de valeur par défaut.

La frontière de sécurité est le bac à sable, non la liste des commandes autorisées : un interpréteur autorisé exécute n’importe quoi (livre blanc sécurité V3, section 4.2). L’analyse des commandes décrite ici relève de l’hygiène, et s’ajoute au confinement.

Une commande de terminal n’est pas jugée comme un bloc. Elle est découpée : les lignes, les enchaînements, les canalisations. Chaque morceau est jugé séparément, et le verdict le plus restrictif de tous les morceaux s’applique. Une commande anodine suivie d’une commande dangereuse est une commande dangereuse. Une redirection qui écrit dans un fichier demande votre accord, comme une canalisation vers un interprète (sh, bash, python).

Plusieurs familles sont refusées sans qu’un accord puisse les débloquer :

  • l’élévation de privilèges (sudo, su, doas et leurs équivalents Windows) ;
  • la destruction de l’arborescence système : l’effacement récursif et forcé d’une racine ou d’un répertoire système, l’effacement de fichiers système, l’écriture directe sous /dev/, le formatage ;
  • l’ouverture des permissions (chmod 777, bit setuid) et le changement de propriétaire vers root ;
  • le chargement de modules noyau et la modification du pare-feu ;
  • eval et exec.

Deux détails comptent à l’usage. Une commande dont le nom vient d’une variable est traitée comme inconnue, donc soumise à votre accord : le nom réel n’est lisible qu’à l’exécution. Une commande dont rien n’est connu demande votre accord ; elle n’est ni refusée, ni laissée passer.

Dans le bac à sable, l’interprète est bash, lancé sans charger de profil. Les variables d’environnement qui peuvent transporter du code exécutable (BASH_ENV, ENV, SHELLOPTS, BASHOPTS, ZDOTDIR, et toute variable BASH_FUNC_*) sont retirées avant l’exécution. Aucune variable d’environnement de votre poste n’y est transmise.

Pendant son travail, l’agent peut confier une partie de la tâche à un sous-agent. Le sous-agent tourne dans sa propre session, avec son propre contexte et ses propres bornes, n’élargit pas les droits de l’agent qui délègue, et rend un rapport. Ses demandes de permission remontent à vous. Les règles sont expliquées dans Déléguer à des sous-agents, et la marche à suivre dans Déléguer une tâche à un sous-agent.

Ce que la session produit sort par un seul canal : une différence de code, qui vous est présentée et que vous acceptez ou refusez. Aucune écriture retenue n’est appliquée à votre dépôt sans cette validation (livre blanc sécurité V3, sections 1.2 et 4.1).

L’agent n’a pas de compte et ne pousse rien. La différence validée est appliquée à votre dépôt, et vous l’intégrez sous votre propre identité, par vos outils habituels et dans vos circuits de revue (livre blanc sécurité V3, section 4.3).

La demande de validation montre la différence ou la commande exacte, et signale les fichiers qui relèvent d’un contrôle dédié : configuration d’intégration continue, scripts de build, hooks, manifestes de dépendances. Voir Les garde-fous d’exécution.

Toute session a une fin prévue. Quatre budgets bornent chaque session : la durée, le volume d’échanges avec le modèle, le nombre de commandes et les ressources du bac à sable (processeur, mémoire, processus). Leur dépassement provoque un arrêt propre, journalisé (livre blanc sécurité V3, sections 4.2 et 4.5).

Le budget de session est évalué avant chaque appel au modèle. Il compte :

  • des actions : chaque appel d’outil en consomme une (150 par défaut) ;
  • une durée écoulée depuis le début de la session (45 minutes par défaut) ;
  • un nombre d’extensions (5 par défaut), chacune accordant 50 actions et 15 minutes de plus.

Ces valeurs se règlent par les variables LEMNISCATE_SESSION_MAX_ACTIONS, LEMNISCATE_SESSION_MAX_MINUTES et LEMNISCATE_SESSION_MAX_EXTENSIONS, ou par les clés de configuration execution.sessionMaxActions, execution.sessionMaxMinutes et execution.sessionMaxExtensions.

Quand le budget est épuisé, en session interactive, la session vous demande une extension. Le message énumère ce qui a été consommé et ce que l’extension suivante accorde. Dans l’IDE, l’extension s’accorde en envoyant un nouveau message. Une fois le nombre d’extensions épuisé, la session s’arrête, et il faut en ouvrir une nouvelle. Dans le terminal, le budget repart de zéro à chaque message de l’utilisateur, et la session arrêtée produit un dernier résumé, sans outils.

Hors session interactive, il n’y a personne pour répondre : la session s’arrête et sort en code 3 ; voir Les garde-fous d’exécution.

Deux conséquences pratiques :

  • le budget ne survit pas au redémarrage de l’IDE. Une borne restaurée d’une session de la veille serait une borne fausse ;
  • une valeur de budget illisible est refusée au lieu de retomber sur le défaut : erreur de configuration fatale dans l’IDE, erreur à la première lecture des bornes dans le terminal. Un réglage qui desserre ou resserre le défaut est annoncé au démarrage.

Les commandes ont leur budget propre, indépendant : une durée maximale (10 minutes par défaut, LEMNISCATE_COMMAND_MAX_SECONDS) et un volume de sortie maximal (10 Mio par défaut, LEMNISCATE_COMMAND_MAX_OUTPUT_BYTES). Ce qui dépasse le volume est compté puis jeté, pas accumulé en mémoire.

Toute session peut être arrêtée immédiatement (livre blanc sécurité V3, section 4.5).

  • Vous disposez d’un arrêt en un geste depuis votre environnement. Il interrompt le travail en cours, détruit le bac à sable et rejette les écritures non validées.
  • L’administrateur dispose d’une révocation depuis la console, par session ou par utilisateur. Elle prend effet sans redémarrage, elle est journalisée, et elle détruit le bac à sable.

Chaque session est journalisée de la consigne initiale au résultat de chaque commande : l’authentification, la consigne, les fichiers lus et modifiés, les actions proposées par l’agent, vos décisions (validation, refus, arrêt), les exécutions dans le bac à sable avec leur code de retour et leur durée, et la politique en vigueur (livre blanc sécurité V3, section 7.2). Chaque extension de borne y laisse aussi une entrée. Dans le terminal, un cadre de session est écrit avant le premier acte.

Les journaux sont en ajout seul. Les entrées sont chaînées : chacune porte le condensat de la précédente, si bien que modifier une entrée passée rompt la chaîne et se détecte. Chaque événement est horodaté et porte l’identité issue de l’annuaire. Les journaux s’exportent vers le SIEM de votre organisation (livre blanc sécurité V3, section 7.1).

Par défaut, le journal ne contient ni le texte de la consigne, ni le code, ni la sortie des commandes : il ne crée pas de copie du code source. Votre organisation peut activer la conservation du contenu, avec un chiffrement et une rétention distincts. Un secret détecté n’est pas journalisé en clair.