Análise no Habr: por que r.Context() não interrompe o LLM e como construir tarefas de IA em Go

O Habr publicou uma análise de uma armadilha comum em backends Go que usam LLM. O esquema habitual cancela o trabalho junto com a requisição HTTP, via r.Context(). Mas, se o usuário fechar a aba no meio da análise de um documento, o provedor pode continuar a geração e cobrar pelos tokens. O autor propõe levar as operações longas de IA para tarefas em segundo plano no PostgreSQL e tratar separadamente idempotência, retry e proteção contra cobranças duplicadas. • O contexto da requisição não equivale ao ciclo de vida da tarefa de IA. • O estado da tarefa é melhor armazenado no PostgreSQL, e não na memória do processo. • Retry sem idempotência significa pagar duas vezes pela API.

Processado por IA de Habr AI; editado por Hamidun News
Análise no Habr: por que r.Context() não interrompe o LLM e como construir tarefas de IA em Go
Fonte: Habr AI. Colagem: Hamidun News.
Ouvir artigo
Ficha técnica e números-chave
ParâmetroDados
Fonte originalHabr AI ↗
Editor-chefe
Dado-chave #1O contexto da requisição não equivale ao ciclo de vida da tarefa de IA.
Dado-chave #2O estado da tarefa é melhor armazenado no PostgreSQL, e não na memória do processo.
Dado-chave #3Retry sem idempotência significa pagar duas vezes pela API.

Fechar a aba do navegador cancela a requisição HTTP, mas não obriga o modelo a parar do lado do provedor. O autor de uma análise recente no Habr mostra como essa diferença vira custo e bugs em um backend Go comum. Se o usuário iniciou a análise de um documento e foi embora, o serviço pode ficar sem resultado, e a cobrança pelos tokens chega do mesmo jeito. Para operações longas de IA, o autor propõe tarefas em segundo plano no PostgreSQL, idempotência estrita e uma política consciente de repetições.

Onde o modelo habitual falha

Desenvolvedores Go estão acostumados a uma regra simples: o trabalho vive enquanto `r.Context()` estiver vivo. O cliente derrubou a conexão, o contexto foi cancelado, as goroutines terminaram e os recursos foram liberados. Para uma consulta ao banco ou a um microsserviço interno, isso é prático e correto.

Com LLMs, a situação é outra. Uma chamada pode durar dezenas de segundos ou minutos, sobretudo na análise de documentos grandes ou em cadeias com várias chamadas ao modelo. A tarifação é feita por tokens processados e gerados, e o cancelamento do seu lado não garante que o provedor interrompa o trabalho imediatamente. Isso depende da API específica e da forma como a conexão foi encerrada.

Surgem dois problemas ao mesmo tempo:

  • o trabalho é cancelado, não há resultado e o dinheiro já foi gasto;
  • o usuário volta, clica no botão de novo e a operação paga é iniciada outra vez.

A tarefa em segundo plano como entidade independente

A ideia central da análise é que a operação de IA não deve pertencer à requisição HTTP. O handler recebe a entrada, cria a tarefa e responde de imediato com um identificador. Dali em diante, a tarefa segue seu próprio curso, e o cliente acompanha o andamento por polling, SSE ou WebSocket.

Como armazenamento, o autor escolhe o PostgreSQL. Para muitas equipes, é uma escolha razoável: o banco já está na stack, as transações são bem compreendidas e não é preciso um broker de mensagens separado. Um conjunto típico para uma fila desse tipo é:

  • tabela de tarefas com status (criada, em execução, concluída, erro, cancelada);
  • captura de tarefas pelos workers com `FOR UPDATE SKIP LOCKED`, para que dois workers não peguem a mesma tarefa;
  • horário do último heartbeat, usado para devolver à fila as tarefas travadas;
  • contador de tentativas e horário da próxima tentativa;
  • resultado salvo e dados sobre os tokens utilizados.

O principal ganho é que o estado sobrevive à reinicialização do serviço. Em um deploy ou na queda de um pod, a tarefa não desaparece junto com a goroutine: outro worker a assume.

Idempotência e dinheiro

O ponto mais caro de um sistema assim fica na junção entre retry e faturamento. Repetir uma requisição ao LLM custa dinheiro, por isso não dá para fazer isso às cegas, como se repete uma consulta ao cache.

Princípios práticos que decorrem da lógica da análise:

  • a chave de idempotência é criada no nível da ação do usuário e protege contra o clique duplo no botão;
  • a tarefa tem um identificador único, pelo qual o resultado é gravado uma única vez;
  • o retry só é permitido para erros em que repetir faz sentido (timeout, 429, 5xx), e não para entrada inválida;
  • a cobrança do usuário está vinculada à tarefa, e não a cada tentativa;
  • o cancelamento pelo usuário vira um status explícito, e não um efeito colateral de uma conexão interrompida.

Por onde começar

Se no seu serviço a chamada ao LLM é executada diretamente no handler, verifique três coisas. Primeiro: o que acontece com a geração já paga se o cliente se desconectar no meio. Segundo: se uma requisição repetida pode criar uma duplicata da operação paga. Terceiro: se a tarefa sobrevive à reinicialização do processo.

Se a resposta a pelo menos uma dessas perguntas for incômoda, é preciso um modelo de tarefa separado. O passo mínimo é uma tabela com status e um único worker; `SKIP LOCKED`, heartbeat e a política de repetições podem ser adicionados conforme a carga cresce. Faz sentido incluir um broker separado quando o PostgreSQL deixar de dar conta da fila. Até esse ponto, ele costuma apenas complicar o sistema.

O texto não trata de um novo modelo nem de um lançamento, e sim da disciplina de engenharia em torno dos LLMs. É justamente aí que hoje se perde uma parte considerável dos orçamentos de produtos de IA: os erros surgem na infraestrutura ao redor, e não nos prompts.

Perguntas frequentes sobre a notícia

Onde o modelo habitual falha?

Desenvolvedores Go estão acostumados a uma regra simples: o trabalho vive enquanto `r.Context()` estiver vivo. O cliente derrubou a conexão, o contexto foi cancelado, as goroutines terminaram e os recursos foram liberados. Para uma consulta ao banco ou a um microsserviço interno, isso é prático e correto. Com LLMs, a situação é outra. Uma chamada pode durar dezenas de segundos ou minutos, sobretudo na análise de documentos grandes ou em cadeias com várias chamadas ao modelo. A tarifação é feita por tokens processados e gerados, e o cancelamento do seu lado não garante que o

O que se sabe sobre A tarefa em segundo plano como entidade independente?

A ideia central da análise é que a operação de IA não deve pertencer à requisição HTTP. O handler recebe a entrada, cria a tarefa e responde de imediato com um identificador. Dali em diante, a tarefa segue seu próprio curso, e o cliente acompanha o andamento por polling, SSE ou WebSocket. Como armazenamento, o autor escolhe o PostgreSQL. Para muitas equipes, é uma escolha razoável: o banco já está na stack, as transações são bem compreendidas e não é preciso um broker de mensagens separado. Um conjunto típico para uma fila desse tipo é: tabela de tarefas com status (criada,

O que se sabe sobre Idempotência e dinheiro?

O ponto mais caro de um sistema assim fica na junção entre retry e faturamento. Repetir uma requisição ao LLM custa dinheiro, por isso não dá para fazer isso às cegas, como se repete uma consulta ao cache. Princípios práticos que decorrem da lógica da análise: a chave de idempotência é criada no nível da ação do usuário e protege contra o clique duplo no botão; a tarefa tem um identificador único, pelo qual o resultado é gravado uma única vez; o retry só é permitido para erros em que repetir faz sentido (timeout, 429, 5xx), e não para entrada inválida; a cobrança do usuário

ZK
Zhemal Khamidun — CPO da Alpina Digital, gerente de produto do AlpinaGPT. Cria produtos de IA desde 2019. O Hamidun News é um projeto pessoal que virou um agregador em produção com mais de 6.000 leitores.
✦ Unified AI Workspace

Access Frontier LLMs & Multimodal AI in One Workspace

Discover NomadNet AI (nomadnet.ai) — unified access to frontier language models, research synthesis, and multimodal workflows.

Try NomadNet AI →Launch NomadNet Agent
O que você acha?
Carregando comentários…