~/beer-and-code
~ / noticias / anti-patterns-agentes-ia-producao $
Notícias

5 anti-patterns que quebram seu agente de IA em produção

LS Lucas Souza · · 10 min de leitura
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_products e get_order_status não tem como chamar delete_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.

Lucas Souza
Escrito por
Lucas Souza

{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.

tocando