Python & AI Tutorials Logo
LangChain & LangGraph

3. Créez votre premier chat CLI en streaming

Dans le chapitre 1, vous avez effectué votre premier appel à un LLM et vous avez vu une réponse complète apparaître d’un coup. Dans le chapitre 2, vous avez appris les bases conceptuelles de l’IA agentique et pourquoi LangChain existe. Il est maintenant temps de construire quelque chose de pratique : une application de chat en streaming qui semble réactive et professionnelle.

Pourquoi le streaming est important : Lorsque vous posez une question complexe à un LLM, attendre 10 à 30 secondes pour une réponse complète donne l’impression que l’application est cassée. Le streaming permet aux tokens d’apparaître au fur et à mesure de leur génération, créant un flux conversationnel naturel. Ce chapitre construit une application de chat en CLI avec une sortie en streaming, une bonne gestion de la configuration, des capacités de débogage et une gestion robuste des erreurs.

Ce que vous allez construire : À la fin de ce chapitre, vous aurez un script chat.py fonctionnel qui :

  • Diffuse les réponses du LLM token par token dans le terminal
  • Charge les clés API de manière sécurisée depuis des variables d’environnement
  • Gère différents types de modèles (chat vs modèles de raisonnement) avec des paramètres appropriés
  • Fournit des outils de débogage pour inspecter ce qui est réellement envoyé au LLM
  • Gère proprement les erreurs courantes (clés API manquantes, pannes réseau, entrées invalides)

3.1) Créer un dossier de travail et installer les packages

Avant d’écrire du code, vous avez besoin d’une structure de projet propre et des bonnes dépendances. Cette section établit les fondations d’un projet Python maintenable.

Structure du projet

Créez un nouveau répertoire pour votre application de chat :

bash
mkdir langchain-chat
cd langchain-chat

Configuration de l’environnement Python

Créez un environnement virtuel pour isoler les dépendances :

bash
# Créer un environnement virtuel
python -m venv venv
 
# L’activer (macOS/Linux)
source venv/bin/activate
 
# L’activer (Windows)
venv\Scripts\activate

Pourquoi des environnements virtuels ? LangChain a de nombreuses dépendances (par ex., OpenAI SDK, Pydantic, bibliothèques async). Un environnement virtuel garantit que :

  • Votre Python système reste propre
  • Différents projets peuvent utiliser différentes versions de LangChain
  • Les dépendances sont reproductibles (via requirements.txt)

Vous verrez (venv) dans l’invite de votre terminal lorsque l’environnement virtuel est activé.

Installer LangChain

Installez les packages LangChain de base :

bash
pip install langchain-core==1.2.7 langchain-openai==1.1.7 python-dotenv

Détail des packages :

  • langchain-core : Abstractions centrales (messages, prompts, chaînes, runnables)
  • langchain-openai : Implémentations spécifiques à OpenAI (ChatOpenAI, embeddings)
  • python-dotenv : Charge les variables d’environnement depuis des fichiers .env

Note sur les versions : Ce livre utilise LangChain 1.2.x en date de janvier 2026. Si vous lisez ceci plus tard, consultez la documentation LangChain pour la version la plus récente.

Vérifier l’installation

Créez un test simple pour confirmer que tout fonctionne :

python
# test_install.py
try:
    from langchain_core.messages import HumanMessage
    from langchain_openai import ChatOpenAI
    print("✓ langchain-core: OK")
    print("✓ langchain-openai: OK")
    print("\nInstallation réussie !")
except ImportError as e:
    print(f"✗ Échec de l'import : {e}")
    print("Assurez-vous que votre environnement virtuel est activé.")

Exécutez-le :

bash
python test_install.py

Sortie attendue :

✓ langchain-core: OK
✓ langchain-openai: OK
 
Installation réussie !

Si vous voyez « Installation réussie ! », vous êtes prêt à continuer. Si vous obtenez une erreur d’import, revérifiez que :

  • Votre environnement virtuel est activé (cherchez (venv) dans votre invite)
  • Les packages ont été installés avec succès (essayez d’exécuter pip list)

Créer requirements.txt

Vous venez d’installer des packages avec des commandes pip install. Même si cela fonctionne pour apprendre, il y a une meilleure méthode : les fichiers requirements.txt. C’est une pratique standard dans les projets Python pour plusieurs raisons :

Pourquoi utiliser requirements.txt ?

  • Reproductibilité : D’autres (ou vous dans 6 mois) peuvent installer exactement les mêmes versions de packages
  • Gestion claire des dépendances : Voir en un coup d’œil quels packages votre projet nécessite
  • Collaboration en équipe : Les membres de l’équipe utilisent des versions identiques, évitant les problèmes « ça marche sur ma machine »
  • Automatisation : Les serveurs ou pipelines CI/CD peuvent configurer l’environnement en une ligne : pip install -r requirements.txt

Créez un fichier requirements.txt à la racine de votre projet :

txt
# requirements.txt
langchain-core==1.2.7
langchain-openai==1.1.7
python-dotenv

Notez la syntaxe :

  • ==1.2.7 fixe une version exacte (recommandé pour la reproductibilité)
  • Sans spécificateur de version (comme python-dotenv) installe la dernière version stable
  • Les lignes commençant par # sont des commentaires

Désormais, n’importe qui peut installer toutes les dépendances avec une seule commande :

bash
pip install -r requirements.txt

C’est bien mieux que de retaper chaque package individuellement. Si un coéquipier clone votre projet, il lui suffit de :

  1. Créer un environnement virtuel
  2. Exécuter pip install -r requirements.txt

Pas besoin de se souvenir des noms de packages ou des versions — tout est dans le fichier.

Structure de votre projet

Après avoir terminé cette section, votre dossier devrait ressembler à :

langchain-chat/
├── venv/                 # Environnement virtuel (ne pas le committer dans git)
├── requirements.txt      # Liste des dépendances
└── test_install.py       # Script de vérification de l’installation

Suite : La section 3.2 montre comment charger des clés API de manière sécurisée avec des fichiers .env.

3.2) Variables d’environnement avec .env

Les clés API sont des secrets. Les coder en dur dans votre code est un risque de sécurité (surtout si vous committez dans git). Cette section montre l’approche standard : des variables d’environnement chargées depuis un fichier .env.

Pourquoi des variables d’environnement ?

Le problème des clés codées en dur :

python
# ❌ NE FAITES JAMAIS ÇA
llm = ChatOpenAI(api_key="sk-proj-abc123...")

Si vous committez ce code sur GitHub, votre clé API est publique. N’importe qui peut l’utiliser, générer des frais sur votre compte, ou faire révoquer votre clé.

La solution : Stocker les secrets dans des variables d’environnement, et les charger à l’exécution.

Créer le fichier .env

Créez un fichier .env à la racine de votre projet :

bash
# .env
OPENAI_API_KEY=sk-proj-your-actual-key-here

Obtenez votre clé API :

  1. Allez sur platform.openai.com/api-keys
  2. Créez une nouvelle clé secrète
  3. Copiez-la immédiatement (vous ne pourrez plus l’afficher ensuite)
  4. Collez-la dans votre fichier .env, en remplaçant sk-proj-your-actual-key-here

Étape de sécurité critique : Avant de faire quoi que ce soit d’autre, protégez votre clé API pour éviter qu’elle soit commitée dans git.

Créez un fichier .gitignore à la racine de votre projet et ajoutez ces lignes :

bash
# .gitignore
venv/
__pycache__/
*.pyc
.env

La ligne .env indique à git d’ignorer votre fichier de clé API. Cela évite de commit accidentellement des secrets dans le contrôle de version.

Structure de votre projet maintenant :

langchain-chat/
├── venv/
├── .env                  # Votre clé API (ignorée par git)
├── .gitignore           # Contient : .env, venv/, etc.
├── requirements.txt
└── test_install.py

Charger les variables d’environnement

Le package python-dotenv charge les fichiers .env dans os.environ :

python
# chat.py
import os
from dotenv import load_dotenv
 
# Charge le fichier .env
load_dotenv()
 
# Accède aux variables d’environnement
api_key = os.environ.get("OPENAI_API_KEY")
 
if not api_key:
    raise ValueError("OPENAI_API_KEY introuvable dans l’environnement")
 
print(f"Clé API chargée : {api_key[:8]}...")  # Affiche uniquement les 8 premiers caractères

Comment load_dotenv() fonctionne :

  1. Cherche un fichier .env en partant de l’endroit où vous exécutez le script
  2. Lit chaque ligne au format KEY=value
  3. Ajoute chaque variable à os.environ
  4. Si une variable est déjà définie (par ex., par votre plateforme d’hébergement), elle ne sera pas écrasée — la valeur existante reste

Utiliser la clé API avec LangChain

Les implémentations OpenAI de LangChain (ChatOpenAI, etc.) recherchent automatiquement OPENAI_API_KEY dans os.environ :

python
from langchain_openai import ChatOpenAI
 
load_dotenv()
 
# Ceci utilise automatiquement os.environ["OPENAI_API_KEY"]
llm = ChatOpenAI(model="gpt-4o-mini")

Convention de LangChain : Lorsque vous créez ChatOpenAI() sans paramètre api_key, il recherche automatiquement OPENAI_API_KEY dans l’environnement. C’est un pattern standard dans les intégrations LangChain.

Clé API explicite (pour les tests ou plusieurs clés) :

python
llm = ChatOpenAI(
    model="gpt-4o-mini",
    api_key=os.environ.get("OPENAI_API_KEY")
)

C’est utile lorsque vous avez plusieurs clés API (développement vs production) ou que vous voulez être explicite sur la clé utilisée.

Variables d’environnement en production

En production (plateformes cloud, conteneurs Docker), vous n’utilisez pas de fichiers .env. À la place, vous configurez les variables d’environnement via les paramètres de la plateforme :

  • Docker : Utilisez le flag -e lors de l’exécution des conteneurs
  • Plateformes cloud : Définissez les variables d’environnement dans les dashboards de configuration
  • CI/CD : Utilisez des outils de gestion des secrets

L’important : votre code ne change pas. os.environ.get("OPENAI_API_KEY") fonctionne de la même manière que la variable provienne d’un fichier .env ou d’une plateforme cloud. Nous couvrirons le déploiement en détail dans les chapitres suivants.

Vérifier votre configuration

Pour confirmer que tout fonctionne, vous pouvez tester le code de chargement des variables d’environnement montré plus haut. Si votre fichier .env est correctement configuré, os.environ.get("OPENAI_API_KEY") retournera votre clé API.

Si os.environ.get("OPENAI_API_KEY") retourne None, vérifiez que :

  1. Vous avez appelé load_dotenv() avant d’accéder à la variable d’environnement
  2. .env existe à la racine du projet
  3. OPENAI_API_KEY=sk-proj-... est correctement écrit dans .env
  4. Vous exécutez depuis le répertoire racine du projet

Suite : La section 3.3 implémente la boucle de chat réelle avec une sortie en streaming.

3.3) Implémenter la boucle de chat avec une sortie en streaming

Vous allez maintenant construire la boucle de chat principale. Cette section introduit le streaming — la différence clé entre un chatbot lent et un chatbot réactif.

Comprendre le streaming

Sans streaming (approche du chapitre 1) :

python
response = llm.invoke("Rédigez un essai de 500 mots sur l’IA")
print(response.content)  # Attendre 20 secondes, puis l’essai complet apparaît

Avec streaming :

python
for chunk in llm.stream("Rédigez un essai de 500 mots sur l’IA"):
    print(chunk.content, end="", flush=True)  # Les tokens apparaissent au fur et à mesure

Pourquoi le streaming est important :

  • Retour immédiat : Au lieu de fixer un écran vide pendant 20 secondes, vous voyez des mots apparaître tout de suite
  • Sensation de conversation naturelle : Comme avec une personne — les réponses arrivent progressivement, pas d’un coup
  • Gagner du temps et de l’argent : Si le LLM commence à donner une mauvaise réponse, vous pouvez l’arrêter tôt au lieu d’attendre une réponse complète (inutile)
  • Meilleur débogage : Lors de la construction d’applications, vous pouvez repérer les problèmes (comme des erreurs de formatage) au moment où ils surviennent, pas après une longue attente

Ce qu’est réellement le streaming : Le streaming est la livraison incrémentale du même texte de réponse. Il n’expose pas un raisonnement caché ni des processus internes du modèle — il vous montre simplement une sortie partielle au fur et à mesure qu’elle devient disponible via l’API. Pensez-y comme au téléchargement d’un fichier : vous voyez l’avancement au fur et à mesure que des morceaux arrivent, mais le contenu du fichier est le même que vous le téléchargiez d’un coup ou par morceaux.

Note sur les limites des chunks : Les chunks ne sont pas garantis de s’aligner sur des mots ou des phrases. L’API envoie des tokens en petits lots pour l’efficacité ; ainsi un chunk peut être « Hel », « lo! How », « can I », « help you », « ? ». C’est normal et attendu — n’essayez pas d’interpréter le sens à partir de chunks individuels.

La boucle de chat de base

Voici une boucle de chat minimale avec streaming :

python
# chat.py
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
def main():
    load_dotenv()
    llm = ChatOpenAI(model="gpt-4o-mini")
    
    print("Chat démarré. Tapez 'quit' ou 'exit' pour arrêter.\n")
    
    while True:
        user_input = input("You: ")
        
        if user_input.lower() in ["quit", "exit"]:
            print("Au revoir !")
            break
        
        print("Assistant: ", end="", flush=True)
        
        for chunk in llm.stream([HumanMessage(content=user_input)]):
            print(chunk.content, end="", flush=True)
        
        print("\n")
 
if __name__ == "__main__":
    main()

Comment cela fonctionne :

  1. while True: : Boucle infinie pour une conversation continue
  2. input("You: ") : Récupère l’entrée utilisateur depuis le terminal
  3. llm.stream([HumanMessage(...)]) : Diffuse la réponse du LLM en streaming
  4. Sortie en streaming avec des paramètres spéciaux :
    • end="" : N’ajoute pas de saut de ligne après chaque chunk (garde la sortie sur la même ligne)
    • flush=True : Force l’affichage immédiat dans le terminal sans mise en tampon

Pourquoi [HumanMessage(content=user_input)] ?

Les modèles de chat de LangChain attendent une liste de messages, pas une chaîne brute. Chaque message a un rôle :

  • HumanMessage : Entrée utilisateur
  • AIMessage : Réponse du LLM
  • SystemMessage : Instructions pour le LLM (couvert au chapitre 4)

Même pour un seul message utilisateur, vous passez une liste : [HumanMessage(content="Hello")].

Limitation clé — conversations en un seul tour : Cette boucle de chat est volontairement sans état. Chaque requête n’envoie que le message actuel, pas l’historique de la conversation. Cela signifie :

  • Le LLM ne se souviendra pas de ce que vous avez demandé avant
  • Les questions de suivi comme « Et sa population ? » ne fonctionneront pas après avoir demandé « Quelle est la capitale de la France ? »
  • C’est une caractéristique fondamentale des LLM — ils n’ont pas de mémoire à moins que vous ne fournissiez explicitement du contexte

Exemple de la limitation :

You: Quelle est la capitale de la France ?
Assistant: Paris.
You: Quelle est sa population ?
Assistant: Je n’ai pas assez de contexte. De quelle ville parlez-vous ?

La boucle while True apporte une continuité UX (vous pouvez continuer à discuter), mais chaque tour est indépendant. À venir au chapitre 8 : Nous implémenterons la mémoire de conversation en stockant et en renvoyant l’historique des messages à chaque requête.

Exécuter la boucle de chat

bash
python chat.py

Exemple d’interaction :

Chat démarré. Tapez 'quit' ou 'exit' pour arrêter.
 
You: Qu’est-ce que LangChain ?
Assistant: LangChain est un framework pour développer des applications basées sur des modèles de langage. Il fournit des outils pour la gestion des prompts, les chaînes, les agents et la mémoire.
 
You: Donne-moi un exemple simple
Assistant: Voici un exemple simple : ...
 
You: quit
Au revoir !

Comprendre l’API de streaming

Qu’est-ce qu’un « chunk » ?

Chaque chunk est un objet AIMessageChunk avec :

  • content : Les tokens de texte générés
  • response_metadata : Infos du modèle, comptages de tokens, etc.
python
for chunk in llm.stream([HumanMessage(content="Hello")]):
    print(f"Chunk: {chunk}")
    print(f"Content: {chunk.content}")
    print(f"Type: {type(chunk)}")

Sortie :

Chunk: content='Hello' response_metadata={'model_provider': 'openai', ...}
Content: Hello
Type: <class 'langchain_core.messages.ai.AIMessageChunk'>
 
Chunk: content='!' response_metadata={...}
Content: !
Type: <class 'langchain_core.messages.ai.AIMessageChunk'>
 
Chunk: content=' How' response_metadata={...}
Content:  How
Type: <class 'langchain_core.messages.ai.AIMessageChunk'>

Accumuler la réponse complète

Parfois, vous avez besoin de la réponse complète (pour le logging, les tests ou un traitement ultérieur) :

python
def chat_with_accumulation():
    load_dotenv()
    llm = ChatOpenAI(model="gpt-4o-mini")
    
    user_input = input("You: ")
    
    full_response = ""
    print("Assistant: ", end="", flush=True)
    
    for chunk in llm.stream([HumanMessage(content=user_input)]):
        print(chunk.content, end="", flush=True)
        full_response += chunk.content
    
    print("\n")
    
    # Maintenant vous avez la réponse complète
    print(f"[DEBUG] Longueur de la réponse complète : {len(full_response)} chars")
    return full_response

Ce schéma est courant lorsque vous devez :

  • Sauvegarder la conversation dans une base de données
  • Parser la réponse pour des données structurées
  • Calculer l’usage de tokens ou les coûts
LLMChatLoopUserLLMChatLoopUserloop[Multiple chunks]"What is LangChain?"stream([HumanMessage(...)])chunk: "Lang"print("Lang")chunk: "Chain "print("Chain ")chunk: "is a"print("is a")Stream complete"\n" (new line)Next input...

Structure de votre projet après cette section :

langchain-chat/
├── venv/
├── .env
├── .gitignore
├── requirements.txt
├── test_install.py
└── chat.py              # Streaming chat loop (new!)

Suite : La section 3.4 montre comment gérer différents types de modèles avec une configuration intelligente des paramètres.

3.4) Configuration intelligente : gérer les paramètres pour les modèles de raisonnement vs chat

OpenAI propose deux types de modèles avec des capacités et des mécanismes de contrôle différents :

Modèles de chat (gpt-4o, gpt-4o-mini) :

  • Rapides et conversationnels
  • Supportent temperature pour contrôler l’aléatoire et la créativité
  • Idéaux pour les tâches générales, l’écriture créative, le code routinier

Modèles de raisonnement (o1, o3, GPT-5) :

  • Plus lents mais plus logiques et cohérents
  • Ne supportent PAS temperature (utilisent un raisonnement interne à la place)
  • Idéaux pour les maths complexes, la planification multi-étapes, l’analyse formelle

La différence clé : Les modèles de chat utilisent un échantillonnage probabiliste (vous contrôlez l’aléatoire), tandis que les modèles de raisonnement utilisent une logique interne déterministe (le modèle contrôle son propre processus de raisonnement).

Comprendre la température (modèles de chat uniquement)

Qu’est-ce que la température ?

La température est un nombre entre 0.0 et 2.0 qui contrôle à quel point les réponses du modèle sont créatives. À faible valeur (près de 0), vous obtenez des réponses cohérentes et prévisibles. À valeur élevée (près de 2.0), vous obtenez des réponses créatives et variées. Pensez-y comme un « bouton de créativité ».

Comment ça marche : Lors de la génération de chaque mot, le modèle voit de nombreux mots suivants possibles avec des probabilités différentes. La température influence la manière dont le modèle choisit :

  • Faible température (0.0) : Choisit presque toujours le mot le plus probable → réponses cohérentes et focalisées
  • Haute température (2.0) : Plus susceptible de choisir des mots moins probables → réponses diverses et créatives

Important : La température ne fonctionne qu’avec les modèles de chat (gpt-4o, gpt-4o-mini). Elle ne s’applique pas aux modèles de raisonnement (GPT-5, o1, o3), qui utilisent une logique interne au lieu d’un échantillonnage probabiliste.

Guide des valeurs de température :

  • 0.0 : Très déterministe, focalisée et cohérente

    • À utiliser pour : Q&R factuelles, génération de code routinière, sortie structurée
    • Même entrée → sortie presque identique à chaque fois
    • Exemple : « What is 2+2? » → Toujours « 4 »
  • 0.7–1.0 : Comportement d’échantillonnage standard (la valeur par défaut est 1.0)

    • À utiliser pour : conversation générale, explications, réponses équilibrées
    • Variation modérée dans la formulation et les exemples
    • Exemple : « Explain photosynthesis » → Formulation différente à chaque fois, mêmes infos clés
  • 1.2–2.0 : Plus créatif et divers, moins prévisible

    • À utiliser pour : écriture créative, brainstorming, idéation
    • Forte variation de ton, de structure et de formulation
    • Exemple : « Write a poem about the moon » → Styles très différents à chaque fois

Note : Les valeurs au-dessus de 1.0 augmentent la créativité mais peuvent réduire la précision factuelle et la cohérence. La valeur maximale est 2.0.

Exemple : impact de la température (modèles de chat uniquement)

python
# Température 0.0 - déterministe, même réponse à chaque fois
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0.0)
response = llm.invoke([HumanMessage(content="Combien font 2+2 ?")])
print(response.content)  # Output: 4
 
# Température 1.0 - comportement par défaut, légère variation possible
llm = ChatOpenAI(model="gpt-4o-mini", temperature=1.0)
response = llm.invoke([HumanMessage(content="Combien font 2+2 ?")])
print(response.content)  # Output: 4 (peut inclure une brève explication)

Pour les questions fermées et factuelles, la température a peu d’effet sur la justesse.

Pour les tâches ouvertes ou créatives, la température influence fortement la diversité, le ton et le style.

Que se passe-t-il si vous utilisez des paramètres de modèle de chat sur des modèles de raisonnement ?

Cela dépend du modèle — certains le rejettent, d’autres l’ignorent silencieusement :

python
# ❌ Ceci échouera avec les modèles o3
llm = ChatOpenAI(model="o3-mini", temperature=0.7)

Erreur :

BadRequestError: Temperature is not supported with this model

Modèles différents, politiques différentes :

  • Modèles o1 / o3 : Rejettent explicitement les paramètres non supportés. Si la température est incluse, l’API renvoie immédiatement une erreur 400 BadRequest.
  • Modèles GPT-5 : Plus permissifs — le paramètre est accepté mais ignoré silencieusement. Votre requête réussit, mais la température n’a aucun effet.

Pourquoi c’est important : Vérifiez toujours quel modèle vous utilisez et configurez les paramètres en conséquence. Utiliser les mauvais paramètres peut soit provoquer des erreurs, soit échouer silencieusement, ce qui fait perdre du temps de débogage.

Comment contrôler le comportement des modèles de raisonnement

Vous savez maintenant que les modèles de chat utilisent temperature et que les modèles de raisonnement ne l’utilisent pas. Alors comment contrôler les modèles de raisonnement ?

Les modèles de raisonnement sont ajustés via le prompt design, pas via des paramètres :

  • Les modèles de raisonnement n’exposent pas temperature ni des contrôles similaires
  • À la place, vous guidez le comportement par la manière dont vous écrivez le prompt :
    • Instructions explicites : « Think step-by-step », « Show your work »
    • Contraintes comme règles : « You must not assume... », « Always verify... »
    • Exigences structurées : « Output in JSON format », « Include reasoning before answer »
    • Logique de décision : « If condition A, then do X, otherwise do Y »

Exemple : paramètres chat vs prompts de raisonnement

python
# ❌ Approche chat - ne fonctionnera pas avec les modèles de raisonnement
llm = ChatOpenAI(model="o3-mini", temperature=0.5)
# Error: BadRequestError: Temperature is not supported
 
# ✅ Approche raisonnement - guide via la structure du prompt
prompt = """
Résolvez ce problème étape par étape :
1. Dites ce que vous savez
2. Montrez vos calculs
3. Vérifiez votre réponse
 
Problème : Si x + 5 = 12, quelle est la valeur de x ?
"""
llm = ChatOpenAI(model="o3-mini")
response = llm.invoke([HumanMessage(content=prompt)])
print(response.content)

Sortie :

1. Ce que je sais : x + 5 = 12
2. Calculs : x = 12 - 5 = 7
3. Vérification : 7 + 5 = 12 ✓
 
Réponse : x = 7

Idée clé : Les modèles de chat sont contrôlés par des paramètres, les modèles de raisonnement sont contrôlés par des prompts.

Tableau de décision pour la sélection des modèles

Maintenant que vous comprenez comment contrôler les deux types de modèles, voici quand utiliser chacun :

Type de tâcheModèle recommandéPourquoi
Conversation généralegpt-4o-miniRapide, faible coût, conversationnel
Q&R simplegpt-4o-miniSuffisant pour une recherche factuelle
Écriture créativegpt-4o-mini (temp 0.8–1.0)La température permet la créativité
Génération de codeGPT-5Meilleure planification logique
Raisonnement complexeGPT-5Optimisé pour une logique multi-étapes
Problèmes de mathso3 / o1Modèles de raisonnement dédiés
Planification multi-étapesGPT-5Fort sur la planification à long horizon
Analyse formelle (juridique/politique)o3Strictement déterministe

Arbitrages coût et latence

Comprendre les arbitrages pratiques vous aide à choisir le bon modèle pour votre cas d’usage :

Type de modèleVitesse (latence typique)Coût (relatif)Idéal pour
gpt-4o-miniTrès rapide (<2s)Très faibleConversation générale, tâches simples
gpt-4oRapide (1–4s)MoyenChat de meilleure qualité, tâches multimodales
GPT-5Modéré (3–8s)ÉlevéRaisonnement complexe, planification
o1 / o3Le plus lent (5–15s+)Le plus élevéRaisonnement déterministe, logique formelle

Notes :

  • La vitesse reflète une latence de réponse typique (varie selon la longueur et la complexité du prompt)
  • Le coût est une comparaison relative — vérifiez les tarifs actuels sur le site d’OpenAI
  • Les modèles de raisonnement échangent vitesse et coût contre cohérence et justesse
  • Les modèles de chat privilégient réactivité et efficacité

Quand utiliser des modèles de raisonnement (GPT-5, o1, o3) :

  • Problèmes de maths et STEM(Science, Technology, Engineering, Mathematics) multi-étapes nécessitant des étapes intermédiaires correctes
  • Analyse logique complexe avec dépendances et contraintes
  • Débogage de code avec plusieurs causes qui interagissent
  • Tâches de planification avec de nombreuses règles, cas limites ou compromis
  • Workflows d’agents nécessitant cohérence et réflexion à long horizon

Quand utiliser des modèles de chat (gpt-4o, gpt-4o-mini) :

  • Conversation générale et chat interactif
  • Q&R simple avec une profondeur de raisonnement limitée
  • Génération de contenu (blogs, résumés, écriture créative)
  • Génération de code routinière et tâches boilerplate
  • Applications où la vitesse et le coût comptent plus que le raisonnement profond

Suite : La section 3.5 montre des techniques de débogage pour inspecter ce qui est réellement envoyé au LLM.

3.5) Débogage : inspecter les réponses et l’usage des tokens

Quand votre LLM se comporte de manière inattendue, vous devez voir exactement ce qui a été envoyé et reçu. Cette section montre comment inspecter les appels LLM et déboguer les problèmes.

Pourquoi le débogage est important

Scénarios de débogage courants :

  • « Pourquoi le LLM a-t-il donné cette réponse ? » → Vérifier le prompt exact
  • « Combien cette requête a-t-elle coûté ? » → Vérifier l’usage des tokens
  • « Pourquoi est-ce si lent ? » → Mesurer la latence
  • « Mon format de message est-il correct ? » → Inspecter la structure des messages

Le défi : Quand vous appelez llm.invoke(), vous obtenez un objet réponse. Mais qu’y a-t-il réellement dedans ? Quelles informations sont disponibles pour le débogage ?

Comprendre l’objet réponse

Avant de déboguer, vous devez comprendre ce que llm.invoke() renvoie.

Structure de base :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])
 
# Qu’y a-t-il dans la réponse ?
print(type(response))  # AIMessage
print(response.content)  # Le texte réel
print(response.response_metadata)  # Usage des tokens, infos modèle, etc.

Sortie :

<class 'langchain_core.messages.ai.AIMessage'>
Hello! How can I assist you today?
{
  'token_usage': {
    'completion_tokens': 9,
    'prompt_tokens': 8,
    'total_tokens': 17
  },
  'model_name': 'gpt-4o-mini-2024-07-18',
  'finish_reason': 'stop',
  ...
}

Éléments clés de la réponse :

  • response.content : Le texte généré par le LLM
  • response.response_metadata : Dictionnaire avec :
    • token_usage : Nombre de tokens utilisés (pour calculer le coût)
    • model_name : Version exacte du modèle qui a répondu
    • finish_reason : Pourquoi la génération s’est arrêtée (voir la section Mode Debug pour les détails)

Accéder à l’usage des tokens :

python
token_usage = response.response_metadata['token_usage']
print(f"Prompt tokens: {token_usage['prompt_tokens']}")
print(f"Response tokens: {token_usage['completion_tokens']}")
print(f"Total: {token_usage['total_tokens']}")

Sortie :

Prompt tokens: 8
Response tokens: 9
Total: 17

Pourquoi c’est important : Vous avez besoin de ces valeurs pour le débogage, le suivi des coûts et l’optimisation de vos prompts.

Calculer les coûts à partir de l’usage des tokens

L’usage des tokens détermine le coût. Chaque modèle a une tarification différente :

GPT-4o-mini (en date de janvier 2026) :

  • Entrée : 0,15 $ par 1M tokens
  • Sortie : 0,60 $ par 1M tokens

GPT-4o :

  • Entrée : 2,50 $ par 1M tokens
  • Sortie : 10,00 $ par 1M tokens

Fonction de calcul des coûts :

python
def calculate_cost(token_usage, model_name):
    """Calcule le coût en fonction de l’usage des tokens."""
    prompt_tokens = token_usage.get('prompt_tokens', 0)
    completion_tokens = token_usage.get('completion_tokens', 0)
    
    # Tarification par 1M tokens (en date de janvier 2026)
    pricing = {
        'gpt-4o-mini': {'input': 0.15, 'output': 0.60},
        'gpt-4o': {'input': 2.50, 'output': 10.00},
        'gpt-5': {'input': 1.25, 'output': 10.00},
    }
    
    if model_name not in pricing:
        return None
    
    input_cost = (prompt_tokens / 1_000_000) * pricing[model_name]['input']
    output_cost = (completion_tokens / 1_000_000) * pricing[model_name]['output']
    
    return input_cost + output_cost
 
# Exemple
response = llm.invoke([HumanMessage(content="Explain quantum computing")])
token_usage = response.response_metadata['token_usage']
cost = calculate_cost(token_usage, "gpt-4o-mini")
print(f"Cost: ${cost:.6f}")

Sortie :

Cost: $0.000123

Pourquoi c’est important : Les apps en production peuvent gérer 50 000+ requêtes/jour. À 0,002 $ par requête, cela fait 3 000 $/mois. Utilisez le mauvais modèle ou des prompts trop longs, et les coûts montent à 30 000 $/mois. Un bug de boucle de retry peut brûler des milliers en une nuit. Suivez l’usage des tokens dès le premier jour.

Activer le mode debug (quand vous avez besoin des détails bruts de l’API)

L’objet réponse et un wrapper personnalisé couvrent la plupart des besoins de débogage. Mais parfois vous devez voir exactement ce que LangChain envoie à OpenAI — la requête et la réponse JSON brutes.

Quand vous pourriez en avoir besoin :

  • Déboguer le formatage des messages de LangChain
  • Vérifier que les paramètres API sont correctement définis
  • Investiguer des erreurs API inattendues
  • Comprendre le payload API exact

LangChain a un logging debug intégré via langchain_core.globals :

python
from langchain_core.globals import set_debug
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
 
set_debug(True)
 
# Maintenant tous les appels LLM afficheront des infos de debug
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])

Sortie :

[llm/start] [llm:ChatOpenAI] Entering LLM run with input:
{
  "prompts": [
    "Human: Hello"
  ]
}
[llm/end] [llm:ChatOpenAI] [1.45s] Exiting LLM run with output:
{
  "generations": [
    [
      {
        "text": "Hello! How can I assist you today?",
        "generation_info": {
          "finish_reason": "stop",
          "logprobs": null
        },
        "type": "ChatGeneration",
        ...
      }
    ]
  ],
  "llm_output": {
    "token_usage": {
      "completion_tokens": 9,
      "prompt_tokens": 8,
      "total_tokens": 17,
      ...
    },
    "model_provider": "openai",
    "model_name": "gpt-4o-mini-2024-07-18",
    ...
  },
}

Note : Le format de sortie varie selon le fournisseur du LLM. Cet exemple montre la structure d’OpenAI.

Ce que révèle la sortie de debug :

Le mode debug montre le flux complet de communication LangChain → OpenAI :

1. Transformation du format des messages :

python
# Votre code
[HumanMessage(content="Hello")]
 
# Ce que vous voyez dans la sortie de debug
{
  "prompts": ["Human: Hello"]
}

Le mode debug montre comment LangChain représente votre message en interne avant de l’envoyer au LLM.

2. Statut de fin de génération :

python
"finish_reason": "stop"

Pourquoi la génération s’est terminée :

  • "stop" : Le modèle a terminé la réponse naturellement
  • "length" : La réponse a été coupée car elle a atteint la limite max_tokens
  • "tool_calls" : Le modèle a terminé la génération en produisant des instructions d’appel d’outil plutôt qu’un texte final (chapitre 12)
  • "content_filter" : La réponse a été bloquée ou supprimée en raison de règles de sécurité ou de modération de contenu

Si vous voyez "length", augmentez max_tokens pour obtenir la réponse complète.

3. Détail de l’usage des tokens :

python
"token_usage": {
  "completion_tokens": 9,
  "prompt_tokens": 8,
  "total_tokens": 17,
  "completion_tokens_details": {
    "reasoning_tokens": 0  # Pour les modèles de raisonnement (o1/o3, etc.)
  },
  "prompt_tokens_details": {
    "cached_tokens": 0  # Mise en cache du prompt (réduit les coûts)
  }
}

Au-delà des compteurs de base, vous pouvez voir :

  • reasoning_tokens : Étapes de raisonnement internes (uniquement pour les modèles de raisonnement)
  • cached_tokens : Nombre de tokens de prompt servis depuis le cache (réduit le coût)

4. Version du modèle et empreinte (fingerprint) :

python
"model_name": "gpt-4o-mini-2024-07-18",
"system_fingerprint": "fp_8bbc38b4db"
  • model_name : Version snapshot exacte (explique pourquoi les réponses changent dans le temps)
  • system_fingerprint : ID de configuration backend d’OpenAI (change quand ils mettent à jour les systèmes)

5. Timing de requête :

python
[llm/end] [llm:ChatOpenAI] [1.56s]

Le [1.45s] montre la durée totale de la requête — utile pour identifier des requêtes lentes.

Suite : La section 3.6 montre comment gérer proprement les erreurs courantes.

3.6) Gérer les échecs (simuler et corriger des erreurs courantes)

Les applications LLM en production rencontrent des modes de panne prévisibles : identifiants manquants, timeouts réseau, limites de débit, et entrées invalides. Cette section vous montre comment gérer ces erreurs proprement et construire des applications robustes dès le premier jour.

Les six erreurs courantes

1. Clé API manquante

Quand cela arrive : Vous essayez de créer une instance ChatOpenAI, mais OPENAI_API_KEY n’est pas défini dans votre environnement.

Exemple :

python
# Le fichier .env n’existe pas, ou OPENAI_API_KEY n’est pas défini
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])

Erreur que vous verrez :

OpenAIError: The api_key client option must be set either by passing api_key to the client or by setting the OPENAI_API_KEY environment variable

Comment corriger :

  1. Vérifiez que votre fichier .env existe à la racine du projet
  2. Vérifiez que le nom de clé est exactement OPENAI_API_KEY (typo courante : OPENAPI_KEY)
  3. Assurez-vous que load_dotenv() est appelé avant de créer le LLM

2. Mauvaise clé API

Quand cela arrive : Votre fichier .env contient une clé API invalide, expirée, ou mal copiée.

Exemple :

python
# .env contient : OPENAI_API_KEY=sk-invalid-key-12345
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke([HumanMessage(content="Hello")])

Erreur que vous verrez :

AuthenticationError: Incorrect API key provided

Comment corriger :

  1. Allez sur https://platform.openai.com/api-keys
  2. Vérifiez que votre clé est toujours active (non révoquée ou expirée)
  3. Générez une nouvelle clé si nécessaire
  4. Copiez soigneusement la clé entière (erreur courante : premiers/derniers caractères manquants)
  5. Collez dans .env sans espaces en trop :
bash
OPENAI_API_KEY=sk-proj-exactkeyhere

3. Pannes réseau

Quand cela arrive : Votre connexion internet coupe, ou les serveurs d’OpenAI sont temporairement inaccessibles pendant une requête.

Exemple :

python
# Le WiFi se déconnecte en pleine requête, ou l’API OpenAI est down
response = llm.invoke([HumanMessage(content="Hello")])

Erreur que vous verrez :

APIConnectionError: Connection error

Comment corriger :

  1. Vérifiez votre connexion internet
  2. Vérifiez le statut d’OpenAI sur https://status.openai.com

4. Limites de débit

Quand cela arrive : Vous envoyez trop de requêtes en peu de temps et dépassez votre quota API.

Exemple :

python
# Envoi de 1000 requêtes instantanément
for i in range(1000):
    llm.invoke([HumanMessage(content=f"Request {i}")])

Erreur que vous verrez :

RateLimitError: Rate limit reached for requests

Comment corriger :

  1. Vérifiez vos limites de débit sur https://platform.openai.com/account/limits
  2. Passez à une offre supérieure si vous avez besoin de limites plus élevées
  3. Utilisez le traitement en lot (batch) pour les gros volumes (couvert au chapitre 6)

5. Nom de modèle invalide

Quand cela arrive : Vous spécifiez un nom de modèle qui n’existe pas ou n’est pas disponible dans votre offre.

Exemple :

python
llm = ChatOpenAI(model="gpt-99-ultra")  # N’existe pas
response = llm.invoke([HumanMessage(content="Hello")])

Erreur que vous verrez :

NotFoundError: The model `gpt-99-ultra` does not exist or you do not have access to it

Comment corriger :

  1. Vérifiez les modèles disponibles dans votre offre sur https://platform.openai.com/docs/models

6. Limite de tokens dépassée

Quand cela arrive : Votre prompt est trop long et dépasse la fenêtre de contexte maximale du modèle.

Exemple :

python
# Création d’un prompt de 1 million de caractères
huge_prompt = "x" * 1_000_000
response = llm.invoke([HumanMessage(content=huge_prompt)])

Erreur que vous verrez :

BadRequestError: This model's maximum context length is 128000 tokens. However, your messages resulted in 250000 tokens.

Comment corriger :

  1. Vérifiez la longueur d’entrée avant l’envoi
  2. Connaissez les limites de votre modèle :
    • gpt-4o-mini : 128K tokens
    • gpt-4o : 128K tokens
    • gpt-5 : 400K tokens
  3. Pour les longs documents, utilisez la segmentation (chunking) ou le résumé / la synthèse (summarization) (couvert au chapitre 9)

Prochaines étapes : Le chapitre 4 montre comment concevoir des templates de prompt réutilisables qui séparent le prompt engineering du code applicatif.