En 2026, la valeur des systèmes IA passe massivement par les architectures agentiques — workflows multi-étapes, raisonnement long, outils externes, mémoire persistante, décisions autonomes. Nous mettons LangGraph en pivot, complété par les SDK natifs (Anthropic, OpenAI), les frameworks alternatifs (AutoGen, CrewAI, Pydantic AI), et des harness in-house sur-mesure quand le générique ne suffit pas.
Un LLM nu sait répondre à une question. Un système agentique sait résoudre un problème — planifier, exécuter, vérifier, se corriger, mobiliser des outils, persister une mémoire. C'est cette transition qui fait basculer l'IA d'assistant en exécutant.
À gauche : l'IA 2024 — un appel LLM nu, sans état ni outils. À droite : le système agentique 2026 — un graphe d'état déterministe (LangGraph) qui orchestre planification, appels d'outils, observation, réflexion, et boucle jusqu'à atteindre l'objectif.
Un appel LLM, une réponse, une fin. Pas d'outils, pas d'état, pas de vérification. Le moindre problème complexe nécessitait l'humain dans la boucle à chaque étape.
Un graphe d'état qui planifie, appelle des outils, observe, réfléchit, persiste une mémoire, se corrige. L'humain ne valide plus à chaque étape : seulement aux décisions importantes.
Modélisation explicite : nœuds, arêtes, état partagé, checkpointing. Observable, reproductible, testable. Là où LangChain Agents v1 était une boîte noire, LangGraph est un graphe que vous lisez.
Quatre primitives suffisent à modéliser n'importe quel workflow agentique : l'état, les nœuds, les arêtes (et les arêtes conditionnelles), et le checkpointing. Maîtriser ces quatre concepts, c'est maîtriser 90 % du framework.
L'état (TypedDict) circule entre les nœuds. Les arêtes conditionnelles aiguillent le flow selon l'état courant. Le checkpointer persiste chaque transition — c'est ce qui permet l'observabilité, le replay et le human-in-the-loop.
Voici un graphe LangGraph à 2 nœuds qui illustre les 4 primitives en action. L'agent analyse la question, et soit appelle un outil (RAG), soit répond directement.
from typing import TypedDict, Annotated from langgraph.graph import StateGraph, START, END from langgraph.graph.message import add_messages from langgraph.checkpoint.postgres import PostgresSaver # ① État global partagé class GraphState(TypedDict): messages: Annotated[list, add_messages] needs_tool: bool # ② Nœud "planner" — décide de la suite def planner(state: GraphState) -> GraphState: last = state["messages"][-1].content decision = llm.invoke(f"Cette question nécessite-t-elle un RAG ? {last}") return {"needs_tool": "oui" in decision.content.lower()} # ② Nœud "call_rag" — appelle le retrieval def call_rag(state: GraphState) -> GraphState: docs = retriever.invoke(state["messages"][-1].content) response = llm.invoke(f"Docs: {docs}\nRéponds.") return {"messages": [response]} # ③ Arête conditionnelle def router(state: GraphState) -> str: return "call_rag" if state["needs_tool"] else END # Construction du graphe g = StateGraph(GraphState) g.add_node("planner", planner) g.add_node("call_rag", call_rag) g.add_edge(START, "planner") g.add_conditional_edges("planner", router) g.add_edge("call_rag", END) # ④ Checkpointer pour persistance & replay checkpointer = PostgresSaver.from_conn_string(POSTGRES_URI) graph = g.compile(checkpointer=checkpointer)
Chaque appel à graph.invoke() est observable étape par étape, persisté dans le checkpointer, et reproductible à l'identique. Quand quelque chose part de travers en production, vous avez l'historique exact — pas une trace dégradée, l'état réel à chaque transition.
Chacun adresse une famille de problèmes distincte. Le choix se fait sur deux questions : est-ce que la solution se découpe en étapes prévisibles à l'avance ? et est-ce qu'il faut plusieurs spécialités ?
Le pattern le plus simple, et souvent le plus efficace. L'agent alterne pensée (« je dois chercher X »), action (appel d'outil), observation (résultat), jusqu'à atteindre la réponse.
create_react_agentL'agent commence par produire un plan explicite en plusieurs étapes, puis exécute chaque étape une par une. À chaque étape, il peut re-planifier si une étape a échoué ou si de nouvelles informations émergent.
Après chaque tentative, un nœud séparé critique le résultat (« est-ce que cette réponse est juste, complète, traçable ? »). Si la critique est négative, l'agent retravaille — jusqu'à max_iterations. Souvent réalisé via llm-as-judge avec un modèle différent du producteur.
Un agent superviseur reçoit la requête et l'aiguille vers l'agent spécialisé adéquat (researcher, coder, writer, reviewer…). Chaque worker a son propre prompt, ses propres outils, son propre modèle. Le superviseur synthétise.
command patternPas de superviseur central : chaque agent peut passer la main (handoff) à n'importe quel autre agent du swarm en fonction du contexte. Modélisé par OpenAI dans Swarm/Agents SDK et désormais natif dans LangGraph via langgraph-swarm.
Un superviseur de top niveau dirige plusieurs équipes, chacune avec son propre sous-superviseur et ses workers. Idéal pour les problèmes très larges qu'on peut décomposer en domaines (recherche / synthèse / vérification, ou métier A / métier B / métier C).
Les frameworks (LangGraph, CrewAI, AutoGen) couvrent 80 % des besoins. Mais pour les usages critiques — audit AI Act, replay forensique, contrôle de flux financier, contraintes de latence sub-second, environnements air-gap — il faut souvent concevoir un harness in-house qui s'inspire des frameworks mais reste votre code.
« Tout framework est un compromis. Quand le compromis ne tient plus, il faut savoir écrire le harness. » — Nous concevons et livrons des harness en moins de 4 semaines, basés sur des state machines explicites, observables, replayables, intégrés à votre stack MLflow / Langfuse.
Trace immuable de chaque décision, signature cryptographique des transitions, time-travel queries. Plus de boîte noire — exigence des secteurs régulés (banque, santé, défense).
Frameworks Python introduisent 50-200 ms d'overhead par nœud. Pour les usages temps-réel (trading, voix), un harness Rust/Go ou Python optimisé devient nécessaire.
Pas de packages PyPI tiers, audit ANSSI de chaque dépendance, contraintes linux/amd64 seul. Un harness in-house minimise la surface d'attaque.
Bus d'événements existant (Kafka, NATS), workflow engine maison, persistence custom. Le framework générique combat votre architecture au lieu de la servir.
max_tokens, max_steps, max_cost_eur, max_latency_ms. Un agent qui dépasse est tué, pas redémarré indéfiniment.from dataclasses import dataclass, replace from typing import Callable, Protocol import mlflow @dataclass(frozen=True) class AgentState: messages: tuple plan: str | None budget_eur: float budget_steps: int decisions: tuple class Transition(Protocol): name: str def __call__(self, state: AgentState) -> AgentState: ... class Harness: def __init__(self, transitions, router, event_store, supervisor): self.transitions = {t.name: t for t in transitions} self.router = router self.event_store = event_store self.supervisor = supervisor def step(self, state: AgentState) -> AgentState: next_name = self.router(state) if next_name == "END" or state.budget_steps <= 0: return state with mlflow.start_span(name=next_name) as span: self.event_store.append("transition_start", next_name, state) try: new_state = self.transitions[next_name](state) self.event_store.append("transition_ok", next_name, new_state) except Exception as e: self.event_store.append("transition_fail", next_name, e) return self.supervisor.handle(state, next_name, e) return replace(new_state, budget_steps=new_state.budget_steps - 1) def run(self, initial: AgentState) -> AgentState: state = initial while self.router(state) != "END" and state.budget_steps > 0: state = self.step(state) return state
Aucun framework n'est universel. Nous choisissons par cas d'usage. LangGraph reste notre pivot, mais nous utilisons régulièrement les SDK natifs (Anthropic, OpenAI) et les frameworks spécialisés.
| Framework | Force principale | Observabilité | Multi-agent | Production | Notre usage |
|---|---|---|---|---|---|
| LangGraph | State machines explicites, checkpointing, human-in-the-loop natif | native LangSmith | excellent | prod-grade | Pivot 80 % des cas |
| LangChain | Briques (LLM wrappers, retrievers, parsers) — pas pour les agents v1 | native LangSmith | via LangGraph | prod-grade | Composants RAG et tools |
| LlamaIndex | RAG premium, ingestion, indexation, agentic RAG | OK via OTel | workflows | prod-grade | RAG complexe + agentic RAG |
| AutoGen (Microsoft) | Multi-agent conversationnel, group chat | tracing partiel | cœur de cible | en montée | POC multi-agent rapide |
| CrewAI | Rôles & processus métaphoriques (équipe), DX | basique | cœur | jeune | Démos métier, POC business |
| Pydantic AI | Agents typés, validation forte, output structuré | Logfire natif | simple | prod-grade | Agents single-purpose typés |
| Anthropic Agent SDK | Tool use natif Claude, computer use, MCP | console Anthropic | simple | prod-grade | Quand tout passe par Claude |
| OpenAI Agents SDK | Successeur Swarm, handoffs natifs | Traces OpenAI | swarm | prod-grade | Swarm GPT-5 simple |
| Mastra | TypeScript-first, agents stateful, workflows | en cours | workflows | jeune | Front Next.js + agents |
① Tout passe par Claude → Anthropic Agent SDK · ② Tout passe par GPT-5 avec handoffs simples → OpenAI Agents SDK · ③ Stack TypeScript / Next.js → Mastra · ④ RAG complexe en cœur du système → LlamaIndex · ⑤ Tout le reste, et notamment les workflows critiques en production → LangGraph.
Un agent sans mémoire est un poisson rouge. Trois couches de mémoire à concevoir séparément : la mémoire de travail (état du graphe), la mémoire épisodique (historiques de conversations passées), et la mémoire sémantique (faits stables, préférences utilisateur).
Évaluer un agent, ce n'est pas évaluer une réponse : c'est évaluer une trajectoire — la séquence d'étapes prise pour atteindre un objectif. Trois familles de métriques se combinent.
Le résultat final correspond-il à l'attendu ? Mesuré par llm-as-judge (Claude Opus en évaluateur), ou par règles déterministes quand c'est possible (assertion sur la sortie).
Les bons outils ont-ils été appelés, dans le bon ordre ? L'agent a-t-il évité les détours inutiles ? Métriques : tool selection precision, path efficiency, redundant steps.
Tokens consommés (par modèle), latence end-to-end, coût €/trajectoire. Indispensable pour le pilotage en production. Tracé via MLflow + Langfuse.
RAGAS et DeepEval pour le RAG en sortie. LangSmith Evals pour les trajectoires LangGraph. MLflow Evaluation pour orchestrer tout ça dans des suites reproductibles.
La métrique précède le code. Avant d'écrire le premier nœud LangGraph, nous construisons un jeu d'évaluation représentatif de 100 à 500 trajectoires de référence. Sans ce jeu, vous codez en aveugle et la régression est invisible.
Plus un agent a d'autonomie (outils, accès SI, budget de tokens), plus la surface d'attaque s'élargit : prompt injection indirecte (via documents), data exfiltration via tool, jailbreak progressif, dérive de plan. Notre approche d'ingénierie est defense-in-depth, à 4 couches.
NeMo Guardrails ou Lakera Guard avant tout LLM : détection prompt injection, filtrage hors-domaine, classification topic. Llama Guard 3 pour la modération de contenu.
Chaque agent a une liste explicite d'outils autorisés, avec des arguments validés (Pydantic AI). Pas d'exec(), pas de fetch() arbitraire. Sandboxing des actions sensibles.
Microsoft Presidio en sortie pour caviarder les PII. Classifieurs de toxicité. Validation que la réponse ne contient pas d'informations sensibles exfiltrées via tool calls.
Pyrit (Microsoft) et Garak orchestrent des attaques connues en continu. Chaque release d'agent passe par une batterie de tests adversariaux avant promotion.
Cette architecture est exigée par l'AI Act (art. 9 — gestion des risques, art. 15 — exactitude, robustesse, cybersécurité). Nous concevons les agents avec ces couches dès le jour 1, pas en rattrapage après incident.
Architecture agentique, choix LangGraph vs harness in-house, sélection des frameworks, évaluation, sécurisation — échangeons. Première analyse de faisabilité offerte, sous 24 h ouvrées.