1. Introduction
Parfois, dans les cours de prompt engineering, on montre une formule magique :
« N’invente pas de faits et dis “je ne sais pas” s’il n’y a pas d’information. »
Malheureusement (ou heureusement), pour une ChatGPT App, ce n’est pas une solution miracle. La raison est simple : le modèle voit non seulement votre system‑prompt, mais aussi :
- la liste des outils avec leurs descriptions et schémas ;
- les résultats de ces outils ;
- l’historique du dialogue, y compris les follow‑ups précédents.
Si ces trois couches — system‑prompt, descriptions des tools, patterns de follow‑up — se contredisent ou « pendent » simplement, le modèle se comporte comme un junior classique : appliqué, mais enclin à inventer.
Dans le cours, nous utilisons l’idée d’une « protection à trois niveaux contre les hallucinations » (défense en profondeur) :
- niveau 1 — règles globales dans le system‑prompt ;
- niveau 2 — contraintes locales dans la définition de chaque outil ;
- niveau 3 — gestion des erreurs, des résultats vides et des follow‑ups.
Dans cette leçon, nous rassemblons les trois niveaux dans un contrat unique et cohérent pour notre application pédagogique GiftGenius.
Insight
Dans la nouvelle architecture des ChatGPT Apps, le champ pour le system‑prompt a disparu — formellement il n’existe plus. Mais cela ne veut pas dire qu’il faut renoncer aux instructions globales pour le modèle. Il existe une astuce : pour ChatGPT, les descriptions d’outils sont un texte de prompt à part entière, comme un system classique. C’est précisément via celles‑ci que l’on peut rétablir le « firmware mental » de l’application.
Créez un outil de service, par exemple about ou about_app. Il n’est pas destiné à être appelé souvent, mais sa description est lue par le modèle au même titre que les autres outils. On peut donc y intégrer un system‑prompt complet. Il vaut mieux le placer après une courte description technique de l’outil lui‑même. Au final, au démarrage, le modèle reçoit votre system‑prompt tel quel — et l’applique à tout le reste du dialogue et aux appels des tools.
Pour faciliter la maintenance, je recommande d’ajouter au début du system‑prompt un numéro de version explicite, par exemple SYSTEM_PROMPT_VERSION: v3. Cela permet d’identifier pourquoi le modèle se comporte différemment : vous voyez immédiatement sur quel ensemble d’instructions il fonctionne. Si un comportement étrange apparaît en prod, il est facile de comprendre si l’erreur concerne l’ancienne version du prompt ou la nouvelle.
Exemple :
tool description
### Global assistant behavior for the entire GiftGenius App (ver 3.01)
*(system-level guidelines, not user-facing text)*
system prompt text
2. Quels types d’hallucinations surviennent dans une ChatGPT App (exemple d’un catalogue de cadeaux)
Pour savoir comment soigner, il est utile de nommer le mal. Dans le contexte d’un catalogue (cadeaux, produits, forfaits), on rencontre le plus souvent les types d’hallucinations suivants.
Premièrement, des articles de catalogue inventés. L’utilisateur demande « un bon numérique pour un abonnement annuel à un service précis » ou un cadeau très exotique qui n’existe pas dans votre flux, et le modèle, voulant être utile, invente joyeusement « Super Space Flight 3000 — un vol dans l’espace », qui n’a jamais existé dans la base GiftGenius.
Deuxièmement, des attributs inventés. Le cadeau existe dans le catalogue, mais le modèle « enjolive » la réalité : il change le prix, le type de cadeau (numérique vs physique), la disponibilité de la livraison dans le pays de l’utilisateur ou la durée de validité du bon, parce que « c’est plus logique » ou « ça sonne mieux ».
Troisièmement, des actions hallucinées. Le modèle écrit « j’ai déjà acheté ce cadeau et envoyé le code sur votre e‑mail, la carte a été débitée de $49 », alors que votre back‑end GiftGenius n’a même pas tenté d’acheter quoi que ce soit et qu’aucun flux ACP/Stripe n’a été lancé.
Enfin, il y a un cas combiné : l’outil a renvoyé une liste vide (aucun cadeau ne correspond aux filtres et au budget), et le modèle, pour ne pas décevoir l’utilisateur, invente deux « options approximatives » et n’explique pas qu’elles n’existent pas dans le catalogue.
Notre objectif est de faire en sorte que, dans ces situations, le modèle :
- reconnaisse honnêtement qu’il n’y a pas de correspondance exacte dans le catalogue ;
- n’invente pas de nouveaux cadeaux et ne modifie pas les valeurs des champs ;
- explique à l’utilisateur ce qui s’est passé et propose une prochaine étape claire.
Et cela, non pas via des « incantations » disséminées, mais via un contrat relié system‑prompt ↔ tools ↔ follow‑ups.
3. Couche 1 : renforcer le system‑prompt contre les hallucinations
Pour stopper les cadeaux, attributs et actions inventés du chapitre précédent, commençons par renforcer la couche supérieure — le system‑prompt : donnons au modèle une « philosophie » générale de comportement dans le catalogue.
Dans la première leçon du module, vous avez déjà écrit un system‑prompt de base du type : « Tu es GiftGenius, tu aides à choisir des cadeaux… » et fixé les limites de responsabilité de l’assistant et le travail avec les outils.
Ajoutons maintenant des règles anti‑hallucinations explicites.
La logique est la suivante : le system‑prompt définit la philosophie générale du comportement. Il ne connaît pas les détails de chaque outil, mais il peut :
- interdire d’inventer des cadeaux / des prix / de la disponibilité en dehors du catalogue ;
- définir le comportement si les outils renvoient un résultat vide ou une erreur ;
- exiger une distinction explicite entre les données du catalogue de cadeaux et « toute connaissance générale du modèle ».
Exemple de fragment de system‑prompt (constante TypeScript dans notre projet Next.js, par exemple config/systemPrompt.ts) :
// config/systemPrompt.ts
export const SYSTEM_PROMPT = `
# Rôle
Tu es GiftGenius, assistant pour la sélection de cadeaux
sur la base du catalogue de notre application.
# Données et contraintes
- N’invente pas des cadeaux qui n’existent pas dans le catalogue ou dans les réponses des outils.
- N’invente pas les prix, la disponibilité, le type de cadeau (numérique/physique),
les régions de livraison ou d’autres attributs.
- Si l’outil n’a pas renvoyé les données nécessaires ou s’il y a eu une erreur,
annonce-le honnêtement et n’essaie pas de deviner les valeurs.
# Travail avec les outils
- Utilise toujours les outils du catalogue de cadeaux pour toute donnée factuelle :
liste des cadeaux, prix, types, disponibilité, SKU.
- Si l’outil a renvoyé un résultat vide, dis que selon les conditions actuelles
il n’y a pas de cadeaux adaptés et propose d’assouplir les filtres
(modifier le budget, la catégorie, le type de cadeau).
`;
Plusieurs points sont importants ici.
Premièrement, nous séparons les « connaissances générales du modèle » des « données de l’application ». Le modèle peut toujours expliquer en quoi un cadeau numérique diffère d’un cadeau physique ou quels sont les motifs habituels, mais tout cadeau concret, prix et SKU doivent provenir uniquement des outils de GiftGenius.
Deuxièmement, nous décrivons explicitement ce qu’il faut faire en cas d’erreur/réponse vide : ne pas se taire, ne pas « être créatif », mais dire honnêtement à l’utilisateur que rien n’a été trouvé et proposer de modifier les paramètres.
Troisièmement, nous nous concentrons non pas sur un abstrait « n’hallucine pas », mais sur des types de comportements concrets, liés à notre domaine (catalogue de cadeaux et achat de SKU numériques/physiques).
Cette couche aide déjà beaucoup en soi, mais elle peut être facilement « neutralisée » par une description d’outil imprécise. Passons aux descriptions des outils.
4. Couche 2 : descriptions des outils (tool descriptions) et schémas comme partie formelle du contrat
Le modèle décide quand et comment appeler votre outil principalement selon :
- le nom de l’outil ;
- la description de l’outil ;
- inputSchema / outputSchema (JSON Schema décrivant les champs).
Autrement dit, la description d’outil (tool description) n’est pas une documentation « pour humains », mais une partie du prompt, simplement plus formalisée. Et de nombreuses hallucinations naissent précisément ici.
Imaginons notre outil recommend_gifts, que le back‑end implémente comme une sélection de cadeaux à partir du catalogue GiftGenius.
Une mauvaise description pourrait ressembler à ceci :
// MAUVAIS : trop vague
const recommendGiftsTool = {
name: "recommend_gifts",
description: "Sélectionne des cadeaux pour l’utilisateur",
inputSchema: {
type: "object",
properties: {
profile: { type: "string" }
}
}
};
Formellement, tout est correct, mais le modèle ne comprend pas :
- les limites de l’outil ;
- quoi faire si aucun cadeau n’est trouvé ;
- qu’il ne faut pas inventer des cadeaux et des prix en dehors du catalogue.
Une bonne description fait plusieurs choses à la fois : elle définit clairement le domaine, explique quand appeler l’outil et fixe strictement qu’il ne faut pas inventer des résultats.
Exemple (adapté au contrat GiftGenius avec segments, budget, locale, occasion) :
// config/tools.ts
export const recommendGiftsTool = {
name: "recommend_gifts",
description: `
Sélection de cadeaux dans le catalogue GiftGenius.
Utilise cet outil lorsqu’il faut obtenir une liste de cadeaux réels
selon les segments du profil du destinataire, le budget, la locale et l’occasion.
L’outil renvoie uniquement les cadeaux qui existent réellement
dans le catalogue GiftGenius.
NE PAS inventer des cadeaux ni leurs attributs en dehors des résultats de cet outil.
Si l’outil renvoie une liste vide, ne propose pas d’alternatives inventées,
mais passe au dialogue : propose de modifier le budget, le type de cadeau,
l’occasion ou d’autres paramètres.
`.trim(),
inputSchema: {
type: "object",
properties: {
segments: {
type: "array",
description:
"Segments du profil du destinataire, par exemple ['tech', 'fitness'].",
items: { type: "string" }
},
budget: {
type: "object",
description: "Plage de budget pour le cadeau.",
properties: {
min: {
type: "number",
description: "Montant minimum, non négatif.",
minimum: 0
},
max: {
type: "number",
description: "Montant maximum, supérieur à 0.",
exclusiveMinimum: 0
},
currency: {
type: "string",
description: "Code de devise sur trois lettres, par exemple 'USD' ou 'RUB'.",
minLength: 3,
maxLength: 3
}
},
required: ["min", "max", "currency"]
},
locale: {
type: "string",
description:
"Locale de l’utilisateur (format langue/région), par exemple 'ru-RU' ou 'en-US'.",
minLength: 2
},
occasion: {
type: "string",
description:
"Occasion pour le cadeau : par exemple 'birthday', 'anniversary', 'new_year'."
}
},
required: ["segments", "budget", "locale", "occasion"]
}
};
Ici, la description apporte plusieurs choses utiles.
Elle indique clairement que l’outil fonctionne uniquement avec le catalogue de cadeaux GiftGenius et que tout cadeau concret et tout prix doivent provenir uniquement de son résultat.
Elle explique quand utiliser l’outil : lorsqu’il faut une liste de cadeaux concrets selon les paramètres du destinataire, et non une théorie générale sur les cadeaux.
Elle définit le comportement en cas de résultat vide : ne pas inventer, mais passer au dialogue (ce que nous formalisons ensuite dans les follow‑ups).
Et inputSchema aide le modèle à extraire plus fiablement les entités de la requête de l’utilisateur : segments, budget, locale et occasion. Indiquer une structure explicite et des contraintes pour les champs (min, max, currency avec longueur fixe) réduit également la probabilité de combinaisons étranges et d’erreurs de parsing.
On peut ajouter l’envers du décor — non seulement « quand appeler », mais aussi quand ne pas appeler. Par exemple, si la requête est manifestement théorique :
description: `
...
N’utilise pas cet outil si l’utilisateur pose une question générale
sur les cadeaux sans demande de sélection pour une personne concrète
(par exemple, "quels sont les cadeaux populaires pour le Nouvel An en général").
Dans ces cas, réponds toi‑même dans le chat.
`.trim()
Vous synchronisez ainsi la description de l’outil avec les règles du system‑prompt concernant les requêtes « théoriques » vs « pratiques ».
5. Couche 3 : les follow‑ups comme couche UX et de sécurité
Même si le system‑prompt et les descriptions des tools sont parfaits, la vie ne l’est pas :
- le back‑end peut renvoyer une erreur ;
- le catalogue — une liste vide ;
- les résultats — ambigus ou trop nombreux.
Si l’on ne précise pas quoi dire après l’appel de l’outil, le modèle va improviser : parfois bien, parfois avec des faits inventés.
Dans la leçon 2, vous avez déjà vu des instructions UX de base : comment le modèle annonce le lancement de l’App, comment il conclut le scénario et ce qu’il dit à l’utilisateur « en sortie ». Ajoutons maintenant des patterns de follow‑ups qui réduisent les hallucinations.
Ces patterns sont généralement décrits directement dans le system‑prompt dans un bloc séparé, par exemple « Dialogue après utilisation des outils ».
Exemple de fragment :
// suite de SYSTEM_PROMPT
export const SYSTEM_PROMPT = `
# ... sections précédentes ...
# Dialogue après utilisation des outils
- Si l’outil de sélection de cadeaux renvoie une liste vide :
1) dis honnêtement que selon les filtres actuels aucun cadeau adapté n’a été trouvé ;
2) propose à l’utilisateur de modifier 1–2 paramètres clés
(budget, type de cadeau, centres d’intérêt du destinataire, occasion).
- Si l’outil renvoie trop d’options :
1) choisis 3–7 des plus pertinentes ;
2) indique explicitement selon quels critères tu les as sélectionnées
(adéquation aux intérêts, respect du budget, note).
- S’il y a eu une erreur de l’outil :
1) n’invente pas de données ;
2) dis qu’une erreur technique s’est produite et propose
d’essayer plus tard ou de simplifier la requête.
`.trim();
Ce faisant, nous « flashons » dans le modèle des répliques de follow‑up exemplaires. Il les formulera avec ses propres mots, mais avec la structure attendue :
- constat du fait (vide / trop / erreur) ;
- reconnaissance honnête de la limitation ;
- proposition mesurée de la prochaine étape.
Pour un catalogue de cadeaux, c’est crucial : au lieu de « tout va bien, voici trois cadeaux » en cas de résultat vide, le modèle dira quelque chose comme :
« Selon vos conditions actuelles (cadeau numérique sur l’espace jusqu’à $5 avec livraison uniquement aux USA), notre catalogue ne contient rien. Voulez‑vous que j’élargisse le budget ou que je propose d’autres catégories ? »
Et notez bien : ce n’est plus du code d’outil, mais bien des instructions dans le prompt qui définissent l’UX attendu.
6. Relier le tout : évolution de notre GiftGenius
Voyons la mini‑évolution de notre application et diminuons progressivement le niveau des hallucinations.
Version initiale : où tout casse
Supposons que nous avions un system‑prompt très minimaliste :
export const SYSTEM_PROMPT = `
Tu es un assistant pour la sélection de cadeaux.
Aide l’utilisateur à trouver des idées adaptées.
`;
L’outil était décrit ainsi :
export const recommendGiftsTool = {
name: "recommend_gifts",
description: "Sélectionne des cadeaux pour l’utilisateur",
inputSchema: { type: "object" }
};
Il n’y a pas d’instructions de follow‑up.
Ce qui se passera en pratique :
- si l’utilisateur demande « un cadeau numérique pour un ami gamer jusqu’à $10 », et que la base ne contient rien pour de tels filtres, le modèle peut :
- soit ne pas appeler l’outil du tout et inventer des cadeaux ;
- soit appeler l’outil, obtenir une liste vide, mais ne pas l’annoncer et inventer des options ;
- si le back‑end renvoie une erreur, le modèle peut penser que « quelque chose doit bien exister » et commencer à deviner.
Ainsi naît la situation classique : dans le chat — une belle réponse, dans la base — rien de similaire.
Nouvelle version du system‑prompt
Réécrivons le system‑prompt en tenant compte des trois niveaux de protection. Vous en avez vu une partie plus haut, rassemblons‑le en entier :
// config/systemPrompt.ts
export const SYSTEM_PROMPT = `
# Rôle
Tu es GiftGenius, assistant pour la sélection de cadeaux
sur la base du catalogue de notre application.
# Zone de responsabilité
- Ta tâche est d’aider l’utilisateur à choisir des cadeaux adaptés
depuis le catalogue, en expliquant les avantages et inconvénients des options.
- Ne promets pas l’achat effectif ou l’envoi du cadeau —
tu aides seulement à sélectionner et comparer des options.
L’achat et la délivrance d’un code/lien sont réalisés par le back-end après l’accord explicite de l’utilisateur.
# Données et contraintes
- N’invente pas des cadeaux qui n’existent pas dans le catalogue ou dans les réponses des outils.
- N’invente pas les prix, le type de cadeau, la disponibilité ou les régions de livraison.
- Si l’outil n’a pas renvoyé de données ou s’il y a eu une erreur,
n’essaie pas de deviner et annonce-le.
# Travail avec les outils
- Utilise les outils du catalogue de cadeaux (par exemple, profile_to_segments,
recommend_gifts, get_gift) pour toute donnée factuelle
(liste de cadeaux, prix, types, SKU, descriptions).
- Réponds toi‑même (sans outils) si la question est théorique
et ne nécessite pas la sélection d’un cadeau concret.
# Dialogue après utilisation des outils
- En cas de résultat vide : explique honnêtement que rien n’a été trouvé
selon les conditions actuelles, et propose de modifier 1–2 paramètres.
- En cas de trop nombreuses options : choisis 3–7 des plus pertinentes
et explique les critères de choix.
- En cas d’erreur de l’outil : n’invente pas de données, excuse-toi pour l’incident
et propose d’essayer à nouveau ou de simplifier la requête.
`.trim();
Désormais, le modèle sait clairement :
- où il est conseiller en cadeaux, et où se trouve le « back‑office » (derrière les outils) ;
- quelles données il ne faut absolument pas inventer ;
- comment se comporter dans les cas typiques non idéaux.
Nouvelle description et schéma pour recommend_gifts
Nous avons renforcé le system‑prompt, perfectionnons maintenant l’outil et assemblons la version finale de sa description — sur la base des idées de la section 4.
// config/tools.ts
export const recommendGiftsTool = {
name: "recommend_gifts",
description: `
Sélection de cadeaux dans le catalogue GiftGenius.
Utilise cet outil lorsque tu dois :
- obtenir une liste de cadeaux réels avec des prix à jour, le type
(digital/physical) et des tags ;
- affiner la sélection selon les segments d’intérêts, le budget, la locale et l’occasion.
N’UTILISE PAS l’outil :
- si l’utilisateur pose une question théorique générale sur les cadeaux
sans demande de sélection pour une personne concrète ;
- pour inventer des cadeaux qui n’existent pas dans le catalogue.
Si le résultat est vide, NE commence pas à inventer des cadeaux toi‑même,
mais rends la main au dialogue (suis les instructions du system‑prompt).
`.trim(),
inputSchema: {
type: "object",
properties: {
segments: {
type: "array",
description:
"Segments du profil du destinataire : par exemple, 'tech', 'sport', 'books'.",
items: { type: "string" }
},
budget: {
type: "object",
description:
"Plage de budget pour le cadeau dans la devise de l’utilisateur (min/max).",
properties: {
min: { type: "number", minimum: 0 },
max: { type: "number", exclusiveMinimum: 0 },
currency: {
type: "string",
minLength: 3,
maxLength: 3,
description: "Code de devise ISO 4217, par exemple 'USD' ou 'RUB'."
}
},
required: ["min", "max", "currency"]
},
locale: {
type: "string",
description: "Locale de l’utilisateur, par exemple 'ru-RU' ou 'en-US'."
},
occasion: {
type: "string",
description:
"Occasion pour le cadeau : 'birthday', 'anniversary', 'new_year', etc."
}
},
required: ["segments", "budget", "locale", "occasion"]
}
};
Quelques nuances ici.
Nous lions explicitement le comportement de l’outil au system‑prompt : la phrase « suis les instructions du system‑prompt » rappellera au modèle que « résultat vide = conversation honnête, pas créativité ».
Nous posons des conditions négatives (« n’utilise pas… », « n’invente pas… »), ce qui, dans la pratique, est tout aussi important que les descriptions positives.
Nous rendons inputSchema sémantiquement riche : des descriptions et contraintes correctes aident le modèle à faire correspondre correctement les requêtes aux champs et à « se tromper » moins — avant même l’appel de l’outil.
Patterns de follow‑up dans le code du widget et le format de réponse
Au‑delà des instructions textuelles, nous avons un autre levier — le format de réponse de l’outil lui‑même. Par son intermédiaire, nous pouvons également indiquer au modèle ce qui s’est passé, et réduire son champ de fantaisie.
Formellement, les follow‑ups sont définis par du texte dans le system‑prompt, mais dans votre widget Next.js vous pouvez en plus normaliser le ToolOutput pour faciliter la vie du modèle et réduire la marge d’invention.
Par exemple, convenons que le back‑end pour recommend_gifts renvoie toujours :
// type de réponse de l’outil côté back-end
export type RecommendGiftsResult = {
items: Array<{
id: string;
title: string;
price: number;
currency: "USD" | "EUR" | "RUB";
tags: ("digital" | "physical" | "education" | "fitness" | "tech")[];
}>;
// champ que le back-end remplit pour indiquer explicitement au modèle ce qui s’est passé
status: "ok" | "empty" | "error";
errorMessage?: string;
};
Le widget peut afficher cela joliment, et le modèle — s’appuyer sur status lors de la génération de la réponse. Dans Apps SDK, vous retournez souvent le ToolOutput au modèle sous forme d’objet JSON, et il voit ce champ.
Dans le system‑prompt, on peut ajouter un petit bloc :
# Interprétation du statut de l’outil
- Si status = "empty" : voir la section « Dialogue après utilisation des outils » et
n’invente pas de cadeaux.
- Si status = "error" : annonce l’erreur technique et n’essaie pas de deviner
le contenu du catalogue.
Oui, le modèle aurait pu le déduire, mais une instruction explicite réduit la probabilité de « devinettes ».
7. Pratique : améliorer votre App
Pour éviter l’impression « joli sur les slides, moins dans la vraie vie », décrivons un exercice concret que vous pouvez faire sur votre App actuelle (dans notre cas — GiftGenius). Faisons un petit refactoring selon les trois couches de protection : system‑prompt, descriptions des outils et traitement des résultats.
Premièrement, au niveau du system‑prompt, prenez votre system‑prompt de la première leçon et trouvez la partie où sont décrites la zone de responsabilité et le travail avec les outils. Ajoutez‑y :
- l’interdiction d’inventer des entités en dehors du catalogue/de la base (cadeaux, SKU, prix) ;
- les règles en cas de résultat vide et d’erreur d’outil ;
- une section « Dialogue après utilisation des outils » avec 2–3 scénarios (vide, trop, erreur).
Deuxièmement, au niveau des descriptions des tools, ouvrez la description de l’outil clé (chez vous, cela peut être recommend_gifts, search_gifts, search_tariffs, calculate_quote, peu importe). Réécrivez la description pour qu’elle :
- indique explicitement que l’outil travaille uniquement avec votre source de données (catalogue de cadeaux, forfaits, etc.) ;
- explique quand il est nécessaire, et quand il ne l’est pas ;
- contienne des contraintes négatives explicites : « n’invente pas… », « n’utilise pas pour… ».
Troisièmement, au niveau de la structure de la réponse de l’outil, si la réponse de l’outil n’a pas encore de champ décrivant explicitement le statut (status, resultType, hasMore) — ajoutez‑le au type côté back‑end et au ToolOutput. Ensuite, dans le system‑prompt, indiquez comment le modèle doit interpréter ce statut dans le dialogue.
Enfin, testez quelques requêtes en mode Dev, y compris celles où les résultats sont délibérément vides ou limites. Observez si le modèle a cessé d’inventer des entités et à quel point il explique honnêtement les limitations à l’utilisateur.
Dans la leçon suivante, vous formaliserez ces requêtes dans ce qu’on appelle un golden prompt set et en ferez un artefact de test reproductible ; pour l’instant, l’important est que vous ressentiez manuellement la différence.
8. Erreurs typiques lors de la lutte contre les hallucinations via les prompts et les outils
Erreur n°1 : une seule phrase magique « n’hallucine pas » dans le system‑prompt.
Le développeur écrit à la fin du prompt : « N’invente pas d’informations » — et considère la tâche réglée. En pratique, le modèle continue d’inventer, car les descriptions des outils et les follow‑ups ne lui définissent pas de comportement alternatif. Sans règles concrètes « quoi faire à la place d’inventer » (reconnaître le résultat vide, proposer de modifier les filtres, signaler l’erreur), une telle phrase aide très peu.
Erreur n°2 : contradiction entre le system‑prompt et la description de l’outil.
Dans le system‑prompt, vous dites : « N’invente pas de cadeaux qui n’existent pas dans le catalogue », et dans la description de l’outil — « Sélectionne des cadeaux appropriés, et si rien n’est trouvé — peut proposer des options similaires à sa discrétion ». Le modèle hésite alors entre deux sources de vérité, et, malheureusement, la plus concrète l’emporte (généralement la description de l’outil). Il faut que les deux couches disent la même chose : si des options similaires sont autorisées, il faut aussi le formaliser (et expliquer impérativement à l’utilisateur que ce n’est pas une correspondance exacte).
Erreur n°3 : descriptions d’outils trop vagues.
Une description du type « Un outil qui aide l’utilisateur à résoudre des tâches » ne donne presque aucune information au modèle sur les limites de l’outil. Dans ce cas, soit il ne l’utilisera pas du tout, soit il l’appellera à tout bout de champ — puis « complètera » les données quand l’outil aura renvoyé peu ou rien. Une bonne description doit être discriminante : dire explicitement ce que fait l’outil et quand ne pas l’appeler.
Erreur n°4 : absence de stratégie pour les résultats vides et erronés.
Le développeur écrit avec soin un back‑end qui renvoie { items: [], status: "empty" }, mais n’explique nulle part au modèle ce que cela signifie. En conséquence, le modèle voit un tableau vide et décide : « bon, je vais proposer quelque chose de mes connaissances générales ». Il manque dans le system‑prompt une section expliquant comment interpréter ces statuts et quoi dire à l’utilisateur. Pourtant, l’ajout de quelques règles claires pour les résultats vides/erronés apporte un énorme gain de qualité.
Erreur n°5 : tenter de « soigner » les hallucinations uniquement au niveau du code du widget.
Parfois, on veut tout déporter sur le front‑end : « si la liste est vide — j’afficherai un placeholder et je n’autoriserai pas l’utilisateur à voir la réponse textuelle du modèle ». Cela peut légèrement atténuer l’UX, mais le modèle lui‑même continue à croire aux entités inventées et à se comporter de même dans les répliques suivantes. La bonne approche — d’abord modifier les instructions (system‑prompt, descriptions d’outils, follow‑ups), puis compléter avec des protections dans l’UI.
Erreur n°6 : ignorer l’influence des métadonnées et des schémas sur le comportement du modèle.
Certains développeurs perçoivent les JSON Schema et descriptions de champs comme « uniquement pour les formulaires et la validation ». En réalité, pour ChatGPT, c’est une partie importante du prompt : à partir de ces descriptions, le modèle comprend quels paramètres extraire de la requête et à quoi ressemble une réponse correcte. Des descriptions de champs faibles ou incohérentes (description, enum) augmentent le risque d’erreurs et provoquent indirectement des hallucinations.
Erreur n°7 : interdictions trop strictes sans alternatives.
Il arrive que le prompt devienne une liste continue de « ne fais pas ceci, ne fais pas cela », sans qu’il soit dit quoi faire dans les cas difficiles. Par exemple, nous avons interdit d’inventer des cadeaux et nous nous sommes limités au catalogue, mais nous n’avons rien dit sur les questions théoriques. Le modèle répond alors parfois « je ne sais pas », alors qu’il pourrait utilement expliquer les principes généraux de choix de cadeaux. Essayez toujours non seulement d’interdire, mais aussi d’ouvrir des voies autorisées : « si tu ne peux pas trouver de cadeau dans le catalogue — dis‑le honnêtement et propose comment modifier la requête » ou « si la requête est théorique — réponds toi‑même, sans outils ».
GO TO FULL VERSION