Aller au contenu
Chandler Nguyen
IA11 min de lecture

Comment j'écris des prompts système qui sont vraiment respectés (après que mes animateurs IA ont dit « Trời ơi » 13 fois)

J'ai passé des jours à concevoir un prompt système pour mes animateurs de podcast IA. Ils ont lu chaque mot et ignoré presque tout. La solution n'était pas de meilleurs mots — c'était de comprendre que les LLM traitent les règles différemment selon l'endroit où elles se trouvent dans ton pipeline.

J'ai découvert quelque chose d'embarrassant la semaine dernière.

Mon pipeline de podcast IA a un prompt système élaboré — un « guide de dialogue » — que j'ai passé des jours à concevoir. Il explique aux animateurs (deux commentateurs de football générés par IA) exactement comment se parler. Ne répète pas la même ouverture. Varie la structure entre les segments. Utilise les tics de langage avec parcimonie. N'invente jamais de statistiques.

Le LLM a lu chaque mot. Puis a ignoré presque tout.

L'animateur vietnamien a dit « Trời ơi » (Oh mon dieu) 13 fois dans un seul épisode. L'animateur japonais a ouvert chaque segment par « いやー、健一さん! » (Eh bien, Kenichi-san !). L'animateur anglais a cité avec une autorité totale un xG de 0,35 et 65 % de possession — des chiffres qui n'existent nulle part dans ma base de données.

Le prompt a été poliment reconnu. Puis ignoré.

C'est la chose la plus utile que j'ai apprise sur le prompt engineering : les LLM ont deux modes de lecture, et ils les traitent de manière complètement différente.


Le problème des deux modes de lecture

Voici à quoi ressemblait mon guide de dialogue original. J'ai traduit la version vietnamienne en français puisque la structure est identique :

GUIDE DE STYLE :

PATRON D'INTERACTION DES ANIMATEURS : Animateur Émotionnel + Animateur Analytique.

RÈGLES STRICTES POUR CHAQUE SEGMENT :

0. NE JAMAIS RÉPÉTER LA MÊME OUVERTURE. Varie quel animateur initie, 
   à quel moment du match ils réagissent en premier.

1. DÉMARRAGE ÉMOTIONNEL (obligatoire) : Chaque segment commence par 
   la réaction pure de l'animateur émotionnel.

2. RÈGLE D'ANCRAGE AUX DONNÉES (obligatoire) : Chaque affirmation doit citer 
   des chiffres spécifiques. Pas « ils ont dominé » mais « 61 % de possession, 
   3,17 xG contre 0,65. »

3. TICS DE LANGAGE :
   Animateur émotionnel : « T'as vu ça ?! » « Attends attends attends... »
   Animateur analytique : « Il y a une raison plus profonde... » « Laisse-moi t'expliquer. »

Ça a l'air complet, non ? J'ai sincèrement cru avoir fait le boulot.

Mais quand j'ai lu les scripts générés, voici ce qui s'est passé :

RègleCe que disait le guideCe que le LLM a fait
Règle 0 (ne jamais répéter)Varie l'ouvertureChaque segment ouvrait avec le tic de l'animateur émotionnel
Règle 2 (données)Cite des chiffres spécifiquesCitait des chiffres inventés avec une autorité totale
Règle 3 (tics)Liste de phrases signaturesUtilisait chaque phrase de 5 à 13 fois par épisode

Les règles étaient là. Le LLM les a lues. Mais elles avaient la force d'une suggestion, pas d'une exigence.

C'est là que j'ai compris : **le LLM traitait mes règles comme du « contexte de style » — pas comme des « contraintes applicables ». **


Le pattern : Contexte vs. Commande

Réfléchis à la façon dont tu lis différents types de texte. Si je te tends un menu de restaurant, tu le lis comme une information sur laquelle agir. Si je te tends un article Wikipédia sur l'histoire des pâtes, tu le lis comme une connaissance de fond.

Les LLM font quelque chose de similaire avec les prompts. Quand les règles apparaissent dans une section intitulée « GUIDE DE STYLE » ou « PATRON D'INTERACTION DES ANIMATEURS » ou « RÈGLES POUR LES SEGMENTS », le modèle les traite comme de l'information ambiante — du contexte qui colore la réponse mais ne la contraint pas.

Quand les règles apparaissent dans une section intitulée « AMÉLIORATIONS CRITIQUES REQUISES » ou « LIMITES SPÉCIFIQUES DE PHRASES », le modèle les traite comme des commandes applicables — des choses qu'il doit vérifier et corriger.

L'idée clé n'est pas dans les mots « critique » ou « requis ». Elle est dans l'endroit où la règle vit dans le pipeline.


La solution : faire en sorte que l'étape de révision applique ce que l'étape de génération ignore

Mon pipeline a deux appels au LLM :

  1. Étape de génération — écrit le script segment par segment, en utilisant le guide de dialogue comme contexte
  2. Étape de révision — lit le script complet assemblé et l'améliore

L'étape de génération, c'était là que toutes mes règles vivaient. L'étape de révision avait des règles anti-répétition génériques (« ne dis pas "C'est fascinant" deux fois ») qui étaient entièrement centrées sur l'anglais et ne connaissaient rien des tics de langage de mes animateurs ni des exigences structurelles.

La solution était structurelle, pas lexicale. J'ai déplacé l'application des règles vers l'étape de révision, où le script complet est visible d'un coup.

Voici ce qui a changé :

Avant : Règles dans le prompt de génération (ignorées)

GUIDE DE STYLE :
0. NE JAMAIS RÉPÉTER LA MÊME OUVERTURE...
1. DÉMARRAGE ÉMOTIONNEL...
2. ANCRAGE AUX DONNÉES...
3. TICS DE LANGAGE : « T'as vu ça ?! » ...

Après : Règles extraites et injectées dans le prompt de révision (appliquées)

AMÉLIORATIONS CRITIQUES REQUISES SUR L'ENSEMBLE DU SCRIPT :

2a. APPLIQUE LES RÈGLES STRUCTURELLES DU GUIDE DE STYLE :
     Le GUIDE DE STYLE ci-dessus contient des exigences spécifiques 
     concernant la structure des segments. Ce sont des EXIGENCES, pas des suggestions.
     - Si le guide dit « X segments sur Y doivent varier », CHANGE 
       les segments qui violent cette règle.
     - Vérifie comment CHAQUE segment ouvre. Si 4+ segments sur 5 ouvrent 
       de manière identique, VARIE au moins 2.

3b. LIMITES DE PHRASES PAR LOCALE (extraites des profils d'animateurs — 
     MAXIMUM 2 FOIS chacune sur l'ensemble du script) :
     - "Trời ơi" / "Trời má" — max 4x combinés
     - "Có một lý do sâu hơn" — max 2x
     - "Mày thấy chưa?!" — max 2x
     ...

Deux choses se sont passées ici :

  1. Les règles structurelles ont été promues de contexte à commande — reformulées comme « APPLIQUE ceci » et « CHANGE les segments qui violent ceci » plutôt que « voici le pattern préféré »

  2. Les tics de langage ont été extraits des profils d'animateurs et présentés comme des limites explicites, pas comme une liste de suggestions. « Tics de langage : en voici quelques-uns » est devenu « Ces phrases : MAXIMUM 2 FOIS chacune. »

Les deux changements vivent dans le prompt de révision, pas dans celui de génération. C'est la décision architecturale clé. L'étape de génération reçoit le guide de style comme contexte — elle a besoin de la saveur de la personnalité. L'étape de révision reçoit les règles comme obligation — elle a besoin du mandat de conformité.


Le détail de l'extraction (pourquoi ça marche dans 7 langues)

Je ne voulais pas maintenir des listes de phrases par langue à la main. Chaque fois que je modifierais un profil d'animateur, je devrais aussi mettre à jour le prompt de révision. C'est le genre de dérive qui tue le produit d'un développeur solo.

À la place, j'ai écrit une petite fonction d'extraction qui analyse les profils d'animateurs au moment de la révision. Le profil de chaque locale a une section de tics de langage avec une étiquette standard :

  • Vietnamien : Câu cửa miệng:
  • Japonais : 口癖:
  • Espagnol : Latiguillos:
  • Français : Phrases fétiches:
  • Coréen : 입버릇:
  • Chinois : 口头禅:

La fonction trouve cette étiquette, extrait toute chaîne entre guillemets qui suit et les formate comme des limites applicables :

@staticmethod
def _extract_catchphrase_limits(host_profiles: str) -> str:
    patterns = [
        (r'Câu cửa miệng:?', r'"([^"]+)"'),       # Vietnamese
        (r'口癖:?',             r'「([^」]+)」'),    # Japanese
        (r'Latiguillos:?',      r'"([^"]+)"'),       # Spanish
        # ... etc for all 7 languages
    ]
    limits = []
    for marker_pattern, quote_pattern in patterns:
        match = re.search(marker_pattern, host_profiles)
        if match:
            phrases = re.findall(quote_pattern, host_profiles[match.end():])
            for phrase in phrases:
                limits.append(f'   - "{phrase}" — max 2x')
    return "\n".join(limits)

Maintenant, quand j'édite un profil d'animateur et que je change ses tics de langage, le prompt de révision se met à jour automatiquement. Pas de dérive. Pas de langue oubliée.


Ce que ça a vraiment corrigé (avec des chiffres)

Voici ce qui a changé entre l'épisode généré avant ces correctifs et un autre généré après :

MétriqueAvantAprès
« Trời ơi » (quota : max 4)13Pas encore testé en vi, mais en ja : les 4 tics dans les quotas
« Có một lý do sâu hơn » (quota : max 2)5Dans le quota lors du test équivalent en ja
Statistiques inventées (xG, possession %, largeur de formation)4 chiffres inventésZéro chiffre inventé
Ouvertures de segment avec pattern identique6/6 identiquesToujours 5/5 en ja — voir ci-dessous
Connexions Vietnam/Japon préservées1/4 a survécu (la révision en a supprimé 3)11/11 ont survécu
Contamination croisée des voix d'animateursLes deux utilisaient les phrases de l'autreSéparation nette

Les quotas de tics, les règles anti-fabrication et la préservation de la localisation ont tous fonctionné. Les profils d'animateurs dictent désormais les personnalités avec suffisamment de clarté pour que le LLM ne confonde plus qui dit quoi.


Ce qui ne marche toujours pas

Une règle échoue encore : la variété structurelle. Le guide de dialogue dit « au moins 2 segments sur 5 doivent utiliser des entrées structurelles différentes. » Après les correctifs, l'épisode japonais ouvrait toujours chaque segment de manière identique : « いやー、健一さん! » — cinq fois dans un épisode.

Ce n'est pas un problème d'application du prompt. C'est un problème d'architecture.

L'étape de génération produit chaque segment indépendamment. Le segment 3 n'a aucune conscience que les segments 1 et 2 ont ouvert avec le tic de l'animateur émotionnel. Chaque segment est généré de manière isolée avec le même guide de dialogue, donc chacun arrive à la même décision « ouvrons avec l'animateur émotionnel. »

L'étape de révision devrait détecter ça — elle voit le script complet d'un coup. Et j'ai ajouté un langage d'application explicite lui demandant de varier les ouvertures. Mais Gemini 3.5 Flash, le modèle qui fait la révision, trouve apparemment plus difficile de restructurer des ouvertures de segments que de réduire des tics ou d'adoucir des affirmations inventées. La variété d'ouverture nécessite de régénérer des parties du contenu, pas seulement de resserrer ou supprimer. C'est un travail plus lourd pour le modèle.

Je travaille encore là-dessus — probablement un modèle de révision plus performant ou une restructuration du pipeline où les segments partagent l'état d'ouverture. Le point plus large : l'application par prompt ne peut pas résoudre les problèmes d'architecture. Si la structure de ton pipeline rend une règle impossible à suivre, aucune quantité de « CRITIQUE » ou « OBLIGATOIRE » dans tes prompts ne te sauvera.


Le framework : Trois questions pour chaque règle

Après cette expérience, j'audite maintenant chaque prompt système que j'écris avec trois questions :

1. Où vit cette règle — génération ou révision ? Les règles sur la personnalité et le ton appartiennent à la génération. Les règles sur la conformité, la cohérence et la structure appartiennent à la révision. Si une règle traverse les deux (comme les tics de langage — c'est de la personnalité mais ça a besoin de limites), mets-la dans les deux.

2. Cette règle est-elle formulée comme contexte ou comme commande ? « Voici les tics de langage » est du contexte. « Maximum 2 fois chacun » est une commande. Les deux sont nécessaires pour la même règle — l'étape de génération a besoin de savoir CE QUE SONT les tics, et l'étape de révision a besoin d'appliquer À QUELLE FRÉQUENCE ils apparaissent.

3. Le pipeline peut-il vraiment suivre cette règle ? La génération par segment ne peut pas appliquer la variété entre segments. Aucun prompt ne résoudra ça. Tu dois soit déplacer la règle vers l'étape de révision (qui voit le script complet), soit restructurer le pipeline pour que les segments partagent leur état.

Ce framework n'est pas spécifique à la génération de podcasts. Il s'applique à tout pipeline LLM multi-étapes — génération de contenu, revue de code, résumé de documents, tout ce qui a des passages séparés de génération et de contrôle qualité.


Je continue d'itérer là-dessus, mais si tu construis ton premier pipeline LLM multi-étapes : divise-le en au moins deux étapes. La première étape génère avec personnalité et liberté — donne-lui du contexte, des exemples, des indications de ton. La deuxième étape révise avec précision et application — donne-lui des limites explicites, des vérifications de conformité et l'autorité de changer les choses.

L'étape de génération devrait ressembler à un briefing avec un collaborateur créatif. L'étape de révision devrait ressembler à une checklist remise à un éditeur.

Et quand tes règles sont encore ignorées après avoir fait les deux, pose-toi d'abord la question de l'architecture. Est-ce le prompt, ou est-ce le pipeline ?


Questions fréquentes

Est-ce que ça s'applique à une configuration à un seul prompt (sans pipeline) ?

Oui, mais de manière limitée. Dans un seul prompt, tu demandes au même modèle d'être créatif et rigoureux simultanément — il favorisera toujours la créativité sur la rigueur quand les deux entrent en conflit. Même dans un seul prompt, séparer tes instructions en une section « contexte » et une section explicite « contraintes » aide. Mais tu vas heurter un plafond. Deux appels séparés avec des instructions différentes surpassent presque toujours un seul.

Pourquoi n'as-tu pas simplement mis les règles dans le prompt de génération de façon plus forte ?

J'ai essayé. J'ai ajouté « OBLIGATOIRE », « CRITIQUE », « JAMAIS » et tout en majuscules. L'étape de génération a trop d'exigences concurrentes — elle essaie d'être engageante, naturelle, variée, localement authentique et conforme, tout à la fois. Quand le modèle doit choisir entre « sonner comme un supporter vietnamien excité » et « ne dire 'Trời ơi' que deux fois », la personnalité gagne toujours. L'étape de révision n'a pas ce conflit — son seul boulot est d'appliquer.

Quel modèle utilises-tu pour l'étape de révision ?

Gemini 3.5 Flash (pour le coût — la révision tourne sur chaque épisode dans 7 langues). Il gère bien l'application simple (réduire les tics, adoucir les stats inventées). Il peine sur les tâches qui nécessitent de la régénération de contenu, comme restructurer les ouvertures de segments. Un modèle plus fort aiderait, mais je n'ai pas encore justifié le coût.


Je construis DIALOGUE, une plateforme de podcast IA, seul le soir et le week-end. Le code pour l'extraction des tics de langage et les changements du prompt de révision est ouvert — tu peux lire le prompt système complet et la logique d'application dans mon dépôt podcast-engine. J'écris sur ce que j'apprends en chemin.

Si tu as rencontré un problème similaire — des règles qui sont lues mais pas appliquées — je serais curieux de savoir : quelle était la règle, et où vivait-elle dans ton pipeline ?

C'est tout pour moi pour l'instant.

À bientôt, Chandler