AI Agent Task Queues: Como Criar Workflows Confiaveis em 2026
Fonte: Unsplash
Toda vez que um agente de IA dispara uma cobrança, um e-mail ou uma gravação no banco, aparece a mesma pergunta chata: e se a chamada demorar 30 segundos para falhar, enquanto o sistema já tentou de novo? Fila de tarefas não é enfeite de infra — é o que transforma "acho que rodou" em "rodou exatamente uma vez".
Fila é contrato de execução, não lista de espera
Quem vem do mundo de background jobs simples leva para o agente uma expectativa errada: mandou, rodou, acabou. Agente de IA quebra esse desenho porque cada execução envolve rede instável, provedor com rate limit, resposta que pode chegar gigante e um custo que se multiplica a cada nova tentativa.
Três conceitos sustentam tudo que vem depois:
- Entrega pelo menos uma vez (at-least-once) — a fila reentrega se o worker morrer no meio. É o padrão da maioria dos sistemas gerenciados.
- Idempotência — a propriedade que faz a reexecução produzir o mesmo resultado, para que a reentrega seja inofensiva. É o que aproxima o sistema do "uma vez só" na prática.
- Mensagem venenosa — a tarefa que falha sempre, do mesmo jeito, e continuaria reentregando para sempre se ninguém a isolar.
A arquitetura mínima que aguenta produção
O desenho básico tem produtor, fila, pool de workers, política de retry e uma fila morta. Todo componente depois disso é otimização:
[aplicação / agente]
| evento com idempotency_key
v
[producer] ----push----> [ FILA PRINCIPAL ]
|
v
[ worker pool ]
/ \
sucesso falha
| |
efeito + tentativa < máx ?
checkpoint |
sim ------+------ não
| |
v v
[ backoff exponencial ] [ FILA MORTA (DLQ) ]
| |
reentrega na fila v
[ alerta + reprocesso manual ]
Note o que o diagrama não tem: nenhuma seta direto do erro para a frente. Sem limite de tentativas, um serviço fora do ar transforma sua fila em gerador de custo de LLM — cada repetição paga o token de novo.
Idempotência: a chave que evita cobrança duplicada
A regra prática é simples: todo evento carrega uma identidade própria (idempotency key), derivada do negócio, não do acaso — por exemplo assinatura:123:fatura:2026-03. Antes de enfileirar, o produtor tenta gravar essa chave. Se já existe, é porque alguém já publicou: devolva o resultado anterior e pare.
import { Queue, Worker } from 'bullmq';
import Redis from 'ioredis';
const connection = new Redis(process.env.REDIS_URL);
const fila = new Queue('agentes:resumo', { connection });
export async function publicar(evento) {
// SET NX: só grava se a chave ainda não existir
const novo = await connection.set(
`idem:${evento.chave}`, evento.jobId, 'EX', 86400, 'NX'
);
if (!novo) return { status: 'duplicado', chave: evento.chave };
await fila.add(evento.tipo, evento, {
jobId: evento.jobId,
attempts: 5,
backoff: { type: 'exponential', delay: 2000 },
removeOnComplete: { age: 86400 },
removeOnFail: false, // guarda a falha para investigar
});
}
new Worker('agentes:resumo', async (job) => {
await gerarResumo(job.data); // rodar de novo é seguro: mesma chave, mesmo efeito
}, { connection, lockDuration: 60000 });
Do lado do worker, idempotência também significa efeito colateral protegido: escrita condicional no banco (upsert pela chave), chamada de pagamento com a mesma chave para o provedor, envio de mensagem com deduplicação. Reexecutar não pode significar cobrar duas vezes nem mandar o mesmo e-mail para o cliente.
Backoff, teto de tentativas e o custo do retry
Tentar de novo imediatamente ataca um provedor que já estava em dificuldade. O padrão que funciona é espera exponencial com jitter — dobrar o intervalo e somar aleatoriedade para que todos os jobs não voltem juntos quando o serviço volta (o chamado thundering herd).
espera = min(intervalo_base * 2^tentativa, teto) + aleatorio(0..jitter)
ex.: base 2s, teto 5min
tentativa 1 -> ~2s tentativa 2 -> ~4s
tentativa 3 -> ~8s tentativa 4 -> ~16s depois disso, fila morta
Antes de fixar o teto, faça a conta de custo: um job de agente que gastou tokens caros e falhou no timeout repetir cinco vezes paga cinco vezes o token. Para tarefas de LLM caras, faz sentido separar erros em duas classes — retryável (timeout, 429, 5xx) e terminal (prompt inválido, resposta fora do schema) — e só a primeira volta para a fila.
Estado fora do processo: checkpoint em agente longo
Agente que roda cinco minutos e é morto pelo lock da fila perde tudo o que pensou. A solução é tratar a execução como sequência de passos com estado persistido: cada passo concluído grava checkpoint, e a reexecução retoma do último ponto salvo, não do zero. É o modelo de engine de workflow (Temporal, Restate) e também dá para montar com um registro de passos em banco: entrada, ferramenta chamada, resposta bruta, custo.
Guarde também o histórico do LLM: prompt enviado, modelo, parâmetros, resposta completa e correlation id. Quando o cliente reclamar de uma resposta, sem esse histórico a investigação vira adivinhação.
Onde encaixa cada ferramenta que existe hoje
| Ferramenta | Modelo | Quando faz sentido | Custo operacional |
|---|---|---|---|
| BullMQ (+ Redis) | Fila em Node.js | Job assíncrono simples em app já com Redis | Baixo — você monta o resto |
| Celery (+ Redis/RabbitMQ) | Fila em Python | Backends Django/FastAPI que já usam Redis | Baixo a médio |
| AWS SQS + Step Functions | Fila + orquestração gerenciada | Ambiente já na AWS, time sem operação própria | Por mensagem/chamada |
| Temporal | Workflow durável com histórico | Fluxo longo, passos humanos, garantia de reexecução | Alto — exige operação séria |
| Restate | Estado embutido no serviço | Quer durable execution sem redesenhar tudo | Médio |
| Inngest / Trigger.dev / Hatchet | Plataforma de jobs de longa duração | Agentes LLM com passos, retry e agendamento | Por execução/assinatura |
| Prefect / Dagster | Orquestração de dados | Pipeline de ingestão que alimenta o agente | Médio |
Regra de escolha: se o fluxo cabe em "evento chega, worker processa, erro tenta de novo", uma fila clássica basta. Se existe espera por aprovação humana, passos que podem falhar em dias diferentes e histórico que precisa sobreviver a deploy, você quer engine de workflow — não reinvente checkpoint em cron.
Checklist antes do primeiro job em produção
- Chave de idempotência em todo evento, derivada do negócio e testada com duplo envio.
- Limite de tentativas + backoff com jitter + teto, e separação entre erro retryável e erro terminal.
- Fila morta ativa: alerta no canal do time e forma de republishar um job depois da correção.
- Lock maior que o pior caso de execução — senão outro worker assume no meio e você tem dois efeitos simultâneos.
- Payload versionado: campo de versão do schema para não quebrar jobs que ficaram pendurados durante o deploy.
- Métricas mínimas: profundidade da fila, idade do job mais antigo, taxa de erro por tipo, custo médio por job.
- Simulação de falha — corte o provedor em staging e veja se retry, DLQ e alerta funcionam como você acredita que funcionam.
Confiança não nasce de a fila não cair: nasce de você conseguir responder, às três da manhã, qual job falhou, por quê, quanto já custou tentar e qual botão devolve ele para a fila corrigido.
E aí, fez sentido?
Conte nos comentários como vocês tratam retry, idempotência e fila morta nos agentes de IA — e se o artigo te ajudou, compartilhe com alguém que está montando o primeiro workflow confiável. 👇