Aller au contenu
Blog

AI

Pourquoi votre agent IA a envoyé ce message deux fois, et ce qu'il faut exiger avant le lancement

Les agents IA envoient en double des messages, des e-mails et des commandes parce que l'infrastructure sous-jacente réessaie par défaut. Voici ce que les fondateurs doivent exiger avant qu'un agent n'entre en contact avec de vrais clients.

Par Pykero Agency · Engineering team23 oct. 20267 min de lecture

TL;DR

Les messages en double ne sont pas un bug du LLM, c'est un bug de retry : les webhooks, les files d'attente et les appels au modèle livrent tous au moins une fois, donc chaque action externe entreprise par un agent nécessite une clé d'idempotence, une étape de réservation et une table de déduplication. Exigez cela avant qu'un agent n'écrive à un vrai client.

notifications de chat en double sur téléphone, Pourquoi votre agent IA a envoyé ce message deux fois, et ce qu'il faut exiger avant le lancement

Votre agent IA a envoyé le message deux fois parce que quelque chose en dessous a réessayé, pas parce que le modèle l'a « décidé ». Les webhooks, les files d'attente, les workers et les clients d'API LLM fonctionnent tous par défaut selon le principe « au moins une fois », et un agent qui appelle le monde extérieur sans clé d'idempotence finira par envoyer en double, réserver en double ou facturer en double. La correction est architecturale et peu coûteuse comparée aux dégâts, elle doit donc figurer dans le cahier des charges, pas dans un correctif post-lancement.

Les retries sont la norme, pas l'exception

Les fondateurs ont tendance à imaginer un agent comme une conversation unique : un client écrit, le modèle réfléchit, une réponse part. En production, c'est une chaîne d'étapes, et presque chaque étape se répétera sous contrainte.

  • Le webhook entrant. Les plateformes de messagerie renvoient une notification lorsque votre endpoint est lent à répondre ou renvoie une erreur. Si votre handler avait déjà commencé à travailler avant le timeout, vous avez désormais deux copies du même événement en cours de traitement.
  • La file d'attente. Les files d'attente standards comme Amazon SQS sont explicitement documentées comme « au moins une fois » : un message peut être livré plus d'une fois, et votre consommateur doit savoir y faire face (documentation AWS). La plupart des runtimes d'agents managés reposent sur un mécanisme équivalent.
  • Le worker. Un crash, un déploiement ou un kill out-of-memory en plein traitement signifie que la tâche repart du début. Tout ce qu'elle avait déjà fait, elle le refait.
  • L'appel au modèle. Chaque SDK LLM sérieux réessaie en cas de rate limit ou d'erreur transitoire. Si la première tentative a réellement réussi côté serveur mais que la réponse a été perdue, vous payez deux fois et, pire, votre code peut agir sur les deux résultats.
  • L'appel d'outil. L'agent appelle votre fonction d'envoi de message ou de création de commande. Cette fonction parle à une API tierce avec ses propres timeouts. Un timeout n'est pas un échec ; c'est une inconnue.

Rien de tout cela n'est exotique. C'est le comportement normal d'une infrastructure normale. Un agent « fiable à 99 % » en démo est testé à une charge où rien de tout cela ne se déclenche. Au deuxième mois, avec quelques milliers de conversations par jour, tout se déclenche.

Pourquoi le LLM rend cela plus difficile qu'une tâche classique

Un worker classique qui réessaie se comporte au moins de la même manière la deuxième fois. Un agent, non. Si un worker réexécute une étape de planification, le modèle peut choisir une action légèrement différente : la première tentative a envoyé « Votre commande expédie mardi », la relance envoie « Votre commande expédie demain » parce que la fenêtre de contexte inclut désormais un état à moitié écrit. Le client voit deux messages confiants et contradictoires venant de la même entreprise.

C'est pourquoi les corrections au niveau du prompt échouent. Dire au modèle « n'envoie jamais un message deux fois » suppose que le modèle sache que la première tentative a eu lieu. Souvent, il ne le sait pas, car l'échec s'est produit après que le modèle a répondu et avant que votre code n'enregistre le résultat. La déduplication doit se situer en dessous du modèle, à la frontière où votre système touche le monde extérieur. C'est le même principe que les garde-fous déterministes que nous abordons dans la fiabilité des fonctionnalités IA : le modèle propose, le code décide si cette action est encore autorisée.

Ce que signifie réellement « exactement une fois » pour un agent

On ne peut pas obtenir une livraison « exactement une fois » du réseau. On peut obtenir un effet « exactement une fois » en rendant idempotente chaque action externe. Trois éléments font le travail.

Une clé d'idempotence par action prévue

Avant que l'agent n'envoie quoi que ce soit, votre code dérive une clé à partir de ce qu'est l'action, pas de la tentative. Pour une réponse, c'est typiquement l'ID du message entrant plus l'étape de la conversation. Pour une commande, l'ID du panier plus le client. Deux relances de la même intention produisent la même clé ; deux actions réellement différentes, non. L'API de Stripe est la référence en la matière : le client fournit la clé, le serveur stocke le premier résultat et le renvoie en cas de répétition (documentation Stripe). La fonction d'envoi de votre agent doit se comporter de la même façon, que l'API en aval prenne en charge les clés nativement ou que vous les imposiez vous-même.

Réserver, puis agir, puis enregistrer

L'échec le plus douloureux est « envoyé mais non enregistré ». L'agent appelle l'API de messagerie, l'API accepte le message, votre processus meurt avant d'écrire dans la base de données, et la relance ne voit aucune trace et renvoie le message. La correction consiste à réserver d'abord : insérer la clé dans une table actions avec un statut pending avant l'appel externe. Une contrainte d'unicité sur la clé fait qu'une tentative concurrente ou répétée échoue dès l'insertion et s'arrête (contraintes PostgreSQL). Une fois l'appel externe réussi, mettez à jour la ligne en sent avec l'ID de message du fournisseur. Si vous trouvez une ligne pending plus ancienne que votre timeout, c'est un cas de réconciliation, pas un cas de renvoi : interrogez le fournisseur par clé, ou faites intervenir un humain.

Dédupliquer les événements entrants à la frontière

Les handlers de webhook doivent acquitter rapidement et stocker l'ID de l'événement dans une table de déduplication avec la même technique de contrainte d'unicité. Une livraison en double tombe sur la contrainte et est écartée avant qu'aucun modèle ne soit invoqué. Cela permet aussi d'économiser de l'argent : chaque doublon écarté à la frontière est un appel LLM que vous ne payez jamais, ce qui compte aux volumes que nous abordons dans combien coûte un agent de vente WhatsApp.

Ce que nous avons appris avec notre propre moteur de prospection

Notre système de prospection à froid scrape le site de chaque prospect avec un crawler auto-hébergé et un modèle local, puis rédige un e-mail personnalisé par entreprise. Au début, nous pensions que le problème difficile était la qualité de rédaction, et c'était en partie vrai. Le schéma qui l'a emporté à la fois en coût et en qualité fut un appel unique au modèle qui extrait les faits et rédige l'e-mail en même temps, plutôt qu'une chaîne en plusieurs étapes. Un avantage sous-estimé est apparu plus tard : un appel unique n'a qu'une seule sortie à rendre idempotente, si bien que la promesse « un e-mail par entreprise » a pu être garantie avec une seule ligne de réservation indexée sur le domaine de l'entreprise. Chaque étape supplémentaire dans une chaîne est un endroit de plus où une relance peut se déclencher en double, donc réduire la chaîne a simplifié la fiabilité autant que la facture. Nous avons détaillé le volet rédaction dans appel unique vs chaînes d'agents.

Ce qu'il faut exiger avant qu'un agent n'entre en contact avec un vrai client

Inscrivez ces points dans le cahier des charges ou le contrat avec le prestataire. Chacun est une question de type oui/non avec une réponse démontrable.

  • Chaque action sortante a une clé d'idempotence nommée, et la méthode de dérivation est documentée par type d'action.
  • Réservation avant envoi. Montrez la table, la contrainte d'unicité et la machine à états. Demandez ce qui se passe pour une ligne bloquée en pending.
  • Déduplication entrante au niveau du webhook, avec une fenêtre de rétention précisée. Les plateformes peuvent redistribuer des événements plusieurs heures plus tard.
  • Appels au modèle résistants aux retries. Les retries du SDK sont acceptables, mais le code environnant ne doit pas traiter « pas de réponse » comme « rien ne s'est passé ».
  • Un test de chaos en recette. Tuez le worker en plein envoi, rejouez trois fois le même webhook, forcez un timeout sur l'API de messagerie. Zéro doublon ne doit atteindre le numéro de test. Si l'agence ne peut pas exécuter ce test, elle n'a pas conçu pour cela.
  • La réconciliation, pas le renvoi, comme comportement par défaut en cas d'état inconnu. Une file visible par un humain pour les cas incertains vaut toujours mieux qu'une relance automatique.
  • Observabilité sur les doublons. Un compteur sur les violations de contrainte est votre alerte précoce indiquant que l'infrastructure réessaie plus que prévu, bien avant qu'un client ne se plaigne. Associez-le à l'alerting décrit dans surveillance des exécutions d'agent manquées.

La question du coût

Rendre un agent résistant aux retries n'est pas une grosse ligne budgétaire. Pour un agent de vente ou de support typique avec trois ou quatre types d'actions, comptez quelques jours d'ingénierie ciblée, l'essentiel étant consacré à la conception des clés et du flux de réservation, plus une journée pour les tests de chaos. La version coûteuse est celle où l'on s'en dispense : remboursements de doublons de facturation, numéro de messagerie signalé pour comportement proche du spam, et l'érosion lente qui survient lorsque les clients apprennent que votre agent n'est pas tout à fait fiable. Si une proposition pour un agent qui envoie de vrais messages ne mentionne nulle part l'idempotence, c'est la question à poser avant de signer.

Si vous définissez le périmètre d'un agent qui agira pour le compte de votre entreprise et que vous souhaitez formaliser les exigences de fiabilité avant le début du développement, parlons-en.

ai agentsreliabilityidempotencywhatsapp

Questions fréquentes

Pourquoi mon agent IA a-t-il envoyé le même message WhatsApp deux fois ?

Presque toujours parce qu'un webhook, une file d'attente ou un worker a réessayé après un timeout, et que l'agent n'avait pas de clé d'idempotence liée à l'action prévue. Le modèle a fait son travail ; c'est la plomberie qui l'a exécuté deux fois.

Le prompt engineering peut-il empêcher les actions en double ?

Non. Le prompt s'exécute à l'intérieur du retry. La déduplication doit se trouver à la frontière où l'agent appelle le monde extérieur, dans le code et dans la base de données, pas dans les instructions données au modèle.

Combien coûte le fait de rendre un agent résistant aux retries dans un projet ?

En général quelques jours d'ingénierie par type d'action externe, l'essentiel étant consacré à la conception de la clé et de l'étape de réservation. C'est bien moins coûteux que les remboursements, les numéros bloqués et l'atteinte à la confiance causés par les doublons.

Ce problème touche-t-il aussi les plateformes d'agents managées ?

Oui. Les runtimes managés réessaient pour vous les étapes échouées, ce qui est utile, mais l'idempotence de vos propres actions reste votre responsabilité. Demandez au fournisseur où exactement se fait la déduplication.

Comment Pykero peut aider

Services liés à cet article.

Vous construisez quelque chose de similaire ?

Pykero Agency conçoit et livre des produits web, mobiles, SaaS et IA prêts pour la production.

Parlons-en

Discussion

Be the first to comment.

À lire aussi