2. Les essentiels pour construire un agent
Dans le Chapitre 1, vous avez effectué votre premier appel à un LLM et vu le schéma requête-réponse de base. Maintenant, nous devons comprendre ce que nous construisons réellement : des systèmes d’IA agentique (agentic AI). Ce chapitre pose les concepts fondamentaux que vous utiliserez tout au long du livre.
À la fin de ce chapitre, vous comprendrez :
- Ce qui rend un système d’IA « agentique » (et pourquoi c’est important)
- Pourquoi des frameworks comme LangChain et LangGraph existent
- Comment les LLM fonctionnent réellement sous le capot (et pourquoi cela affecte la conception des agents)
- L’économie de l’utilisation des LLM (tokens, coûts et sélection de modèle)
- Comment écrire des prompts efficaces pour des systèmes d’agents
C’est un chapitre conceptuel — nous reviendrons au code pratique au Chapitre 3. Mais ces concepts sont essentiels pour comprendre les décisions de conception que vous prendrez lorsque vous construirez des agents.
2.1) Qu’est-ce que l’IA agentique ? (Chatbot vs Agent)
Quand on pense aux « applications d’IA », on imagine des chatbots : vous posez une question, l’IA répond, la conversation se termine. Mais l’IA agentique est fondamentalement différente. Un agent ne se contente pas de répondre — il agit de manière autonome pour atteindre des objectifs.
La distinction centrale
Commençons par une définition claire :
Chatbot : un système qui répond aux messages de l’utilisateur avec du texte généré. L’interaction est purement conversationnelle. Le chatbot n’a pas la capacité d’agir sur le monde réel au-delà de générer son prochain message.
Agent : un système qui peut :
- Percevoir son environnement (par ex., lire le statut d’une commande, vérifier l’inventaire)
- Décider quelles actions entreprendre (par ex., choisir entre : créer un ticket, envoyer un e-mail ou escalader)
- Agir sur ces décisions (par ex., appeler une API, mettre à jour une base de données)
- Itérer jusqu’à atteindre un objectif (par ex., vérifier la commande → créer un ticket → confirmer → terminé)
La différence clé, c’est l’autonomie et l’action. Un chatbot est réactif ; un agent est proactif.
Exemple concret : support client
Voyons cette distinction en pratique.
Approche chatbot :
Utilisateur : "Je veux un remboursement pour la commande #12345"
Chatbot : "Je comprends que vous souhaitez un remboursement. Pour le traiter, veuillez :
1. Visiter notre portail de remboursements à l’adresse example.com/refunds
2. Saisir votre numéro de commande
3. Sélectionner un motif
4. Envoyer le formulaire
5. Attendre 24 à 48 heures pour une revue manuelle
Y a-t-il autre chose avec laquelle je peux vous aider ?"Le chatbot fournit des informations mais n’effectue aucune action. L’utilisateur doit faire tout le travail.
Approche agent :
Utilisateur : "Je veux un remboursement pour la commande #12345"
Agent (raisonnement interne) :
1. L’utilisateur veut un remboursement pour la commande #12345
2. Je dois vérifier que cette commande existe
3. [Appelle get_order_details(order_id="12345")]
4. Commande trouvée, éligible au remboursement
5. [Appelle create_refund_ticket(order_id="12345", reason="customer_request")]
6. Ticket créé : TICKET-789
Agent : "J’ai créé le ticket de remboursement TICKET-789 pour la commande #12345.
Notre équipe de remboursement le traitera sous 3 à 5 jours ouvrés.
Vous recevrez bientôt un e-mail de confirmation."L’agent a agi : il a vérifié la commande, créé un ticket et confirmé le résultat. Le problème de l’utilisateur est résolu sans étapes manuelles.
Pourquoi c’est important pour le développement
Comprendre cette distinction influence la manière dont vous concevez votre système :
Développement de chatbot :
- Se concentrer sur la qualité des réponses et le flux de conversation
- Préoccupation principale : générer un texte utile et précis
- Architecture simple : prompt → LLM → réponse
- Pas besoin d’intégrations externes
Développement d’agent :
- Se concentrer sur la prise de décision et l’exécution d’actions
- Préoccupations principales : choisir les bonnes actions, gérer les erreurs, maintenir l’état
- Architecture complexe : perception → raisonnement → sélection d’action → exécution → vérification
- Nécessite des intégrations d’outils, la gestion d’erreurs, la gestion de l’état
Le spectre de l’autonomie
Tous les agents ne sont pas aussi autonomes. Il existe un spectre :
Niveau 1 : actions assistées
- L’agent suggère des actions, l’utilisateur approuve chacune d’elles
- Exemple : « Je peux créer un ticket de remboursement. Dois-je continuer ? »
- Approche la plus sûre pour les opérations à forts enjeux
Niveau 2 : autonomie bornée
- L’agent agit dans des contraintes prédéfinies
- Exemple : peut créer des tickets et envoyer des e-mails, mais ne peut pas traiter des remboursements au-delà de 500 $ ni accéder directement aux systèmes de paiement
- Le plus courant en production (équilibre entre efficacité et sécurité)
Niveau 3 : autonomie totale
- L’agent agit de manière indépendante pour atteindre des objectifs
- Exemple : gère tout le flux de travail de remboursement sans intervention humaine
- Nécessite des garde-fous et une surveillance robustes
Caractéristiques clés des agents
Pour résumer, un système d’IA agentique possède ces propriétés centrales :
- Utilise des outils : peut appeler des fonctions, des API et des services externes (Sans cela, ce n’est qu’un chatbot)
- Orienté objectifs : vise des résultats précis, pas seulement des réponses
- Multi-étapes : découpe des tâches complexes en séquences d’actions
- Adaptatif : ajuste son comportement en fonction des résultats intermédiaires
- Avec état (stateful) : maintient le contexte sur plusieurs interactions
Les deux premières sont essentielles — sans outils ni objectifs, vous n’avez pas un agent. Les autres sont des facteurs de qualité qui distinguent les bons agents des excellents.
Vous comprenez maintenant ce que sont les agents et pourquoi ils sont puissants.
2.2) Pourquoi LangChain et LangGraph ?
Vous vous demandez peut-être : « Pourquoi ai-je besoin de frameworks ? Je ne peux pas appeler directement l’API d’OpenAI ? » Explorons pourquoi les frameworks existent et quels problèmes ils résolvent.
La complexité du développement d’agents
Construire un chatbot simple avec des appels API directs est relativement simple :
import openai
response = openai.chat.completions.create(
model="gpt-5-mini",
messages=[{"role": "user", "content": "Bonjour !"}]
)
print(response.choices[0].message.content)Cela fonctionne bien pour des cas d’usage basiques. Mais dès que vous voulez construire un agent, la complexité explose :
Défi 1 : flux de travail multi-étapes Votre agent doit :
- Récupérer des documents pertinents depuis une base de connaissances
- Décider quel outil appeler en fonction de l’intention de l’utilisateur
- Exécuter l’outil et gérer les erreurs
- Mettre en forme les résultats et répondre à l’utilisateur
Chaque étape nécessite une orchestration soignée, la gestion des erreurs et la gestion de l’état.
Défi 2 : couche d’abstraction multi-fournisseurs Que se passe-t-il si vous voulez :
- Passer d’OpenAI à Anthropic ou Google ?
- Utiliser des modèles différents selon les tâches ?
- Basculer sur un modèle moins cher si le modèle principal échoue ?
Avec des appels API directs, vous devriez réécrire une part importante du code pour chaque fournisseur.
Défi 3 : mémoire conversationnelle Les agents doivent se souvenir du contexte :
- Les messages précédents dans la conversation
- Les documents récupérés lors de requêtes précédentes
- Les résultats intermédiaires des appels d’outils
Gérer cet état manuellement est source d’erreurs et fastidieux.
Défi 4 : intégration d’outils Votre agent doit :
- Définir les outils disponibles avec des schémas
- Laisser le LLM choisir quel outil appeler
- Extraire les arguments de l’outil à partir de la sortie du LLM
- Exécuter les outils de manière sûre, avec validation
- Gérer les erreurs d’outil et la logique de reprise
Cela demande beaucoup de boilerplate (code répétitif) et des considérations de sécurité à chaque étape.
Défi 5 : routage complexe Les agents réels ont besoin de logique conditionnelle :
- « Si l’utilisateur demande des remboursements, récupérer les documents de politique »
- « Si l’utilisateur veut un remboursement, créer un ticket »
- « Si la question est hors sujet, refuser poliment »
Implémenter cela avec des if-else devient rapidement ingérable.
Ce que fournit LangChain
LangChain est un framework pour construire des applications LLM. Il fournit :
1. Abstraction de modèle
Interface unifiée pour différents fournisseurs de LLM (OpenAI, Anthropic, Google, etc.).
from langchain_openai import ChatOpenAI
from langchain_anthropic import ChatAnthropic
# Même interface, fournisseurs différents
openai_llm = ChatOpenAI(model="gpt-5-mini")
anthropic_llm = ChatAnthropic(model="claude-4-5-sonnet")
# Les deux utilisent .invoke() avec le même format de message
response = openai_llm.invoke([{"role": "user", "content": "Bonjour"}])Changez de fournisseur sans réécrire la logique de votre application.
2. Chaînes composables (LCEL)
LangChain Expression Language — une syntaxe pour connecter des composants en pipelines.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
# Composer un pipeline avec l’opérateur | (comme les pipes Unix)
chain = prompt | llm | output_parser
# Exécuter tout le pipeline avec un seul appel
result = chain.invoke({"input": "question utilisateur"})Construisez des flux de travail complexes sans passer manuellement les données entre les étapes. Nous apprendrons LCEL au Chapitre 6.
3. Mémoire conversationnelle
Gestion de l’historique des messages pour des conversations avec état.
from langchain_core.chat_history import InMemoryChatMessageHistory
# Helper pour l’historique des messages
history = InMemoryChatMessageHistory()
history.add_user_message("Salut")
history.add_ai_message("Bonjour !")
# Récupérer les messages quand nécessaire
messages = history.messages
response = llm.invoke(messages)Abstraire le stockage des messages avec des classes helper au lieu de gérer des listes manuellement. À ce stade, l’historique est encore passé explicitement au modèle. Nous intégrerons cela aux chaînes au Chapitre 8. LangGraph (Chapitres 15+) rend cela encore plus simple avec une gestion d’état intégrée.
4. Intégration d’outils
Système basé sur des décorateurs pour exposer des fonctions Python aux LLM.
from langchain_core.tools import tool
@tool
def create_ticket(order_id: str, reason: str) -> str:
"""Crée un ticket de support pour une commande."""
# Implémentation ici
return f"Ticket créé pour {order_id}"
# LangChain gère la génération de schéma et l’intégration LLMTransformez n’importe quelle fonction Python en outil qui peut être découvert et appelé par des LLM capables d’utiliser des outils ou par des agents — sans écrire manuellement de schéma JSON.
5. Chargeurs de documents et vector stores (bases vectorielles)
Composants prêts à l’emploi pour charger des documents depuis diverses sources et les stocker comme des embeddings recherchables.
from langchain_community.document_loaders import TextLoader
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
# Charger des documents
docs = TextLoader("support_docs.txt").load()
# Créer un index recherchable
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_documents(docs, embeddings)
# Récupérer des docs pertinents
results = vectorstore.similarity_search("politique de remboursement")Construisez des systèmes RAG en utilisant des chargeurs et des vector stores préconstruits au lieu d’implémenter vous-même le parsing, l’embedding et la récupération.
Ce que fournit LangGraph
LangGraph étend LangChain pour des flux de travail d’agents complexes. Il fournit :
1. Gestion explicite de l’état
Définir toutes les données de l’agent dans un seul schéma typé au lieu de les disperser dans des variables.
from typing import TypedDict, Optional
from langchain_core.messages import BaseMessage
from langchain_core.documents import Document
class AgentState(TypedDict):
messages: list[BaseMessage]
retrieved_docs: list[Document]
current_step: Optional[str]
# Toutes les données de l’agent vivent ici — un seul endroit à inspecter lors du debugFini de chercher où se trouve la donnée ou comment elle circule entre les étapes. Les changements d’état sont explicites : les nœuds lisent state et renvoient des mises à jour. Votre IDE autocomplète les noms de champs, et les vérificateurs de types détectent les erreurs avant l’exécution.
2. Flux de travail basés sur des graphes
Construire des flux de travail en déclarant les étapes (nœuds) et leurs connexions (arêtes), plutôt qu’en écrivant du code d’orchestration.
graph = StateGraph(AgentState)
# Définir les nœuds (étapes de votre flux de travail)
graph.add_node("retrieve", retrieve_docs)
graph.add_node("answer", generate_answer)
# Définir les arêtes (transitions entre étapes)
graph.add_edge("retrieve", "answer")Vous définissez la structure—« retrieve s’exécute, puis answer s’exécute »—et LangGraph gère l’exécution. Il n’y a pas besoin d’écrire du code d’orchestration pour passer l’état entre les étapes. Le contrôle de flux, comme le séquencement ou l’embranchement, est déclaré dans la structure du graphe elle-même.
3. Routage conditionnel
Prise de décision à l’exécution sur le chemin à suivre dans le flux de travail.
from langchain_core.messages import BaseMessage
def route_request(state):
last_message = state["messages"][-1].content
if "refund" in last_message:
return "create_ticket"
else:
return "answer_question"
graph.add_conditional_edges(
"classify",
route_request,
{
"create_ticket": "create_ticket",
"answer_question": "answer_question",
}
)L’idée clé : la fonction de routage retourne le nom du nœud suivant (« create_ticket » ou « answer_question »).
Pourquoi c’est important : votre logique de décision est séparée de l’exécution. Changer les règles de routage ? Modifiez une fonction. Voir tous les chemins possibles ? Regardez la définition du graphe. Pour savoir quel chemin a été emprunté ? Inspectez la trace d’exécution — sans fouiller dans des appels de fonctions imbriqués.
4. Checkpointing et persistance
L’état est automatiquement enregistré après chaque étape, permettant la récupération après crash et des flux de travail pause-reprise.
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver("agent_state.db")
graph = graph.compile(checkpointer=checkpointer)
# L’état est automatiquement sauvegardé à chaque étape
result = graph.invoke(
input_state,
config={"configurable": {"thread_id": "user-123"}}
)Après chaque étape, LangGraph enregistre l’état courant dans un stockage persistant. Si le processus crash, l’exécution peut reprendre depuis le dernier checkpoint associé au même thread ID.
Le checkpointing permet des flux de travail pause-et-reprise, comme attendre une approbation humaine, lorsqu’il est combiné avec du routage conditionnel ou des interruptions.
Quand utiliser chaque framework
Utilisez LangChain quand :
- Vous construisez des chaînes simples (prompt → LLM → parser)
- Vous implémentez des systèmes RAG
- Vous gérez du contexte basé sur des messages (en passant l’historique de chat explicitement)
- Vous abstraiez entre fournisseurs de LLM
Utilisez LangGraph quand :
- Vous construisez des flux de travail d’agents multi-étapes
- Vous implémentez une logique de routage conditionnel
- Vous gérez un état complexe entre étapes
- Vous avez besoin de checkpointing et de récupération après crash
2.3) Comment fonctionnent les LLM (pour les développeurs d’agents)
Pour construire des agents efficaces, vous devez comprendre comment les LLM fonctionnent réellement. Il ne s’agit pas ici des mathématiques des transformers — mais du modèle mental qui guide votre conception des systèmes d’agents.
Le mécanisme central : la prédiction de tokens
Voici l’idée clé : les LLM ne « connaissent » pas des faits comme le font des bases de données. Ils prédisent le token suivant.
Décomposons cela avec un exemple concret.
Entrée : « La capitale de la France est »
Ce que vous pourriez penser qu’il se passe :
- Le LLM « cherche » la capitale de la France dans sa base de connaissances
- Le LLM « récupère » la réponse : Paris
- Le LLM renvoie « Paris »
Ce qui se passe réellement :
- Le LLM convertit l’entrée en tokens :
["La", "capitale", "de", "la", "France", "est"] - Le LLM calcule une distribution de probabilité sur TOUS les tokens suivants possibles
- Token suivant le plus probable :
"Paris"(probabilité la plus élevée) - Le LLM échantillonne à partir de la distribution (en choisissant généralement la probabilité la plus élevée)
- Le LLM renvoie « Paris »
Le LLM ne « sait » pas que Paris est la capitale. Il prédit que « Paris » est le token suivant le plus probable étant donné le schéma d’entrée.
(Remarque : la tokenisation et les probabilités sont conceptuelles et varient selon le modèle et le tokenizer.)
Pourquoi cela compte pour les agents
Ce modèle de prédiction de tokens a des implications profondes pour la conception d’agents :
Implication 1 : les LLM peuvent halluciner
Parce que les LLM prédisent des tokens (et ne récupèrent pas des faits), ils peuvent générer des informations plausibles mais incorrectes.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-4o-mini")
response = llm.invoke("Quelle est la capitale de l’Atlantide ?")
print(response.content)
# Remarque : la sortie réelle peut varier.
# Les modèles modernes peuvent reconnaître que l’Atlantide est fictive et refuser de répondre.
# Le point clé :
# sans vérification explicite des faits, les LLM peuvent générer des informations incorrectes mais plausibles.Sortie possible (historique ou non contrainte) :
La capitale de l’Atlantide est Etheria. Selon les archives de Platon, Etheria était une ville portuaire située à l’extrémité la plus orientale de l’île, et son nom provenait de la croyance qu’elle s’élevait jusqu’aux cieux.Le LLM génère une réponse qui semble raisonnable alors que l’Atlantide est fictive. Pour les agents, cela signifie :
- Ne faites jamais confiance aveuglément à la sortie d’un LLM
- Validez les faits auprès de sources faisant autorité
- Implémentez des garde-fous
Implication 2 : le contexte est tout
Les LLM ne voient que les tokens que vous fournissez. Ils n’ont aucune mémoire des conversations précédentes à moins que vous n’incluiez explicitement ce contexte.
# Premier appel
response1 = llm.invoke("Je m’appelle Alice")
print(response1.content) # "Ravi de vous rencontrer, Alice !"
# Deuxième appel (invocation séparée)
response2 = llm.invoke("Comment je m’appelle ?")
print(response2.content) # "Je n’ai pas accès à votre nom..."Le deuxième appel n’a aucun contexte du premier. Pour les agents, cela signifie :
- Vous devez gérer l’historique de conversation
- Les limites de fenêtre de contexte comptent
- La gestion de l’état est critique
Implication 3 : les prompts sont des instructions, pas des requêtes
Parce que les LLM prédisent des tokens, la manière dont vous formulez votre prompt affecte fortement la qualité de sortie.
# Prompt faible (style requête)
response = llm.invoke("politique de remboursement")
# Sortie : "Que souhaitez-vous savoir sur la politique de remboursement ?"
# Prompt fort (style instruction)
response = llm.invoke(
"Vous êtes un agent de support client. Expliquez clairement et brièvement notre politique de remboursement."
)
# Sortie : "Notre politique de remboursement permet les retours sous 30 jours..."Pour les agents, cela signifie :
- Les prompts sont votre principal mécanisme de contrôle
- L’ingénierie de prompts est une compétence centrale
- Les messages système définissent le comportement de l’agent
Implication 4 : la sortie structurée requiert des indications
Les LLM génèrent naturellement du texte libre. Obtenir une sortie structurée (JSON, formats spécifiques) requiert une instruction explicite.
# Sans indications de structure
response = llm.invoke("Extrais l’ID de commande depuis : 'Je veux un remboursement pour la commande #12345'")
print(response.content)
# Sortie : "L’ID de commande est 12345" (texte libre, format incohérent)
# Avec indications de structure
response = llm.invoke(
'Extrais l’ID de commande et renvoie UNIQUEMENT un objet JSON au format : {"order_id": "..."}\n\n'
"Texte : 'Je veux un remboursement pour la commande #12345'"
)
print(response.content)
# Sortie : {"order_id": "12345"} (structuré, exploitable)Pour les agents, cela signifie :
- Utilisez des schémas pour contraindre le format de sortie
- Spécifiez explicitement les formats de sortie
- Validez et analysez les réponses du LLM
Le texte libre est optimisé pour les humains. Les agents nécessitent un formatage explicite pour garantir la lisibilité machine.
Déterminisme, aléa et LLM modernes
Les LLM sont fondamentalement des systèmes probabilistes. Ils génèrent du texte en prédisant les tokens suivants les plus probables, pas en exécutant des règles déterministes. Par conséquent, la même entrée ne garantit pas toujours la même sortie.
Dans les modèles plus anciens, les développeurs contrôlaient explicitement cette part d’aléa via des paramètres tels que temperature. Des valeurs plus faibles produisaient des sorties plus prévisibles, tandis que des valeurs plus élevées encourageaient la variation et la créativité.
De nombreux modèles axés sur le raisonnement (reasoning-oriented) modernes n’exposent pas toujours des paramètres comme temperature ou top_p. À la place, ils gèrent en interne les stratégies de décodage et d’échantillonnage afin de privilégier un raisonnement stable et structuré. Toutefois, cela ne signifie pas que ces modèles sont pleinement déterministes.
Même ces modèles ne garantissent pas des sorties identiques. Les sorties peuvent varier en raison de :
- Échantillonnage interne : le modèle peut emprunter des chemins de raisonnement différents, produisant des sorties qui varient en structure, en détail ou en formulation.
- Mises à jour du modèle : les fournisseurs mettent à jour les modèles en continu sans préavis ; le même prompt peut donc produire des réponses différentes au fil du temps.
- Filtres de sécurité : la modération de contenu peut faire que le modèle réponde directement dans un cas, mais temporise, refuse ou reformule dans un autre.
- Politiques d’outils : dans des systèmes d’agents, le modèle peut invoquer des outils différents — ou aucun outil — pour la même entrée, modifiant les chemins d’exécution.
Ce que cela signifie pour les développeurs d’agents :
La préoccupation clé n’est pas le réglage des paramètres — c’est la prévisibilité. Les systèmes d’agents doivent être conçus en partant du principe que les sorties des LLM peuvent varier en formulation, en structure, voire en conclusions, sauf si elles sont explicitement contraintes.
Cela mène à plusieurs principes de conception concrets pour les systèmes d’agents :
- Ne vous appuyez jamais sur un libellé exact pour des décisions logiques — le contrôle de flux doit dépendre de signaux structurés (schémas, enums, flags), pas de la correspondance à des phrases spécifiques dans la sortie du modèle.
- Forcez la structure aux frontières — chaque fois qu’une sortie de LLM est consommée par du code, contraignez-la via des schémas, des validateurs ou des formats stricts afin que le programme n’ait jamais à « interpréter » du texte libre.
- Vérifiez tout ce qui compte — les faits qui impactent l’argent, les permissions ou des actions irréversibles doivent être vérifiés avec des outils ou des systèmes externes, pas simplement crus sur la base du modèle.
- Traitez les sorties du LLM comme des propositions, pas des décisions — le modèle suggère quoi faire, mais le système décide si, quand et comment agir.
Dans les systèmes d’agents modernes, la fiabilité vient de la conception du système, pas du réglage des paramètres. Plus la tâche est critique, moins le modèle devrait avoir de liberté — et plus votre agent devrait imposer de structure.
Point clé : construisez des agents fiables via des schémas, de la validation et l’intégration d’outils — pas en espérant des sorties de LLM cohérentes.
Dans la section suivante, nous explorerons les implications économiques du traitement basé sur les tokens.
2.4) Tokens : la ressource fondamentale
Les tokens sont l’unité de base que les LLM traitent. Comprendre les tokens est essentiel car ils définissent à la fois ce qui est possible (contraintes) et ce qui est coûteux (coûts) dans les systèmes d’agents.
Qu’est-ce qu’un token ?
Un token est la plus petite unité de texte sur laquelle un LLM raisonne et qu’il génère.
Selon la langue et le contexte, un token peut représenter :
- Un mot (
agent) - Une partie de mot (
calculat,ion) - Un nombre ou un symbole (
#,123) - De la ponctuation ou des espaces
Les tokens ne sont ni des caractères ni des mots — ce sont des unités spécifiques au modèle créées par le tokenizer.
Chaque information envoyée au modèle ou générée par lui est mesurée en tokens :
- Instructions système
- Messages utilisateur
- Documents récupérés
- Descriptions d’outils
- Sorties du modèle
Les tokens sont la monnaie fondamentale de l’interaction avec un LLM.
Les tokens comme contrainte système
Les tokens ne sont pas seulement un coût — c’est une limite stricte sur ce que votre agent peut faire dans une seule requête.
Chaque LLM a une fenêtre de contexte : un nombre maximal fixe de tokens qu’il peut traiter en une fois.
Scénario d’exemple : Votre agent de support client a besoin de :
- Instructions système : 200 tokens
- 10 derniers messages : ~2 000 tokens
- 3 articles d’aide récupérés : ~1 500 tokens
- Réponse générée : ~200 tokens
- Total : 3 900 tokens
Si la fenêtre de contexte de votre modèle est de 4 000 tokens, vous êtes à 97,5 % de capacité. Un message long de plus et le système cesse de fonctionner.
(Les modèles modernes ont typiquement des fenêtres de contexte de 128K+ tokens, mais le principe reste le même : le contexte est fini et vous devez concevoir autour de cette limite.)
Ce qui se passe quand vous dépassez la limite :
- Les messages plus anciens sont supprimés → l’agent oublie le contexte antérieur, ce qui casse la continuité de la conversation
- Les documents récupérés sont tronqués → des informations critiques sont perdues, menant à de mauvaises réponses
- La requête échoue complètement → le système ne peut pas répondre du tout
Vous ne pouvez pas payer pour plus d’espace. La fenêtre de contexte est un plafond strict — comme essayer de faire tenir 2 litres dans une bouteille de 1 litre.
C’est pourquoi les agents de longue durée doivent gérer activement ce qui reste dans le contexte et ce qui n’y reste pas. La gestion des tokens est une préoccupation d’architecture centrale, pas un détail d’optimisation.
Ce sur quoi les tokens ont un impact direct
Au-delà de la limite immédiate de fenêtre de contexte, les tokens influencent deux décisions de conception critiques :
1. Stratégie de mémoire : historique complet vs résumé
Garder l’historique complet de conversation préserve les détails mais fait croître continuellement l’utilisation de tokens.
Une alternative courante est le résumé de la mémoire :
- Remplacer les messages plus anciens par un résumé compact
- Préserver l’intention tout en réduisant le coût en tokens
Ce compromis affecte :
- Le coût
- La précision
- La cohérence de l’agent sur le long terme
La conception de la mémoire est donc un problème de gestion des tokens.
2. Taille des chunks RAG et stratégie de récupération
En Retrieval-Augmented Generation (RAG), les documents sont découpés en chunks avant la récupération.
-
Grands chunks
- Moins d’appels de récupération
- Coût en tokens plus élevé par requête
- Plus de contexte non pertinent
-
Petits chunks
- Coût en tokens plus faible
- Précision plus élevée
- Risque de manquer des informations clés
La taille des chunks est une décision de conception critique — elle impacte directement à la fois le coût et la qualité des réponses.
Économie des tokens
Comprendre les coûts en tokens vous aide à construire des systèmes rentables.
Tarification typique (2026) :
| Modèle | Entrée (par 1M tokens) | Sortie (par 1M tokens) |
|---|---|---|
| GPT-5 | $1.25 | $10.00 |
| GPT-5-mini | $0.25 | $2.00 |
| Claude 4.5 Sonnet | $3.00 | $15.00 |
| Gemini 3 Pro | $2.00 | $12.00 |
| Gemini 3 Flash | $0.50 | $3.00 |
Idée clé : les tokens de sortie coûtent 4 à 8 fois plus cher que les tokens d’entrée, ce qui signifie qu’une génération non contrôlée est souvent le principal facteur de coût en production.
Estimation rapide des coûts :
Pour une requête typique avec 500 tokens en entrée et 50 tokens en sortie en utilisant GPT-5-mini :
- Entrée : (500 / 1 000 000) × $0.25 = $0.000125
- Sortie : (50 / 1 000 000) × $2.00 = $0.0001
- Total : ~$0.000225 par requête
À 10 000 requêtes par jour, cela représente environ 67,5 $ par mois.
Optimisation des coûts en pratique
Stratégie 1 : faire correspondre le modèle à la complexité de la tâche
Utiliser des modèles plus petits et moins chers pour les tâches simples :
from langchain_openai import ChatOpenAI
# Modèle coûteux pour du raisonnement complexe
complex_llm = ChatOpenAI(model="gpt-5")
# Modèle bon marché pour des tâches simples
simple_llm = ChatOpenAI(model="gpt-5-nano")
def get_llm_for_task(task_type):
if task_type == "complex_reasoning":
return complex_llm
else:
return simple_llmStratégie 2 : équilibrer la taille du contexte avec la qualité
N’inclure que le contexte nécessaire :
# Efficace : inclure uniquement les chunks pertinents
relevant_chunks = retrieve_top_k(user_question, k=3) # ~500 tokens
prompt = f"Contexte : {relevant_chunks}\n\nQuestion : {user_question}"Important : une réduction trop agressive du contexte peut nuire à la précision des réponses. Équilibrez les économies de coûts et la qualité.
Stratégie 3 : contrôler la longueur de sortie
Limiter la quantité de texte générée par le modèle :
# Coûts contrôlés
llm = ChatOpenAI(model="gpt-5-mini", max_tokens=100)
response = llm.invoke(messages)
# Maximum 100 tokens de sortiePoint clé : la gestion des tokens ne consiste pas seulement à réduire les coûts — il s’agit de concevoir des systèmes d’agents fiables et évolutifs sous des contraintes de ressources strictes.
Dans la section suivante, nous explorerons le paysage des modèles et apprendrons à choisir le bon modèle pour chaque tâche.
2.5) Comprendre le paysage des modèles
Choisir le bon LLM pour votre agent nécessite d’équilibrer :
- Fenêtre de contexte : quelle quantité de texte le modèle peut-il traiter ?
- Coût : combien coûte chaque requête ?
- Latence : à quelle vitesse le modèle répond-il ?
- Capacité : à quel point le modèle raisonne-t-il bien ?
Faisons un tour d’horizon du paysage des modèles en 2026.
Grandes familles de modèles
Modèles OpenAI GPT
| Modèle | Fenêtre de contexte | Coût entrée | Coût sortie | Latence | Meilleur pour |
|---|---|---|---|---|---|
| GPT-5 | 400K tokens | $1.25 / 1M | $10.00 / 1M | ~2–4s | Raisonnement complexe, code avancé |
| GPT-5-mini | 400K tokens | $0.25 / 1M | $2.00 / 1M | ~1.5–3s | Tâches générales, chat, résumé |
| GPT-5-nano | 400K tokens | $0.05 / 1M | $0.40 / 1M | ~1–2s | Classification, extraction |
Modèles Anthropic Claude
| Modèle | Fenêtre de contexte | Coût entrée | Coût sortie | Latence | Meilleur pour |
|---|---|---|---|---|---|
| Claude Opus 4.5 | 200K tokens | $5.00 / 1M | $25.00 / 1M | ~2–4s | Raisonnement profond, analyse |
| Claude Sonnet 4.5 | 200K tokens | $3.00 / 1M | $15.00 / 1M | ~1.5–3s | Performance équilibrée, programmation |
Modèles Google Gemini
| Modèle | Fenêtre de contexte | Coût entrée | Coût sortie | Latence | Meilleur pour |
|---|---|---|---|---|---|
| Gemini 3.0 Pro | 1M tokens | $2.00 / 1M | $12.00 / 1M | ~3–5s | Contexte massif, recherche |
| Gemini 3.0 Flash | 1M tokens | $0.50 / 1M | $3.00 / 1M | ~1–2s | Applications à fort débit |
Fonctionnalités spécifiques aux fournisseurs
OpenAI :
- Meilleur pour : agents généralistes, flux de travail multi-domaines, function calling
- Fort sur : raisonnement polyvalent, outils développeurs et écosystème solides, mises à jour fréquentes des modèles
Anthropic :
- Meilleur pour : flux de travail sensibles à la sécurité, raisonnement structuré et prolongé
- Fort sur : analyse profonde, sorties méthodiques, réflexion étendue avec un fort alignement
Google :
- Meilleur pour : ingestion de contextes massifs et tâches multimodales
- Fort sur : analyse de documents à grande échelle, compréhension multimodale, traitement à haut débit
Faire correspondre les modèles aux tâches
Choisissez selon la complexité de la tâche, la taille du contexte et les besoins de latence :
Par complexité de tâche
Tâches simples → modèles optimisés pour les coûts (GPT-5-nano, GPT-5-mini) :
- Classification d’intention, analyse de sentiment, extraction de mots-clés, mise en forme simple
- À choisir quand : le coût est la préoccupation principale
Tâches modérées → modèle équilibré (GPT-5-mini) :
- Questions-réponses, résumé, sélection d’outils
- À choisir quand : il faut équilibrer coût et qualité
Tâches complexes → modèles orientés performance (GPT-5, Claude Sonnet, Gemini Pro) :
- Raisonnement multi-étapes, génération de code, analyse détaillée
- À choisir quand : la capacité de raisonnement compte, le coût est acceptable
Complexité la plus élevée → modèles premium (Claude Opus) :
- Raisonnement profondément complexe, décisions critiques
- À choisir quand : la précision est primordiale, le coût est secondaire
Par besoins de latence
Temps réel (~1s ou moins de latence perçue) → modèles rapides (GPT-5-nano, Gemini Flash) :
- Chat orienté utilisateur
- Applications interactives
Quasi temps réel (1–3s) → la plupart des modèles :
- Tâches d’agents standard
Batch (>3s) → modèles capables :
- Analyse en arrière-plan
Considérations sur la fenêtre de contexte
Tâches standard : tous les grands modèles supportent 200K+ tokens, suffisant pour la plupart des flux de travail d’agents.
Cas particuliers :
- Besoin de 400K tokens : famille GPT-5 (analyse de documents complète, grandes bases de code)
- Besoin de 1M tokens : modèles Gemini (livres entiers, ensembles massifs de documents)
Conseil pratique : même avec de grandes fenêtres de contexte, la récupération sélective (RAG) produit généralement de meilleurs résultats.
Points clés
- Pas de modèle « meilleur » unique → différents modèles excellent selon les tâches
- Faites correspondre le modèle à la complexité de la tâche → ne surpayez pas pour des tâches simples
- Fenêtre de contexte ≠ mieux → utilisez une récupération sélective
- La latence affecte l’expérience utilisateur → prenez en compte le temps de réponse pour les tâches interactives
En pratique : la plupart des agents utilisent plusieurs modèles — des modèles bon marché pour les tâches simples, des modèles capables pour le raisonnement complexe. Nous implémenterons cela dans des chapitres ultérieurs.
Dans la section suivante, nous apprendrons à contrôler le comportement du modèle via un prompting efficace.
2.6) Les bases du prompting pour les systèmes d’agents
Les prompts sont votre interface principale pour contrôler le comportement d’un LLM. Pour les agents, un prompting efficace est crucial — il détermine si votre agent prend les bonnes décisions, appelle les bons outils et produit des sorties fiables.
L’anatomie d’un prompt
Un prompt complet a trois composants :
1. Message système (rôle et contraintes) Définit la persona, les capacités et les limites de l’agent.
2. Contexte (informations pertinentes) Fournit l’information nécessaire pour accomplir la tâche.
3. Instruction (tâche spécifique) Dit à l’agent exactement quoi faire.
Voyons cela en pratique :
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model="gpt-5-mini")
response = llm.invoke([
# Message système : définir le rôle et les contraintes
{
"role": "system",
"content": """Vous êtes un agent de support client pour TechCorp.
Vos capacités :
- Répondre aux questions sur les politiques de remboursement
- Créer des tickets de support
- Fournir une aide au dépannage
Vos contraintes :
- Ne répondre qu’aux questions sur les produits TechCorp
- Ne jamais faire de promesses sur les délais de remboursement
- Toujours être poli et professionnel"""
},
# Message utilisateur : Contexte + Instruction
{
"role": "user",
"content": """Contexte : le client a acheté l’ordinateur portable modèle X500 le 2026-01-15.
Aujourd’hui, nous sommes le 2026-02-20. Notre politique de remboursement autorise les retours sous 30 jours.
Instruction : le client veut un remboursement. Que dois-je lui dire ?"""
}
])
print(response.content)Sortie :
Je comprends que vous souhaitez un remboursement pour votre ordinateur portable modèle X500. Malheureusement,
comme votre achat date du 15 janvier et qu’aujourd’hui nous sommes le 20 février, nous sommes
au-delà de notre fenêtre de retour de 30 jours. Toutefois, je peux créer un ticket de support
afin d’explorer d’autres options, comme le service de garantie ou un échange.
Souhaitez-vous que je procède ainsi ?Messages système : définir le comportement de l’agent
Le message système est l’endroit où vous définissez la personnalité et les capacités de votre agent. C’est la partie la plus importante du prompting d’agent.
Message système faible :
system_message = "Vous êtes un assistant utile."Message système fort :
system_message = """Vous êtes un agent de support client pour TechCorp.
RÔLE :
Vous aidez les clients avec des demandes de remboursement, des questions produit et des problèmes techniques.
CAPACITÉS :
- Répondre aux questions en utilisant la documentation fournie
- Créer des tickets de support si nécessaire
- Fournir un dépannage étape par étape
CONTRAINTES :
- Ne répondre qu’aux questions sur les produits TechCorp
- Si vous ne savez pas quelque chose, dites-le — ne devinez jamais
- Citez toujours les sources lorsque vous utilisez la documentation
- Ne promettez jamais de délais ou de résultats spécifiques
TON :
Professionnel, empathique et orienté solution.
"""Pourquoi la version forte fonctionne mieux :
- Capacités explicites → l’agent sait ce qu’il peut faire
- Contraintes claires → l’agent sait ce qu’il doit éviter
- Ton défini → personnalité cohérente
- Instructions spécifiques → réduit l’ambiguïté
Clarté des instructions : soyez spécifique
Les LLM suivent les instructions littéralement. Des instructions vagues produisent des résultats peu fiables.
Instruction vague :
instruction = "Aidez le client avec son remboursement."Instruction spécifique :
instruction = """Analysez la demande du client et déterminez :
1. La commande est-elle éligible au remboursement ? (Vérifiez la date d’achat vs la politique de remboursement)
2. Si éligible : expliquez le processus de remboursement
3. Si non éligible : expliquez pourquoi et proposez des alternatives
Formatez votre réponse ainsi :
- Éligibilité : [OUI/NON]
- Raison : [Explication brève]
- Prochaines étapes : [Ce que le client doit faire]
"""Gestion du contexte : fournissez ce qui est nécessaire
Les agents ont besoin de contexte pour prendre des décisions, mais trop de contexte gaspille des tokens et embrouille le modèle.
Trop de contexte (gaspillage) :
context = f"""
Historique de l’entreprise : TechCorp a été fondée en 1995...
Catalogue produits : nous vendons plus de 500 produits, dont...
Politique de remboursement : {refund_policy_text}
Politique d’expédition : {shipping_policy_text}
Politique de garantie : {warranty_policy_text}
Historique client : {full_customer_history}
"""
# 5000+ tokens, la plupart non pertinentsContexte sélectif (efficace) :
context = f"""
Politique pertinente : {refund_policy_text}
Détails de la commande : {order_details}
"""
# 200 tokens, tous pertinentsFormatage de sortie : structurez vos réponses
Pour les agents, vous avez souvent besoin d’une sortie structurée (JSON, formats spécifiques) plutôt que du texte libre.
Sortie non structurée (difficile à parser) :
response = llm.invoke([
{"role": "system", "content": "Vous êtes un agent de support."},
{"role": "user", "content": "Devons-nous créer un ticket pour cette demande de remboursement ?"}
])
print(response.content)
# Sortie : "Oui, je pense que nous devrions créer un ticket parce que..."
# Problème : difficile à parser, format incohérentSortie structurée (facile à parser) :
response = llm.invoke([
{"role": "system", "content": """Vous êtes un agent de support.
Répondez toujours dans ce format JSON :
{
"action": "CREATE_TICKET" or "ANSWER_QUESTION" or "ESCALATE",
"reason": "Brève explication",
"response_text": "Quoi dire au client"
}"""},
{"role": "user", "content": "Le client veut un remboursement pour la commande #12345, achetée il y a 40 jours"}
])
print(response.content)Sortie :
{
"action": "CREATE_TICKET",
"reason": "La commande est en dehors de la fenêtre de remboursement de 30 jours, nécessite une revue manuelle",
"response_text": "J’ai créé un ticket de support pour examiner votre demande de remboursement. Notre équipe vous contactera sous 24 heures."
}Nous utiliserons des schémas Pydantic pour une sortie structurée robuste au Chapitre 7.
Exemples few-shot : montrez, ne dites pas seulement
Pour des tâches complexes, fournir des exemples est plus efficace que de longues instructions.
Zero-shot (instructions uniquement) :
prompt = """Extraire l’ID de commande, le nom du produit et le problème depuis des messages client.
Message client : "Mon laptop X500, commande #12345, ne s’allume pas"
"""
# Le modèle peut avoir du mal avec le formatFew-shot (avec exemples) :
prompt = """Extraire l’ID de commande, le nom du produit et le problème depuis des messages client.
Exemple 1 :
Entrée : "Ma commande #12345 pour un laptop X500 ne s’allume pas"
Sortie : {"order_id": "12345", "product": "laptop X500", "issue": "ne s’allume pas"}
Exemple 2 :
Entrée : "Commande 67890 - téléphone qui ne charge pas"
Sortie : {"order_id": "67890", "product": "phone", "issue": "not charging"}
Maintenant, extraire depuis ce message :
Entrée : "Ma commande #11111 pour une tablette a l’écran fissuré"
Sortie :
"""Le modèle apprend le pattern à partir des exemples et l’applique de manière cohérente.
Patterns de prompt engineering pour les agents
Pattern 1 : Chain of Thought (raisonnement)
Pour des décisions complexes, demandez au modèle de « penser étape par étape » :
prompt = """Vous devez décider s’il faut créer un ticket de support.
Réfléchissez étape par étape :
1. Que demande le client ?
2. Peut-on répondre avec la documentation existante ?
3. Est-ce que cela nécessite une intervention manuelle ?
4. Quelle action devons-nous entreprendre ?
Message client : "Je veux un remboursement pour la commande #12345 mais j’ai perdu le reçu"
Raisonnement :
"""Le modèle montrera explicitement son raisonnement, rendant les décisions plus transparentes et fiables.
Pattern 2 : génération contrainte (sécurité)
Limitez les sorties possibles du modèle :
prompt = """Classifiez l’intention du client. Répondez avec EXACTEMENT UNE des options suivantes :
- REFUND_REQUEST
- PRODUCT_QUESTION
- TECHNICAL_ISSUE
- OFF_TOPIC
Message client : "Comment réinitialiser mon mot de passe ?"
Classification :
"""Cela empêche le modèle de générer des sorties inattendues. En contraignant explicitement les options de sortie :
- Évite les erreurs de parsing (toujours une des quatre options définies)
- Bloque les actions non intentionnelles (empêche l’exécution d’actions non définies)
- Simplifie le débogage (un espace de sortie limité rend les problèmes plus faciles à tracer)
Pattern 3 : auto-critique (qualité)
Demandez au modèle de vérifier et d’améliorer sa propre sortie :
prompt = """Générez une réponse au client, puis critiquez-la.
Message client : "Je veux un remboursement"
Étape 1 - Générer la réponse :
[Votre réponse ici]
Étape 2 - Critique :
- Cette réponse est-elle exacte ?
- Est-elle utile ?
- Respecte-t-elle la politique de l’entreprise ?
- Qu’est-ce qui pourrait être amélioré ?
Étape 3 - Réponse finale (en incorporant la critique) :
[Réponse améliorée ici]
"""Cette approche en plusieurs étapes produit souvent des sorties de meilleure qualité car :
- Détecte les erreurs tôt : le modèle revoit son propre raisonnement avant de finaliser
- Améliore le ton et la clarté : l’auto-réflexion aide à repérer un langage flou ou inadapté
- Assure la conformité à la politique : l’étape de critique vérifie le respect des directives
Erreurs courantes de prompting
Erreur 1 : supposer que le modèle « sait » des choses
Les LLM n’ont pas accès à des informations en temps réel ni à un contexte implicite. Fournissez toujours explicitement toutes les données nécessaires.
# Mauvais : suppose que le modèle connaît la date actuelle
prompt = "Cette commande est-elle éligible au remboursement ? Commande #12345"
# Bon : fournir toutes les informations nécessaires
prompt = f"""Cette commande est-elle éligible au remboursement ?
Commande #12345
achetée {purchase_date}
Aujourd’hui : {current_date}
Politique : retours sous 30 jours
"""Erreur 2 : instructions ambiguës
Des verbes vagues comme « gérer », « traiter » ou « s’occuper de » laissent trop de place à l’interprétation. Soyez explicite sur l’action exacte nécessaire.
# Mauvais : que signifie "gérer" ?
prompt = "Gérez cette demande de remboursement"
# Bon : action explicite
prompt = "Déterminez si cette demande de remboursement est éligible. Si oui, créez un ticket. Si non, expliquez pourquoi."Erreur 3 : surcharge de contexte
Inclure des informations non pertinentes gaspille des tokens, augmente la latence et peut embrouiller le modèle. Ne récupérez que ce qui est nécessaire pour la tâche spécifique.
# Mauvais : 10 000 tokens de contexte, la plupart non pertinents
prompt = f"""
{entire_knowledge_base}
Question : Quelle est la politique de remboursement ?
"""
# Bon : ne récupérer que la section pertinente
prompt = f"""
{refund_policy_section}
Question : Quelle est la politique de remboursement ?
"""Erreur 4 : formatage incohérent
Quand le format de sortie varie, le code en aval casse. Spécifiez toujours le format exact attendu, surtout pour des données structurées.
# Mauvais : parfois JSON, parfois texte libre
prompt = "Répondez avec votre décision"
# Bon : toujours spécifier le format
prompt = 'Répondez en format JSON : {"decision": "...", "reason": "..."}'Points clés
Un prompting efficace pour les agents requiert :
- Messages système clairs → définir rôle, capacités, contraintes
- Instructions spécifiques → dire au modèle exactement quoi faire
- Contexte sélectif → fournir uniquement l’information pertinente
- Sortie structurée → spécifier explicitement le format
- Exemples few-shot → montrer le pattern souhaité
- Prompts de raisonnement → demander une réflexion étape par étape
Le prompting est une compétence qui s’améliore avec la pratique. Tout au long de ce livre, vous verrez ces patterns appliqués dans des systèmes d’agents réels.
Résumé du chapitre
Vous comprenez maintenant les concepts fondamentaux pour construire des systèmes d’IA agentique :
IA agentique vs chatbots :
- Les agents agissent de manière autonome pour atteindre des objectifs
- Les agents utilisent des outils et prennent des décisions multi-étapes
- Les agents maintiennent un état et s’adaptent en fonction des résultats
Pourquoi les frameworks comptent :
- LangChain fournit l’abstraction de modèle, des chaînes, de la mémoire, des outils et du RAG
- LangGraph ajoute la gestion d’état, le routage et le checkpointing
- Les frameworks réduisent le boilerplate (code répétitif) et permettent des flux de travail complexes
Mécanique des LLM :
- Les LLM prédisent des tokens, ils ne récupèrent pas des faits
- Le contexte est explicite, pas implicite
- Les prompts sont des instructions, pas des requêtes
- La sortie structurée requiert des indications
- Les fenêtres de contexte sont finies
Économie des tokens :
- Les tokens de sortie coûtent 4 à 8 fois plus cher que les tokens d’entrée
- Le choix du modèle a un impact de coût de 10 à 100 fois
- Le contexte sélectif réduit drastiquement les coûts
- La surveillance et les budgets évitent des dépenses incontrôlées
Sélection de modèle :
- Faites correspondre le modèle à la complexité de la tâche
- Tenez compte des besoins de contexte et des contraintes de latence
- Utilisez des architectures multi-modèles pour optimiser les coûts
- Commencez avec un modèle économique, montez en gamme uniquement si nécessaire
Fondamentaux du prompting :
- Les messages système définissent le comportement de l’agent
- Des instructions spécifiques produisent des résultats fiables
- Le contexte sélectif améliore la qualité et réduit les coûts
- La sortie structurée permet le parsing et la validation
- Les exemples few-shot enseignent efficacement des patterns