Python & AI Tutorials Logo
LangChain & LangGraph

4. Concevoir des prompts réutilisables avec des templates

Dans le Chapitre 3, nous avons construit une interface CLI de chat en streaming fonctionnelle où les prompts étaient intégrés directement dans notre code Python. Cela fonctionne pour des prototypes rapides, mais à mesure que vos applications IA se développent, les prompts codés en dur deviennent un cauchemar de maintenance. Imaginez devoir mettre à jour la même logique de prompt dans plusieurs fichiers, ou essayer de tester A/B différentes variations de prompts sans redéployer le code.

Ce chapitre vous apprend à concevoir des prompts réutilisables et maintenables en utilisant le système de templates de LangChain. Vous apprendrez à séparer la logique des prompts du code applicatif, à exploiter la messagerie basée sur les rôles pour un meilleur contrôle des LLM, à externaliser les prompts dans des fichiers YAML pour la collaboration en équipe, et à valider les templates avant l'exécution pour détecter les erreurs tôt.

Ce que ce chapitre couvre (et ce qu'il ne couvre pas) :

Dans ce chapitre, nous travaillerons avec les templates et les prompts manuellement—vous rendrez explicitement les templates en messages, puis enverrez ces messages au LLM en utilisant llm.invoke(). Cette approche pratique vous aide à comprendre exactement ce que font les templates et comment ils fonctionnent.

Dans le Chapitre 6, vous apprendrez LCEL (LangChain Expression Language), qui vous permet de composer des templates et des LLM en pipelines en utilisant l'opérateur |. Pour l'instant, nous nous concentrons sur les fondamentaux des templates sans cette couche d'orchestration.

À la fin de ce chapitre, vous disposerez d'un système de gestion de prompts robuste qui évolue des chatbots simples aux workflows multi-agents complexes.

4.1) Séparation des préoccupations : Découpler le code des prompts

Pourquoi séparer les prompts du code ?

Lorsque vous codez en dur les prompts directement dans votre logique applicative, vous créez un couplage fort qui conduit à plusieurs problèmes :

Charge de maintenance : Modifier un prompt nécessite de modifier le code Python, d'exécuter des tests et de redéployer. Les changements de prompts se produisent généralement beaucoup plus fréquemment que les changements de code, rendant ce cycle modification-test-redéploiement très inefficace pour ce qui devrait être de simples éditions de texte.

Défis de contrôle de version : Lorsque le code et les prompts sont mélangés, le contrôle de version devient difficile. Les conflits de fusion sont plus probables, et chaque conflit nécessite une résolution manuelle et du refactoring.

Friction de collaboration : Les membres non techniques de l'équipe (chefs de produit, experts métier) ne peuvent pas éditer directement les prompts qui se trouvent dans des fichiers .py et doivent s'appuyer sur l'assistance des développeurs. Cette dépendance ralentit considérablement les cycles d'amélioration des prompts.

Complexité des tests : Tester différentes variations de prompts signifie copier du code, modifier des chaînes et gérer plusieurs branches—rendant les expérimentations lentes et sujettes aux erreurs.

Pensez aux prompts comme aux requêtes SQL dans les applications traditionnelles. Vous ne coderiez pas en dur des chaînes SQL dans tout votre code Python—vous utiliseriez un ORM ou au moins centraliseriez les requêtes. Les prompts méritent la même discipline architecturale.

Le système de templates de LangChain

LangChain fournit les classes PromptTemplate et ChatPromptTemplate pour séparer la structure fixe de votre prompt des données changeantes. Écrivez votre prompt une fois avec des {placeholders}, puis insérez différentes valeurs à chaque fois—plus besoin de reconstruire les prompts avec des f-strings ou de la concaténation.

Syntaxe et utilisation des templates

Syntaxe des placeholders

Les templates utilisent {nom_variable} comme placeholders. À l'exécution, vous fournissez un dictionnaire avec des clés correspondantes :

python
from langchain_core.prompts import PromptTemplate
 
# Définir le template avec des placeholders
template = PromptTemplate.from_template(
    "Traduire {content} de {source_lang} vers {target_lang}"
)
 
# Remplir les placeholders avec un dictionnaire
result = template.invoke({
    "content": "Hello world",
    "source_lang": "Anglais", 
    "target_lang": "Coréen"
})
 
print(result.text)

Sortie :

Traduire Hello world de Anglais vers Coréen

Règles clés :

  • Les noms de placeholders doivent correspondre exactement aux clés du dictionnaire
  • Tous les placeholders doivent être fournis (les clés manquantes lèvent une KeyError)
  • Les clés supplémentaires du dictionnaire sont ignorées
  • Utilisez invoke() pour rendre le template avec vos valeurs

PromptTemplate vs ChatPromptTemplate

PromptTemplate : Retourne une chaîne simple (encapsulée dans StringPromptValue)

  • Pour la complétion de texte simple ou les modèles legacy
  • Sortie : Chaîne unique comme "Résumer : {content}"

ChatPromptTemplate : Retourne des messages structurés avec des rôles (encapsulés dans ChatPromptValue)

  • Pour les modèles de chat modernes (GPT-4, Claude, Gemini)
  • Sortie : Messages séparés par rôle (system/user/assistant)
  • Choix préféré : Plus clair et plus maintenable pour maintenir les instructions système séparées de l'entrée utilisateur

Quand utiliser lequel ?

  • Par défaut, utilisez ChatPromptTemplate pour les modèles de chat—c'est plus clair et plus maintenable
  • Utilisez PromptTemplate uniquement pour les complétions simples ou lorsque la séparation des rôles n'est pas nécessaire
python
# PromptTemplate - sortie chaîne unique
from langchain_core.prompts import PromptTemplate
 
template1 = PromptTemplate.from_template("Résumer : {content}")
result1 = template1.invoke({"content": "LangChain est un framework..."})  
print(result1)

Sortie :

text='Résumer : LangChain est un framework...'
python
# ChatPromptTemplate - messages basés sur les rôles
from langchain_core.prompts import ChatPromptTemplate
 
template2 = ChatPromptTemplate.from_messages([
    ("system", "Vous êtes un assistant utile"),
    ("user", "{question}")
])
result2 = template2.invoke({"question": "Qu'est-ce que LangChain ?"})
print(result2)

Sortie :

messages=[SystemMessage(content='Vous êtes un assistant utile'), HumanMessage(content='Qu'est-ce que LangChain ?')]

Le template est défini une fois. Vous pouvez le réutiliser avec différentes valeurs sans modifier la définition du template. PromptTemplate.invoke() et ChatPromptTemplate.invoke() retournent tous deux des valeurs de prompt prêtes à être envoyées directement à un LLM.

Du formatage de chaînes aux templates

Refactorisons un prompt codé en dur pour utiliser des templates. Voici la version "avant" du Chapitre 3 :

python
# Approche codée en dur (style Chapitre 3)
from langchain_openai import ChatOpenAI
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Expliquer l'informatique quantique"
# Logique de prompt mélangée avec le code
prompt = f"Vous êtes un assistant utile. Répondez à cette question : {user_input}"
 
response = llm.invoke(prompt)
print(response.content)

Maintenant avec des templates—en utilisant l'approche étape par étape que nous pratiquerons tout au long de ce chapitre :

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Template défini séparément
template = ChatPromptTemplate.from_messages([
    ("system", "Vous êtes un assistant utile."),
    ("user", "{user_input}")
])
 
# Logique applicative - exécution étape par étape
llm = ChatOpenAI(model="gpt-4o-mini")
 
user_input = "Expliquer l'informatique quantique"
 
# Étape 1 : Rendre le template en messages
messages = template.invoke({"user_input": user_input})
 
# Étape 2 : Envoyer les messages au LLM
response = llm.invoke(messages)
print(response.content)

Qu'est-ce qui a changé ?

  1. Définition du template : La structure du prompt est définie une fois dans template, séparée de la logique d'exécution.
  2. Syntaxe des placeholders : {user_input} est un placeholder qui est rempli à l'exécution.
  3. Exécution étape par étape : Nous rendons explicitement le template (template.invoke()), puis envoyons le résultat au LLM (llm.invoke()). Ce processus en deux étapes vous aide à comprendre ce que font réellement les templates.
  4. Réutilisabilité : Le même template peut être utilisé pour n'importe quelle question utilisateur sans modification.
  5. Structure des messages : template.invoke() retourne un ChatPromptValue correctement formaté que le LLM attend.

Pourquoi l'approche étape par étape ?

Tout au long de ce chapitre, vous verrez ce pattern répété :

python
messages = template.invoke(inputs)  # Étape 1 : Rendre le template
response = llm.invoke(messages)     # Étape 2 : Envoyer au LLM

Nous utilisons intentionnellement cette approche en deux étapes pour l'apprentissage—elle montre exactement ce que font les templates : transformer les données d'entrée en messages structurés. Dans le Chapitre 6, vous apprendrez le pattern de production réel : combiner ces étapes avec les pipelines LCEL (template | llm). Mais comprendre chaque étape séparément d'abord construit une base solide.

Validation des templates

Les templates détectent les erreurs tôt. Si vous référencez un placeholder qui n'existe pas, LangChain lève une erreur avant de faire un appel API :

python
template = PromptTemplate.from_template("Résumer : {text}")
 
# Cela échouera - clé 'text' manquante
try:
    template.invoke({"content": "Du texte"})  # Mauvais nom de clé
except KeyError as e:
    print(f"Erreur de template : {e}")

Sortie :

Erreur de template : "Input to PromptTemplate is missing variables {'text'}.  Expected: ['text'] Received: ['content']

Cette validation se produit au moment du rendu du template, pas pendant l'exécution du LLM—vous économisant à la fois du temps et des coûts d'API.

4.2) Templates de prompts sensibles aux rôles (System, User, Assistant)

Comprendre les rôles des messages

Les LLM modernes (GPT-4, GPT-5, Claude, Gemini) comprennent la structure conversationnelle à travers les rôles des messages. Chaque message a un rôle spécifique qui indique au modèle comment l'interpréter.

Les trois rôles principaux :

System : Définit comment l'IA doit se comporter

  • Objectif : Définit la personnalité, l'expertise et les règles opérationnelles de l'IA
  • Exemple : "Vous êtes un expert Python qui écrit des exemples de code concis"
  • Quand il s'applique : Défini une fois au début, influence toutes les réponses
  • Pensez-y comme : Le manuel d'instructions de l'IA

User : Représente l'entrée humaine

  • Objectif : Pose des questions ou fait des demandes
  • Exemple : "Comment lire un fichier en Python ?"
  • Quand il s'applique : Chaque fois qu'un humain envoie un message
  • Pensez-y comme : Les questions que vous posez

Assistant : Représente les réponses précédentes de l'IA

  • Objectif : Fournit l'historique de conversation
  • Exemple : "Vous pouvez utiliser la fonction open() pour lire des fichiers"
  • Quand il s'applique : Lorsque vous avez besoin de conversations multi-tours
  • Pensez-y comme : La mémoire de l'IA des réponses précédentes

Messages système : Le mécanisme de contrôle

Le message système indique à l'IA qui elle est et comment elle doit fonctionner—avant toute interaction utilisateur.

Ce que vous pouvez contrôler :

  1. Expertise : "Vous êtes un développeur Python senior"
  2. Format de sortie : "Répondez toujours au format JSON"
  3. Règles comportementales : "Si incertain, dites 'Je ne sais pas'"
  4. Style de réponse : "Soyez concis et technique"

Pourquoi c'est important :

Sans message système → réponses génériques et verbeuses

Avec message système → comportement cohérent et adapté

Messages système en action

Voyons l'impact réel des messages système en comparant la même question avec et sans message système. Faites attention à la façon dont la réponse change radicalement—non seulement en longueur, mais en ton, complexité et approche pédagogique.

Sans message système :

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
template = ChatPromptTemplate.from_messages([
    ("user", "Qu'est-ce que Python ?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Sortie :

Python est un langage de programmation de haut niveau, interprété, connu pour sa lisibilité et sa simplicité.
Il a été créé par Guido van Rossum et publié pour la première fois en 1991.
Python met l'accent sur la lisibilité du code, permettant aux programmeurs d'exprimer des concepts en moins de lignes de code par rapport à des langages comme C++ ou Java.
 
Les caractéristiques clés de Python incluent :
...

Avec message système :

python
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Contrôler la persona et le style de sortie avec le message System
template = ChatPromptTemplate.from_messages([
    ("system", """Vous êtes un instructeur Python senior avec 15 ans d'expérience d'enseignement.
Vos étudiants sont des débutants complets qui n'ont jamais programmé auparavant.
 
Style d'enseignement :
- Utilisez des analogies simples et quotidiennes
- Évitez le jargon technique
- Montrez des exemples pratiques de la vie quotidienne
- Soyez encourageant et patient"""),
    ("user", "Qu'est-ce que Python ?")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({})
response = llm.invoke(messages)
print(response.content)

Sortie :

Excellente question ! Pensez à Python comme à un outil vraiment utile dans votre boîte à outils.
Tout comme un marteau ou un tournevis vous aide à construire ou réparer des choses dans la maison, Python vous aide à créer des logiciels ou automatiser des tâches sur un ordinateur.
 
Imaginez que vous vouliez faire un gâteau.
Vous avez besoin d'une recette à suivre, n'est-ce pas ? Dans cette analogie, Python est comme cette recette.
Il indique à l'ordinateur quelles étapes suivre pour atteindre un objectif, que ce soit faire des calculs, organiser des fichiers, ou même exécuter un jeu.
...

La différence :

Sans message système :

  • L'IA utilise son comportement par défaut : poli, informatif, mais générique
  • Les réponses sont encyclopédiques et formelles—optimisées pour un large public
  • Pas de persona cohérente : chaque réponse peut varier en ton et en style
  • Pas de contraintes : l'IA décide elle-même du niveau de détail ou de technicité

Avec message système :

  • L'IA suit vos instructions spécifiques : persona, style et règles que vous avez définis
  • Les réponses sont cohérentes et prévisibles—chaque réponse correspond à vos exigences
  • Persona claire maintenue : agit selon le rôle que vous avez assigné (enseignant, expert, assistant)
  • Contraintes explicites appliquées : format de sortie, niveau de langage et limites comportementales que vous avez définis

Point clé : Sans message système, vous obtenez le mode par défaut de l'IA. Avec un message système, vous obtenez votre IA—adaptée aux besoins de votre application. Le message système transforme l'IA d'un outil généraliste en un assistant spécialisé qui se comporte exactement comme vous le souhaitez, à chaque fois.

Rôles User et Assistant : Construire des conversations

Question unique (User uniquement) :

python
template = ChatPromptTemplate.from_messages([
    ("system", "Vous êtes un expert Python."),
    ("user", "{question}")
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
messages = template.invoke({"question": "Comment lire un CSV ?"})
response = llm.invoke(messages)

Fonctionne bien pour des questions indépendantes.

Multi-tours avec contexte (User + Assistant) :

Sans historique :

python
template = ChatPromptTemplate.from_messages([
    ("system", "Vous êtes un expert Python."),
    ("user", "Comment ça fonctionne ?")  # "ça" = ???
])

L'IA ne sait pas à quoi "ça" fait référence.

Avec historique :

python
template = ChatPromptTemplate.from_messages([
    ("system", "Vous êtes un expert Python."),
    ("user", "Qu'est-ce que la bibliothèque pandas ?"),
    ("assistant", "Pandas est une bibliothèque d'analyse de données."),
    ("user", "Comment ça fonctionne ?")  # Maintenant "ça" = pandas
])

L'historique de conversation (question utilisateur précédente + réponse assistant) fournit le contexte. L'IA comprend maintenant que "ça" signifie pandas.

Exemple : Construire une conversation avec historique

Construisons maintenant un exemple qui se souvient des échanges précédents. Cette fonction maintient l'historique de conversation et le transmet à l'IA avec chaque nouvelle question :

python
llm = ChatOpenAI(model="gpt-4o-mini")
 
def chat_with_history(user_input: str, history: list):
    messages = [("system", "Vous êtes un expert Python.")]
    
    # Ajouter l'historique
    for msg in history:
        messages.append((msg["role"], msg["content"]))
    
    # Ajouter l'entrée actuelle
    messages.append(("user", user_input))
    
    # Utiliser le format mustache pour éviter les erreurs quand le contenu contient {des accolades}
    template = ChatPromptTemplate.from_messages(messages, template_format="mustache")
    
    formatted = template.format()
    response = llm.invoke(formatted)
    return response.content
 
# Utilisation
history = []
 
# Tour 1
resp1 = chat_with_history("Qu'est-ce qu'un dictionnaire Python ?", history)
print(resp1)
 
history.append({"role": "user", "content": "Qu'est-ce qu'un dictionnaire Python ?"})
history.append({"role": "assistant", "content": resp1})
 
# Tour 2 - utilise le contexte
resp2 = chat_with_history("Montrez un exemple.", history)
print(resp2)

Règles d'ordre des messages

Les LLM attendent une structure de conversation spécifique : System → User → Assistant → User → Assistant → ...

Pourquoi cet ordre ?

Ce pattern reflète les conversations naturelles humain-IA :

  1. System vient en premier (optionnel) : Parce qu'il définit les règles comportementales qui s'appliquent à toute la conversation, il doit être défini avant le début de toute interaction. Tout comme vous briefez quelqu'un avant qu'il ne commence à travailler, pas au milieu d'une tâche.

  2. User puis Assistant alternent : Dans les vraies conversations, les humains parlent (User), l'IA répond (Assistant), les humains relancent (User), l'IA répond à nouveau (Assistant). Ce pattern de tour de rôle est la façon dont l'IA a été entraînée, donc elle attend cette structure.

  3. Doit se terminer par User : L'IA génère une réponse au dernier message User. Si la conversation se termine par Assistant, il n'y a rien à quoi l'IA peut répondre.

Exemples valides :

python
# System + User unique
[("system", "..."), ("user", "...")]
 
# System + conversation
[("system", "..."), ("user", "..."), ("assistant", "..."), ("user", "...")]

Patterns problématiques :

python
# Assistant avant User - l'IA est confuse sur le contexte
[("system", "..."), ("assistant", "..."), ("user", "...")]
# L'IA voit une réponse sans question. Elle pourrait halluciner quelle question
# cela répondait, conduisant à des réponses non pertinentes ou confuses.
python
# Deux messages User d'affilée - réponse IA manquante
[("system", "..."), ("user", "..."), ("user", "...")]
# L'IA ne sait pas à quel message User répondre, ou pourrait les fusionner
# maladroitement. Perd le flux conversationnel.
python
# Se termine par Assistant - rien à quoi répondre
[("system", "..."), ("user", "..."), ("assistant", "...")]
# La conversation est complète. L'IA n'a rien à générer puisqu'il n'y a
# pas de question User en attente. Produira probablement une erreur ou une réponse vide.

Point clé : Ces patterns ne causent pas toujours d'erreurs dures, mais ils confondent l'IA parce qu'ils brisent la logique conversationnelle sur laquelle elle a été entraînée. L'IA pourrait générer des réponses, mais elles seront peu fiables ou absurdes. Suivez toujours le pattern attendu pour un comportement prévisible.

Au-delà de l'historique simple : Patterns de production (Aperçu)

Note importante : Le pattern d'historique de conversation que vous venez d'apprendre est une excellente base, mais les systèmes de production utilisent des approches plus sophistiquées.

Le problème avec l'historique brut :

Passer simplement tout l'historique de conversation à l'IA a des limitations :

  1. Gaspillage de tokens : Chaque message (même les anciens) compte dans votre limite de tokens et coûte
  2. Perte de focus : L'IA pourrait être distraite par des conversations antérieures non pertinentes
  3. Pas de tâche explicite : L'IA déduit quoi faire de l'historique, plutôt que de recevoir des instructions claires

Une meilleure approche :

Les systèmes de production séparent le contexte des instructions :

Approche historique simple (ce que nous venons d'apprendre) :

python
messages = [
    ("system", "Vous êtes un expert Python."),
    ("user", "Qu'est-ce qu'un dictionnaire ?"),
    ("assistant", "Un dictionnaire est une structure de données clé-valeur."),
    ("user", "Montrez un exemple.")
]

Approche de production (à venir dans les chapitres suivants) :

python
messages = [
    ("system", "Vous êtes un expert Python."),
    ("user", """Contexte : L'utilisateur a précédemment demandé des informations sur les dictionnaires Python et a appris qu'ils sont des structures clé-valeur.
 
Tâche : Fournir un exemple de code démontrant l'utilisation des dictionnaires.""")
]

La différence :

  • Historique brut : L'IA voit la conversation complète et détermine quoi faire
  • Pattern de production : L'IA reçoit un contexte résumé + instruction explicite

Avantages de la séparation :

  • Moins de tokens (coût réduit, réponses plus rapides)
  • Comportement plus fiable (instructions claires)
  • Meilleur contrôle (vous décidez quel contexte compte)

Où vous apprendrez cela :

  • Chapitre 8 : Gestion de l'état de conversation et de la mémoire
  • Chapitre 11 : Récupération contextuelle (combiner RAG avec mémoire de conversation)
  • Chapitre 16 : Routage dynamique basé sur le contexte de conversation

Pour l'instant, comprendre l'historique brut est essentiel—c'est la base de ces patterns avancés. Mais gardez à l'esprit : ce que vous venez d'apprendre est un outil pédagogique, pas la solution finale.

4.3) Externaliser les prompts : Gérer les fichiers de templates (.yaml)

Pourquoi externaliser les prompts ?

À mesure que votre application IA se développe, gérer les prompts dans le code Python devient difficile. Externaliser les prompts dans des fichiers YAML offre :

Collaboration non technique : Les chefs de produit, experts métier et ingénieurs de prompts peuvent éditer les fichiers YAML sans toucher au code Python ou comprendre les concepts de programmation.

Clarté du contrôle de version : Suivez les changements de prompts séparément des changements de code. Plus de commits mixtes où les ajustements de prompts et les mises à jour de logique apparaissent ensemble.

Prompts spécifiques à l'environnement : Différents prompts pour le développement, le staging et la production sans changements de code.

Tests A/B : Testez des variations de prompts en chargeant différents fichiers—aucun changement de code nécessaire.

Pensez aux fichiers de prompts YAML comme aux fichiers de configuration dans les applications traditionnelles—ils définissent le comportement sans nécessiter de changements de code ou de redéploiement.

Qu'est-ce que YAML ?

YAML est un format de données lisible par l'humain couramment utilisé pour les fichiers de configuration. Si vous n'avez jamais vu YAML auparavant, pensez-y comme une alternative plus propre à JSON—il utilise l'indentation au lieu des crochets et est plus facile à lire et à éditer.

Structure des prompts YAML

LangChain définit une structure de fichier YAML standard pour les prompts. Regardons des exemples :

Exemple 1 : Prompt sans variables

Lorsqu'un prompt n'a pas besoin de valeurs à l'exécution, définissez input_variables sur une liste vide :

yaml
# prompts/system_prompt.yaml
_type: prompt
input_variables: []
template: |
  Vous êtes un assistant utile.
  Veuillez répondre sur un ton amical et encourageant.

Le symbole | vous permet d'écrire du texte multiligne, et les sauts de ligne sont préservés.

Exemple 2 : Prompt avec variables

Lorsqu'un prompt a besoin de valeurs à l'exécution, listez-les dans input_variables :

yaml
# prompts/user_prompt.yaml
_type: prompt
input_variables:
  - user_input
template: |
  Question de l'utilisateur : {user_input}
  Veuillez fournir une réponse claire.

À l'exécution, le placeholder {user_input} est remplacé par la valeur réelle.

Composants clés :

  • _type: prompt : Identifie ceci comme un template de prompt
  • input_variables : Liste tous les placeholders utilisés dans le template (liste vide [] si aucun)
  • template : Le texte réel du prompt avec des {placeholders}

Charger et utiliser les prompts YAML

Chargement de base :

Chargeons maintenant les fichiers YAML que nous avons créés et utilisons-les avec un LLM :

python
from langchain_core.prompts import load_prompt, ChatPromptTemplate
from langchain_openai import ChatOpenAI
 
# Charger les prompts depuis les fichiers YAML
system_prompt_template = load_prompt("prompts/system_prompt.yaml")
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Combiner les prompts chargés dans un template de chat
chat_template = ChatPromptTemplate.from_messages([
    ("system", system_prompt_template.template),
    ("user", user_prompt_template.template)
])
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
# Étape 1 : Rendre le template avec les valeurs d'exécution
messages = chat_template.invoke({"user_input": "Qu'est-ce que LangChain ?"})
 
# Étape 2 : Envoyer au LLM
response = llm.invoke(messages)
print(response.content)

Vérifier les templates chargés :

Avant d'utiliser un template, vérifiez qu'il s'est chargé correctement :

python
from langchain_core.prompts import load_prompt
 
# Charger le template
user_prompt_template = load_prompt("prompts/user_prompt.yaml")
 
# Vérifier quelles variables il attend
print("Variables d'entrée :", user_prompt_template.input_variables)
 
# Voir le texte du template
print("Template :", user_prompt_template.template)

Sortie :

Variables d'entrée : ['user_input']
Template : Question de l'utilisateur : {user_input}
Veuillez fournir une réponse claire.

Erreurs YAML courantes

Erreur 1 : Indentation incohérente

YAML nécessite une indentation cohérente (typiquement 2 espaces). Chaque niveau doit utiliser la même quantité d'espacement :

Incorrect :

yaml
_type: prompt
input_variables:
- user_input      # Incorrect : les éléments de liste doivent être indentés
  - question      # Incorrect : niveaux d'indentation mixtes

Correct :

yaml
_type: prompt
input_variables:
  - user_input    # Correct : les deux éléments au même niveau d'indentation
  - question

Erreur 2 : Décalage de placeholder

Les placeholders dans template doivent correspondre à input_variables :

Incorrect :

yaml
input_variables:
  - user_input
template: "Question : {question}"  # 'question' n'est pas dans input_variables !

Correct :

yaml
input_variables:
  - user_input
template: "Question : {user_input}"

LangChain lèvera une erreur si les placeholders ne correspondent pas aux variables déclarées.

4.4) Prévisualiser et valider les templates avant l'exécution

Pourquoi prévisualiser les templates ?

L'ingénierie de prompts est itérative. Vous ajustez la formulation, modifiez la structure, ajoutez des exemples—et chaque itération coûte des tokens API et du temps. Prévisualiser les templates avant l'exécution vous permet de :

Économiser du temps et de l'argent : Détecter les erreurs avant de faire des appels API coûteux.

Vérifier l'exactitude : S'assurer que les variables sont remplies correctement et que le formatage est comme prévu.

Déboguer efficacement : Voir le prompt exact envoyé au LLM, avec toutes les variables remplies et le formatage appliqué.

Pensez à la prévisualisation de template comme au débogage par print—vous inspectez l'état intermédiaire avant l'exécution pour vérifier l'exactitude.

Prévisualisation de template de base

Inspecter la structure du template :

Avant d'utiliser un template avec un LLM, inspectez sa structure et prévisualisez comment il se rend avec des données d'exemple :

python
from langchain_core.prompts import ChatPromptTemplate
 
template = ChatPromptTemplate.from_messages([
    ("system", "Vous êtes un {role}."),
    ("user", "{user_input}")
])
 
# Prévisualiser la structure du template
print("Variables d'entrée :", template.input_variables)
print("Nombre de messages :", len(template.messages))
 
# Prévisualiser avec des données d'exemple
prompt_value = template.invoke({
    "role": "expert en programmation Python",
    "user_input": "Qu'est-ce que Python ?"
})
 
print("\nPrévisualisation :")
for msg in prompt_value.to_messages():
    print(f"{msg.type} : {msg.content}")

Sortie :

Variables d'entrée : ['role', 'user_input']
Nombre de messages : 2
 
Prévisualisation :
system : Vous êtes un expert en programmation Python.
human : Qu'est-ce que Python ?

Cela montre exactement ce qui sera envoyé au LLM, vous permettant de vérifier le prompt avant l'exécution.

Valider les templates : Détecter les variables manquantes

L'erreur de template la plus courante est les variables requises manquantes. Voici une fonction de validation réutilisable qui détecte les variables manquantes :

python
from langchain_core.prompts import ChatPromptTemplate
 
def preview_template(template: ChatPromptTemplate, inputs: dict):
    """Prévisualiser le template avec les entrées données, en détectant les erreurs."""
    try:
        prompt_value = template.invoke(inputs)
        
        print("PRÉVISUALISATION DU TEMPLATE")
        print("=" * 60)
        
        for i, msg in enumerate(prompt_value.to_messages(), 1):
            print(f"Message {i} ({msg.type.upper()}) :")
            print(msg.content)
            print("-" * 60)
                
    except KeyError as e:
        print(f"ERREUR : {e}")
        print(f"Variables requises : {template.input_variables}")
 
# Utilisation
template = ChatPromptTemplate.from_messages([
    ("system", "Vous êtes un {role}."),
    ("user", "{user_input}")
])
 
# Entrées valides
preview_template(template, {
    "role": "expert en programmation Python",
    "user_input": "Qu'est-ce que Python ?"
})
 
# Variable manquante
preview_template(template, {
    "user_input": "Qu'est-ce que Python ?"  # 'role' manquant
})

Sortie :

PRÉVISUALISATION DU TEMPLATE
============================================================
Message 1 (SYSTEM) :
Vous êtes un expert en programmation Python.
------------------------------------------------------------
Message 2 (HUMAN) :
Qu'est-ce que Python ?
------------------------------------------------------------
 
ERREUR : "Input to ChatPromptTemplate is missing variables {'role'}.
Expected: ['role', 'user_input'] Received: ['user_input']
...
Variables requises : ['role', 'user_input']

Workflow de validation :

Voici le processus typique de validation de template :

Erreurs

Valide

Non

Oui

Définir le template

Charger des données d'exemple

Valider les entrées

Corriger template/données

Prévisualiser les messages

Prêt ?

Exécuter avec LLM

Ce processus itératif aide à détecter les erreurs avant les appels LLM coûteux.

Liste de vérification pré-exécution

Avant d'envoyer les templates en production :

  • Toutes les input_variables sont déclarées dans YAML/template
  • Les données d'exemple se rendent sans erreurs
  • Les prompts multilignes s'affichent correctement
  • Les placeholders correspondent exactement aux noms de variables
  • Tester avec des cas limites (chaînes vides, texte long)

Résumé du chapitre :

Vous avez appris à concevoir des prompts maintenables et réutilisables en utilisant le système de templates de LangChain :

  1. Séparation des préoccupations : Découpler les prompts du code pour une maintenance et une itération plus faciles
  2. Templates sensibles aux rôles : Utiliser les messages system, user et assistant pour des interactions LLM structurées avec une hiérarchie d'instructions appropriée
  3. Prompts externalisés : Gérer les prompts dans des fichiers YAML pour la collaboration non technique et le contrôle de version
  4. Prévisualisation et validation : Détecter les erreurs tôt et vérifier les templates avant l'exécution

Prochaines étapes :

Dans le Chapitre 5, vous verrez comment les templates permettent la prise de décision autonome dans des exemples d'agents de prévisualisation. Puis dans le Chapitre 6, vous apprendrez LCEL (LangChain Expression Language) pour composer ces templates en pipelines puissants en utilisant l'opérateur |.