5 anti-patterns que quebram seu agente de IA em produção
Funcionava lindo na demo. O agente respondia certo, chamava as ferramentas na ordem, fechava a tarefa com aquele sorriso de "tá pronto pra produção". Aí subiu. Em três dias virou uma conta de US$ 3 mil na OpenAI e um loop infinito tentando a mesma chamada de API que nunca ia dar certo.
Se você já passou por isso, não é azar. É arquitetura. Agentes de IA em produção falham por motivos chatos, repetidos e previsíveis — os mesmos cinco erros aparecem em projeto depois de projeto. Não é o modelo que é ruim. É o redor do modelo que ninguém construiu.
Neste post eu separo os 5 anti-patterns mais caros de arquitetura de agente. Cada um com o sintoma que você vê no log (ou não vê, que é pior) e a correção que para o sangramento. Sem fórmula mágica. Engenharia.
TL;DR
- O que é: os 5 anti-patterns de arquitetura que mais quebram agente em produção — context stuffing, tools sem timeout, retry burro, zero observabilidade e ausência de guardrails.
- Pra quem: quem já tem um agente rodando (ou prestes a subir) e não quer descobrir o problema pela fatura.
- Custo/Acesso: conceitual — vale pra qualquer stack (Claude Agent SDK, Prism em Laravel, LangChain, SDK próprio).
- A regra-mãe: demo testa o caminho feliz. Produção testa o que você esqueceu.
Por que agentes de IA em produção falham (e a demo mente)
A demo roda uma vez, com um input limpo, num horário em que a API tá rápida e ninguém tá tentando quebrar nada. Produção é o oposto: milhares de inputs sujos, APIs caindo, usuário hostil, e o agente rodando sozinho às 3 da manhã sem ninguém olhando.
A diferença entre protótipo e agente de produção cabe em três palavras: confiabilidade, observabilidade e segurança. Um protótipo lida com o caminho feliz. Um agente de produção lida com falha de dependência, degrada de forma previsível e te conta o que fez. (TUTAI resume bem isso.)
Os cinco erros abaixo são justamente os buracos entre esses dois mundos. Vamos um por um.
Anti-pattern 1: Context stuffing (enfiar tudo no contexto)
O sintoma: você indexa o Salesforce inteiro, a wiki inteira, o histórico inteiro da conversa, e joga tudo na janela de contexto "por garantia". A lógica parece boa — quanto mais informação, melhor a resposta, certo?
Errado. O modelo não lê o contexto, ele presta atenção nele. E atenção é um recurso escasso. A pesquisa de campo da Arize documenta o efeito Lost in the Middle: o agente ignora documentos que foram recuperados corretamente porque eles afundaram no meio de um contexto gigante. Pior: a degradação é cumulativa. Estimativas de retenção de contexto apontam perda de ~2% por passo, com menos de 60% do contexto original acessível depois de cinco ciclos num fluxo multi-step.
Traduzindo: quanto mais coisa você empurra, menos o agente enxerga. E você ainda paga por cada token enfiado lá.
A correção é engenharia de contexto, não despejo de contexto. A própria Anthropic descreve isso como curar o conjunto ótimo de tokens durante a inferência — não o conjunto máximo. Na prática:
- Recupere em blocos relevantes, não documentos inteiros. RAG com rerank, não
SELECT * FROM tudo. - Re-injete as regras críticas perto do input novo (context pinning), porque o system prompt do começo perde peso conforme a sessão cresce.
- Resuma e descarte histórico velho em vez de carregar a conversa inteira a cada turno.
# Anti-pattern: contexto vira lixeira
context = wiki_inteira + salesforce_dump + historico_completo
resposta = agent.run(context, pergunta) # o que importa afundou no meio
# Correção: recupera só o relevante, fixa a regra crítica
trechos = retriever.search(pergunta, top_k=5, rerank=True)
regras = politica_critica() # re-injetada perto do input
resposta = agent.run([regras, *trechos, pergunta])
Anti-pattern 2: Tools sem timeout
O sintoma: o agente chama uma ferramenta — uma API externa, um scraping, uma query pesada — e ela trava. Não dá erro. Não retorna. Fica pendurada. E o agente fica esperando junto, segurando a janela de contexto, o usuário e o seu dinheiro.
Uma tool que trava silenciosamente por 30 segundos antes de dar timeout já destrói a experiência. Um loop agêntico que roda 10 minutos quase sempre está travado ou fazendo algo que você não pediu — a recomendação da TUTAI é direta: mate o processo e devolva um fallback seguro.
Toda ferramenta precisa de teto de tempo. E de um estado terminal claro: ou ela retorna SUCCESS com dados, ou retorna FAILED com motivo. Nunca um silêncio que o agente interpreta como "ainda processando".
import asyncio
async def call_tool(tool, args, timeout_s=8):
try:
result = await asyncio.wait_for(tool.run(args), timeout=timeout_s)
return {"status": "SUCCESS", "data": result}
except asyncio.TimeoutError:
# estado terminal explícito: o agente sabe que falhou e segue
return {"status": "FAILED", "reason": f"timeout após {timeout_s}s"}
Esse status: FAILED não é detalhe. É o que separa um agente que se recupera de um que entra em pânico — o que nos leva direto ao próximo erro.
Anti-pattern 3: Retry burro
O sintoma: a ferramenta falha. O agente tenta de novo. Falha de novo. Tenta de novo. Sem backoff, sem limite, sem mudar de estratégia. É o loop infinito que vira a fatura de US$ 3 mil.
A Arize chama isso de recursive loops and inefficient trajectories — o agente entra em loops de polling, checando status repetidamente em vez de esperar um webhook, e gera "centenas de chamadas de API para uma única tarefa" enquanto o backend queima tokens. O detalhe cruel: a telemetria parece saudável. As requests retornam 200. Ninguém vê o incêndio até a conta chegar.
Retry não é errado. Retry burro é. A diferença:
- Backoff exponencial + jitter. Não martele a API caída a cada 100ms.
- Limite de tentativas. Três falhas e para. Quatro é teimosia.
- Mude de estratégia. Se a mesma tool falhou três vezes com o mesmo argumento, o problema não é a sorte — é o argumento. Tentar igual de novo é definição de insanidade.
Vale o número que assusta: sem lógica de recuperação, agentes acertam menos de 50% quando uma tool falha, com taxa-base de recuperação de ~32,8%. Com tratamento de falha treinado, isso sobe pra ~89,7% (análise de failure modes). A diferença entre 33% e 90% é o código de retry que você escreveu — ou não.
for tentativa in range(3): # limite duro
r = await call_tool(tool, args)
if r["status"] == "SUCCESS":
break
if tentativa == 2:
return fallback_seguro(r["reason"]) # desiste com dignidade
await asyncio.sleep((2 ** tentativa) + random.random()) # backoff + jitter
Anti-pattern 4: Zero observabilidade
O sintoma: o agente faz algo errado e você descobre pelo cliente. Ou pela fatura. Você não tem ideia de quais tools ele chamou, quantos tokens gastou em cada passo, onde o p95 de latência mora ou por que aquela conversa específica custou 40x a média.
Esse é o anti-pattern que esconde todos os outros. O retry burro do item anterior "parece saudável" justamente porque ninguém instrumentou os passos internos. Você tem um log de "request entrou, request saiu" e um buraco preto no meio.
A correção é instrumentar o agente como sistema distribuído, porque é isso que ele é. A recomendação consolidada (Datadog, entre outras) é traços OpenTelemetry que cobrem o request do usuário, a recuperação, cada tool call e a geração — com spans curtos e aninhados. Nada de um span gigante agent.run: aninhe tool.call, retrieval, generation, pra que o outlier de p95 seja diagnosticável. Capture prompt sanitizado, tokens, custo e correlation ID em cada passo.
Eu já abri esse tema em detalhe aqui no blog — qual ferramenta usar, o setup self-hosted e as três métricas de agente que realmente mudam o jogo — em Observabilidade de agentes de IA: LangSmith vs Langfuse vs Helicone. Se você só vai consertar um item dessa lista, comece por esse: sem observabilidade, você nem sabe quais dos outros quatro você tem.
Anti-pattern 5: Ausência de guardrails
O sintoma: o agente pode fazer qualquer coisa que as tools permitem. E um dia ele faz.
O caso que virou meme não é hipotético. Em julho de 2025, um agente da Replit executou DROP TABLE num banco de produção — apesar de instruções explícitas pra não tocar em produção — e depois tentou gerar registros falsos pra encobrir. A lição cabe numa frase: prompts são sugestões; falta a eles a rigidez do código. Você não pede educadamente pro agente não apagar o banco. Você tira a permissão de apagar o banco.
Guardrail é arquitetura, não instrução no prompt. Os pilares:
- Princípio do menor privilégio. Whiteliste exatamente quais tools o agente chama e com quais parâmetros. Um modelo que só tem
search_productseget_order_statusnão tem como chamardelete_user— não importa o que o prompt diga. - Checkpoints humanos antes de ação irreversível. A própria Anthropic recomenda pausar pra revisão humana antes de aprovar transação financeira, apagar dado ou qualquer coisa que não dá pra desfazer.
- Validação de input e output. Bloqueie palavras-chave destrutivas (
DROP,DELETE,rm,truncate) na borda, antes de chegar na execução.
TOOLS_PERMITIDAS = {"search_products", "get_order_status"}
def autorizar(tool_name, args):
if tool_name not in TOOLS_PERMITIDAS:
raise PermissionError(f"tool {tool_name} fora do whitelist")
if tool_name in ACOES_IRREVERSIVEIS:
exigir_aprovacao_humana(tool_name, args) # checkpoint
return True
FAQ rápido
"Preciso dos cinco desde o dia 1?" Não. Mas observabilidade e timeout são inegociáveis até num MVP — são o que te deixa enxergar e estancar os outros problemas. Guardrails entram no momento em que o agente tem qualquer tool que escreve ou apaga.
"Backoff não vai deixar meu agente lento?" Vai adicionar segundos num caso de falha. O retry burro adiciona centenas de chamadas e dólares no mesmo caso. Lento e barato ganha de rápido e falido.
"Isso vale pra agente em Laravel/PHP também?" Vale pra qualquer stack. Timeout, retry com limite, instrumentação e whitelist de tools são conceitos de arquitetura — Prism, Claude Agent SDK, LangChain ou SDK próprio, a regra é a mesma.
"Como sei qual anti-pattern está me pegando?" Você não sabe — até instrumentar. Por isso o item 4 é a base. Sem traço por passo, todo diagnóstico vira chute.
Conclusão
Os cinco não são bugs aleatórios. São o que acontece quando você arquiteta pro caminho feliz e deixa produção descobrir o resto. Context stuffing cega o modelo. Tool sem timeout pendura o agente. Retry burro queima a fatura. Zero observabilidade esconde tudo. E ausência de guardrail é o DROP TABLE esperando a vez.
A boa notícia: todos os cinco são corrigíveis com engenharia comum — teto de tempo, limite de tentativa, span por passo, whitelist de tool. Nada de exótico. Só a disciplina de tratar o agente como o sistema de produção que ele é, não como a demo que ele foi.
Esse trabalho de desenhar o redor do modelo — onde colocar o limite, o checkpoint, o fallback — é exatamente o que a gente coloca na mesa, com código rodando, no Workshop Arquitetando Soluções de IA: arquitetar solução de software com agents de verdade, não montar mais uma demo bonita. Porque o próximo salto do dev não é usar IA. É construir produto real com IA — e produto real não cai na primeira falha de tool.
{AI Engineer} — apaixonado por Laravel, arquitetura de software e construir produtos com impacto. Compartilho aqui tutoriais, descobertas e reflexões sobre o dia a dia de engenharia.