Cycle de vie de la requête
Ce qui se passe entre « l’utilisateur envoie un message » et « l’agent répond » : le chemin complet, avec annotations sur le streaming, les appels d’outils et les barrières de sécurité.
Entrant
flowchart LR
A[External event] -->|webhook / push / poll / WS| B[Channel adapter]
B -->|decode, dedup, pair-check| C[Inbound envelope]
C -->|workspace binding| D[Runtime: process_message]
Un adaptateur de canal (par exemple discord.rs, telegram.rs, email_channel.rs) reçoit des événements natifs à la plateforme et les convertit en une enveloppe d’entrée uniforme. L’adaptateur gère :
- Décodage : payload spécifique à la plateforme → format de message canonique
- Déduplication : empêche de rejouer deux fois le même message (redémarrages, nouvelles tentatives)
- Vérification par paire : applique la politique
[channels.<name>.allowed_users]/ IAM avant que l’événement n’atteigne le runtime
Si le canal n’est pas appairé ou si l’utilisateur n’est pas autorisé, l’événement est supprimé avant que le runtime ne le voie.
Boucle d’agent
sequenceDiagram
participant CH as Channel
participant RT as Runtime
participant SEC as Security
participant MEM as Memory / history
participant PR as Provider
participant TL as Tool
CH->>RT: process_message(envelope)
Note over RT: resolve memory-inject policy from the turn's TurnOrigin
RT->>MEM: recall(query, session scopes)
MEM-->>RT: entries
Note over RT: render [Memory context] preamble (engine-side)
RT->>PR: chat(system, history, tools)
loop Streaming
PR-->>RT: StreamEvent::TextDelta
RT-->>CH: draft update (if channel supports it)
end
PR-->>RT: StreamEvent::ToolCall(args)
RT->>SEC: evaluate_tool_access(name, args, risk)
alt Blocked
SEC-->>RT: Err(reason)
RT->>PR: chat(..., + tool_error)
else Approval required
SEC->>CH: ask_operator(prompt)
CH-->>SEC: approved / denied
else Allowed
SEC-->>RT: Ok
end
RT->>TL: invoke(args)
TL-->>RT: ToolResult
RT->>MEM: append to turn/session history
RT->>PR: chat(..., + tool_result)
PR-->>RT: StreamEvent::TextDelta (final)
RT-->>CH: reply(final)
RT->>MEM: persist conversation/session history
Propriétés clés :
- Le streaming est de bout en bout. Le fournisseur diffuse des jetons. Si l’adaptateur de canal indique
supports_draft_updates(), l’exécution modifie un message envoyé en place au fur et à mesure que le texte arrive. Discord, Slack et Telegram prennent en charge cette fonctionnalité. - Les appels d’outils sont transmis en continu. Le modèle peut émettre un appel d’outil alors qu’il génère encore du texte. L’environnement d’exécution lit le flux jusqu’à son terme, affiche le texte visible au fur et à mesure de son arrivée, puis récupère les appels d’outils, les valide, les exécute, renvoie le résultat et démarre un nouveau flux pour le tour suivant.
- La complétion en streaming suit les événements du protocole. Les fournisseurs considèrent un flux réussi comme terminé à l’arrivée de son événement SSE terminal, plutôt que d’attendre que le serveur ferme la connexion. Les corps de réponse silencieux échouent après l’expiration du délai d’inactivité en octets du fournisseur, tandis que les générations actives peuvent dépasser le délai d’expiration des requêtes sans streaming.
- La sécurité contrôle chaque appel d’outil.
evaluate_tool_accessconsulte le niveau d’autonomie, les listes d’autorisation/de refus et les limites de chemins. Les appels à risque moyen sous l’autonomieSupervisedpassent par le circuit d’approbation de l’opérateur. - Le contexte mémoire est injecté par le moteur. Avant le premier appel fournisseur, le moteur de tour résout une politique d’injection à partir du
TurnOrigindu tour (l’initiateur du tour) : les sous-tours imbriqués n’injectent jamais, les origines planifiées (cron, daemon) injectent en excluant les entrées de catégorie de conversation, et les origines côté utilisateur injectent (en excluant les entrées de conversation lorsque le tour n’a pas de portée de session). Un site de spawn peut supprimer l’injection pour toute origine (par exemple une tâche cron avecuses_memory = false), et les tours ne disposant d’aucun backend mémoire le sautent entièrement. Un seul rendu applique la décroissance temporelle, le filtrage de pertinence, un ensemble de sauts anti-empoisonnement de prompt et des plafonds de budget uniformément sur chaque chemin ; les backends mémoire ne répondent qu’àrecall, ils ne formatent pas le contexte. - L’historique et la mémoire sont distincts. L’historique de session préserve la continuité des conversations, des appels d’outils et des résultats d’outils. Les écritures explicites en mémoire persistent les entrées sélectionnées dans le backend de mémoire. Les accusés de réception sont transmis in-band dans le texte de la conversation, plutôt que sous la forme d’un artefact persistant distinct. Pour plus de détails sur la propriété des payloads, consultez Cycle de vie de la mémoire et des payloads.
Reçus d’outils
Les exécutions d’outils réussies peuvent recevoir un reçu HMAC-SHA256 qui est ajouté au texte du résultat d’outil et renvoyé au modèle dans la conversation, prouvant que le résultat signé provient du runtime. Le HMAC utilise une clé éphémère en mémoire et est calculé sur tool_name || args || result || timestamp. Les reçus ne sont pas écrits dans un journal distinct sur disque et ne sont pas chaînés ; le modèle peut les renvoyer mais ne peut pas en forger un nouveau valide sans la clé. Voir Reçus d’outils.
Sortant
Les messages sortants repassent par le même adaptateur de canal. Les adaptateurs prenant en charge les messages multiples (Discord, Slack) peuvent diffuser de longues réponses sous forme d’une séquence de messages ; les autres (email, SMS) sont vidés à la fin du flux.
Où il se trouve dans le code
- Boucle d’agent :
crates/zeroclaw-runtime/src/agent/turn/(run_tool_call_loop), avec les points d’entrée danscrates/zeroclaw-runtime/src/agent/loop_.rs(process_message,run) - Injection de contexte mémoire :
crates/zeroclaw-runtime/src/agent/memory_inject.rs(resolve_inject_policy,render_memory_context), indexée surTurnOrigindes types d’entrée dezeroclaw-apiet invoquée par le moteur de tour - Vérifications d’accès aux appels d’outils :
crates/zeroclaw-runtime/src/security/(iam_policy.rsevaluate_tool_access) - Orchestration des canaux :
crates/zeroclaw-channels/src/orchestrator/ - Streaming du fournisseur :
crates/zeroclaw-api/src/model_provider.rs(énumérationStreamEvent, réexportée depuiszeroclaw-providers),compatible.rs(analyseur SSE)
Depuis #7415, chaque transport (channels, CLI, cron, gateway WebSocket, RPC/zerocode, ACP et l’API Agent intégrée) exécute le même moteur de tour : run_tool_call_loop dans crates/zeroclaw-runtime/src/agent/turn/. Les points d’entrée de streaming et intégrés sont de fins wrappers dans agent.rs qui définissent des paramètres propres à chaque appelant (déduplication, comportement du plafond d’itérations, émission d’événements) autour de la boucle partagée. Le module turn/ comporte un fichier par étape :
| Fichier(s) | Étape |
|---|---|
mod.rs | orchestrateur : contrôle d’itération, paramètres, vidage de pilotage |
history_window.rs · tool_specs.rs · vision_route.rs | pré-appel : maintenance de l’historique, spécifications des outils, routage de la vision |
provider_call.rs · stream_consume.rs · stream_guard.rs | l’appel du LLM, la consommation du flux, la protection du protocole en cours de flux |
parse_response.rs · protocol_detect.rs · context_recovery.rs | interprétation des réponses, détection des problèmes d’analyse, récupération après dépassement |
approval_gate.rs · call_prep.rs | approbation et préparation des appels d’outils (déduplication, hooks, valeurs par défaut de livraison) |
post_exec.rs · results_collect.rs · history_append.rs · max_iter.rs | enregistrement des résultats, détection de boucle, ajout à l’historique, plafond d’itérations |
context.rs · events.rs · knobs.rs · steering.rs · outcome.rs · redact.rs · delivery_defaults.rs | types partagés : contexte de tour, événements, paramètres par appelant, pilotage, résultats, nettoyage des identifiants |