Le protocole de handshake entre deux agents
Les agents aussi ont besoin de « passer un contrat ». Le request_id est le numéro de contrat.
Pourquoi un protocole ?
Dans s09, send_message permet d'envoyer n'importe quel contenu. Mais quand deux agents doivent se mettre d'accord sur quelque chose (par exemple « je veux t'arrêter, d'accord ? »), envoyer une simple chaîne de caractères ne suffit pas — il vous faut :
- Un identifiant clair pour la requête (request_id), pour que la réponse puisse être associée à la bonne demande.
- Une machine à états : pending → approved | rejected, avec des conséquences claires à chaque état.
- Des champs cohérents : les deux parties savent ce que signifie
approve: true.
C'est la raison d'être de shutdown_request, shutdown_response, plan_approval et plan_approval_response.
Protocole shutdown · flux complet
Le lead veut qu'alice termine sa journée :
lead: # envoie un shutdown_request, le serveur enregistre le request_id req_id = uuid4()[:8] shutdown_requests[req_id] = {"target": "alice", "status": "pending"} send("alice", "shutdown_request", extra={"request_id": req_id}) alice: # au prochain tour de boucle, lit son inbox, voit le shutdown_request # décide : finir le travail en cours, puis approuver tool_use("shutdown_response", request_id=req_id, approve=True) lead: # reçoit le shutdown_response, met à jour le tracker à approved shutdown_requests[req_id]["status"] = "approved" # alice détecte que son shutdown est approuvé, passe status=shutdown et sort de la boucle
Visualisation de la FSM shutdown
Parcourez la machine à états pas à pas. Alice peut aussi rejeter — dire « je suis sur une tâche critique, impossible de m'arrêter maintenant ».
Plan Approval · même pattern, domaine différent
Avant de faire une grosse opération, un teammate soumet un plan pour approbation :
alice: tool_use("plan_approval", plan="Je vais réécrire entièrement le module auth avec JWT") # alice reste en idle en attendant le lead lead: # voit la demande plan_approval_response d'alice tool_use("plan_approval", request_id="...", approve=False, feedback="Ne touche pas encore à auth, on fait un refactor global le mois prochain")
En lisant le code de s10, on constate que les deux protocoles utilisent exactement le même pattern de suivi par request_id — seul le nom du dictionnaire change (shutdown_requests vs plan_requests). C'est le principe « un pattern, deux domaines ».
Pourquoi ne pas abstraire en une classe Protocol générique ? s10 évite délibérément cette abstraction. Raison : avec seulement deux protocoles, le bénéfice de l'abstraction est encore marginal ; chacun est compréhensible de façon autonome. On pourra extraire une base commune quand un quatrième ou cinquième protocole apparaîtra (rule of three).