Décryptage sur Habr : pourquoi r.Context() n'arrête pas un LLM, et comment construire des tâches IA en Go
Habr a publié un décryptage d'un piège classique des backends Go qui appellent des LLM. Le schéma habituel annule le travail en même temps que la requête HTTP, via r.Context(). Mais si l'utilisateur ferme l'onglet en pleine analyse d'un document, le fournisseur peut poursuivre la génération et facturer les tokens. L'auteur propose de sortir les opérations IA longues dans des tâches d'arrière-plan sur PostgreSQL, et de traiter séparément l'idempotence, les retry et la protection contre les débits en double. • Le contexte d'une requête n'est pas le cycle de vie d'une tâche IA. • Mieux vaut stocker l'état de la tâche dans PostgreSQL que dans la mémoire du processus. • Un retry sans idempotence revient à payer deux fois l'API.
Traité par IA depuis Habr AI ; édité par Hamidun News
Fermer l'onglet d'un navigateur annule la requête HTTP, mais n'oblige pas le modèle à s'arrêter côté fournisseur. L'auteur d'un décryptage récent sur Habr montre comment cet écart se traduit en coûts et en bugs dans un backend Go ordinaire. Si un utilisateur lance l'analyse d'un document puis s'en va, le service peut se retrouver sans résultat, et la facture de tokens arrivera quand même. Pour les opérations IA longues, l'auteur recommande des tâches d'arrière-plan sur PostgreSQL, une idempotence stricte et une politique de retry réfléchie.
Où le modèle habituel se brise
Les développeurs Go ont une règle simple : le travail dure tant que `r.Context()` est vivant. Le client coupe la connexion, le contexte est annulé, les goroutines se terminent, les ressources sont libérées. Pour une requête vers une base de données ou un microservice interne, c'est pratique et pertinent.
Avec un LLM, tout change. Un appel peut durer des dizaines de secondes, voire des minutes, surtout pour l'analyse de gros documents ou des chaînes de plusieurs appels au modèle. La facturation porte sur les tokens traités et générés, et une annulation de votre côté ne garantit pas que le fournisseur cesse immédiatement le travail. Cela dépend de l'API concernée et de la manière dont la connexion est fermée.
Deux problèmes surgissent aussitôt :
- le travail est annulé, il n'y a pas de résultat, mais l'argent est dépensé ;
- l'utilisateur revient, clique de nouveau sur le bouton, et l'opération payante est relancée.
La tâche d'arrière-plan comme entité à part entière
L'idée centrale du décryptage : une opération IA ne doit pas appartenir à la requête HTTP. Le handler reçoit l'entrée, crée une tâche et répond immédiatement avec un identifiant. La tâche vit ensuite sa propre vie, et le client en suit l'avancement par polling, SSE ou WebSocket.
L'auteur choisit PostgreSQL comme stockage. Pour beaucoup d'équipes, c'est un choix raisonnable : la base est déjà dans la stack, les transactions sont maîtrisées, et il n'y a pas besoin d'un broker de messages dédié. Une telle file d'attente repose typiquement sur les éléments suivants :
- une table de tâches avec un statut (créée, en cours, terminée, erreur, annulée) ;
- une prise en charge des tâches par les workers via `FOR UPDATE SKIP LOCKED`, pour que deux workers ne prennent pas la même ;
- l'horodatage du dernier heartbeat, qui permet de remettre en file les tâches bloquées ;
- un compteur de tentatives et l'heure de la prochaine tentative ;
- le résultat enregistré et les données sur les tokens consommés.
Le principal gain : l'état survit au redémarrage du service. Lors d'un déploiement ou de la chute d'un pod, la tâche ne disparaît pas avec la goroutine, elle est reprise par un autre worker.
Idempotence et argent
Le point le plus coûteux d'un tel système se situe à la jonction entre retry et facturation. Rejouer une requête vers un LLM coûte de l'argent : on ne peut donc pas le faire à l'aveugle, comme on relancerait un appel au cache.
Voici les principes pratiques qui découlent de la logique du décryptage :
- la clé d'idempotence est créée au niveau de l'action de l'utilisateur et protège contre le double clic sur le bouton ;
- la tâche possède un identifiant unique, grâce auquel le résultat n'est écrit qu'une seule fois ;
- le retry n'est autorisé que pour les erreurs où une nouvelle tentative a du sens (timeout, 429, 5xx), mais pas pour une entrée invalide ;
- le débit de l'utilisateur est rattaché à la tâche, et non à chaque tentative ;
- l'annulation par l'utilisateur devient un statut explicite, et non un effet de bord d'une connexion rompue.
Par où commencer
Si, dans votre service, l'appel au LLM s'exécute directement dans le handler, vérifiez trois points. Premièrement : que devient la génération déjà payée si le client se déconnecte en cours de route ? Deuxièmement : une requête répétée peut-elle créer un doublon de l'opération payante ? Troisièmement : la tâche survivra-t-elle au redémarrage du processus ?
Si la réponse à l'une de ces questions est gênante, il faut un modèle de tâche distinct. L'étape minimale est une table avec des statuts et un seul worker ; `SKIP LOCKED`, le heartbeat et la politique de retry peuvent s'ajouter à mesure que la charge augmente. Un broker dédié n'a de sens que lorsque PostgreSQL ne suffit plus pour gérer la file. D'ici là, il ne fait généralement que compliquer le système.
Ce texte ne parle ni d'un nouveau modèle ni d'une sortie, mais de la discipline d'ingénierie autour des LLM. C'est précisément là que se perd aujourd'hui une part notable des budgets des produits IA : les erreurs surviennent dans le code d'intégration, pas dans les prompts.
❓ Questions clés sur cet article
Que sait-on sur Où le modèle habituel se brise ?
Les développeurs Go ont une règle simple : le travail dure tant que `r.Context()` est vivant. Le client coupe la connexion, le contexte est annulé, les goroutines se terminent, les ressources sont libérées. Pour une requête vers une base de données ou un microservice interne, c'est pratique et pertinent. Avec un LLM, tout change. Un appel peut durer des dizaines de secondes, voire des minutes, surtout pour l'analyse de gros documents ou des chaînes de plusieurs appels au modèle. La facturation porte sur les tokens traités et générés, et une annulation de votre côté ne garan
Que sait-on sur La tâche d'arrière-plan comme entité à part entière ?
L'idée centrale du décryptage : une opération IA ne doit pas appartenir à la requête HTTP. Le handler reçoit l'entrée, crée une tâche et répond immédiatement avec un identifiant. La tâche vit ensuite sa propre vie, et le client en suit l'avancement par polling, SSE ou WebSocket. L'auteur choisit PostgreSQL comme stockage. Pour beaucoup d'équipes, c'est un choix raisonnable : la base est déjà dans la stack, les transactions sont maîtrisées, et il n'y a pas besoin d'un broker de messages dédié. Une telle file d'attente repose typiquement sur les éléments suivants : une table
Que sait-on sur Idempotence et argent ?
Le point le plus coûteux d'un tel système se situe à la jonction entre retry et facturation. Rejouer une requête vers un LLM coûte de l'argent : on ne peut donc pas le faire à l'aveugle, comme on relancerait un appel au cache. Voici les principes pratiques qui découlent de la logique du décryptage : la clé d'idempotence est créée au niveau de l'action de l'utilisateur et protège contre le double clic sur le bouton ; la tâche possède un identifiant unique, grâce auquel le résultat n'est écrit qu'une seule fois ; le retry n'est autorisé que pour les erreurs où une nouvelle te
Access Frontier LLMs & Multimodal AI in One Workspace
Discover NomadNet AI (nomadnet.ai) — unified access to frontier language models, research synthesis, and multimodal workflows.
L'essentiel de l'IA — une fois par semaine
Sept actus qui ont vraiment compté, choisies à la main. Sans bruit ni communiqués.
C'est fait ! Vérifiez votre boîte mail pour la confirmation.