1. Qu’est-ce qu’un run multi‑étapes et en quoi diffère‑t‑il d’une requête « ponctuelle »
Lorsque vous ne travailliez qu’avec ChatGPT App et les outils MCP, le schéma était assez linéaire : une requête utilisateur arrive → GPT décide d’appeler un ou plusieurs outils → vous répondez à l’utilisateur. Cela peut encore être considéré comme « un seul pas logique », même si, à l’intérieur d’un outil, vous faisiez quelque chose de plus complexe.
Pour un agent, un run est une cible + une série d’étapes. On ne pense plus en catégories « un prompt — une réponse », mais on perçoit la tâche comme un mini‑projet que l’agent mène du début à la fin.
On peut présenter la différence ainsi :
| Type d’interaction | Ce que fait le modèle | Où se trouve la logique |
|---|---|---|
| Appel d’outil classique dans ChatGPT App | Décide s’il faut appeler un outil, renseigne les arguments, compose la réponse à partir du résultat | La logique métier principale et la séquence d’actions résident dans un seul outil ou dans le backend |
| Run agent (Agents SDK) | Planifie plusieurs étapes, décide quand et quel tool appeler, analyse les résultats intermédiaires, peut réviser le plan | La logique « comment progresser vers l’objectif » est en partie dans l’instruction system de l’agent, en partie naît dans la « tête » du modèle |
Point important : vous n’êtes pas obligé de confier toute la planification au modèle. Le plus souvent, on obtient un hybride : vous codez strictement les grandes phases du scénario (par exemple « d’abord collecter les exigences, puis choisir des cadeaux, puis préparer la fiche »), et à l’intérieur de chaque phase vous laissez à l’agent une assez grande liberté d’utiliser ses outils.
Mini-analogie
Un appel d’outil ponctuel, c’est comme appeler un coursier : « viens récupérer un document et apporte‑le au bureau ».
Un run agent multi‑étapes, c’est comme un assistant personnel : « Prépare‑moi un cadeau pour un collègue pour son anniversaire : découvre ce qu’il aime, propose plusieurs options, vérifie la livraison et assemble le tout dans une belle présentation ». L’assistant décide lui‑même des actions à effectuer en chemin.
Un peu plus loin dans le cours, nous verrons aussi comment ces runs multi‑étapes s’intègrent à la pile déjà connue Apps SDK → MCP → backend, afin que, pour ChatGPT et le widget, la logique d’agent ressemble à un outil MCP propre et net.
2. Comment le modèle planifie lui‑même les étapes : vue d’ensemble
En termes d’Agents SDK, chaque run peut être commodément représenté comme une triple :
- Goal (objectif) : une description textuelle de la tâche qui se retrouve dans les instructions system/user de l’agent.
- Tools : un ensemble d’outils disponibles avec de bonnes descriptions et un JSON Schema.
- State : l’historique des étapes et l’état structuré que vous stockez à l’extérieur (BD, Redis, autre).
Ensuite, le cycle de run habituel démarre : le modèle regarde l’objectif et les outils disponibles et, à chaque étape, décide :
- « J’ai suffisamment d’informations — je peux fournir le résultat final à l’utilisateur » ;
- ou « J’ai besoin d’appeler l’outil X avec tels arguments » ;
- ou « J’ai reçu le résultat d’un outil, je dois maintenant l’interpréter, le filtrer et, éventuellement, appeler un autre outil ».
Au niveau du pseudo‑code, l’idée ressemble à ceci (rappel : c’est un modèle mental, pas une API réelle) :
while (!done && steps < MAX_STEPS) {
const modelResponse = await callModel({
system: agentPolicy,
messages: history,
tools,
});
if (modelResponse.type === "tool_call") {
const toolResult = await callTool(modelResponse.toolName, modelResponse.args);
history.push({ role: "tool", content: toolResult });
} else {
// réponse finale
done = true;
return modelResponse.content;
}
steps++;
}
Dans un Agents SDK réel, tout ce cycle est déjà implémenté et « caché » dans la bibliothèque. Vous décrivez l’agent de façon déclarative, et le SDK fait tourner le modèle et les outils en boucle jusqu’à obtenir une réponse finale ou atteindre les limites d’étapes/temps.
La tâche de l’architecte est de :
- formuler le goal et l’instruction system de façon à ce que le modèle planifie des étapes raisonnables ;
- construire un ensemble d’outils sans chevauchements sémantiques ;
- définir des limites sur les étapes et le temps ;
- réfléchir à quelles étapes peuvent être parallélisées.
Quand nous avons l’objectif, les outils et une représentation de l’état, la question suivante est : par quelles étapes aller vers cet objectif. Toutes les étapes ne se valent pas : certaines sont strictement séquentielles, d’autres peuvent être parallélisées.
3. Étapes séquentielles et parallèles
Maintenant que nous avons une compréhension de base du cycle de run d’un agent, il est important de comprendre quels types d’étapes existent dans un tel processus. Dans un workflow d’agent, on distingue deux grands types : séquentielles et parallèles.
Étapes séquentielles
C’est lorsque le résultat de l’étape A est crucial pour l’étape B. Par exemple, dans notre GiftGenius pédagogique :
- D’abord, comprendre qui est le destinataire du cadeau : collègue, proche, âge, centres d’intérêt.
- Puis sélectionner un ensemble de candidats via l’outil search_gifts.
- Ensuite, les filtrer en fonction du budget et des contraintes.
- Puis mettre en forme de jolies cartes pour le widget.
- Et seulement après, éventuellement proposer de passer au checkout.
Chaque étape suivante dépend des données de la précédente, d’où une exécution strictement séquentielle.
Dans le pseudo‑code du comportement de l’agent, cela peut ressembler à un « plan interne » du modèle :
1. Poser des questions à l’utilisateur sur le destinataire et le budget
2. Appeler l’outil search_gifts(profile, budget)
3. Appeler l’outil filter_by_constraints(gifts, constraints)
4. Former la liste finale et la description
Le modèle n’écrit pas réellement une telle liste en code, mais nous pouvons l’orienter vers une structure similaire via des instructions system, des exemples de dialogues et la description des outils.
Étapes parallèles
Parfois, des étapes peuvent être exécutées indépendamment. Par exemple, nous voulons comparer des offres de cadeaux provenant de trois boutiques à la fois :
- search_gifts_amazon
- search_gifts_etsy
- search_gifts_local_store
Du point de vue de l’agent, ce sont trois appels d’outils indépendants qu’on peut lancer en parallèle pour réduire le temps de réponse global.
Dans Agents SDK (et, de manière générale, dans les frameworks d’agents modernes), il existe souvent une prise en charge intégrée des appels parallèles d’outils si le modèle propose plusieurs appels en une seule étape. Le scénario canonique : le modèle décrit dans sa réponse une liste de tels appels, le SDK les exécute en concurrence, agrège les résultats et les injecte comme un ensemble de messages tool à l’étape suivante du modèle.
Du point de vue de la planification, cela ressemble à ceci :
// Étape de l’agent : le modèle a décidé d’appeler trois outils
const calls = [
{ name: "search_gifts_amazon", args: {...} },
{ name: "search_gifts_etsy", args: {...} },
{ name: "search_gifts_local_store", args: {...} },
];
const results = await Promise.all(
calls.map(c => callTool(c.name, c.args))
);
// Ensuite, tous les résultats sont ajoutés au contexte avant l’étape suivante du modèle
Si vous avez fait du frontend en JS/TS, vous avez déjà rencontré l’idée des requêtes parallèles : par exemple, lorsque vous lancez plusieurs fetch() simultanément via Promise.all. La même idée apparaît maintenant à l’intérieur du cycle de run de l’agent, sauf que la décision de ce qui peut être exécuté en parallèle est largement prise par le modèle lui‑même.
4. Exemple de workflow pour GiftGenius : étapes, objectifs et outils
Dans la section sur les étapes séquentielles, nous avons déjà intuitivement découpé le comportement de GiftGenius en phases. Maintenant, formalisons ce même scénario multi‑étapes en tant que workflow d’agent : décrivons l’objectif, les étapes et relions‑les aux outils et à la configuration de l’agent. Nous ne nous attacherons pas encore à une API concrète d’Agents SDK ; décrivons la structure et ajoutons un peu de code TypeScript fictif pour ancrer les idées.
Objectif (goal)
Formulons l’objectif ainsi :
Aider l’utilisateur à choisir 3–5 options de cadeaux pour un destinataire précis, en tenant compte du budget, des occasions et des contraintes de livraison, et fournir une liste structurée de cartes cadeaux pour le widget GiftGenius.
Étapes principales
Décrivons une variante minimale en 4 étapes :
- Affinage du contexte du destinataire
Objectif : recueillir des informations sur la personne à qui l’on offre (âge, sexe, centres d’intérêt, relation avec le donneur), ainsi que le budget et la date de l’événement.
Outils : éventuellement aucun, purement un dialogue modèle ↔ utilisateur. - Recherche et présélection des cadeaux
Objectif : obtenir un échantillon « brut » de cadeaux.
Outils : search_gifts(profile, budget) — un tool qui interroge notre catalogue/moteur de recherche et renvoie une liste de candidats. - Filtrage et tri
Objectif : écarter les options inadaptées (pas de livraison dans la région, dépassement de budget, contraintes non respectées) et trier par pertinence.
Outils : filter_and_score_gifts(candidates, constraints) — un outil pur et idempotent. - Mise en forme du résultat pour le widget
Objectif : mettre les données dans un format adapté au UI : titre, courte description, image, prix, CTA.
Outils : format_gift_cards(gifts) — peut être un outil côté code (génération de structure) ou un outil LLM (textes esthétiques).
À quoi cela peut ressembler dans la configuration de l’agent
Imaginons que nous ayons un constructeur d’agent (pseudo‑code) :
import { createAgent } from "@acme/agents-sdk";
import { tools } from "./gift-tools";
export const giftAgent = createAgent({
name: "gift-guru",
system: `
Tu es l’agent GiftGenius, tu aides à choisir des cadeaux.
Objectif : proposer 3–5 options réellement achetables,
en tenant compte du profil du destinataire, du budget et des contraintes de livraison.
Commence par préciser les informations importantes, puis utilise les outils de recherche et de filtrage.
N’appelle pas les outils si tu ne connais pas encore le budget ou les centres d’intérêt clés.
Termine le travail quand tu as une liste claire de cartes cadeaux.
`,
tools, // ici seront search_gifts, filter_and_score_gifts, format_gift_cards
maxSteps: 12,
timeoutMs: 15000,
});
Remarquez quelques détails :
- Dans l’instruction system, nous indiquons explicitement que l’agent doit d’abord préciser les détails, puis seulement appeler les outils de recherche. Cela réduit le risque que le modèle appelle des outils avec un contexte trop flou.
- Nous avons limité maxSteps pour éviter que l’agent ne boucle indéfiniment.
- Le timeout timeoutMs est nécessaire pour que tout le run ne prenne pas la moitié de la vie de l’utilisateur.
5. Auto‑orchestration par le modèle : ce qu’il faut « laisser au modèle », et ce qu’il faut figer
Un agent, c’est un équilibre entre la liberté du modèle et la structure stricte que vous imposez.
Si vous donnez trop de liberté au modèle sans poser de limites, vous obtiendrez un « bazar créatif » : appels d’outils superflus, étapes répétées, boucles inattendues. À l’inverse, si vous codez tout trop strictement dans le backend comme un automate fini, le modèle se réduit à un décorateur de texte, pas à un exécutant intelligent.
Ce qu’on laisse généralement au modèle
Dans le contexte de GiftGenius et de scénarios similaires, il est raisonnable de confier au modèle :
- la formulation des questions à l’utilisateur (comment préciser les intérêts, comment demander le budget de façon adéquate) ;
- la décision de quand l’information est suffisante pour lancer la recherche ;
- le choix des outils à utiliser à l’intérieur d’une phase (par exemple, quel tool de recherche de boutique utiliser s’il y en a plusieurs) ;
- la génération de textes de description, d’explications, de comparaisons.
Ce qu’il vaut mieux figer
En revanche, il convient de fixer à l’avance :
- les grandes phases du scénario (« Collecte d’informations » → « Recherche » → « Filtrage » → « Mise en forme » → « Final ») ;
- les limites sur les étapes et le temps ;
- les conditions où l’agent doit « s’arrêter » et dire honnêtement à l’utilisateur que la tâche est insoluble (par exemple, si le budget est de 5 dollars mais qu’on veut un gadget électronique coûteux livré pour le lendemain) ;
- la politique d’idempotence des outils et les stratégies de retry.
Exemple hybride : des phases comme état, le détail au modèle
On peut ajouter dans le state de l’agent un champ phase, qui prend les valeurs "collect_profile" | "search" | "filter" | "format" | "done". Votre backend (ou Agents SDK lui‑même, s’il prend en charge une machine à états personnalisée) contrôlera alors quels outils sont disponibles à quelle phase.
Pseudo‑code :
type Phase = "collect_profile" | "search" | "filter" | "format" | "done";
interface GiftAgentState {
phase: Phase;
profile?: UserProfile;
candidates?: GiftCandidate[];
finalGifts?: GiftCard[];
}
L’instruction system de l’agent peut inclure une brève description des phases, et vous limitez en code la liste des tools exposés au modèle selon la phase courante. C’est un exemple de tool gating, traité plus en détail dans le module sur le workflow.
6. Contrôle des boucles infinies et des répétitions inutiles
Si l’on laisse un cycle de run d’agent sans contrôle, il finira tôt ou tard par se comporter comme un étudiant à la veille d’un rendu : « préciser et réécrire » indéfiniment pour ne pas rendre le travail. Notre objectif est d’éviter qu’il ne se bloque.
Il existe trois sources typiques de boucles infinies :
- Le modèle n’est pas sûr de la réponse et continue de reformuler la même requête vers l’outil avec des changements insignifiants.
- L’outil renvoie systématiquement une erreur ou un résultat vide, et l’agent s’entête à « réessayer ».
- L’agent est coincé entre deux outils, appelant tour à tour l’un puis l’autre, sans progresser vers la réponse finale.
Limite d’étapes (maxSteps)
Le mécanisme le plus simple et indispensable est la limitation du nombre d’étapes. Dans la plupart des implémentations d’Agents SDK, vous pouvez indiquer maxSteps lors du lancement d’un run ou dans la configuration de l’agent. Dès que la limite est atteinte, le SDK termine le run avec un statut particulier (par exemple, aborted_by_max_steps). À vous ensuite de décider comment l’afficher à l’utilisateur.
Dans GiftGenius, on peut considérer qu’une sélection de cadeaux raisonnable tient en ~10 étapes (quelques précisions, quelques recherches, filtrage, mise en forme). Fixons, par exemple, 12–15 étapes avec une marge et traitons correctement la situation où la limite est atteinte :
const run = await giftAgent.run({
input: userGoal,
maxSteps: 12, // on écrase la valeur par défaut
});
if (run.status === "max_steps_exceeded") {
// Afficher un message honnête à l’utilisateur
}
Limite de temps (timeout)
Parfois, le problème ne vient pas du nombre d’étapes, mais de la durée totale. Les outils peuvent être lents, le réseau instable. Il est donc utile de préciser timeoutMs à la fois au niveau d’un appel d’outil et au niveau de l’ensemble du run.
Par exemple, vous pouvez décider que :
- chaque appel d’API externe (recherche de cadeaux chez un partenaire) ne doit pas prendre plus de 3–5 secondes ;
- l’ensemble du run de sélection des cadeaux doit tenir en 15 secondes.
Si le timeout se déclenche, vous terminez proprement le run, en affichant éventuellement un résultat partiel et une explication honnête indiquant que « certaines sources n’ont pas répondu à temps ».
Détection des répétitions
Un pattern plus avancé (mais utile) consiste à détecter les appels d’outil répétitifs avec les mêmes arguments. Si vous voyez que l’agent a appelé trois fois de suite search_gifts(profile, budget) avec les mêmes paramètres, c’est le signal qu’il est bloqué.
Vous pouvez ajouter dans le state un compteur d’appels indexé par la clé (toolName, argsHash) et, si le compteur dépasse un seuil, soit :
- interrompre le run et renvoyer une erreur compréhensible à l’utilisateur ;
- soit fournir au modèle une instruction supplémentaire : « tu as déjà essayé d’appeler cet outil trois fois avec les mêmes paramètres, essaie de changer de stratégie ou pose une question à l’utilisateur ».
Pseudo‑code :
function shouldAbortToolCall(toolName: string, args: unknown, state: GiftAgentState) {
const key = `${toolName}:${hashArgs(args)}`;
const count = state.toolCallCounts[key] ?? 0;
if (count >= 3) return true;
state.toolCallCounts[key] = count + 1;
return false;
}
Où hashArgs est n’importe quelle fonction déterministe de sérialisation des arguments (par exemple, JSON.stringify avec tri des clés).
7. Critères clairs de terminaison de la tâche
L’une des différences clés entre un agent « jouet » et un agent de production est la présence de critères de terminaison explicites. Sans eux, le modèle peut soit abandonner la tâche trop tôt (« voici quelques cadeaux, débrouillez‑vous »), soit, au contraire, continuer indéfiniment à « améliorer » le résultat.
Dans GiftGenius, on peut définir une règle simple :
- L’agent se termine lorsqu’il possède de 3 à 5 cadeaux avec champs remplis : id, title, shortDescription, price, imageUrl, purchaseUrl, et qu’ils ont passé le filtrage par budget et livraison.
- Si, après au maximum N tentatives de recherche et de filtrage, il y a moins de 3 cadeaux adéquats, l’agent informe honnêtement l’utilisateur qu’il n’a pas pu trouver mieux et propose d’augmenter le budget ou d’assouplir les contraintes.
Ces critères peuvent être codés directement dans l’instruction system de l’agent et/ou dans la vérification du résultat après le run.
Exemple de vérification du résultat après le run :
if (run.status === "completed") {
const gifts = run.output.gifts; // supposons que notre agent renvoie un JSON structuré
if (!gifts || gifts.length < 3) {
// L’agent "s’est terminé", mais le résultat est faible — on peut :
// 1) afficher une explication honnête,
// 2) proposer à l’utilisateur de modifier les conditions.
} else {
// Tout est OK — on affiche le widget avec les cadeaux
}
}
Il est important de ne pas attendre du modèle une compréhension « magique » du succès métier. En tant que développeur, vous devez formuler explicitement les conditions d’un résultat « satisfaisant » et les vérifier.
8. Où l’orchestration est‑elle mise en œuvre : agent, backend, widget
Nous avons déjà évoqué le fait que l’orchestration peut vivre à différents niveaux : dans l’agent, dans le backend, dans le widget.
Du point de vue des processus multi‑étapes, la logique est à peu près la suivante.
Agent (Agents SDK) — responsable du workflow « mental » :
- comment découper l’objectif en étapes ;
- quels outils appeler et dans quel ordre ;
- quelles questions supplémentaires poser à l’utilisateur.
Backend assure en général :
- l’implémentation des outils (recherche, filtrage, commerce, etc.) ;
- le stockage de l’état et des checkpoints ;
- des contraintes métier strictes (plafonds de budget, droits, disponibilité par région).
Widget (Apps SDK) gère :
- l’affichage de la progression (stepper, barre de progression, « étape 2 sur 4 ») ;
- les formulaires de saisie ;
- les détails UX, comme la désactivation de boutons quand toutes les données ne sont pas renseignées.
Une bonne pratique est de raisonner ainsi : l’agent met en scène le travail des outils et le dialogue, tandis que le widget UI met en scène l’expérience visuelle de l’utilisateur. Ils communiquent via des données structurées (ToolOutput, agent run output).
9. Mini‑exemple de code : lancer l’agent multi‑étapes GiftGenius depuis un outil MCP
Maintenant, comme promis au début, relions la nouvelle notion à la pile déjà connue Apps SDK → MCP → backend et montrons un petit exemple de la façon dont un outil MCP peut lancer un run agent.
Imaginons que, dans votre app/mcp/route.ts, vous ayez le tool run_gift_workflow, qui :
- prend la requête textuelle de l’utilisateur (son objectif) ;
- démarre l’agent giftAgent ;
- renvoie un résultat structuré pour le widget.
Le code est simplifié et fictif, mais illustre bien l’enchaînement :
// app/mcp/route.ts
import { server } from "@modelcontextprotocol/sdk/server";
import { z } from "zod";
import { giftAgent } from "@/agents/giftAgent";
server.registerTool(
"run_gift_workflow",
{
title: "Choisir des cadeaux",
description: "Lance l’agent multi-étapes de sélection de cadeaux",
inputSchema: {
userGoal: z
.string()
.describe("Objectif de l’utilisateur, par ex. : je veux un cadeau pour un collègue jusqu’à 50 $"),
},
},
async ({ userGoal }) => {
const run = await giftAgent.run({ // ici nous lançons l’agent avec 12 étapes et 15 s de timeout
input: userGoal,
maxSteps: 12,
timeoutMs: 15000,
});
return {
status: run.status,
gifts: run.output?.gifts ?? [],
debug: run.debugInfo, // on pourra l’enlever plus tard
};
}
);
Ensuite, ChatGPT App peut appeler cet outil MCP comme n’importe quel autre, et votre widget GiftGenius peut construire le UI à partir de gifts. Vous obtenez un workflow multi‑étapes « sous le capot », tandis qu’extérieurement, pour ChatGPT, tout ressemble à un tool MCP soigné.
10. Erreurs typiques lors de la conception de processus multi‑étapes
Erreur n° 1 : « Laissons le modèle se débrouiller, je vais juste lui donner tous les outils ».
Quand un agent a accès à une dizaine de tools qui se chevauchent sémantiquement sans instruction system claire ni phases, le modèle se met à hésiter : appeler la même chose de différentes façons, dupliquer les requêtes, entrer en boucle. Mieux vaut investir dans le design : découper le scénario en phases, restreindre la liste des outils dans chaque phase et décrire explicitement la stratégie dans le prompt system.
Erreur n° 2 : absence de limites sur les étapes et le temps.
Si vous ne définissez pas maxSteps et timeout, en production vous obtiendrez rapidement des runs « errants » qui consomment des ressources pendant que les utilisateurs ne voient rien. Les limites ne sont pas « optionnelles », c’est de l’hygiène de base. Il est tout aussi important de traiter intelligemment les dépassements de limites, pas juste de tomber sur un 500 silencieux.
Erreur n° 3 : pas de critères explicites de terminaison.
Le modèle termine le run lorsqu’il estime que « ça suffit », mais sa notion de « suffisant » est loin des exigences métier. Si vous ne formalisez pas les critères de succès (combien de cadeaux, quels champs, quels filtres passés) et ne les vérifiez pas, vous aurez un UX instable : aujourd’hui cinq excellentes options, demain une « bof » et trois doublons.
Erreur n° 4 : absence de suivi des appels d’outils répétitifs.
Un agent peut rester coincé dans le pattern « a reçu une erreur → a reformulé la requête de deux mots → a rappelé le même outil ». Si vous ne suivez pas les appels répétés par (toolName, args), ces boucles resteront invisibles jusqu’à ce que vous regardiez les logs. De simples compteurs et un hash des arguments aident beaucoup.
Erreur n° 5 : mélange de l’orchestration et de la logique métier dans un seul outil.
Parfois, on essaie de cacher tout un workflow dans un tool MCP unique ou une fonction d’agent : recherche, filtrage, mise en forme et prise de décision. Au final, l’agent perd son sens — le modèle ne peut plus contrôler le processus étape par étape, vous perdez en transparence et en réutilisabilité des parties du scénario. Mieux vaut extraire les étapes en tools autonomes et donner à l’agent leur composition.
Erreur n° 6 : pas de lien avec l’état et les checkpoints.
Un processus multi‑étapes sans sauvegarde de l’état intermédiaire et de checkpoints devient un monolithe fragile : si quelque chose échoue au milieu, l’utilisateur doit tout recommencer. C’est critique pour les scénarios où l’utilisateur navigue entre les étapes ou revient plus tard. Utilisez un store d’état, stockez la phase, le profil, les candidats et donnez à l’agent la possibilité de reprendre au bon endroit.
Erreur n° 7 : ignorance de la couche UX.
Parfois, les développeurs se passionnent pour le workflow interne de l’agent et oublient que l’utilisateur ne voit que le widget et les messages du chat. S’il n’y a pas, dans le UI, de progression claire, de statuts « recherche de cadeaux… », « filtrage des options… », l’utilisateur pensera que l’app « a gelé » ou « ne fait rien », même si l’agent orchestre un processus complexe. En planifiant un run multi‑étapes, pensez dès le départ à son reflet dans l’interface.
GO TO FULL VERSION