8 min de lecture

Empêcher les écritures API en double lorsque les agents IA relancent les requêtes

Empêchez les écritures API en double causées par les agents IA grâce aux jetons d'idempotence, aux empreintes de requête, aux réservations atomiques et aux confirmations liées à chaque action.

Empêcher les écritures API en double lorsque les agents IA relancent les requêtes

Les agents IA commettent plus vite que les humains les erreurs liées aux nouvelles tentatives. Face à un délai d'attente, une personne peut faire une pause, consulter la file de tickets et déterminer ce qui s'est passé. Un agent voit souvent une exception, suit l'instruction de réessayer et envoie une seconde écriture avant que la première requête ne soit terminée quelque part au-delà de la limite du réseau.

Ce comportement crée des tickets en double, des tentatives de paiement répétées, des invitations d'utilisateurs en double et deux déploiements du même changement. La solution n'est pas de demander à l'agent de faire attention. Il faut un contrat d'API qui conserve l'identité d'une même action voulue lors des nouvelles tentatives, détecte les changements dissimulés derrière un identifiant réutilisé et demande une nouvelle confirmation humaine lorsque les conséquences le justifient.

Les nouvelles tentatives sont normales, les effets en double sont facultatifs

Un délai d'attente ne signifie pas que le serveur n'a rien fait. Il a peut-être créé le ticket, puis perdu sa réponse sur le chemin du retour. Il traite peut-être encore la requête. Un répartiteur de charge a pu accepter la connexion alors que le service en amont n'a jamais reçu les octets. Le client ne peut pas déduire le résultat à partir d'une erreur de socket.

C'est pourquoi l'instruction habituelle donnée à un agent, « réessayer en cas d'erreur réseau », est incomplète. Elle considère tout résultat incertain comme une action échouée. Pour les points d'accès d'écriture, un résultat incertain peut correspondre à trois états :

  • le serveur n'a pas reçu la requête
  • le serveur a accepté la requête et terminé le travail
  • le serveur a accepté la requête, mais n'a pas encore terminé

La même nouvelle tentative doit être sûre dans les trois cas. Si elle crée un second effet lorsque le travail est déjà terminé, le point d'accès possède une limite de nouvelle tentative dangereuse.

HTTP ne règle pas ce problème à votre place. La RFC 9110 définit les méthodes idempotentes comme celles dont l'effet voulu reste identique après une ou plusieurs requêtes identiques. Elle cite PUT, DELETE et les méthodes sûres. La RFC indique aussi qu'un client peut relancer une requête idempotente après une panne de communication. C'est utile, mais cela ne rend pas automatiquement sûr tout point d'accès qui utilise une route PUT. Un serveur peut associer l'envoi d'un e-mail, l'émission d'un crédit ou le déclenchement d'un déploiement à un gestionnaire PUT et répéter cet effet de bord s'il n'a pas été conçu pour l'éviter.

POST nécessite un accord explicite. De nombreuses API utilisent POST pour les actions parce que le serveur attribue les identifiants des ressources ou parce que la requête signifie « exécuter cette opération métier ». Un agent ne peut relancer une telle requête que si l'API indique comment reconnaître une même opération entre plusieurs tentatives.

Séparez les nouvelles tentatives de transport des nouvelles tentatives métier. Une nouvelle tentative de transport renvoie la même opération parce que le résultat reste inconnu. Une nouvelle tentative métier démarre une autre opération parce que la première a abouti à un échec terminal connu. Les confondre produit le compte rendu d'incident classique : l'agent a réussi la nouvelle tentative, deux fois.

Un jeton d'idempotence identifie une action voulue

Un jeton d'idempotence est un identifiant opaque généré par le client. Il signifie que « toutes les requêtes portant cette valeur sont des tentatives d'exécuter cette même action ». L'agent le crée avant la première requête, le conserve avec l'état de sa tâche et envoie la même valeur à chaque nouvelle tentative.

Le jeton doit appartenir à l'action logique, et non à une tentative HTTP. Si un agent crée un ticket d'assistance, perd la réponse, puis envoie une autre requête avec un nouveau jeton, l'API n'a aucun moyen de reconnaître une nouvelle tentative. Elle doit créer un second ticket, puisque l'appelant lui a indiqué qu'il s'agissait d'une seconde opération.

Utilisez une valeur aléatoire avec une forte entropie. Un UUID est courant, mais n'importe quel format convient si les appelants ne peuvent pas deviner les valeurs et si le serveur traite le jeton comme une valeur opaque. Placez-le dans un en-tête Idempotency-Key ou dans un champ de requête documenté. Un en-tête sépare l'identité de l'opération du contenu métier et permet plus facilement au middleware de la faire suivre dans les journaux et le traçage.

Une requête pratique ressemble à ceci :

curl -X POST https://api.example.test/v1/tickets \\
  -H 'Authorization: Bearer $TOKEN' \\
  -H 'Content-Type: application/json' \\
  -H 'Idempotency-Key: 81b59b1a-9e75-4de7-a53b-1bb50969c83c' \\
  -d '{"project":"ops","title":"Rotate staging certificate","priority":"high"}'

Lors du premier appel accepté, le serveur enregistre le jeton, une empreinte canonique de la requête, l'état de l'opération et, à terme, la réponse qu'il renverra. Si une requête ultérieure porte le même jeton et la même empreinte, le serveur renvoie le résultat précédent au lieu de créer un autre ticket.

Le client a besoin d'un emplacement durable pour conserver le jeton. Un agent qui ne le stocke que dans son invite actuelle ou dans la mémoire de son processus perd l'identité de l'opération après un redémarrage. Stockez-le à côté de l'enregistrement de la tâche, du travail ou du point de contrôle du workflow. Si une personne demande à l'agent de créer un second ticket volontairement distinct avec le même texte, l'agent doit générer un nouveau jeton, car cette personne a exprimé une nouvelle intention.

Ne faites pas du jeton le nom modifiable d'une tâche, un horodatage ou une requête en langage naturel. Ces valeurs peuvent entrer en collision, changer entre les tentatives ou exposer des informations dans les journaux. Les identifiants opaques sont peu passionnants. C'est précisément pour cela qu'ils fonctionnent.

Une empreinte détecte les nouvelles tentatives modifiées

Un jeton indique si l'appelant affirme que deux requêtes constituent une seule opération. Une empreinte de requête indique si ces requêtes ont réellement le même sens. Il faut les deux.

Supposons qu'un agent demande d'abord à une API de déploiement d'envoyer le commit a1b2c3 en préproduction. La requête expire, l'agent lit une note de tâche plus récente et relance la demande avec le même jeton, mais le commit d4e5f6. Si le serveur renvoie aveuglément la première réponse, il dissimule une erreur de l'agent. S'il exécute le second corps, il permet à un seul identifiant d'opération d'autoriser deux déploiements différents.

Transformez les éléments importants de la requête en une forme canonique, puis hachez le résultat. La plupart des API incluent la méthode HTTP, une route normalisée, le compte ou le locataire authentifié et le corps JSON canonique. Certaines incluent certains en-têtes lorsque ceux-ci modifient l'effet métier. Excluez les en-têtes de traçage variables, les métadonnées de connexion et l'en-tête d'idempotence lui-même.

Le JSON demande de la rigueur. Les hachages d'octets bruts échouent lorsque des JSON équivalents utilisent un ordre de propriétés ou des espaces différents. Une représentation canonique trie les propriétés des objets, conserve l'ordre des tableaux, utilise un format numérique défini et omet les champs attribués par le serveur. Mieux encore, calculez l'empreinte de l'objet de commande validé, après que l'API a appliqué les valeurs par défaut et refusé les champs inconnus. Vous correspondez ainsi à l'opération que le serveur exécutera, et non à un encodage d'entrée arbitraire.

Par exemple, ce pseudo-code enregistre une empreinte après validation :

command = validate_create_ticket(request.body)
canonical = canonical_json({
  "method": "POST",
  "route": "/v1/tickets",
  "account_id": authenticated_account.id,
  "command": command
})
fingerprint = sha256(canonical)

Lorsqu'un jeton existe déjà, comparez les empreintes avant de renvoyer un résultat précédent ou d'attendre sa disponibilité. Si elles diffèrent, refusez la requête avec une réponse de conflit. Incluez l'identifiant et l'état de l'opération enregistrés, mais ne renvoyez pas de détails protégés de la requête à un appelant non autorisé.

Une empreinte ne détecte pas à elle seule les doublons. Deux utilisateurs peuvent légitimement créer deux tickets identiques. Un service de paie peut légitimement effectuer des paiements identiques pour deux employés. Hacher un contenu et dédupliquer chaque correspondance supprime silencieusement des tâches valides. Limitez la déduplication au jeton d'idempotence, puis utilisez des règles d'unicité propres au métier lorsque le domaine l'exige réellement.

Les hachages cryptographiques rendent les collisions accidentelles irréalistes avec une fonction moderne comme SHA-256. Ils ne prouvent pas l'intention de l'appelant. Le jeton porte l'intention, tandis que l'empreinte impose la cohérence. Les équipes qui les considèrent comme interchangeables finissent généralement par avoir une règle de déduplication impossible à expliquer lorsqu'elle refuse une requête légitime.

Le serveur doit réserver le jeton avant d'agir

Une table d'idempotence qui n'enregistre les résultats qu'après la fin de l'effet de bord comporte toujours une condition de concurrence. Deux nouvelles tentatives simultanées peuvent consulter la table, ne rien trouver, créer deux tickets, puis se disputer l'enregistrement d'un résultat. J'ai vu ce problème présenté comme un comportement instable de l'agent alors que le véritable défaut était l'absence de contrainte d'unicité.

Le serveur doit réserver atomiquement le jeton avant d'effectuer un travail irréversible. Ajoutez une contrainte unique sur la portée et le jeton, généralement un identifiant de compte associé au jeton d'idempotence. Dans une transaction, tentez d'insérer une ligne contenant l'empreinte et l'état in_progress. La requête qui gagne prend en charge l'exécution. Toutes les autres lisent la ligne existante.

Une table simplifiée peut contenir les champs suivants :

create table idempotency_operations (
  account_id text not null,
  token text not null,
  fingerprint text not null,
  state text not null,
  response_status integer,
  response_body jsonb,
  created_at timestamptz not null,
  primary key (account_id, token)
);

La clé primaire joue ici un rôle réel. Le code applicatif qui vérifie d'abord puis insère ensuite laisse une ouverture assez grande pour que des workers concurrents, une remise en file et des nouvelles tentatives impatientes passent au travers.

Après la réservation, le gestionnaire effectue l'action métier et écrit la réponse finale dans la ligne de l'opération. Les requêtes ultérieures correspondantes reçoivent ce statut et ce corps enregistrés. Les appelants obtiennent ainsi une réponse stable, même lorsque le gestionnaire d'origine a réussi mais que la connexion est morte avant l'envoi de la réponse.

Le cas délicat est celui d'une requête qui possède une ligne mais dont le processus meurt au milieu du travail. Ne supprimez pas la ligne simplement parce qu'un worker a expiré. Un autre worker peut encore terminer, ou le prestataire externe peut déjà avoir accepté l'opération. Marquez l'opération comme en attente ou inconnue, enregistrez suffisamment de données pour l'analyser et permettez aux appelants de consulter son état. Une tâche de réparation ne peut résoudre les enregistrements obsolètes que si elle comprend l'état du système en aval.

Pour le travail qui traverse une base de données et une API externe, utilisez un modèle outbox ou un jeton d'idempotence fourni par le prestataire. Une transaction de base de données ne peut pas annuler un e-mail, un paiement ou un déploiement cloud après sa sortie de votre processus. Écrivez l'intention et un événement outbox dans une même transaction locale, puis demandez à un worker d'envoyer l'événement avec un identifiant d'opération stable auprès du système en aval. Le code de récupération dispose ainsi d'un élément concret à rejouer sans inventer une seconde action.

La confirmation doit être liée à l'opération exacte

Conservez la preuve des nouvelles tentatives
Son journal d'audit chiffré et chaîné par hachage conserve la preuve des actions passées par la passerelle.

La confirmation humaine empêche un autre type d'échec : un agent peut avoir l'autorisation d'agir, alors que l'action proposée est surprenante, trop large ou répétée après un changement de contexte. Un bouton générique « autoriser le déploiement » ne suffit pas. Il permet à l'agent de remplacer un déploiement par un autre sous la même approbation.

Une confirmation utile indique la cible, l'opération, la conséquence et l'identifiant de l'opération. Pour un déploiement en production, affichez l'environnement, l'artefact ou la référence du commit, le service concerné et la possibilité ou non d'annuler l'action. Pour un paiement, affichez le bénéficiaire, le montant, la devise et la référence de la facture. Pour un ticket, affichez le projet de destination et le titre.

L'enregistrement de confirmation doit être lié à l'empreinte de la requête et expirer lorsque la proposition n'est plus à jour. Si l'agent modifie le corps après l'approbation d'une personne, l'empreinte change et le système doit demander une nouvelle confirmation. Réutiliser une approbation après la modification d'une requête constitue une forme discrète d'élévation de privilèges, même si personne ne l'a voulu.

Ne forcez pas une personne à approuver chaque nouvelle tentative à faible risque. Vous transformeriez une conception d'idempotence correcte en fatigue d'approbation. La première approbation peut autoriser l'opération correspondant à l'empreinte, et les nouvelles tentatives identiques peuvent utiliser cette approbation puisqu'elles ne peuvent pas en modifier le sens. Un contenu modifié nécessite une nouvelle décision.

Certaines équipes s'appuient sur un message de discussion comme « Continuer ? » et considèrent la réponse comme une approbation. Cette méthode échoue sous pression, car l'enregistrement ne contient souvent pas les paramètres exacts et l'agent peut prendre une réponse ultérieure pour le consentement à une requête précédente. Placez l'identifiant de l'opération dans l'enregistrement de confirmation et obligez l'exécuteur à le vérifier avant d'envoyer l'écriture.

Un payload d'approbation simple rend le lien visible :

{
  "operation_id": "op_3f8c",
  "idempotency_token": "81b59b1a-9e75-4de7-a53b-1bb50969c83c",
  "fingerprint": "e5c7...",
  "expires_at": "2025-06-14T15:30:00Z",
  "approved_by": "user_42"
}

Considérez la confirmation comme une autorisation pour une commande précise, et non comme la permission d'improviser autour d'une catégorie de commandes. Cette distinction permet de relancer une opération sans risque, sans donner à l'agent une approbation générale qu'il pourrait réutiliser plus tard.

Les systèmes de tickets ont aussi besoin d'une vérification métier des doublons

Les jetons d'idempotence arrêtent les tentatives de transport en double, mais les systèmes de tickets ont une autre source de duplication : les agents peuvent démarrer des opérations distinctes qui décrivent le même problème. Une alerte de supervision arrive deux fois, deux exécutions d'agent lisent le même canal d'incident ou un planificateur se réveille après un crash et rejoue une tâche sans son état d'origine.

Ne résolvez pas ce problème en dédupliquant sur le texte du titre. Les titres de tickets varient suffisamment pour laisser passer des doublons, et des titres identiques peuvent désigner des incidents différents. Décidez plutôt ce que signifie l'identité dans le domaine des tickets. Il peut s'agir d'un identifiant d'événement d'alerte, d'un identifiant d'incident, d'une référence à un problème de dépôt ou d'une valeur composée comme le service, l'empreinte de l'alerte et la période de l'incident.

Rendez cet identifiant métier explicite dans l'API :

{
  "source_event_id": "alert-7c91",
  "project": "operations",
  "title": "Certificate expiry alert",
  "description": "Alert event alert-7c91 crossed its threshold."
}

Le service de tickets peut imposer l'unicité de source_event_id dans la portée prévue. Une seconde exécution de l'agent reçoit alors l'identifiant du ticket existant au lieu d'ajouter un nouvel élément à la file. C'est différent de l'idempotence. Les deux appels peuvent avoir des jetons d'idempotence différents, puisqu'ils proviennent de deux processus d'agent distincts, tout en représentant le même événement en amont.

Les agents ne doivent rechercher avant de créer que lorsque le résultat de la recherche possède une identité stable et fiable. Les workflows fondés sur une recherche par titre sont tentants parce qu'ils ne demandent aucune modification de l'API. Ils se brisent dès que l'indexation prend du retard, que le classement des résultats change ou qu'un agent paraphrase le titre. Placez la règle d'unicité là où l'écriture est effectuée et renvoyez une réponse claire indiquant si l'API a créé ou réutilisé un ticket.

Soyez prudent avec les commentaires automatiques et les changements de statut. Une opération qui trouve un ticket existant peut tout de même ajouter un commentaire en double ou rouvrir un incident résolu. Donnez à chaque sous-action importante son propre identifiant, ou faites en sorte que la commande d'écriture exprime l'état final souhaité. Les points d'accès vagues comme « mettre à jour ce ticket » sont difficiles à relancer sans risque, car personne ne peut savoir quelle partie de la mise à jour a déjà été exécutée.

Les écritures de paiement nécessitent une requête de résultat, pas de l'optimisme

Contrôlez chaque nouvel agent
Utilisez l'approbation par session par défaut afin qu'un processus d'agent nouvellement lancé ne puisse pas agir à votre insu.

Les actions de paiement exigent un niveau de rigueur supérieur, car un double débit nuit au client même si vous le remboursez ensuite. L'application doit envoyer un jeton d'idempotence stable à son prestataire de paiement et conserver la référence de transaction du prestataire avec l'enregistrement local de l'opération.

Lorsque le client expire, il doit considérer le paiement comme inconnu. Il doit interroger la référence du prestataire, la référence marchand ou le jeton d'idempotence si le prestataire permet cette recherche. Il ne doit pas démarrer une autre tentative de paiement parce que l'agent n'a reçu aucune réponse de succès.

Deux opérations sont souvent confondues : créer une intention de paiement et capturer les fonds. Leur comportement en cas de nouvelle tentative peut être différent. Un service peut créer ou retrouver sans risque un objet de paiement avec un jeton, puis exiger une action de capture explicite distincte après les contrôles. Représentez clairement les états métier au lieu de les cacher derrière un seul point d'accès qui tente tout à chaque appel.

Les montants doivent être traités de manière canonique avant le calcul de l'empreinte. Convertissez les valeurs dans la plus petite unité monétaire prise en charge, ou dans une autre représentation exacte, avant que la requête n'atteigne la couche de déduplication. Ne hachez pas une valeur d'affichage en virgule flottante en espérant que des calculs équivalents seront comparés correctement. Une requête de paiement doit aussi inclure une référence de facture ou de commande lorsque le domaine en possède une, car elle permet au personnel d'identifier une intention en double au-delà des nouvelles tentatives réseau.

La fonction d'idempotence d'un prestataire ne dispense pas votre propre API d'agir. Votre application doit toujours empêcher deux tâches d'agent de lancer deux requêtes distinctes auprès du prestataire pour la même commande. Ajoutez une contrainte d'unicité à l'état payable de la commande, utilisez un enregistrement d'opération local et faites interroger cet enregistrement par l'agent après une incertitude.

Les remboursements exigent la même attention. « Relancer le remboursement » peut signifier relancer la même demande de remboursement ou lancer un autre remboursement partiel. Conservez un identifiant stable pour chaque instruction de remboursement et enregistrez le montant déjà demandé. Si l'agent doit effectuer un second remboursement, faites-en une instruction nouvelle et explicitement autorisée, avec un nouvel identifiant.

Les déploiements ont besoin de références immuables et d'un verrou de version

Une nouvelle tentative de déploiement n'est sûre que si elle désigne la même version. Les noms de branches comme main et les tags modifiables comme latest ne respectent pas cette exigence. Après un délai d'attente, une nouvelle tentative peut résoudre le même nom vers un code différent, puis sembler réussir alors qu'elle déploie un élément que l'approbateur n'a jamais examiné.

Utilisez une empreinte d'artefact immuable, un identifiant de commit ou une version dont votre système de publication garantit qu'elle ne changera pas. Incluez-la dans l'empreinte de la requête et dans la confirmation. Si un agent soumet le même jeton d'idempotence avec une référence d'artefact différente, refusez la requête comme un conflit au lieu de traiter la seconde comme une nouvelle tentative mise à jour.

Il vous faut aussi une règle de concurrence pour l'environnement. Deux opérations distinctes peuvent légitimement porter deux jetons différents tout en entrant en conflit parce qu'elles ciblent toutes deux la production. Un verrou de version, une vérification optimiste de version ou une file de déploiement peut sérialiser ces changements. L'idempotence ne décide pas laquelle de deux versions distinctes doit gagner. Elle empêche seulement un même déploiement de s'exécuter deux fois.

Considérez cette séquence d'échec. L'agent démarre le déploiement dep-118 pour le commit a1b2c3 et le contrôleur de déploiement l'accepte. L'agent perd la réponse, suppose un échec et démarre dep-119 avec le commit d4e5f6 parce qu'un commit plus récent est apparu. Les deux tâches modifient alors le même environnement. Un jeton aurait empêché uniquement une véritable nouvelle tentative de dep-118 ; le verrou de version ou la vérification de la version attendue de l'environnement empêche le second plan conflictuel.

L'API de déploiement doit exposer une ressource d'état de l'opération indiquant si celle-ci est en file, en cours, réussie, échouée, annulée ou inconnue. Après un délai d'attente, les agents doivent interroger cet état. Ils ne doivent pas déduire la fin de l'opération d'une réponse absente ou d'une ligne de journal qui ne contient pas l'identifiant de l'opération.

Un rollback a besoin de son propre identifiant d'opération et de sa propre approbation. Traiter un rollback comme une nouvelle tentative du déploiement masque un changement important d'intention. Il peut être automatique selon une règle de sécurité documentée, mais il doit laisser un enregistrement distinct de celui de la version d'origine.

Les outils d'agent doivent conserver l'identité de l'opération au-delà de la limite

Identifiez le processus qui effectue la nouvelle tentative
Approuvez un nouveau processus d'agent une seule fois, puis révoquez sa session si une nouvelle tentative se passe mal.

L'interface d'un outil d'agent doit rendre le comportement sûr plus facile que le comportement dangereux. Donnez à l'agent une action qui accepte un identifiant d'opération stable, un payload et un mode de nouvelle tentative déclaré. Renvoyez un résultat indiquant si le service a créé le travail, renvoyé un résultat précédent, trouvé une opération en cours ou refusé une nouvelle tentative modifiée.

Évitez les outils qui génèrent silencieusement un nouveau jeton d'idempotence à chaque invocation. Ils semblent pratiques dans une démonstration et échouent dès le premier délai d'attente réel. Si l'outil gère la génération du jeton, il doit le renvoyer immédiatement et le conserver à un endroit qu'une invocation ultérieure peut consulter. Dans la plupart des systèmes, la couche de workflow doit gérer le jeton, car elle sait quels appels appartiennent à une même action demandée par l'utilisateur.

Sallyport peut conserver les identifiants d'API en dehors de l'agent pendant que celui-ci soumet l'action HTTP voulue par sa connexion MCP. Cette séparation réduit l'exposition des identifiants, mais l'API en aval doit toujours gérer l'idempotence. Un identifiant protégé ne transforme pas un POST ambigu en nouvelle tentative sûre.

Rendez explicite la règle de nouvelle tentative de l'agent dans le contrat de l'outil :

if response is a known success:
    record operation complete
if response is a timeout or connection failure:
    query operation status using the same token
    retry only with the same token if the API permits it
if response says fingerprint conflict:
    stop and request a new operation or human review
if response is a known business failure:
    do not retry until the task changes

Ne laissez pas l'agent utiliser l'attente exponentielle comme substitut à l'état de l'opération. Elle réduit la pression sur un service, ce qui est important, mais elle ne dit pas si la dernière écriture a réussi. L'agent doit conserver l'identifiant de l'opération avant d'attendre.

Les journaux doivent prouver ce qui s'est passé après une écriture contestée

Lorsqu'un client affirme avoir été débité deux fois ou qu'un ingénieur trouve deux tickets, vous devez répondre à quatre questions : quelle exécution d'agent a émis chaque requête, quel jeton a été utilisé, quelle empreinte le serveur a calculée et quel résultat le service en aval a renvoyé. Les journaux généraux de requêtes omettent souvent au moins un de ces éléments.

Enregistrez une opération à la limite où l'API accepte l'action. Incluez le principal authentifié, le jeton, l'empreinte, la route de la requête, les transitions d'état de l'opération, la référence de réponse et la référence du prestataire en amont lorsqu'il y en a une. Gardez les secrets et les corps sensibles complets hors des journaux courants. Une empreinte permet de comparer les requêtes sans stocker chaque champ privé dans tous les systèmes de journalisation.

Un enregistrement d'audit en ajout seul est utile lorsqu'un agent possède l'autorité d'effectuer des écritures externes. L'enregistrement doit distinguer les états tenté, approuvé, envoyé, accepté, terminé et rejoué. Ils ne sont pas interchangeables. Une nouvelle tentative qui reçoit une réponse précédente enregistrée doit être indiquée comme replayed, et non comme created, sinon vos opérateurs la compteront comme une seconde action.

Sallyport enregistre les sessions des agents et les actions individuelles dans des journaux dérivés d'un journal d'audit chiffré et chaîné par hachage, et sp audit verify peut vérifier cette chaîne hors ligne. Cela peut établir ce qui est passé par la passerelle d'action. Associez cette preuve aux enregistrements d'idempotence du service qui reçoit la requête, car c'est ce service qui détermine si l'action métier a été exécutée.

Testez le parcours d'une écriture contestée avant de lui faire confiance. Forcez le serveur à terminer une requête puis à supprimer la réponse. Envoyez des copies simultanées avec un seul jeton. Redémarrez l'agent entre les tentatives. Réutilisez un jeton avec un payload modifié. Arrêtez un worker après sa réservation du jeton et avant l'enregistrement de la fin. Une conception qui ne résiste qu'aux réponses de succès normales n'a pas résolu le problème des écritures en double.

Commencez par le point d'accès d'écriture dont la répétition serait la plus dommageable. Ajoutez un jeton stable, réservez-le atomiquement avant tout effet de bord, liez-le à une empreinte et donnez aux appelants un moyen de consulter l'état des résultats inconnus. Faites ensuite en sorte que l'agent conserve cet identifiant jusqu'à ce qu'il puisse prouver que l'opération a atteint un état final.

FAQ

Comment empêcher un agent IA de créer des enregistrements en double après un délai d'attente ?

Attribuez à chaque opération logique un jeton d'idempotence stable avant que l'agent n'envoie sa première requête. Le serveur enregistre la première réponse terminée pour ce jeton et la renvoie lors des nouvelles tentatives. Ne générez pas un nouveau jeton après un délai d'attente, car la nouvelle tentative deviendrait une nouvelle opération.

Un hachage de requête suffit-il pour assurer l'idempotence d'une API ?

Non. Une empreinte de requête peut détecter qu'un même contenu est arrivé deux fois, mais elle ne permet pas de déterminer de façon fiable si deux contenus identiques correspondent à une seule action voulue ou à deux actions distinctes. Utilisez un jeton d'idempotence fourni par le client pour établir l'intention, puis une empreinte pour refuser la réutilisation d'un jeton avec un contenu différent.

Un agent IA peut-il relancer des requêtes POST en toute sécurité ?

Seulement si le point d'accès possède un contrat d'idempotence documenté et que l'agent conserve le même jeton lors des nouvelles tentatives. POST n'est pas automatiquement idempotent selon la sémantique HTTP. Un agent sûr a aussi besoin de nouvelles tentatives limitées, d'une gestion claire des délais d'attente et d'un moyen de récupérer le résultat de l'opération.

Que doit renvoyer une API lorsqu'elle reçoit deux fois le même jeton d'idempotence ?

Le serveur doit renvoyer la réponse enregistrée pour la première requête acceptée, avec l'identifiant et le statut de la ressource d'origine. Il ne doit pas répéter les effets de bord ni créer un second enregistrement. Si la requête d'origine est encore en cours, renvoyez un statut explicite indiquant qu'elle est en cours, ou faites attendre l'appelant jusqu'à l'enregistrement du résultat.

Comment les jetons d'idempotence empêchent-ils les doubles débits bancaires ?

Utilisez un jeton d'idempotence pour toute la tentative d'achat et laissez le prestataire de paiement déterminer si le client a été débité. Après une panne réseau incertaine, ne relancez jamais votre propre point d'accès de paiement avec un nouveau jeton. Interrogez l'opération enregistrée ou la transaction du prestataire avant d'effectuer une autre action de paiement.

Les demandes de confirmation empêchent-elles les doubles déploiements ?

Elles aident, mais ne remplacent pas l'autorisation. Une confirmation doit associer une personne à une action précise, avec sa cible, son changement, sa portée et un identifiant d'opération. Sinon, une approbation générale peut autoriser par accident une nouvelle tentative dont le contenu a changé.

Combien de temps une API doit-elle conserver les jetons d'idempotence ?

Faites expirer les jetons pendant la période où les appelants peuvent raisonnablement relancer ou rejouer une requête. La durée exacte dépend du budget de nouvelles tentatives du client et du risque métier. Conservez plus longtemps un enregistrement métier lorsqu'une ancienne nouvelle tentative pourrait encore causer un dommage, par exemple pour un paiement ou la création d'un ticket externe.

Que se passe-t-il si le même jeton d'idempotence est associé à un autre corps de requête ?

Considérez la réutilisation d'un jeton avec une empreinte différente comme une erreur du client et refusez de l'exécuter. Une réponse de conflit pousse l'agent à s'arrêter et à examiner son propre état au lieu d'attribuer silencieusement une nouvelle signification à un ancien identifiant d'opération.

Que doit faire un agent après l'expiration du délai d'une requête API ?

L'appelant doit considérer le résultat comme inconnu, et non comme un échec. Il doit interroger un point d'accès d'état de l'opération avec le même jeton ou le même identifiant de requête, puis ne relancer la requête avec ce même identifiant que si l'API l'autorise. Envoyer aveuglément un nouveau POST est la cause des tickets et des débits en double.

PUT et DELETE peuvent-ils automatiquement être relancés sans risque ?

PUT et DELETE possèdent une sémantique HTTP idempotente lorsque le serveur les implémente correctement, mais un délai d'attente laisse toujours le client dans l'incertitude quant à l'effet de la première requête. POST peut aussi être rendu sûr avec un jeton d'idempotence, et de nombreuses opérations métier ont besoin de ce contrat explicite.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov