Introduction : L'écosystème LangChain en 2026
LangChain est le framework open-source le plus répandu au monde pour construire des applications basées sur les Large Language Models. Né fin 2022 comme bibliothèque Python pour enchaîner les appels aux LLM, il a évolué en un écosystème complet qui comprend l'orchestration d'agents, la gestion de la mémoire, l'intégration avec des centaines d'outils externes et le support natif pour la Retrieval-Augmented Generation (RAG). En 2026, l'écosystème LangChain n'est plus une simple bibliothèque : c'est une constellation de paquets modulaires conçus pour aborder chaque aspect du développement d'applications IA, du prototypage rapide à la production enterprise.
Le changement le plus significatif dans le paysage de LangChain a été le passage deAgentExécuteur, l'ancien système d'orchestration des agents, à LangGraph, une bibliothèque basée sur les graphiques Ici, vous pouvez créer un flux de travail dynamique, cyclique et hautement personnalisé. Avec la sortie de LangGraph 1.0, l'architecture des agents a radicalement changé : les lignes fluides et les rigidités de l'AgentExecutor non remplacé par des graphiques orientés prenant en charge les conditions de branchement, la persistance et l'interaction homme-machine indigène.
Dans cet article, nous explorerons en profondeur l'ensemble du stack LangChain : de l'architecture modulaire aux tools personnalisés, du Chain Design aux patterns avancés de gestion des erreurs. Nous verrons comment AgentExecutor fonctionnait et pourquoi il a été dépassé, pour ensuite nous concentrer sur LangGraph et sur la construction d'agents modernes, robustes et prêts pour la production.
Ce que vous apprendrez dans cet article
- L'architecture modulaire de LangChain : LLMs, Chains, Agents, Tools et Memory
- Pourquoi AgentExecutor est déprécié et quelles étaient ses limites
- Comment LangGraph résout les problèmes d'AgentExecutor avec une architecture basée sur les graphes
- Comment définir des Custom Tools avec le décorateur
@toolet les type hints - Patterns de Chain Design : sequential, parallel et conditional branching avec LCEL
- Intégration avec différents fournisseurs LLM (OpenAI, Anthropic Claude, Ollama)
- Stratégies de gestion des erreurs : retry, fallback, timeout et limites d'itération
- Étude de cas complète : un assistant de recherche autonome de bout en bout
L'architecture LangChain
L'écosystème LangChain est organisé en modules séparés qui collaborent entre eux. Comprendre cette architecture est fondamental pour choisir les bons composants pour chaque projet et pour éviter d'importer des fonctionnalités non nécessaires.
Les composants fondamentaux
L'architecture s'articule en cinq piliers principaux, chacun avec un rôle bien défini dans l'écosystème global :
- LLMs et Chat Models : wrappers standardisés pour les modèles de langage. Ils fournissent une interface uniforme indépendamment du fournisseur (OpenAI, Anthropic, Google, modèles locaux via Ollama). Les classes
ChatOpenAI,ChatAnthropicouChatOllamaexposent toutes la même méthode.invoke() - Modèles d'invitations : Systèmes de création de modèles pour créer des invitations structurées. Prenez en charge les variables dynamiques, les exemples simples et les messages système. Le type le plus courant est
ChatPromptTemplateVoici les listes de messages typiques - Chains : compositions de composants en séquences de traitement. Une chain prend une entrée, la fait passer à travers une série de transformations et produit une sortie. Avec LCEL (LangChain Expression Language), les chains se composent avec l'opérateur pipe
| - Outils : fonctions que les agents peuvent invoquer pour interagir avec le monde extérieur. L'outil a un nom, une description et un schéma JSON qui y est défini avec les paramètres acceptés. Le LLM utilise la description pour décider quand et comment appeler l'outil
- Memory : systèmes pour maintenir le contexte entre les interactions. De la simple historique des messages (
ConversationBufferMemory) à des systèmes plus sophistiqués commeConversationSummaryMemoryqui résume les conversations précédentes pour économiser des tokens
La structure des colis
Paquets de l'écosystème LangChain
| Paquet | Objectif | Exemple d'utilisation |
|---|---|---|
langchain-core |
Interfaces de base, LCEL, schéma | Runnables, PromptTemplate, BaseMessage |
langchain |
Chains, agents, retrieval | AgentExecutor (legacy), RetrievalQA |
langchain-openai |
Intégration OpenAI | ChatOpenAI, OpenAIEmbeddings |
langchain-anthropic |
Intégration Anthropic | ChatAnthropic |
langchain-community |
Intégrations communautaires | ChatOllama, WikipediaLoader |
langgraph |
Agents basés sur les graphes | StateGraph, MessageGraph |
langsmith |
Observabilité et tracing | Debugging, monitoring, évaluation |
AgentExecutor : Le passé
Pour comprendre les fonctionnalités de l’architecture LangChain, il est essentiel de comprendre ce que nous faisons.
AgentExecutor The s'agit du composant standard pour orchestrar les agents LLM sur LangChain.
La fonction est basée sur un simple bouton : le modèle reçoit une invitation, décide laquelle invoquer,
l'exécuteur exécute l'outil, les résultats sont ajoutés au contexte et le cycle est recommandé tout comme
les modèles décident de donner une réponse définitive.
Comment fonctionnait AgentExecutor
Le cycle d'exécution d'AgentExecutor suit un schéma rigide en quatre phases juste pour obtenir une réponse finale ou pour dépasser le nombre maximum d'itérations :
- Raisonnement : les modèles analysent l'invitation et le contexte pertinent pour décider de la prochaine action
- Sélection d'actions : le modèle choisit une tenue et des paramètres généraux d'entrée
- Exécution : l'exécuteur invoque l'outil avec les paramètres généraux
- Observation : le résultat du tool était ajouté à l'historique des messages comme "observation" et la boucle recommençait au point 1
# Exemple legacy avec AgentExecutor (DÉPRÉCIÉ dans LangChain 0.2+)
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
# Setup
llm = ChatOpenAI(model="gpt-4")
prompt = ChatPromptTemplate.from_messages([
("system", "Tu es un assistant utile avec accès à des outils."),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{input}"),
MessagesPlaceholder(variable_name="agent_scratchpad"),
])
# Création de l'agent et de l'executor
agent = create_openai_tools_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# Exécution
result = executor.invoke({"input": "Cherche des informations sur Python 3.12"})
Pourquoi AgentExecutor a été déprécié
Malgré sa simplicité, AgentExecutor présentait des limitations structurelles qui le rendaient inadapté aux applications complexes et aux environnements de production. Ces limitations ont motivé le développement de LangGraph comme son successeur :
- Flux exclusivement linéaires : AgentExecutor ne supportait qu'un cycle séquentiel de reasoning-action-observation, rendant impossible l'implémentation de branching conditionnel, d'exécution parallèle de tools ou de sous-graphes imbriqués
- Faible modularité : personnaliser le comportement de l'agent nécessitait de surcharger des méthodes internes ou de créer des sous-classes, avec le risque de casser la compatibilité avec les mises à jour futures
- Débogage opaque : les flux internes sont difficiles à retracer. L'option
verbose=TrueProduire des journaux de texte non structurés, inadaptés au débogage en production - Ce n'était pas persistant : l'état de l'agent n'existait qu'en mémoire pendant l'exécution. Il n'est pas possible de surveiller les points de contrôle, de reprendre des exécutions interrompues ou de démarrer des conversations multi-sessions sans solutions personnalisées.
- Aucun support human-in-the-loop : il n'y avait pas de mécanisme natif pour demander confirmation à l'utilisateur avant d'exécuter des actions critiques, une exigence fondamentale pour les applications professionnelles
Notes sur la migration
Si vous avez un code utile existant AgentExecutor, LangChain fournit un guide officiel
migration vers LangGraph. L'équipe LangChain recommande aux utilisateurs create_react_agent de
LangGraph peut être remplacé directement, avec la possibilité de personnaliser le flux de la suite. Le
La migration est généralement simple pour les cas d'utilisation de base, mais offre des opportunités d'amélioration significatives
pour les cas les plus complexes.
LangGraph : Le présent et le futur
LangGraph est la bibliothèque officielle de LangChain pour construire des agents basés sur des graphes. Publiée en version stable avec la version 1.0, elle représente un changement de paradigme fondamental : au lieu d'une boucle linéaire, l'exécution de l'agent est modélisée comme un graphe dirigé où les nœuds représentent des opérations (appels LLM, exécution de tools, logique personnalisée) et les arêtes définissent le flux entre les opérations, incluant le branching conditionnel et les cycles.
Concepts clés de LangGraph
- Conception graphique : le conteneur principal définit le graphe de l'agent. Il accepte un type de statut (typiquement a
TypedDict) représentative des femmes qui circulent ici dans le graphique et sont modifiées pour l'actualité - Nœuds (Nodes) : fonctions Python qui reçoivent l'état actuel, exécutent une opération et retournent une mise à jour partielle de l'état. Chaque nœud est une fonction pure avec une tâche spécifique
- Arètes (Bords) : connexions entre nœuds qui absorbent le flux d'exécution. Les arêtes peuvent être statiques (toujours le même chemin) ou conditionnelles (le chemin dépend de l'état actuel)
- Checkpointing : LangGraph sauvegarde automatiquement l'état après chaque nœud, permettant de reprendre l'exécution depuis n'importe quel point, d'implémenter des rollbacks et de supporter des conversations persistantes multi-sessions
- Human-in-the-loop : mécanisme natif pour interrompre l'exécution du graphe, présenter l'état à l'utilisateur et reprendre après approbation. Implémenté avec
interrupt()et des breakpoints configurables
Architecture d'un agent LangGraph
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langchain_openai import ChatOpenAI
# 1. Définition du modèle avec les tools
llm = ChatOpenAI(model="gpt-4o")
llm_with_tools = llm.bind_tools(tools)
# 2. Définition des nœuds
def call_model(state: MessagesState):
"""Nœud qui appelle le modèle LLM."""
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
# 3. Construction du graphe
graph_builder = StateGraph(MessagesState)
# Ajout des nœuds
graph_builder.add_node("agent", call_model)
graph_builder.add_node("tools", ToolNode(tools=tools))
# Définition des arêtes
graph_builder.add_edge(START, "agent")
graph_builder.add_conditional_edges(
"agent",
tools_condition, # Si le modèle appelle des tools -> nœud "tools", sinon -> END
)
graph_builder.add_edge("tools", "agent") # Après les tools, retour à l'agent
# 4. Compilation du graphe
agent = graph_builder.compile()
# 5. Exécution
result = agent.invoke(
{"messages": [("user", "Cherche des informations sur Python 3.12")]}
)
Dans cet exemple, les graphiques de deux notes : agent (appelé ici le modèle) et tools
(exécuter les outils appelés pour le modèle). L'arête conditionnelle tools_condition vérifie ici
la réponse au modèle contient les appels aux outils : oui, le flux passe tools, sinon
le si (END). Après l'exécution des outils, le flux revient au nouveau agent,
créant le cycle nécessaire au raisonnement itératif.
État persistant et checkpointing
L'une des caractéristiques les plus puissantes de LangGraph est le support natif de l'état persistant.
En utilisant un MemorySaver ou un backend de persistance comme PostgreSQL, chaque exécution
du graphe est automatiquement sauvegardée et peut être reprise à tout moment :
from langgraph.checkpoint.memory import MemorySaver
# Compilation avec mémoire persistante
checkpointer = MemorySaver()
agent = graph_builder.compile(checkpointer=checkpointer)
# Première conversation
config = {"configurable": {"thread_id": "user-123"}}
result1 = agent.invoke(
{"messages": [("user", "Je m'appelle Federico")]},
config=config,
)
# Deuxième conversation - l'agent se souvient du contexte précédent
result2 = agent.invoke(
{"messages": [("user", "Comment je m'appelle ?")]},
config=config, # Même thread_id
)
# L'agent répondra "Tu t'appelles Federico"
Human-in-the-loop natif
LangGraph supporte l'interruption du flux pour demander l'approbation humaine avant d'exécuter des actions potentiellement dangereuses. Ce mécanisme est fondamental pour les applications professionnelles où des actions comme l'envoi d'emails, les modifications de base de données ou les transactions financières nécessitent une supervision :
from langgraph.types import interrupt, Command
def human_approval_node(state: MessagesState):
"""Nœud qui demande l'approbation humaine."""
last_message = state["messages"][-1]
# Interrompt le graphe et présente l'action à l'utilisateur
approval = interrupt(
{"action": "send_email", "details": last_message.content}
)
if approval == "approved":
return {"messages": [("system", "Action approuvée par l'utilisateur.")]}
else:
return {"messages": [("system", "Action refusée par l'utilisateur.")]}
Définir des Custom Tools
Les outils sont le pont entre l’intelligence du LLM et le monde extérieur. Un outil ici fonctionne bien permet à l'agent de mener des actions concrètes : rechercher des informations sur le web, interroger une base de femmes, lire des fichiers, envoyer des notifications ou interagir avec des API externes. La qualité des outils se définit directement les capacités de l'agent.
Les décorateurs @tool
La manière la plus simple et la plus idiomatique de créer une tenue dans LangChain est le décorateur. @tool.
Ce décorateur transforme une fonction Python ordinaire en un outil utile pour les agents,
extraire automatiquement le nom, la description et le schéma des paramètres de la même fonction.
Les indications de type sont essentielles : LangChain est utilisé pour générer le schéma JSON utilisé pour le modèle.
Pour comprendre les commentaires, veuillez vous référer à l'outil.
from langchain_core.tools import tool
@tool
def web_search(query: str, max_results: int = 5) -> str:
"""Recherche des informations sur le web en utilisant un moteur de recherche.
Args:
query: La requête de recherche à exécuter.
max_results: Nombre maximum de résultats à retourner (défaut : 5).
Returns:
Un string avec les résultats de recherche formatés.
"""
# Implémentation de la recherche (ex. avec Tavily, SerpAPI, etc.)
from tavily import TavilyClient
client = TavilyClient()
results = client.search(query, max_results=max_results)
formatted = []
for r in results["results"]:
formatted.append(f"- {r['title']}: {r['content'][:200]}")
return "\n".join(formatted) if formatted else "Aucun résultat trouvé."
La description est une critique
La docstring de la fonction est utilisée comme d'habitude description de l'outil envoyé au LLM. Une description claire, concise et précise est fondamentale : le modèle la lit pour décider quand invoquer les outils et commenter fournir les paramètres. Des descriptions vagues conduisent à d'invocations erronées ou manquantes. Écrivez la docstring car vous aimeriez apprendre l'explication d'un collègue ici la fonction et quand l'utilisateur.
Tools avancés : Base de données et système de fichiers
import sqlite3
from langchain_core.tools import tool
from typing import Optional
@tool
def query_database(sql_query: str, database_path: str = "app.db") -> str:
"""Exécute une requête SQL SELECT sur une base de données SQLite.
IMPORTANT : N'exécute que des requêtes de lecture (SELECT). Ne pas exécuter
d'INSERT, UPDATE, DELETE ou autres requêtes de modification.
Args:
sql_query: La requête SQL SELECT à exécuter.
database_path: Chemin vers le fichier de base de données SQLite.
Returns:
Les résultats de la requête formatés en tableau textuel.
"""
if not sql_query.strip().upper().startswith("SELECT"):
return "Erreur : Seules les requêtes SELECT sont autorisées pour des raisons de sécurité."
try:
conn = sqlite3.connect(database_path)
cursor = conn.execute(sql_query)
columns = [desc[0] for desc in cursor.description]
rows = cursor.fetchall()
conn.close()
if not rows:
return "La requête n'a retourné aucun résultat."
# Formatage tabulaire
header = " | ".join(columns)
separator = "-" * len(header)
body = "\n".join(" | ".join(str(v) for v in row) for row in rows)
return f"{header}\n{separator}\n{body}"
except Exception as e:
return f"Erreur dans l'exécution de la requête : {str(e)}"
@tool
def read_file(file_path: str, max_lines: Optional[int] = None) -> str:
"""Lit le contenu d'un fichier texte depuis le système de fichiers.
Args:
file_path: Chemin complet vers le fichier à lire.
max_lines: Nombre maximum de lignes à lire (toutes si non spécifié).
Returns:
Le contenu du fichier comme string, ou un message d'erreur.
"""
try:
with open(file_path, "r", encoding="utf-8") as f:
if max_lines:
lines = [next(f) for _ in range(max_lines)]
content = "".join(lines)
else:
content = f.read()
if len(content) > 10000:
content = content[:10000] + "\n... [tronqué à 10000 caractères]"
return content
except FileNotFoundError:
return f"Fichier non trouvé : {file_path}"
except Exception as e:
return f"Erreur dans la lecture du fichier : {str(e)}"
Patterns de Chain Design
Les chains sont des compositions de composants qui transforment une entrée en sortie à travers une série
d'étapes. Avec l'introduction de LCEL (LangChain Expression Language), la composition
des chains est devenue déclarative et intuitive, utilisant l'opérateur pipe | pour
connecter les composants.
Sequential Chains
Le modèle le plus simple : un composant reçoit la sortie du précédent et produit une entrée pour le suivant. Ceci est idéal pour les pipelines linéaires qui, à un moment donné, dépendent du précédent.
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
llm = ChatOpenAI(model="gpt-4o")
# Chain séquentielle : prompt -> LLM -> parser
chain = (
ChatPromptTemplate.from_messages([
("system", "Tu es un expert en technologie. Réponds de manière concise."),
("human", "{question}"),
])
| llm
| StrOutputParser()
)
result = chain.invoke({"question": "Qu'est-ce que LangGraph ?"})
Exécution parallèle
Quand différentes opérations sont indépendantes les unes des autres, il est possible de les exécuter en parallèle en utilisant
RunnableParallel. Ce pattern réduit significativement la latence quand il faut
exécuter plusieurs appels LLM ou tools simultanément :
from langchain_core.runnables import RunnableParallel
# Exécution parallèle : trois analyses différentes sur la même entrée
analysis_chain = RunnableParallel(
summary=ChatPromptTemplate.from_template(
"Résume en 2 phrases : {text}"
) | llm | StrOutputParser(),
sentiment=ChatPromptTemplate.from_template(
"Analyse le sentiment (positif/négatif/neutre) : {text}"
) | llm | StrOutputParser(),
keywords=ChatPromptTemplate.from_template(
"Extrais 5 mots-clés : {text}"
) | llm | StrOutputParser(),
)
# Les trois analyses sont exécutées simultanément
results = analysis_chain.invoke({"text": "Le nouveau framework..."})
# results = {"summary": "...", "sentiment": "...", "keywords": "..."}
Conditional Branching
Les conditions de branchement vous permettent de choisir différents chemins selon le contenu de l'entrée
ou des résultats intermédiaires. Avec RunnableBranch, sur la définition des conditions et des chaînes correspondantes :
from langchain_core.runnables import RunnableBranch
# Branching basé sur la langue de l'entrée
branch = RunnableBranch(
(
lambda x: "français" in x["language"].lower(),
ChatPromptTemplate.from_template(
"Réponds en français : {question}"
) | llm | StrOutputParser(),
),
(
lambda x: "anglais" in x["language"].lower(),
ChatPromptTemplate.from_template(
"Answer in English: {question}"
) | llm | StrOutputParser(),
),
# Branche par défaut
ChatPromptTemplate.from_template(
"Answer: {question}"
) | llm | StrOutputParser(),
)
Intégration LLM
LangChain prend en charge les cours LLM de quatre ans via les packages actuels. Normalisation L'interface vous permet de changer de modèle avec une seule ligne de code, ce qui facilite tester différents fournisseurs ou mettre en œuvre des stratégies de repli.
Setup avec différents fournisseurs
# OpenAI
from langchain_openai import ChatOpenAI
llm_openai = ChatOpenAI(
model="gpt-4o",
temperature=0.7,
max_tokens=4096,
)
# Anthropic Claude
from langchain_anthropic import ChatAnthropic
llm_claude = ChatAnthropic(
model="claude-sonnet-4-20250514",
temperature=0.7,
max_tokens=4096,
)
# Ollama (modèles locaux)
from langchain_community.chat_models import ChatOllama
llm_local = ChatOllama(
model="llama3.1:8b",
temperature=0.7,
base_url="http://localhost:11434",
)
Stratégie de fallback
Une stratégie de représentation vous permet de passer automatiquement à un modèle alternatif précédent le fournisseur principal n'est pas disponible. C’est fondamental pour la résilience de la production :
# Fallback : si OpenAI échoue, utiliser Claude ; si Claude échoue, utiliser Ollama
llm_with_fallback = llm_openai.with_fallbacks(
[llm_claude, llm_local]
)
# La chain utilisera automatiquement le premier modèle disponible
chain = prompt | llm_with_fallback | StrOutputParser()
Gestion des erreurs
La gestion des erreurs est un aspect critique dans le développement d’agents IA. Un agent en production doit gérer élégamment les erreurs réseau, les timeouts, les réponses malformées et les limitations d’itération. LangChain et LangGraph proposent différents mécanismes pour créer des agents résilients.
Logique de retry
La méthode .with_retry() aide automatiquement la logique des nouvelles tentatives pour le faire
Composant exécutable, avec la récompense de l'exposant d'intervalle et le nombre maximum de tentatives :
# Retry automatique avec backoff exponentiel
llm_resilient = llm_openai.with_retry(
stop_after_attempt=3,
wait_exponential_jitter=True,
retry_if_exception_type=(TimeoutError, ConnectionError),
)
# Combinaison retry + fallback pour une résilience maximale
llm_production = llm_openai.with_retry(
stop_after_attempt=2
).with_fallbacks(
[llm_claude.with_retry(stop_after_attempt=2)]
)
Limites d'itération et timeout
Dans le LangGraph, il est possible de limiter le nombre de passages (qui ne sont plus visités) pour éviter les boucles infinies et de vérifier les temps maximum d'exécution de l'agent :
# Configuration des limites d'exécution
config = {
"configurable": {"thread_id": "user-123"},
"recursion_limit": 25, # Maximum 25 nœuds visités
}
# Exécution avec timeout
import asyncio
async def run_with_timeout(agent, input_data, timeout_seconds=60):
"""Exécute l'agent avec un timeout global."""
try:
result = await asyncio.wait_for(
agent.ainvoke(input_data, config=config),
timeout=timeout_seconds,
)
return result
except asyncio.TimeoutError:
return {"error": "Timeout : l'agent a pris trop de temps."}
Conseils pratiques pour la gestion des erreurs
| Stratégie | Quand l'utiliser | Implémentation |
|---|---|---|
| Retry avec backoff | Erreurs transitoires (rate limit, timeout) | .with_retry() |
| Fallback de fournisseur | Indisponibilité du fournisseur principal | .with_fallbacks() |
| Limite de récursion | Prévention des boucles infinies | recursion_limit dans la configuration |
| Timeout global | Limite du temps d'exécution | asyncio.wait_for() |
| Gestion d'erreurs dans les tools | Erreurs prévisibles dans les tools | ToolException avec message de fallback |
Étude de cas : Research Assistant autonome
Pour mettre en pratique tous les concepts abordés, construisez un agent complet : un assistant de recherche autonome voici une question de recherche, rechercher des informations sur le web, analyser les résultats et générer une relation structurée. Cet exemple complet des outils personnalisés, LangGraph pour l'orchestration, un état persistant et la gestion des erreurs.
Définition des tools
from langchain_core.tools import tool
from datetime import datetime
@tool
def search_web(query: str, num_results: int = 5) -> str:
"""Recherche des informations sur le web pour une requête de recherche.
Args:
query: La requête de recherche.
num_results: Nombre de résultats à retourner.
Returns:
Résultats de recherche formatés.
"""
from tavily import TavilyClient
client = TavilyClient()
results = client.search(query, max_results=num_results)
formatted = []
for r in results["results"]:
formatted.append(
f"Titre : {r['title']}\nURL : {r['url']}\n"
f"Contenu : {r['content'][:300]}\n"
)
return "\n---\n".join(formatted)
@tool
def save_report(title: str, content: str, format: str = "markdown") -> str:
"""Sauvegarde le rapport de recherche généré dans un fichier.
Args:
title: Titre du rapport.
content: Contenu complet du rapport.
format: Format du fichier (markdown ou text).
Returns:
Confirmation de la sauvegarde avec le chemin du fichier.
"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
ext = "md" if format == "markdown" else "txt"
filename = f"report_{timestamp}.{ext}"
with open(filename, "w", encoding="utf-8") as f:
f.write(f"# {title}\n\n{content}")
return f"Rapport sauvegardé avec succès : {filename}"
tools = [search_web, save_report]
Construction du graphe de l'agent
from langgraph.graph import StateGraph, MessagesState, START, END
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
# Modèle avec tool binding
llm = ChatOpenAI(model="gpt-4o", temperature=0.3)
llm_with_tools = llm.bind_tools(tools)
# Prompt système détaillé pour le research assistant
SYSTEM_PROMPT = """Tu es un Research Assistant autonome. Ta tâche est de :
1. Analyser la question de recherche de l'utilisateur
2. Chercher des informations pertinentes sur le web (utilise plusieurs requêtes différentes)
3. Synthétiser les résultats dans un rapport structuré
4. Sauvegarder le rapport final
Suis toujours ce processus :
- Fais au moins 2-3 recherches avec des requêtes différentes pour avoir des perspectives multiples
- Cite les sources dans le rapport
- Structure le rapport avec des sections claires
- Sauvegarde le rapport quand tu as collecté assez d'informations
"""
def call_model(state: MessagesState):
"""Nœud principal : appelle le modèle avec le contexte actuel."""
messages = [("system", SYSTEM_PROMPT)] + state["messages"]
response = llm_with_tools.invoke(messages)
return {"messages": [response]}
# Construction du graphe
graph = StateGraph(MessagesState)
graph.add_node("agent", call_model)
graph.add_node("tools", ToolNode(tools=tools))
graph.add_edge(START, "agent")
graph.add_conditional_edges("agent", tools_condition)
graph.add_edge("tools", "agent")
# Compilation avec mémoire et limites
checkpointer = MemorySaver()
research_agent = graph.compile(
checkpointer=checkpointer,
)
# Exécution
config = {
"configurable": {"thread_id": "research-001"},
"recursion_limit": 30,
}
result = research_agent.invoke(
{"messages": [("user", "Analyse l'état actuel des frameworks pour agents IA en 2026")]},
config=config,
)
L'agent commencera à rechercher des informations avec des exigences différentes, analysera les résultats et générera un rapport.
structuré avec des sources citées. Grâce au checkpointing, il est possible de reprendre la conversation
chaque instant avec le mème thread_id, et l'agent se souvient de tous les contextes précédents.
Récapitulatif
Dans cet article, nous avons exploré en profondeur l'écosystème LangChain en 2026, de son architecture modulaire aux patterns les plus avancés pour la construction d'agents IA. Voici les points clés :
- Architecture modulaire : LangChain est organisé en paquets séparés (core, spécifiques aux fournisseurs, communauté) pour une flexibilité et des performances maximales
- AgentExecutor est déprécié : le système legacy présentait des limites structurelles en modularité, debugging et support d'états persistants
- LangGraph est la norme : l'architecture basée sur les graphes offre du branching conditionnel, du checkpointing natif et du human-in-the-loop intégré
- Tool design : le décorateur
@toolavec type hints et docstrings détaillés permet de créer des outils que le LLM sait utiliser efficacement - LCEL : LangChain Expression Language rend la composition des chains déclarative et lisible avec l'opérateur pipe
- Résilience : retry, fallback et timeout sont essentiels pour les agents en production
Prochain article
Dans le prochain article de la série, nous explorerons CrewAI, un framework indépendant qui adopte un paradigme complètement différent : le jeu de rôles. Nous verrons comment coordonner des équipes d'agents spécialisés où chaque agent interprète un rôle spécifique (chercheur, rédacteur, réviseur) et collabore avec les autres pour atteindre des objectifs complexes. Nous découvrirons les types de processus (Sequential, Hierarchical, Consensual) et comment CrewAI simplifie drastiquement la construction de systèmes multi-agents.







