Un agent sans bons outils est un cerveau sans mains. Dans le premier agent, on a câblé un outil météo en deux lignes. Ça marche pour une démo — mais en pratique, la qualité des outils détermine la qualité de l’agent bien plus que le choix du modèle. Ce chapitre y est entièrement consacré.

Anatomie d’un outil

Le décorateur @tool transforme une fonction Python en outil exploitable par le modèle. Trois éléments sont extraits et envoyés au modèle :

from langchain_core.tools import tool

@tool
def convertir_devise(montant: float, de: str, vers: str) -> float:
    """Convertit un montant d'une devise à une autre au taux du jour.
    Utiliser pour toute question de conversion monétaire (EUR, USD, GBP...)."""
    taux = recuperer_taux(de, vers)
    return round(montant * taux, 2)

Ce que le modèle reçoit, ce n’est pas le corps de la fonction, mais son schéma :

  • le nom (convertir_devise) — il doit être explicite ;
  • les paramètres typés (montant: float, de: str, vers: str) — les types guident le modèle vers des arguments valides ;
  • la docstring — la seule explication de quand et comment utiliser l’outil.
Fig.01 · Ce que le modèle voit
votre code Fonction Python def convertir_devise(...)
extraction @tool introspection
JSON Schéma nom · args · doc
bind_tools Modèle décide d'appeler
Le décorateur @tool transforme votre fonction en un schéma. Le modèle ne reçoit jamais le code — seulement cette description.

Des arguments structurés avec Pydantic

Pour des entrées plus riches, on décrit explicitement le schéma des arguments avec Pydantic. Les Field(description=...) sont, eux aussi, lus par le modèle.

from pydantic import BaseModel, Field

class RechercheVol(BaseModel):
    depart: str = Field(description="Code IATA de l'aéroport de départ, ex. CDG")
    arrivee: str = Field(description="Code IATA de l'aéroport d'arrivée, ex. JFK")
    date: str = Field(description="Date au format AAAA-MM-JJ")

@tool(args_schema=RechercheVol)
def chercher_vol(depart: str, arrivee: str, date: str) -> str:
    """Recherche les vols disponibles entre deux aéroports à une date donnée."""
    ...

Plus la description des champs est précise (format attendu, exemple), moins le modèle produit d’arguments invalides. C’est un investissement très rentable.

Ce qui se passe vraiment lors d’un appel

Quand le modèle décide d’utiliser un outil, il ne l’exécute pas — il demande son exécution, dans un message structuré. Décortiquons un tour complet :

Le modèle émet un tool_call

Sa réponse ne contient pas de texte final mais une intention structurée :

AIMessage(content="", tool_calls=[
    {"name": "convertir_devise",
     "args": {"montant": 100, "de": "EUR", "vers": "USD"},
     "id": "call_abc123"}
])

Le runtime exécute l’outil

Le ToolNode lit ce tool_call, appelle la vraie fonction Python avec ces arguments, et capture le résultat.

Le résultat repart vers le modèle

Le résultat est emballé dans un ToolMessage, relié à l’appel par son id :

ToolMessage(content="108.5", tool_call_id="call_abc123")

Le modèle intègre et conclut

De retour dans la boucle, le modèle lit le résultat et rédige sa réponse finale — ou demande un autre outil. C’est la boucle ReAct.

Plusieurs outils d’un seul tour

Un modèle moderne peut demander plusieurs outils en une seule réponse quand ils sont indépendants. Le ToolNode les exécute alors en parallèle et renvoie autant de ToolMessage, chacun relié à son appel par son id.

Fig.02 · Appels parallèles
1 tour AIMessage 2 tool_calls
parallèle meteo() → ToolMessage
parallèle addition() → ToolMessage
retour Modèle intègre les 2
Pour des outils indépendants, un seul AIMessage peut porter plusieurs tool_calls ; le ToolNode les exécute de front.

C’est un levier de latence important : trois recherches indépendantes lancées ensemble coûtent le temps d’une seule, pas de trois. Concevez vos outils pour qu’ils soient atomiques et sans dépendance mutuelle quand c’est possible.

Gérer les erreurs sans casser la boucle

Un outil réel échoue : API indisponible, argument invalide, time-out. Si l’exception remonte brute, tout l’agent s’arrête. La bonne approche est de renvoyer l’erreur au modèle comme un résultat, pour qu’il puisse réagir — reformuler, réessayer, ou expliquer poliment l’échec.

Fig.03 · Erreur = signal, pas crash
incident outil lève / échoue
capturé message d'erreur texte clair
rattrapage Modèle reformule / explique
Une erreur d'outil bien gérée revient au modèle comme un message exploitable : il peut alors se rattraper au lieu de planter.
from langchain_core.tools import tool

@tool
def lire_facture(numero: str) -> str:
    """Récupère le détail d'une facture par son numéro."""
    facture = db.get(numero)
    if facture is None:
        # On NE lève PAS d'exception : on informe le modèle.
        return f"Aucune facture trouvée pour le numéro {numero}."
    return facture.to_text()

Par défaut, ToolNode intercepte d’ailleurs les exceptions et renvoie leur message au modèle plutôt que de planter — un comportement configurable via handle_tool_errors. Mais renvoyer un message d’erreur clair et exploitable reste votre responsabilité : « Numéro de facture introuvable » aide le modèle ; une stacktrace Python ne l’aide pas.

Forcer ou restreindre l’usage d’un outil

Parfois, on veut obliger le modèle à utiliser un outil (par exemple toujours passer par la recherche avant de répondre). bind_tools accepte un paramètre pour cela :

# Forcer l'appel d'un outil précis
modele.bind_tools(outils, tool_choice="chercher_doc")

# Forcer l'usage d'au moins un outil (lequel, c'est le modèle qui choisit)
modele.bind_tools(outils, tool_choice="any")

À manier avec parcimonie : forcer un outil retire de l’autonomie à l’agent. C’est utile pour cadrer un comportement critique, pas comme réglage par défaut.

Ce qu’il faut retenir

  • Le modèle ne voit que la description des outils : nom, types, docstring. C’est là que se gagne ou se perd la fiabilité.
  • Préférez des outils atomiques et bien nommés à un outil fourre-tout.
  • Une erreur d’outil doit revenir au modèle comme un message exploitable, pas comme une exception qui casse la boucle.
  • tool_choice permet de forcer un outil quand le comportement est critique.

Votre agent sait maintenant agir. Mais entre deux requêtes, il oublie tout. Au chapitre suivant, on lui donne une mémoire conversationnelle.

#langgraph#tool-calling#outils#fondamentaux