Passer au contenu principal

Boucle d’agent

Cette page décrit de bout en bout comment Copilot CLI traite un message utilisateur (depuis l’envoi du prompt jusqu’à session.idle).

Architecture

Le SDK est la couche de transport. Il envoie les prompts en JSON-RPC au Copilot CLI et relaie les événements vers votre application. C’est le CLI qui exécute la boucle agentique d’utilisation d’outils et qui orchestre un ou plusieurs appels à l’API LLM jusqu’à l’achèvement de la tâche.

Boucle d’utilisation d’outils

Lorsque vous appelez session.send({ prompt }), le CLI entre dans la boucle suivante : À chaque appel, le modèle consulte tout l’historique de la conversation (system prompt / message utilisateur / l’ensemble des appels d’outils et de leurs résultats jusqu’ici). Important : une itération de cette boucle correspond exactement à un appel à l’API LLM, et se traduit dans le journal d’événements par une paire assistant.turn_start / assistant.turn_end. Il n’y a pas d’appels cachés.

Qu’est-ce qu’un Turn (tour) ?

Un turn correspond à un appel à l’API LLM et à son résultat.
  1. Le CLI envoie l’historique de la conversation au LLM
  2. Le LLM répond (avec potentiellement des requêtes d’outils)
  3. En cas de requête d’outil, le CLI l’exécute
  4. assistant.turn_end est émis
Un même message utilisateur déclenche généralement plusieurs tours. Par exemple, une question du type « comment fonctionne X dans cette codebase ? » donnera :
TurnAction du modèletoolRequests ?
1Recherche dans la codebase avec grep et glob✅ Oui
2Lecture de fichiers spécifiques d’après les résultats✅ Oui
3Lectures supplémentaires pour approfondir le contexte✅ Oui
4Génération de la réponse textuelle finale❌ Non → fin de boucle
À chaque tour, le modèle décide « continuer à utiliser des outils » ou « passer à la réponse finale ». Chaque appel voit tout le contexte accumulé (y compris les appels d’outils et résultats précédents), ce qui lui permet d’évaluer s’il dispose d’assez d’informations.

Flux d’événements sur plusieurs tours

Qui déclenche chaque tour

ActeurResponsabilité
Votre applicationEnvoie le prompt initial via session.send()
Copilot CLIExécute la boucle d’outils et renvoie les résultats d’outils comme entrée du tour suivant au LLM
LLMDécide de demander des outils et de continuer, ou de renvoyer une réponse finale et de terminer
SDKSe contente de relayer les événements. Ne contrôle pas la boucle
Le comportement du CLI est mécanique (« le modèle demande un outil → exécution → nouvel appel au modèle »). C’est le modèle qui décide quand s’arrêter.

Différence entre session.idle et session.task_complete

Ces deux événements signalent l’achèvement, mais leurs garanties sont très différentes.

session.idle

  • Toujours émis à la fin de la boucle d’utilisation d’outils
  • Éphémère : non persisté sur disque, non rejoué lors de la reprise d’une session
  • Signification : « l’agent a cessé de traiter et peut recevoir le prochain message »
  • À utiliser comme signal fiable de « terminé »
Le sendAndWait() du SDK attend cet événement.
// Attend jusqu'à ce que session.idle soit émis
const response = await session.sendAndWait({ prompt: "Fix the bug" });

session.task_complete

  • Émission optionnelle (uniquement si le modèle le signale explicitement)
  • Persisté (sauvegardé dans le journal d’événements de la session)
  • Signification : « l’agent estime avoir accompli la tâche globale »
  • Peut inclure un summary optionnel
session.on("session.task_complete", (event) => {
    console.log("Task done:", event.data.summary);
});

Mode Autopilot : le CLI incite au task_complete

En mode autopilot (headless / autonome), le CLI garde trace de l’appel éventuel de task_complete par le modèle. Si la boucle d’outils se termine sans task_complete, le CLI insère le message utilisateur synthétique suivant pour inciter le modèle :
“You have not yet marked the task as complete using the task_complete tool. If you were planning, stop planning and start implementing. You aren’t done until you have fully completed the task.”
Cela relance de fait la boucle d’utilisation d’outils. Le modèle reçoit cette incitation comme un nouveau message utilisateur et poursuit le travail. L’incitation inclut aussi la consigne « ne pas appeler task_complete trop tôt ».
  • Ne pas appeler s’il reste des questions en suspens. Décidez et continuez.
  • Ne pas appeler si vous ne faites que rencontrer une erreur. Essayez de la résoudre.
  • Ne pas appeler s’il reste des tâches. Terminez-les d’abord.
En autopilot, on obtient donc un mécanisme d’achèvement en deux étapes :
  1. Le modèle appelle task_complete avec un résumé → le CLI émet session.task_complete → terminé
  2. Le modèle s’arrête sans l’appeler → le CLI incite → le modèle continue ou appelle task_complete

Pourquoi task_complete n’est parfois pas émis

En mode interactif (chat classique), le CLI n’incite pas à task_complete. Le modèle peut donc l’omettre. Les principales raisons sont :
  • Q&A conversationnelle : le modèle répond à la question et s’arrête, sans « tâche accomplie » discrète
  • Décision du modèle : il renvoie le texte final sans appeler task_complete
  • Session interrompue : la session se termine avant que le modèle n’atteigne le point d’achèvement
En revanche, le CLI émet toujours session.idle. C’est un signal mécanique (fin de boucle) et non un signal sémantique (le modèle juge que c’est fini).

Lequel utiliser ?

Cas d’usageSignal
« Attendre la fin du traitement de l’agent »session.idle
« Savoir que la tâche de codage est terminée »session.task_complete (best-effort)
« Timeout / gestion d’erreurs »session.idle + session.error

Compter les appels au LLM

Le nombre de paires assistant.turn_start / assistant.turn_end dans le journal d’événements correspond au nombre total d’appels à l’API LLM. Il n’y a pas d’appels cachés pour la planification, l’évaluation ou la vérification d’achèvement. Exemple pour compter le nombre de tours d’une session :
# Compter les turns dans le journal d'événements de la session
grep -c "assistant.turn_start" ~/.copilot/session-state/<sessionId>/events.jsonl

Documents associés

Dernière modification le 13 juillet 2026