Comment gérer les erreurs de LLM tool calling ?

Je gère les erreurs de LLM tool calling en séparant ce que l’orchestrateur doit réparer seul et ce que le modèle doit raisonner. C’est là que beaucoup d’agents IA cassent en production. On va voir les erreurs, les retries, les fallbacks et les limites à poser.

Quelles erreurs faut-il distinguer ?

Il faut distinguer quatre familles d’erreurs : les erreurs de transport, les erreurs de service externe, les erreurs de validation d’entrée, et les erreurs logiques ou de format inattendu. Tout mettre dans le même bloc try/catch, c’est le meilleur moyen de fabriquer un agent IA fragile. Une panne réseau ne se traite pas comme un payload mal formé, et une mauvaise décision métier ne se corrige pas avec un simple retry.

Les erreurs de transport, c’est tout ce qui empêche l’appel d’arriver proprement à l’outil. Timeout, DNS qui ne répond pas, connexion coupée, API momentanément inaccessible. Dans ce cas, le modèle n’a pas forcément mal raisonné. L’orchestrateur peut reprendre la main, attendre, relancer, changer de route, ou rendre une réponse honnête à l’utilisateur.

Les erreurs de service externe viennent de l’outil appelé. Un statut HTTP 429 veut dire trop de requêtes, souvent un rate limit ou un quota dépassé. Un 500 indique une erreur côté serveur. Un 503 signale souvent un service indisponible. Quand l’API renvoie un en-tête HTTP Retry-After, c’est une info précieuse : elle dit combien de temps attendre avant de retenter. Là encore, ce n’est pas au modèle d’improviser.

Entre nous, on le sait bien, faire appel à un consultant en automatisation intelligente et en agent IA, c’est souvent le raccourci le plus malin. On en parle ?

Les erreurs de validation d’entrée arrivent quand le LLM appelle le bon outil, mais avec de mauvais paramètres. Champ manquant, type incorrect, date mal formatée, identifiant absent. Exemple classique : le modèle envoie “demain” alors que l’outil attend une date ISO comme “2026-07-22”. Ici, je préfère bloquer tôt, renvoyer une erreur claire au modèle, et lui demander de corriger son appel.

Les erreurs logiques ou de format inattendu sont plus sournoises. L’outil répond bien techniquement, mais le résultat est incohérent pour la tâche. Ou alors la sortie est exploitable par un humain, mais pas vraiment interprétable par le modèle. Chez des clients, je vois souvent que le problème n’est pas le modèle seul. C’est l’absence de contrat clair entre le modèle, les outils et l’orchestrateur.

Type d’erreur Exemple concret Responsable principal Réaction attendue
Erreur de transport Timeout, DNS, connexion coupée Infrastructure ou réseau Retenter, attendre, basculer, informer
Erreur de service externe 429, 500, 503, quota dépassé, Retry-After API appelée Respecter le délai, limiter, relancer proprement
Erreur de validation d’entrée Champ manquant, type incorrect, date invalide LLM ou schéma d’appel Refuser l’appel et demander une correction
Erreur logique ou format inattendu Réponse incohérente ou difficile à interpréter Outil, modèle ou contrat fonctionnel Clarifier, reformater, escalader si nécessaire

Qui doit corriger quoi ?

L’orchestrateur corrige l’infrastructure, le LLM corrige le raisonnement et les payloads. C’est la séparation que je garde en tête dès que je branche des tools à un modèle. Si une requête réseau coupe au milieu, ce n’est pas au modèle de deviner s’il faut retenter. Si un argument métier est mauvais, ce n’est pas au code rigide de bricoler une décision sans repasser par le modèle.

L’orchestrateur gère tout ce qui ressemble à de la plomberie technique. Il fait les retries silencieux, avec un backoff exponentiel, c’est-à-dire qu’il attend de plus en plus longtemps entre les tentatives. Il lit aussi le header Retry-After quand une API dit clairement “reviens dans 10 secondes”. Il pose des timeouts, il arrête après un nombre maximal de tentatives, et si ça ne revient pas, il renvoie une erreur propre au modèle.

Orchestrateur Réseau coupé, timeout, rate limit, API indisponible, retry automatique.
LLM Argument invalide, payload incomplet, mauvais outil, fallback métier, demande de précision.

Le modèle, lui, reprend la main quand il y a une décision à prendre. Il peut corriger un argument invalide, compléter un payload si l’info existe déjà dans le contexte, choisir un autre outil, changer de stratégie, demander une précision à l’utilisateur ou escalader. J’ai vu des systèmes où tout était codé “en dur” pour éviter de refaire appel au modèle. Ça marche deux jours, puis le premier cas métier un peu ambigu casse tout.

Le point clé, c’est le contrat d’erreur. L’orchestrateur ne doit jamais balancer une stack trace brute au modèle. Il doit renvoyer un message court, structuré, lisible, avec ce qui a été tenté et ce qui reste possible.

<tool_error>
  <tool_name>create_invoice</tool_name>
  <error_type>timeout</error_type>
  <retry_count>3</retry_count>
  <recoverable>false</recoverable>
  <user_action_needed>false</user_action_needed>
</tool_error>
<tool_error>
  <tool_name>search_customer</tool_name>
  <error_type>invalid_argument</error_type>
  <retry_count>0</retry_count>
  <recoverable>true</recoverable>
  <user_action_needed>true</user_action_needed>
</tool_error>

Cette séparation évite deux pièges classiques. Le premier, c’est rendre le modèle responsable de pannes techniques qu’il ne contrôle pas. Le second, c’est enfermer toute la logique métier dans du code rigide alors que le LLM est justement là pour raisonner sur les cas ambigus.

Comment retenter sans aggraver la panne ?

Je retente, oui, mais jamais en boucle ouverte. Je mets une limite, un backoff exponentiel, du full jitter, et je respecte Retry-After quand l’API me le donne. Un retry immédiat peut empirer la panne, parce que vous rajoutez du trafic sur un service déjà instable. Avec des agents LLM, c’est encore plus dangereux : un seul raisonnement peut déclencher 5, 10 ou 20 appels d’outils en parallèle. Si tout ce petit monde retente au même instant, vous créez un effet troupeau. Résultat classique : surcharge, rate limit, quota cramé, puis panne plus large.

Le retry doit être géré côté orchestration, pas laissé au hasard dans chaque outil. Je retente seulement les erreurs transitoires : timeout, 429, 500, 503, erreur réseau. Une erreur transitoire, c’est une erreur qui peut disparaître sans changer la requête. Une erreur de validation d’entrée, elle, ne doit pas être retentée à l’identique. Si le payload est mauvais, il faut le corriger. Soit par le modèle, soit par une règle métier. Retenter le même JSON invalide trois fois, c’est juste perdre du temps plus proprement.

Retry-After doit passer avant votre propre calcul. Quand une API renvoie cet en-tête, elle vous dit : “Reviens dans X secondes”. C’est le service appelé qui connaît sa fenêtre de reprise, donc je l’écoute.

Le backoff exponentiel, c’est simple : plus ça échoue, plus j’attends. Tentative 1, j’attends peu. Tentative 2, j’attends plus. Tentative 3, encore plus. Le full jitter ajoute un délai aléatoire dans cette fenêtre, pour éviter que tous les agents repartent exactement au même moment. C’est une pratique très utilisée dans les architectures distribuées, parce qu’elle réduit les collisions de retry. J’ai vu des systèmes consommer leur propre quota juste avec des retries trop agressifs, donc la limite n’est pas un détail.

max_attempts = 3
attempt = 0

while attempt < max_attempts:
    result = call_tool(payload)

    if result.success:
        return result.data

    error = result.error

    if error.type in ["validation_error", "bad_request"]:
        return structured_error(
            retry=false,
            send_to_model=true,
            reason="Payload à corriger"
        )

    if error.status in [429, 500, 503] or error.type in ["timeout", "network"]:
        attempt += 1

        if attempt >= max_attempts:
            return structured_error(
                retry=false,
                send_to_model=true,
                reason="Retries épuisés"
            )

        if error.retry_after:
            wait_seconds = error.retry_after
        else:
            base = 1
            cap = 30
            max_wait = min(cap, base * (2 ** attempt))
            wait_seconds = random_between(0, max_wait)

        sleep(wait_seconds)
        continue

    return structured_error(
        retry=false,
        send_to_model=true,
        reason="Erreur non retentable"
    )
Erreur Retry Stratégie Passage au modèle
Timeout Oui Backoff exponentiel + full jitter Après limite atteinte
429 Oui Retry-After prioritaire, sinon jitter Si quota ou rate limit persiste
500 / 503 Oui Retry limité avec attente progressive Après échec final
Erreur réseau Oui Retry court et borné Après retries épuisés
Validation d’entrée Non Correction du payload Oui, avec message clair

Que faire quand les retries échouent ?

Quand les retries échouent, il faut un chemin de repli prévu avant la mise en production. Un agent IA robuste ne fait pas juste “j’essaie trois fois puis je meurs”. Il doit savoir quoi faire après. Changer d’outil, dégrader la réponse, demander une précision, mettre en file d’attente, escalader à un humain ou arrêter proprement.

Côté modèle, le fallback doit rester honnête. Si un service est indisponible, le modèle peut pivoter vers un autre outil fiable. Si la réponse complète n’est pas possible, il peut proposer une réponse partielle, à condition que ce soit acceptable pour l’utilisateur. Si le payload est incomplet, c’est-à-dire les données envoyées à l’outil, il peut demander l’information manquante. Et si l’action ne peut pas être finalisée, il doit le dire clairement.

Le point important, c’est qu’un fallback ne doit jamais inventer un résultat. Si l’outil de paiement échoue, l’agent ne doit pas dire que le paiement est validé. Si l’outil de réservation plante, il ne doit pas confirmer une réservation fictive. J’ai déjà vu ce cas chez un client avec un agent connecté à un CRM. L’agent disait “j’ai mis à jour la fiche”, alors que l’API avait rejeté la requête. C’est exactement le genre de détail qui casse la confiance.

Côté orchestration, le rôle est différent. L’orchestrateur, c’est la couche qui pilote les appels aux outils, les validations, les retries et les décisions techniques. Lui peut mettre une tâche en file d’attente, appeler une route alternative, ouvrir un ticket, stocker l’événement pour une reprise plus tard, ou renvoyer une erreur structurée au modèle.

Je garde toujours quelques traces simples, parce qu’en production, c’est ce qui permet de comprendre vite :

  • Type d’erreur rencontrée.
  • Outil appelé.
  • Nombre de tentatives.
  • Latence de chaque appel.
  • Payload validé ou rejeté.
  • Décision finale prise par le système.
Situation Décision raisonnable
Action critique Escalade humaine ou arrêt propre
Action non critique Réponse dégradée
Alternative fiable disponible Pivot vers un autre outil
Erreur corrigeable par l’utilisateur Demande de précision

Le bon réflexe, c’est de séparer les responsabilités. L’infrastructure gère les routes, les files, les erreurs et les traces. Le modèle gère la conversation, l’explication et le choix métier quand il a assez d’information. Les deux doivent travailler ensemble, mais il ne faut pas les mélanger.

On laisse le modèle réparer quoi maintenant ?

La bonne gestion des erreurs en LLM tool calling, pour moi, c’est surtout une histoire de frontières nettes. L’orchestrateur absorbe les pannes techniques avec des retries propres, du backoff, du jitter et le respect de Retry-After. Le modèle reprend la main quand il faut corriger un payload, raisonner, pivoter ou demander une précision. Et quand tout échoue, le fallback doit déjà être prévu. Pas improvisé au moment où l’agent casse. Cette approche rend vos agents IA plus stables, plus lisibles et plus simples à faire évoluer. Le bénéfice pour vous est clair : moins d’incidents flous, plus de contrôle en production.

FAQ

  • Qu’est-ce qu’une erreur de LLM tool calling ?
    C’est une erreur qui arrive quand un modèle appelle un outil externe pour exécuter une action ou récupérer une donnée. Elle peut venir du réseau, du service appelé, d’un payload invalide ou d’une réponse logique difficile à exploiter.
  • Pourquoi ne pas laisser le LLM gérer tous les retries ?
    Parce que les retries techniques relèvent de l’infrastructure. Un timeout, un 429 ou un 503 doivent être traités par l’orchestrateur avec des règles fiables. Le modèle, lui, est meilleur pour corriger une demande, choisir une alternative ou raisonner sur le contexte.
  • Quand faut-il utiliser un backoff exponentiel avec jitter ?
    Il faut l’utiliser sur les erreurs transitoires, comme les timeouts, les erreurs réseau, les 429 ou certains 5xx. Le backoff espace les tentatives, le jitter ajoute de l’aléatoire, et ça évite que tous les agents retentent au même instant.
  • Une erreur de validation doit-elle être retentée automatiquement ?
    Pas à l’identique. Si un champ manque ou si un format est mauvais, refaire le même appel ne sert à rien. Il faut renvoyer une erreur structurée au modèle pour qu’il corrige le payload, demande une précision ou choisisse une autre stratégie.
  • Quel fallback prévoir pour un agent IA en production ?
    Le fallback dépend du niveau de risque. On peut changer d’outil, fournir une réponse dégradée, mettre l’action en file d’attente, demander une précision ou escalader à un humain. Le point important, c’est de définir ce chemin avant la mise en production.

 

 

A propos de l’auteur

Je suis Franck Scandolera, expert et formateur en tracking avancé server-side, Analytics Engineering, automatisation No/Low Code avec n8n, intégration de l’IA en entreprise et SEO/GEO. Je dirige l’agence webAnalyste et l’organisme Formations Analytics. J’accompagne des équipes chez Logis Hôtel, Yelloh Village, BazarChic, la Fédération Française de Football, Texdecor et d’autres sur des sujets data, IA et automatisation en production. Si vous voulez fiabiliser vos agents IA, vos workflows ou vos architectures data, contactez-moi.

Retour en haut