1. L’outil d’agent : ce que c’est vraiment
Dans les modules précédents, vous avez déjà vu les outils côté Apps SDK — comme des « fonctions backend » auxquelles ChatGPT accède via votre App. Changeons maintenant de perspective : regardons les outils à travers les yeux de l’agent dans Agents SDK et voyons comment il choisit quoi appeler et quoi faire des erreurs.
Dans un backend classique, vous pensez en termes d’« endpoint », « méthode de contrôleur », « fonction de service ». Dans le monde des agents, l’unité de base de l’action devient l’outil (tool). Les tools de l’agent et les mcp-tools sont différents, bien qu’ils se recoupent.
Pour être précis : un outil dans le contexte de ChatGPT Agents SDK est la description d’une fonction que le modèle peut demander d’exécuter. Le modèle n’exécute pas le code lui‑même ; il génère une requête structurée (généralement JSON), et le runtime (votre code, le serveur MCP ou Agents SDK) exécute l’opération et renvoie le résultat.
Dans l’écosystème ChatGPT Agents SDK, un outil est décrit par une configuration : il possède un name, une description et des parameters (JSON Schema des arguments). L’agent voit cet ensemble d’outils, les conserve dans son contexte et, pendant son reasoning, décide quel tool appeler et avec quels arguments.
L’agent (ou ChatGPT en tant qu’hôte) reçoit cette liste, la « mémorise » dans son contexte et, au fil du raisonnement (reasoning), décide : pour une demande utilisateur donnée, quel outil appeler et avec quels arguments. C’est pourquoi les spécifications répètent sans cesse la maxime « tools are a contract » — les outils sont un contrat entre le modèle et votre code, et non simplement « une fonction en Python/TS ».
On peut faire une analogie avec une API classique. La route /api/gifts/search — c’est de la syntaxe pure : URL, méthode, format du corps. Alors que le tool search_gifts — c’est la sémantique : « recherche de cadeaux selon le profil et le budget ». La description d’un outil est une sorte de prompt, mais structuré et conçu pour une LLM, et non pour un humain.
2. Types d’outils : ce que peut faire un agent LLM
Pour éviter de se perdre dans le chaos des « fonctions qui savent tout faire », il est utile de regarder les outils comme quelques catégories typiques. Ce n’est pas une typisation formelle du SDK, mais une façon de penser architecturale qui vous aidera beaucoup.
Dans notre backend, les agents LLM ont généralement trois sources d’outils.
- Outils métier locaux. C’est ce qui vit dans votre backend : travail avec la base de données, logique métier (filtrage, recommandations, scoring). Par exemple, pour GiftGenius, nous pouvons avoir des outils qui extraient des produits de leur table PostgreSQL ou calculent un scoring personnalisé « à quel point ce cadeau plaira à cette personne ».
- Outils MCP. Ici, le serveur MCP agit comme fournisseur d’outils (tools) : il enregistre des fonctions, des ressources et des prompts et les expose au client (ChatGPT, agent LLM). Les outils via MCP peuvent appeler des API externes, travailler avec des fichiers ou fournir des modèles de prompts.
- Outils d’intégration. Tout ce qui vous relie au reste du monde : ACP/commerce (création de commande et checkout), envoi d’e‑mails, webhooks, écriture dans un CRM. Ces outils (tools) sont souvent plus dangereux, car ils modifient l’état de systèmes externes, et il faut les traiter avec une attention particulière en matière de sécurité et d’idempotence.
Il existe une autre classification utile — par nature d’action. Dans la recherche sur les outils LLM, on distingue généralement : les outils de récupération de données (recherche, RAG, get_*), les outils d’action avec effets de bord (create_order, send_email), les outils purement de calcul (calculate_loan) et les outils système/de contrôle (handoff_to_human, finish_task).
Pour fixer cela, il est pratique de regarder un petit tableau.
| Catégorie | Exemple dans GiftGenius | Effet de bord | Risque |
|---|---|---|---|
| Data Retrieval | |
Non | Faible |
| Action / Mutating | |
Oui | Élevé |
| Computation | |
Non | Moyen |
| Système / Contrôle | |
Non | Logique |
D’un point de vue architectural, l’essentiel : les outils en lecture seule doivent être nombreux et peu coûteux, tandis que ceux qui modifient l’état doivent être rares, extrêmement prudents, avec des logs, de l’idempotence et souvent une confirmation utilisateur.
Nous allons ensuite parler principalement des outils de récupération de données et des outils d’action, car c’est sur eux que repose la logique de GiftGenius.
3. JSON Schema comme contrat entre le modèle et votre code
Approfondissons la description d’un outil. Dans ChatGPT Agents SDK (comme dans Apps SDK), le format standard de description des paramètres d’un outil est JSON Schema : vous décrivez le type object, ses properties, les types des champs, les champs obligatoires, les contraintes, etc.
Il est important de comprendre : JSON Schema ne concerne pas uniquement — ni principalement — la validation. C’est une partie du prompt pour le modèle. Les guides officiels d’OpenAI sur la conception des outils (tools) expliquent clairement que la qualité du travail de l’agent dépend fortement du degré de précision et d’univocité des champs, de leurs noms et de leurs commentaires.
Voyons un exemple pour GiftGenius qui a déjà été mentionné dans le plan du cours.
{
"name": "search_gifts",
"description": "Trouve des cadeaux selon le type de destinataire, ses centres d’intérêt et le budget.",
"parameters": {
"type": "object",
"properties": {
"recipient_type": {
"type": "string",
"description": "Qui est le destinataire du cadeau (par exemple, 'homme', 'femme', 'enfant')."
},
"interests": {
"type": "array",
"items": { "type": "string" },
"description": "Centres d’intérêt clés (sport, livres, technologies, etc.)."
},
"budget": {
"type": "number",
"description": "Budget maximal dans la devise de l’utilisateur."
}
},
"required": ["recipient_type", "budget"]
}
}
Plusieurs points importants ici.
- Premièrement, name et description. Pour le modèle, c’est le signal principal pour savoir quand utiliser cet outil. La documentation sur le routage sémantique souligne que la description de l’outil est en fait une API pour le modèle : si vous l’appelez func1 et écrivez « fait quelque chose d’utile », le modèle ne comprendra pas quand l’appeler. Si vous écrivez search_gifts avec une description claire, le choix devient bien plus trivial.
- Deuxièmement, les parameters. Les noms des champs et leurs descriptions sont cruciaux. Pour une LLM, recipient_type est bien plus clair que type. Une bonne description comme « Qui est le destinataire du cadeau… » suggère au modèle qu’il faut fournir ici le type de destinataire, et non, par exemple, le format d’emballage.
- Troisièmement, required. Ce n’est pas seulement de la validation côté serveur, mais aussi un indice pour le modèle : il s’efforcera de remplir les champs obligatoires et ignorera les facultatifs si le contexte ne les précise pas. Cela réduit le nombre d’appels d’outils « vides » ou incorrects.
Les guides officiels d’Apps SDK recommandent explicitement : créez des outils étroits, à responsabilité unique, avec des noms et descriptions clairs, et évitez les outils « fais tout pour les cadeaux » qui tentent de fusionner des tâches différentes.
4. Concevoir les outils de GiftGenius : du schéma au code
Prenons notre GiftGenius et ajoutons deux outils clés de l’agent LLM, nécessaires dans la plupart des scénarios :
- suggest_gifts(profile, budget) — renvoie une liste de candidats ;
- get_gift_details(gift_id) — donne des détails sur un cadeau précis.
Nos suggest_gifts et get_gift_details sont des exemples typiques d’outils métier locaux de la classification précédente, principalement de la catégorie Data Retrieval.
Schéma pour suggest_gifts
Commençons par un JSON Schema simple, puis montrons à quoi cela peut ressembler en TypeScript côté backend/runtime agent.
{
"name": "suggest_gifts",
"description": "Propose une liste de cadeaux à partir du profil du destinataire et du budget.",
"parameters": {
"type": "object",
"properties": {
"age": {
"type": "integer",
"minimum": 0,
"maximum": 120,
"description": "Âge du destinataire en années."
},
"relationship": {
"type": "string",
"enum": ["friend", "coworker", "partner", "family"],
"description": "Relation avec le destinataire : ami, collègue, partenaire, famille."
},
"interests": {
"type": "array",
"items": { "type": "string" },
"description": "Centres d’intérêt du destinataire (sport, livres, technologies, etc.)."
},
"budget": {
"type": "number",
"minimum": 1,
"description": "Budget maximal dans la devise de l’utilisateur."
}
},
"required": ["budget"]
}
}
Ici, nous utilisons enum pour relationship afin d’éviter que le modèle n’invente des chaînes arbitraires comme "mauvais collègue" et ne les propage plus loin dans le code. Une conception soigneuse du schéma aide autant le modèle (il voit les valeurs autorisées) que le développeur (moins de surprises au runtime).
Imaginons maintenant un serveur MCP en Node.js avec un hypothétique McpServer. L’enregistrement de l’outil peut ressembler à cela :
// exemple simplifié d’enregistrement d’un outil sur un serveur MCP
server.registerTool(
{
name: "suggest_gifts",
description: "Propose des cadeaux selon le profil et le budget.",
inputSchema: suggestGiftsSchema
},
async (input, ctx) => {
const gifts = await findGiftsInDb(input, ctx.userLocale);
return { items: gifts }; // JSON que l’agent verra ensuite
}
);
Le code est très simplifié, mais la logique est claire : d’un côté — la description du contrat (nom, description, schéma), de l’autre — l’implémentation.
Schéma pour get_gift_details
Deuxième outil, presque toujours nécessaire dans une vitrine :
{
"name": "get_gift_details",
"description": "Récupère toutes les informations sur un cadeau à partir de son identifiant.",
"parameters": {
"type": "object",
"properties": {
"gift_id": {
"type": "string",
"description": "UUID du cadeau dans la base GiftGenius."
}
},
"required": ["gift_id"]
}
}
Et un enregistrement analogue :
server.registerTool(
{
name: "get_gift_details",
description: "Renvoie des informations détaillées sur un cadeau.",
inputSchema: getGiftDetailsSchema
},
async ({ gift_id }) => {
const gift = await db.gifts.findById(gift_id);
if (!gift) return { notFound: true };
return { gift };
}
);
Remarquez : nous montrons ici d’emblée que l’outil peut renvoyer notFound : true. Ce sont déjà les prémices des erreurs sémantiques (erreurs métier), dont nous parlerons ci‑dessous. L’agent pourra voir « cadeau introuvable » et décider quoi faire : par exemple, essayer un autre id ou proposer à l’utilisateur de choisir un autre produit.
5. Comment l’agent choisit quel outil appeler
Voici la partie la plus intéressante : le routage. Dans une application web traditionnelle, le routage est rigide : URL → contrôleur spécifique. Dans le monde des Apps ChatGPT et des agents, le choix de l’outil est sémantique et probabiliste.
On peut représenter le cycle de haut niveau ainsi :
flowchart TD
U[Message utilisateur] --> M["Modèle (agent)"]
M -->|analyse de la requête| C{Faut-il un outil ?}
C -->|non| T[Réponse textuelle]
C -->|oui| S[Choix de l’outil]
S --> K[Construction du JSON d’arguments]
K --> R[Exécution de l’outil]
R --> M2[Le modèle voit le résultat]
M2 --> T2[Réponse finale ou étape suivante]
À chaque étape, l’agent voit plusieurs choses :
- Premièrement, des instructions système (le rôle de l’agent, les contraintes) ;
- Deuxièmement, l’historique du dialogue ;
- Et enfin, la liste des outils (tools) avec leurs name, description, inputSchema.
Lorsqu’un nouveau message utilisateur arrive, le modèle compare le sens de la demande avec les descriptions des outils (appariement sémantique). Si la demande est « trouve un cadeau pour un ami jusqu’à 50 $ », la description de suggest_gifts est nettement plus pertinente que celle de get_gift_details, et l’agent choisira très probablement le premier.
Les guides officiels soulignent deux choses qui influencent fortement la qualité du routage.
- Premièrement, évitez les outils qui se recouvrent sémantiquement : si vous avez search_gifts et find_gifts décrits à peu près de la même façon, le modèle s’y perdra.
- Deuxièmement, respectez le principe de responsabilité unique : un tool = une tâche claire, et non « trouver des cadeaux, créer la commande et envoyer un e‑mail ».
Dans divers agents LLM, il existe des mécanismes de contrôle du mode de sélection des outils : par exemple, « auto » (le modèle décide lui‑même s’il faut un outil), « required » (appel de tool obligatoire), « none » (tools désactivés). Cela aide dans des workflows complexes (scénarios multi‑étapes), lorsque, par exemple, à une étape donnée, vous souhaitez forcer l’appel de suggest_gifts plutôt que de laisser le modèle bavarder.
Exemple de routage sémantique dans GiftGenius
Supposons que notre agent dispose d’au moins deux outils : suggest_gifts et get_gift_details.
- L’utilisateur écrit : « Trouve un cadeau pour un collègue jusqu’à 30 $, il aime les jeux de société. »
- L’agent voit que la demande contient l’objectif « trouver un cadeau », des informations sur le budget et les centres d’intérêt. La description de suggest_gifts convient parfaitement — on appelle cet outil.
- L’outil renvoie une liste de cinq cadeaux avec leurs id, leurs noms et une courte description.
- Ensuite, l’utilisateur écrit : « Dis‑moi en plus sur la troisième option. » L’agent fait correspondre « la troisième option » à l’id du résultat précédent, et maintenant, sémantiquement, c’est l’outil get_gift_details qui convient — il est appelé.
Il est important de noter : nulle part dans le code vous n’avez écrit explicitement « si la requête contient le mot “trouve”, alors appelle suggest_gifts ». C’est le modèle lui‑même qui s’en charge, en se basant sur vos descriptions et l’historique du dialogue. Votre responsabilité en tant que développeur est de rendre le choix évident, pour le modèle comme pour l’humain.
6. Erreurs des outils : pas un 500, mais un signal pour le modèle
Souvenez‑vous, dans get_gift_details, nous montrions déjà notFound : true ? C’est précisément un exemple d’erreur métier que l’agent doit voir et traiter intelligemment, plutôt que de recevoir un 500 brut.
Passons maintenant à la partie la plus douloureuse. Dans une API REST classique, quelque chose tombe dans les profondeurs du backend — on renvoie 500 Internal Server Error, on enregistre la trace de pile dans le log — et l’utilisateur se débrouille. Pour un agent, cette approche fonctionne mal.
Les guides pratiques et la documentation d’Agents SDK recommandent de traiter les erreurs d’outils comme des événements observables, et non comme de simples crashs. On parle souvent du pattern « Error as Observation ».
En gros, vous ne devez pas « planter » sans explication ; vous devez renvoyer au modèle une réponse structurée expliquant ce qui n’a pas fonctionné, afin qu’il puisse adapter son comportement : reformuler la requête, interroger l’utilisateur, essayer un autre outil, etc.
On distingue généralement trois types d’erreurs.
- Erreurs de validation des arguments. Le modèle peut générer des paramètres incorrects : oublier un champ obligatoire, mettre une chaîne au lieu d’un nombre, dépasser les bornes autorisées. Ici, votre schéma et votre validation doivent servir non seulement à lancer des exceptions, mais aussi à produire une réponse intelligible : par exemple, indiquer quel champ est invalide et pourquoi.
- Erreurs métier. Des situations attendues comme « produit introuvable », « région indisponible », « budget trop faible pour ce type de cadeaux ». Du point de vue de l’API, ce sont aussi des erreurs, mais elles doivent être renvoyées dans une réponse normale — avec un code et un message clairs, pas comme un crash.
- Erreurs système. Timeouts d’un service externe, problèmes réseau, pannes de base. Ici, il suffit généralement à l’agent d’un message prudent et générique comme « service temporairement indisponible, réessayez plus tard ». Pas de traces de pile, noms de tables ou autres détails inutiles au modèle et potentiellement dangereux pour la sécurité.
La documentation officielle d’Agents SDK propose même un mécanisme spécial failure_error_function permettant de formater proprement le texte d’erreur présenté au modèle, au lieu de simplement relancer l’exception.
Structure d’une erreur « amicale »
Dans l’outil de l’agent (dans votre backend), vous pouvez convenir que toute erreur est renvoyée, par exemple, sous la forme d’un objet :
type ToolError = {
code: string; // 'VALIDATION_ERROR', 'OUT_OF_STOCK', ...
message: string; // pour le modèle
retryable: boolean;
};
Et le résultat de l’outil — comme une union :
type SuggestGiftsResult =
| {
ok: true;
items: GiftSummary[];
}
| {
ok: false;
error: ToolError;
};
Le modèle (ou le runtime agent) verra ce JSON et pourra décider : si retryable : true, on peut réessayer avec de légères modifications ; si c’est une erreur métier non réessayable, il vaut mieux revenir vers l’utilisateur pour expliquer le problème.
7. Exemples : validation, erreur métier et erreur système
Revenons à notre backend/outils d’agent et voyons comment concrétiser ces idées dans le code.
Erreur de validation
Imaginons que l’outil suggest_gifts arrive, mais que le modèle ait décidé de passer un budget négatif.
async function handleSuggestGifts(input: SuggestGiftsInput)
: Promise<SuggestGiftsResult> {
if (input.budget <= 0) {
return {
ok: false,
error: {
code: "VALIDATION_ERROR",
message: "le budget doit être un nombre positif.",
retryable: false
}
};
}
const items = await findGiftsInDb(input);
return { ok: true, items };
}
Ici, nous ne lançons pas d’exception, nous renvoyons une erreur structurée. L’agent peut réinterpréter la requête : peut‑être décider qu’il a confondu la devise, interroger l’utilisateur ou simplement expliquer qu’il ne peut pas proposer de cadeaux avec un tel budget.
Erreur métier
Exemple maintenant avec get_gift_details. Le cadeau avec l’id fourni peut simplement ne pas exister.
async function handleGetGiftDetails(input: { gift_id: string }) {
const gift = await db.gifts.findById(input.gift_id);
if (!gift) {
return {
ok: false,
error: {
code: "GIFT_NOT_FOUND",
message: "Cadeau introuvable pour cet identifiant.",
retryable: false
}
};
}
return { ok: true, gift };
}
Dans la réponse du modèle, on peut s’attendre à quelque chose comme : « Il semble que le cadeau choisi ne soit plus disponible. Puis‑je proposer quelques alternatives dans une catégorie similaire ? ». Pour cela, l’agent n’a pas besoin de voir des erreurs SQL ni des traces de pile — seulement un code et un message compréhensibles.
Erreur système
Enfin, un exemple d’erreur système. Supposons que votre outil contacte une API de livraison externe qui « tombe » parfois.
async function handleEstimateDelivery(input: EstimateDeliveryInput) {
try {
const eta = await callDeliveryApi(input);
return { ok: true, eta_days: eta };
} catch (e) {
return {
ok: false,
error: {
code: "DELIVERY_SERVICE_UNAVAILABLE",
message: "Le service de livraison est temporairement indisponible.",
retryable: true
}
};
}
}
L’agent peut décider : « Le service de livraison semble indisponible pour le moment. Je vous montre quand même des cadeaux, mais le délai de livraison exact peut varier. Voulez‑vous continuer ? ».
8. Sécurité et idempotence des outils (aperçu rapide côté tools)
Une discussion complète sur la sécurité et les permissions fera l’objet d’un autre sujet, mais les outils d’agent sont trop liés à cela pour ne pas l’aborder du tout.
Premièrement, il faut séparer les outils de lecture et les outils d’écriture. Dans les descriptions, les schémas et les permissions, indiquez explicitement quels tools ne font que lire des données et sont absolument sûrs, et lesquels peuvent débiter de l’argent, modifier des commandes, etc. La documentation et les forums sur les scénarios d’agents parlent clairement de la séparation des outils ReadOnly et Mutating.
Deuxièmement, pour les outils qui modifient l’état, pensez à l’idempotence. L’agent ou le client MCP peut parfaitement répéter un appel (par exemple à cause d’une erreur réseau), et vous ne voulez pas que create_order crée deux commandes au lieu d’une. Les patterns typiques ici :
- idempotency‑key, transmis comme argument de l’outil ;
- vérification de l’existence de l’opération avant exécution ;
- séparation en étapes « créer un brouillon de commande » puis « confirmer la commande ».
Tout cela est étroitement lié à la façon dont vous concevez le contrat de l’outil : si le JSON Schema ne comporte pas de champ pour l’idempotency‑key, ajouter l’idempotence plus tard sera bien plus douloureux.
9. Bref aperçu d’Agents SDK : à quoi cela ressemble dans le runtime de l’agent
Cette section est un bref aperçu pour ceux qui travailleront avec un Agents SDK orienté TypeScript. Bien que la plus grande partie du cours porte sur MCP, il est utile de comprendre comment des outils similaires sont vus par Agents SDK et à quoi ressemble un tool typique dans le runtime.
La documentation officielle décrit généralement une entité de type « outil fonctionnel » : toute fonction, décrite via un objet de configuration (ou un helper comme tool(...)) et munie de types, peut être automatiquement transformée en outil pour lequel le SDK générera JSON Schema et description.
Au niveau conceptuel, c’est la même chose que ce que nous avons déjà discuté : le nom de la fonction, ses paramètres et le commentaire/la description jouent le rôle du nom, du schéma et de la description de l’outil. La différence, c’est que le SDK et/ou une bibliothèque d’aide pour les schémas (par exemple Zod ou JSON Schema) fait une grande partie du travail « mécanique » à votre place.
Exemple hypothétique (pseudo‑TypeScript, simplifié) :
type Gift = {
id: string;
title: string;
// ...
};
const suggestGifts = tool({
name: "suggest_gifts",
description: "Propose une liste de cadeaux selon le type de destinataire et le budget.",
parameters: {
type: "object",
properties: {
recipient_type: {
type: "string",
description: "Qui est le destinataire du cadeau (par exemple, 'homme', 'femme', 'enfant')."
},
budget: {
type: "number",
description: "Budget maximal dans la devise de l’utilisateur."
}
},
required: ["recipient_type", "budget"]
}
}, async (args: { recipient_type: string; budget: number }): Promise<Gift[]> => {
// À l’intérieur — votre logique métier
return findGifts(args.recipient_type, args.budget);
});
Le SDK (ou votre helper tool) construira un JSON Schema à partir de l’objet parameters et le transmettra à l’agent, tandis que le runtime s’occupera de la validation et du marshaling des arguments aller‑retour. Conceptuellement, c’est exactement ce que vous faisiez manuellement dans un serveur MCP en TypeScript, sauf que l’outil est désormais « branché » directement dans le runtime de l’agent.
L’important n’est pas de mémoriser la syntaxe exacte du helper tool, mais de retenir l’idée : typage de qualité + description/commentaires clairs = outil de qualité.
Si l’on récapitule : un bon outil d’agent est une fonction étroite, clairement décrite, avec un JSON Schema bien pensé, une description lisible par le modèle et une gestion soignée des erreurs. Le routage sémantique ne fonctionne que si les outils ne se chevauchent pas sémantiquement. Et les opérations mutantes doivent être sûres et idempotentes, sinon l’agent en production devient rapidement source de surprises.
10. Erreurs typiques lors de la conception d’outils d’agent
Erreur n° 1 : des outils trop larges « do_everything ».
La tentation est grande de tout mettre dans un seul outil manage_gifts qui recherche des cadeaux, affiche des détails, crée une commande et envoie un e‑mail. Le modèle en souffre : la description devient floue, le routage sémantique se dégrade, et l’agent commence à appeler cet outil « au cas où » même là où une simple recherche suffit. Mieux vaut découper en outils distincts avec une responsabilité bien comprise.
Erreur n° 2 : des outils qui se recouvrent sémantiquement.
Si vous avez search_gifts et find_gifts, tous deux « cherchent des cadeaux selon les centres d’intérêt », le modèle choisira au hasard entre eux. Le comportement devient instable : des requêtes identiques vont tantôt vers l’un, tantôt vers l’autre. Veillez à ce que chaque nom et description occupe une « niche » unique dans l’espace sémantique.
Erreur n° 3 : noms, descriptions et champs de schéma pauvres ou absents.
Un nom func1, la description « Does something » et un paramètre data : string — c’est la manière classique de rendre l’agent confus. Le modèle n’est pas télépathe et ne peut pas lire votre code source. Il s’appuie sur la description, les properties et leurs description dans le schéma. Si vous n’expliquez pas ce qu’est recipient_type, le modèle va deviner et se tromper.
Erreur n° 4 : ne penser qu’au happy path et ignorer les erreurs.
Beaucoup d’implémentations d’outils supposent : « Nous aurons toujours des arguments corrects et un service disponible. » Dans le monde réel, le modèle génère facilement de mauvais paramètres, les services externes tombent, et la base répond parfois « timeout ». Si vous ne concevez pas des formats d’erreur et n’envoyez pas au modèle des messages intelligibles, il ne pourra pas corriger son comportement et finira soit par planter silencieusement, soit par halluciner.
Erreur n° 5 : renvoyer un 500 brut et une trace de pile à la LLM.
En REST, nous avons l’habitude de journaliser la trace complète pour déboguer plus vite. Dans le contexte d’un agent, une trace de pile envoyée au modèle est à la fois inutile (le modèle ne sait pas ce qu’est une SQLException dans votre bibliothèque) et potentiellement dangereuse (détails d’implémentation en trop et, possiblement, informations confidentielles). Il est bien plus utile d’intercepter l’exception, d’écrire les détails dans le log, et d’envoyer au modèle un code et un message propres.
Erreur n° 6 : absence d’idempotence pour les outils mutables.
Un outil create_order sans idempotency‑key — c’est l’assurance de doublons de commandes, surtout avec des pannes réseau et des retries automatiques. Si votre agent opère dans un contexte commercial, les outils liés à l’argent doivent être conçus pour que des appels répétés n’entraînent pas de débits supplémentaires ni de doublons.
Erreur n° 7 : stocker des secrets et des détails techniques dans le schéma ou la description.
Par habitude, un développeur écrit parfois dans la description : « Appelle en interne le service X sur https://internal-api.example.com ». Le modèle n’a pas besoin de cette information, l’utilisateur non plus. Les schémas et descriptions font partie du prompt, ils vivent dans le contexte du modèle, et il ne faut pas y mettre d’URL de services internes, de noms de tables privées et encore moins de secrets.
Erreur n° 8 : passer tout et n’importe quoi aux outils au lieu d’un jeu de champs réfléchi.
On peut être tenté de « simplement passer tout le prompt utilisateur en chaîne, et on se débrouille dedans ». Vous perdez alors les bénéfices de la structuration via JSON Schema : le modèle ne comprend plus quelles parties de la demande sont importantes pour la logique, vous perdez validation et prédictibilité. Mieux vaut extraire de la requête des champs explicites (budget, interests, user_location) et les décrire dans le contrat de l’outil.
GO TO FULL VERSION