Python & AI Tutorials Logo
LangChain & LangGraph

5. Aperçu des agents — Le moment « Aha ! »

Jusqu'à présent, vous avez construit des systèmes où vous contrôlez le flux. Vous écrivez le prompt, appelez le LLM et traitez la réponse. Le LLM est puissant, mais il ne fait que suivre vos instructions.

Ce chapitre introduit un changement fondamental : et si le LLM décidait de ce qui se passe ensuite ?

Pourquoi ce chapitre existe

Le problème : Si vous continuez à construire des chatbots jusqu'au chapitre 11 sans cet aperçu, vous penserez probablement que « les agents sont juste de meilleurs chatbots ».

La vérité : Les agents sont fondamentalement différents. Ils prennent des décisions qui contrôlent votre programme, pas seulement générer du texte.

La solution : Ce chapitre vous montre où nous allons, donc quand vous construirez des pipelines et la gestion d'état dans les prochains chapitres, vous comprendrez pourquoi chaque élément compte.

Ce que vous apprendrez (et ce que vous n'apprendrez pas)

Ce chapitre (Chapitre 5) :

  • Un aperçu minimal de 15 minutes de la pensée agentique
  • Comment la sortie du LLM peut déclencher différents chemins de code
  • La différence conceptuelle entre le routage et les boucles d'agent

Chapitres ultérieurs (À partir du chapitre 12) :

  • Systèmes d'agents réels avec outils, boucles et auto-correction
  • Implémentations prêtes pour la production
  • Patterns avancés comme la coordination multi-agents

Pourquoi cet écart ? Avant de construire des agents, vous avez besoin de fondations : ingénierie de prompts (Ch 4), pipelines d'exécution (Ch 6), sortie structurée (Ch 7), gestion d'état (Ch 8) et intégration de connaissances (Ch 9-11).

Une note sur l'approche d'apprentissage : Ce chapitre se concentre sur les concepts, pas sur les détails d'implémentation. Les techniques réelles de conception et de développement viennent au chapitre 12 et au-delà. Ne vous inquiétez pas si vous ne savez pas comment implémenter tout ce que vous voyez ici—c'est intentionnel. Comprendre ce qu'est un agent conceptuellement suffit pour l'instant.

Commençons.

5.1) Du chatbot au décideur

Le concept : Au lieu de répondre à la question, le LLM décide quel outil utiliser

Dans les chapitres 3 et 4, vous avez construit des systèmes de chat où le LLM génère des réponses textuelles. Le flux était simple :

  1. L'utilisateur pose une question
  2. Le LLM génère une réponse
  3. Vous affichez la réponse

C'est une chaîne statique : le flux est prédéterminé. Le seul travail du LLM est de produire du texte.

Maintenant, changeons le type de requête. Un utilisateur demande : « Combien font 847 × 923 ? »

Votre chatbot du chapitre 3 tenterait de répondre à cela, mais les LLM ne calculent pas réellement—ils prédisent des tokens plausibles. Pour « 2 + 2 », la réponse « 4 » apparaît si souvent dans les données d'entraînement que le LLM l'obtient correctement. Mais pour « 847 × 923 »—un calcul que le LLM n'a jamais vu—il générera quelque chose qui ressemble à un nombre mais sera probablement faux.

Ce que vous voulez vraiment :

  1. Le LLM reconnaît : « C'est un problème mathématique »
  2. Le LLM décide : « Utiliser l'outil calculatrice, pas ma prédiction de tokens »
  3. Python exécute : 847 × 923 = 781 781
  4. Le système retourne : La réponse correcte

Le travail du LLM n'est pas de calculer—c'est de router vers l'outil qui peut le faire.

Voici l'insight clé : le LLM n'a pas besoin de résoudre les maths—il doit décider d'utiliser une calculatrice.

C'est le passage du chatbot au décideur. Le LLM examine la requête et la route vers l'outil approprié. Il prend une décision sur le flux du programme, pas seulement générer du texte.

Visualisons cette différence :

Chaîne statique (Chatbot)

Utilisateur : Combien font 847 × 923 ?

Le LLM génère du texte

Affiche : 'Environ 780 000...'

Routage dynamique (Décideur)

Utilisateur : Combien font 847 × 923 ?

Le LLM décide : CALC nécessaire

Exécute : Calculatrice Python

Affiche : 781 781

Dans la chaîne statique, le LLM essaie de répondre à tout directement. Dans le système de routage dynamique, le LLM route la requête vers le bon outil.

Le visuel : Comparer une « chaîne linéaire » (statique) vs. un « routeur » (dynamique)

Voici comment une chaîne statique gère n'importe quelle requête utilisateur :

Entrée utilisateur

LLM

Réponse textuelle

Affichage à l'utilisateur

Le LLM essaie de répondre à tout directement. Que vous demandiez « Quelle est la capitale de la France ? » ou « Combien font 15 % de 240 ? », le LLM génère une réponse textuelle. Il pourrait obtenir la capitale correctement (il a vu « Paris » de nombreuses fois dans les données d'entraînement), mais il calculera probablement mal le pourcentage.

Le problème : Le LLM utilise la même approche pour toutes les questions—la prédiction de tokens—même quand de meilleurs outils existent.


Maintenant, introduisons un système qui peut gérer différents types de requêtes différemment :

  1. Questions factuelles : « Quelle est la capitale de la France ? » → Utiliser un outil de recherche
  2. Problèmes mathématiques : « Combien font 15 % de 240 ? » → Utiliser une calculatrice
  3. Conversationnel : « Comment allez-vous ? » → Le LLM répond directement

Voici un routeur dynamique qui rend cela possible :

Factuel

Math

Conversationnel

Entrée utilisateur

Couche de décision LLM

Outil de recherche

Outil calculatrice

Réponse directe

Formater le résultat

Affichage à l'utilisateur

Le LLM examine l'entrée et choisit un chemin en fonction du type de requête. C'est la logique de routage, et le LLM agit comme le routeur.

La différence clé : Au lieu de toujours générer du texte, le LLM décide maintenant comment gérer chaque requête—en la routant vers l'outil approprié.

Point clé : De la génération de réponses au choix d'actions

C'est le changement fondamental dans les systèmes agentiques :

Utilisation traditionnelle du LLM : Vous posez une question, le LLM écrit une réponse.

Utilisation agentique du LLM : Vous posez une question, le LLM décide quoi faire, et votre code exécute cette décision.

La sortie du LLM n'est plus seulement du texte pour l'utilisateur—ce sont des instructions pour votre programme.

Pensez-y comme ceci : dans un chatbot traditionnel, le LLM est un employé qui répond aux questions des clients. Dans un système agentique, le LLM est un manager qui décide quel département devrait gérer chaque requête.

Cette capacité de prise de décision est l'essence des systèmes agentiques. Le LLM ne répond pas seulement—il contrôle ce que votre programme fait ensuite. En fonction de l'entrée, il peut déclencher une calculatrice, appeler une API de recherche ou répondre directement. Le comportement du programme change en fonction de la décision du LLM.

Mais comment implémentons-nous réellement cela ? Voyons le code minimal qui fait fonctionner cela.

5.2) L'exécution minimale

Le déclencheur : Un prompt système qui force une sortie structurée

Pour faire agir le LLM comme un routeur, nous devons contraindre sa sortie. Au lieu de générer une réponse complète, nous voulons qu'il produise une décision structurée—un objet JSON qui indique à notre code à la fois quoi faire et quelles données utiliser.

Voici un prompt système qui fait cela :

python
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage, HumanMessage
import json
 
llm = ChatOpenAI(model="gpt-4o-mini")
 
system_prompt = """Vous êtes un assistant de routage. Analysez la requête de l'utilisateur et répondez avec du JSON.
 
Format de sortie :
{
  "action": "CALC" | "SEARCH" | "CHAT",
  "input": "entrée nettoyée pour l'outil"
}
 
Exemples :
Utilisateur : "Calcule 15 fois 7"
Sortie : {"action": "CALC", "input": "15 * 7"}
 
Utilisateur : "Quelle est la capitale du Japon ?"
Sortie : {"action": "SEARCH", "input": "capitale du Japon"}
 
Utilisateur : "Bonjour !"
Sortie : {"action": "CHAT", "input": "Bonjour !"}
 
Extrayez les informations essentielles et formatez-les pour l'outil approprié.
Ne produisez que du JSON valide, rien d'autre."""
 
def get_routing_decision(user_input: str) -> dict:
    messages = [
        SystemMessage(content=system_prompt),
        HumanMessage(content=user_input)
    ]
    response = llm.invoke(messages)
    
    # Parse la réponse JSON
    try:
        decision = json.loads(response.content.strip())
        return decision
    except json.JSONDecodeError:
        # Repli si le LLM ne produit pas de JSON valide
        return {"action": "CHAT", "input": user_input}
 
# Testez-le avec des entrées DIFFÉRENTES des exemples
print(get_routing_decision("Combien font 25 * 48 ?"))  
# Sortie : {'action': 'CALC', 'input': '25 * 48'}
 
print(get_routing_decision("Qui a écrit Hamlet ?"))  
# Sortie : {'action': 'SEARCH', 'input': 'auteur de Hamlet'}
 
print(get_routing_decision("Comment allez-vous aujourd'hui ?"))  
# Sortie : {'action': 'CHAT', 'input': 'Comment allez-vous aujourd'hui ?'}

Ce prompt est délibérément restrictif. Nous ne demandons pas au LLM d'être créatif—nous lui demandons de classifier l'entrée et extraire les informations pertinentes dans un format structuré.

Remarquez ce qui se passe ici :

  1. Le LLM lit la question de l'utilisateur
  2. Il détermine l'intention (calcul, recherche factuelle, conversation)
  3. Il extrait et nettoie les informations essentielles
  4. Il produit un objet JSON avec à la fois l'action et l'entrée nettoyée
  5. Notre code reçoit ces données structurées et route en conséquence

Le LLM ne répond pas à la question—il décide ce qui devrait répondre à la question.

Le pont : Connecter les décisions du LLM à l'exécution d'outils

Maintenant, nous devons connecter la décision du LLM à l'exécution réelle d'outils. Voici le pont minimal :

python
def safe_calculator(expression: str) -> float:
    try:
        # Utilise eval avec un namespace restreint pour la sécurité
        # Note : eval() présente des risques de sécurité.
        # En production, utilisez une bibliothèque de parsing mathématique comme sympy ou une évaluation basée sur ast.
        result = eval(expression, {"__builtins__": {}}, {})
        return float(result)
    except:
        raise ValueError(f"Impossible de calculer : {expression}")
 
def search(query: str) -> str:
    """Fonction de recherche placeholder"""
    # En réalité, cela appellerait une API de recherche
    # Reçoit maintenant une requête nettoyée comme "auteur de Hamlet"
    return f"Résultats de recherche pour : {query}"
 
def route_and_execute(user_input: str) -> str:
    """Obtient la décision du LLM et exécute l'outil approprié"""
    decision = get_routing_decision(user_input)
    
    action = decision["action"]
    tool_input = decision["input"]  # Entrée nettoyée par le LLM
    
    if action == "CALC":
        try:
            result = safe_calculator(tool_input)
            return f"Résultat du calcul : {result}"
        except ValueError as e:
            return f"Impossible d'effectuer le calcul : {e}"
    
    elif action == "SEARCH":
        result = search(tool_input)
        return result
    
    else:  # CHAT
        # Pour les requêtes conversationnelles, laissez le LLM répondre directement
        response = llm.invoke([HumanMessage(content=tool_input)])
        return response.content
 
# Testez le flux complet
print(route_and_execute("Combien font 25 * 48 ?"))
# Le LLM extrait "25 * 48" → la calculatrice reçoit une entrée propre
# Sortie : Résultat du calcul : 1200.0
 
print(route_and_execute("Qui a écrit Hamlet ?"))
# Le LLM reformate en "auteur de Hamlet" → meilleure requête de recherche
# Sortie : Résultats de recherche pour : auteur de Hamlet
 
print(route_and_execute("Comment allez-vous aujourd'hui ?"))
# Sortie : Je vais bien, merci de demander !

C'est le pattern de routage minimal dans sa forme la plus simple :

  1. Décision du LLM : Obtenir la décision de routage du LLM
  2. Exécution du code : Le code Python vérifie la décision
  3. Invocation d'outil : La fonction appropriée est appelée
  4. Retour du résultat : La sortie est formatée et retournée

L'insight critique : La sortie du LLM n'est plus du texte pour l'utilisateur—ce sont des données structurées qui contrôlent le comportement de votre programme. Le champ action indique à votre code quel chemin prendre, et le champ input fournit les données nettoyées pour cet outil. Cette réponse JSON devient une logique exécutable.

C'est fondamentalement différent d'un chatbot. Dans un chatbot, la sortie du LLM va directement à l'utilisateur. Ici, la sortie du LLM va à votre code, qui décide ensuite quoi exécuter.

Ce que nous avons construit dans cette section est un routeur—un précurseur des agents complets. Il démontre le principe fondamental (le LLM contrôle le flux), mais manque l'itération et l'auto-correction qui définissent les vraies boucles d'agent.

Ce système de routage simple a une limitation majeure : il ne peut pas corriger ses propres erreurs. Explorons pourquoi c'est important et ce qui vient ensuite.

5.3) Comprendre la terminologie : Routeur vs Agent

Ce que nous venons de construire est un routeur. Mais comment se compare-t-il aux agents complets ? Établissons des définitions claires :

Routeur (ce que nous venons de construire)

  • Prend une décision de classification par requête
  • Choisit quel outil/chemin exécuter
  • S'exécute une fois et retourne
  • Pas d'itération, pas d'état, pas d'auto-correction
  • Exemple : Classificateur d'emails, détecteur d'intention, sélecteur d'outils

Assistant avec appel d'outils (Chapitre 13)

  • Le LLM peut invoquer des outils directement via l'API d'appel de fonctions
  • Toujours typiquement à tour unique (une requête → une réponse)
  • Plus sophistiqué que le routage, mais pas nécessairement itératif
  • Exemple : « Rechercher la météo et résumer » en un seul appel

Agent (boucle complète) (Chapitres 14-17)

  • Itère à travers des cycles penser → agir → observer
  • Porte l'état entre les itérations
  • Peut réviser les décisions en fonction des résultats
  • Implémente l'auto-correction
  • Exemple : Assistant de débogage qui essaie des corrections jusqu'à ce que le code fonctionne

Système agentique (terme générique)

  • Tout système où la sortie du LLM influence le flux de contrôle
  • Inclut les routeurs, les assistants avec appel d'outils et les agents complets
  • « Agentique » décrit la propriété ; « agent » décrit une architecture spécifique
  • Exemple : Tous les éléments ci-dessus présentent un comportement agentique

Ce que nous avons construit en 5.2 est un routeur—il prend une décision et l'exécute. Cela fonctionne bien pour les tâches de classification simples, mais ne peut pas gérer les situations qui nécessitent plusieurs étapes, vérification ou correction de trajectoire. Si la décision initiale est sous-optimale ou si la tâche s'avère plus complexe que prévu, le routeur n'a aucun mécanisme pour s'adapter.

Les agents résolvent cette limitation en introduisant l'itération. Ils peuvent observer le résultat d'une action, reconsidérer leur approche et réessayer. Cela les rend adaptés aux tâches complexes en plusieurs étapes où le chemin vers la solution n'est pas clair dès le départ.

Dans les chapitres 14-17, vous apprendrez à construire ces boucles d'agent itératives.

Note : Récemment, les modèles de raisonnement comme o1 et o3 peuvent gérer certaines tâches en plusieurs étapes en interne, réduisant le besoin de boucles explicites dans certains scénarios. Nous explorerons quand utiliser des boucles vs. des modèles de raisonnement lorsque vous construirez de vrais agents dans la partie IV.


Ce que vous devriez comprendre maintenant :

  1. Les systèmes agentiques commencent par le flux de contrôle : La sortie du LLM détermine ce que votre code fait ensuite
  2. Le routage est la forme la plus simple : Une décision, un outil, un résultat—ce que nous avons construit en 5.2
  3. Les vrais agents ont besoin de boucles : Pour gérer les tâches en plusieurs étapes, la vérification et la récupération d'erreurs (Chapitres 14-17)
  4. L'auto-correction est la différence clé : Les agents peuvent observer les résultats et ajuster leur approche

Ce que nous n'avons pas couvert (et ne couvrirons pas avant les chapitres ultérieurs) :

  • Comment composer des pipelines d'exécution (Chapitre 6)
  • Comment gérer la sortie structurée (Chapitre 7)
  • Comment gérer la mémoire de conversation (Chapitre 8)
  • Comment définir correctement les outils (Chapitre 12)
  • Comment implémenter des boucles d'agent avec auto-correction (Chapitres 14-17)
  • Comment rendre les agents prêts pour la production (Chapitres 18-26)

Ce chapitre portait sur le changement conceptuel—comprendre ce qui fait qu'un système présente un comportement agentique. Les détails d'implémentation viennent plus tard.

Dans le prochain chapitre, nous continuerons à construire des fondations pratiques : composer des pipelines d'exécution avec LangChain Expression Language (LCEL). Ce sont les blocs de construction que vous utiliserez lorsque nous implémenterons de vrais agents dans la partie IV.

Le moment « aha ! » est complet. Vous comprenez maintenant que les systèmes agentiques sont ceux où le LLM décide, et votre code exécute. Tout le reste—outils, boucles, gestion d'état—consiste à rendre ce cycle décision-exécution plus robuste et capable.

Continuons à construire les fondations.