Artigo · Blog
O agente que funcionava no teste e quebrava em produção
Construir um agente de IA que funciona em demo é simples. O problema aparece quando o contexto real chega: dados ruidosos, sessões longas, estados ambíguos. A maioria dos times descobre isso depois do deploy.
O agente que funcionava no teste e quebrava em produção
O agente passava em todos os testes. Todas as demos funcionavam. A resposta era coerente, o fluxo estava correto, as ferramentas eram chamadas na ordem certa.
Aí foi pra produção.
Em três dias, o suporte começou a receber reclamações de respostas sem sentido. O agente estava tomando decisões baseadas em contexto que já não existia mais. Às vezes repetia uma ação que o usuário tinha cancelado dois turnos atrás. Às vezes simplesmente parava no meio de um fluxo sem sinalizar nada.
Eu já tinha visto esse padrão antes. E toda vez que ele aparece, o problema não é o modelo. É o que você passou pra ele.
A ilusão do contexto limpo
Quando você constrói um agente em ambiente controlado, o contexto é sempre limpo. Você sabe exatamente o que está no histórico. As ferramentas retornam dados previsíveis. Os turnos são curtos. O usuário segue um fluxo esperado.
Em produção, nada disso é garantido.
O usuário começa uma intenção, muda de ideia no meio, faz uma pergunta paralela e volta pra intenção original três mensagens depois. A ferramenta que busca dados externos retorna um payload com campos nulos que não existiam no ambiente de teste. O histórico acumulou doze turnos e agora está empurrando informação crítica pra fora da janela de atenção do modelo.
O agente não quebra. Ele continua respondendo. Só que as respostas começam a derivar do que o usuário realmente quer.
Eu chamo isso de Deriva de Contexto. Não é um erro explícito. É um degradação silenciosa.
O que a maioria faz errado
A maioria dos times trata o contexto como um log. Você vai adicionando mensagens, o modelo lê tudo, e assume que ele vai entender o estado atual do mundo a partir do histórico.
Isso funciona pra conversas curtas. Falha em agentes que precisam manter estado ao longo de múltiplos turnos, múltiplas ferramentas e intenções que mudam no meio do caminho.
O problema técnico é claro: LLMs não têm memória. Eles têm contexto. E contexto é finito, ruidoso e não uniforme em termos de atenção. Uma informação no turno dois de uma conversa de vinte turnos pesa menos do que a mesma informação no turno dezenove.
Mas o problema arquitetural é mais sutil. Quando você usa o histórico como substituto de estado, você transfere a responsabilidade de rastrear o que está acontecendo pro modelo. E o modelo vai errar.
O problema real por trás do problema
O que descobri, depois de depurar esse tipo de falha mais vezes do que gostaria, é que agentes em produção precisam de duas coisas separadas: memória de fatos e memória de estado.
Memória de fatos é o que o usuário disse, o que as ferramentas retornaram, o que ficou acordado.
Memória de estado é onde o agente está no fluxo: qual intenção está ativa, quais passos já foram concluídos, o que está pendente, o que foi cancelado.
Quando você mistura as duas no mesmo histórico de mensagens, o modelo precisa inferir o estado a partir dos fatos. Às vezes ele acerta. Às vezes ele acha que o usuário ainda quer cancelar o pedido porque foi o que ele disse quatro turnos atrás, mesmo que dois turnos depois tenha mudado de ideia.
O que funciona melhor na prática
A mudança que mais impactou a consistência dos agentes que trabalhei foi separar o estado explícito do histórico narrativo.
Em vez de deixar o modelo inferir onde o fluxo está, você passa o estado atual como parte do prompt de sistema, atualizado a cada turno. Não como texto genérico. Como estrutura.
// Exemplo simplificado de como o contexto de estado é injetado
struct AgentContext {
var activeIntent: String
var completedSteps: [String]
var pendingActions: [String]
var cancelledActions: [String]
var relevantFacts: [String]
}
Antes de cada chamada ao modelo, você serializa esse contexto, injeta no prompt de sistema e trunca o histórico de mensagens pra manter só o que é recente e relevante.
O modelo para de precisar inferir o estado. Você passa o estado. Ele opera em cima disso.
A parte que ninguém gosta de ouvir
Isso significa que você precisa de uma camada de orquestração que rastreia e atualiza o estado fora do modelo. Que decide o que vai e o que não vai pro contexto. Que tem lógica de negócio sobre o que constitui um passo concluído, uma ação cancelada, uma intenção ativa.
É mais trabalho. É uma peça de software real, com suas próprias responsabilidades, seus próprios bugs e seus próprios testes.
A maioria dos times não quer fazer isso. Quer acreditar que o modelo vai resolver. E o modelo resolve, até o momento em que não resolve mais.
Contexto não é o que você coleta. É o que você decide passar.
Essa distinção parece óbvia escrita assim. Em produção, ela custa dias de depuração pra aprender.
Trade-offs honestos
Essa abordagem tem um custo real: você aumenta a complexidade da camada de orquestração. Agora existe lógica de estado fora do modelo, e essa lógica precisa ser testada, mantida e evoluída junto com os fluxos do agente.
Se o agente muda de comportamento, você precisa atualizar tanto o prompt quanto a lógica de estado. Isso pode causar drift entre as duas partes se não houver disciplina.
Também existe o risco de over-engineering. Pra agentes simples, com fluxos curtos e bem definidos, essa separação pode ser desnecessária. O histórico de mensagens é suficiente.
O problema é que você raramente sabe, antes de ir pra produção, se o fluxo vai ser simples.
O que eu faria diferente hoje
Começaria com a separação de estado desde o início, mesmo que o agente pareça simples.
O custo de adicionar essa camada no começo é baixo. O custo de adicionar depois, quando já existe comportamento inesperado em produção e o histórico de conversas reais virou evidência de debug, é muito maior.
Também investiria mais cedo em observabilidade específica pra contexto: logar o que está sendo passado pro modelo a cada turno, não só a resposta que voltou. A maioria dos sistemas de agentes que vi logam output, não input. Você só descobre o que estava no contexto quando já quebrou.
Reflexão final
Existe uma crença comum de que o modelo é o componente crítico do agente. Você escolhe o modelo certo, ajusta o prompt, e o comportamento emerge.
Na prática, o modelo é o componente mais previsível. Dado o mesmo contexto, ele se comporta de forma consistente.
O que muda em produção é o contexto. E o contexto muda porque o mundo real é ruidoso, os usuários não seguem fluxos esperados, e sistemas externos retornam dados que não existiam no ambiente de teste.
Agentes não falham por causa do modelo. Eles falham porque o contexto que chegou pro modelo não era o que você pensava que estava passando.
Isso muda onde você coloca a atenção durante o desenvolvimento. E muda completamente onde você procura quando algo quebra.