Pedro Sousa
‹ ← Artigos

Artigo · Blog

O agente não travou por falta de capacidade. Travou porque você não disse quando parar.

A maioria dos problemas com agentes em produção não vem do modelo sendo burro. Vem do agente sendo inteligente o suficiente para continuar executando quando deveria ter parado. Condição de parada é arquitetura, não detalhe de implementação.

O agente não travou por falta de capacidade. Travou porque você não disse quando parar.

Num app financeiro onde trabalhei, construímos um agente para automatizar parte de um fluxo interno de conciliação. A ideia era simples: o agente recebia dados de entrada, consultava APIs, validava inconsistências e gerava um relatório consolidado.

Funcionava bem nos testes. Nos primeiros dias em produção, também.

No décimo segundo dia, o agente entrou em loop. Ficou chamando a mesma API dezessete vezes seguidas porque a resposta dela tinha um campo com formato levemente diferente do esperado. A cada tentativa, ele reinterpretava a resposta, decidia que ainda não tinha o que precisava, e tentava de novo. Sem timeout configurado corretamente. Sem condição de saída clara. Sem nenhum sinal de que aquilo era um problema e não progresso.

Gastamos créditos, atrasamos o relatório e passamos duas horas debugando um log sem estrutura.

O modelo não estava errado. Ele fez exatamente o que foi ensinado a fazer: persistir até ter a informação completa.

O problema era que ninguém tinha definido o que "completo" significa.

A ilusão de que o agente vai saber quando terminar

Existe um pressuposto silencioso na maioria das implementações de agentes: que o modelo vai perceber quando a tarefa está concluída.

Às vezes percebe. Quando o contexto é simples e o objetivo é óbvio, a condição de parada emerge naturalmente da descrição da tarefa. Mas em produção, os casos simples não são o problema. O problema são os casos que fogem do caminho feliz.

Uma API retorna 200 com payload inválido. Uma ferramenta retorna string vazia. Um dado que deveria estar lá simplesmente não está. O modelo não sabe se aquilo é um erro, um estado intermediário ou o resultado final. E na dúvida, tenta de novo.

Eu chamo isso de Armadilha da Persistência Implícita. Você treina o comportamento certo para o caminho feliz, mas não define o comportamento esperado para o caminho incerto. O agente aplica a mesma lógica de persistência dos casos normais nos casos anômalos. E persiste.

Condição de parada não é validação. É arquitetura.

Quando comecei a trabalhar com orquestração de agentes de forma mais séria, meu instinto era tratar a condição de parada como uma validação de output. Verificar se o resultado final estava correto antes de encerrar.

Estava olhando pro lugar errado.

Condição de parada tem três dimensões distintas, e cada uma precisa ser tratada separadamente:

Parada por sucesso: o agente alcançou o objetivo. Parece óbvio, mas o objetivo precisa ser definido de forma que o modelo consiga verificar por conta própria. "Gere o relatório" é ambíguo. "O relatório está completo quando todos os campos do schema estiverem preenchidos e o campo status for validated" é verificável.

Parada por falha explícita: algo deu errado de forma reconhecível. API retornou 500. Dado não encontrado após N tentativas. Ferramenta retornou erro tipado. Esses casos precisam de tratamento explícito no contexto, não só no código de orquestração. O modelo precisa saber que aquilo é um estado de falha, não um estado de incerteza que justifica mais tentativas.

Parada por incerteza irresolvível: o agente não conseguiu concluir porque as informações necessárias não estão disponíveis e não vão estar. Esse é o caso mais difícil. E o mais comum em produção.

A maioria das implementações trata bem a primeira dimensão. Ignora completamente a terceira.

O que mudou quando passei a tratar parada como contrato

Depois do incidente da conciliação, mudei a forma como estruturo o contexto dos agentes.

Comecei a incluir explicitamente o que chamo de exit contract: uma seção no system prompt que define, em linguagem direta, as condições sob as quais o agente deve encerrar independentemente do estado da tarefa.

A estrutura ficou assim:

## Condições de encerramento

Encerre com status SUCCESS quando:
- [condição verificável 1]
- [condição verificável 2]

Encerre com status FAILED quando:
- A ferramenta X retornar erro em 2 tentativas consecutivas
- O campo Y estiver ausente após consulta às fontes A e B
- O número de passos exceder 15

Encerre com status BLOCKED quando:
- A informação necessária não estiver disponível nas fontes autorizadas
- Houver ambiguidade irresolvível que exige decisão humana

Parece burocrático. Na prática, reduziu drasticamente os loops e os timeouts silenciosos.

O modelo não precisa inferir quando parar. Está no contrato. E quando chega num estado de incerteza, em vez de persistir indefinidamente, ele classifica o estado e encerra com contexto suficiente para que o próximo passo, humano ou automatizado, saiba o que fazer.

O número de passos como sinal, não só como limite

Outro erro que cometi por um tempo foi usar o limite de passos só como mecanismo de segurança. Um teto para evitar loops infinitos.

Passei a tratá-lo como sinal arquitetural.

Se um agente consistentemente chega perto do limite de passos, não é problema de limite. É sinal de que o objetivo está mal definido, o contexto está incompleto, ou as ferramentas disponíveis não são suficientes para a tarefa.

Um agente que conclui em 3 passos quando o esperado são 8 também é um sinal. Pode ter pulado etapas. Pode ter assumido dados que não verificou.

Monitorar a distribuição de passos por execução é uma das métricas mais úteis que tenho hoje. Mais útil que taxa de sucesso isolada.

O trade-off que ninguém menciona

Definir condições de parada explícitas tem um custo real: você aumenta o tamanho do system prompt, o que consome tokens e pode comprimir o espaço disponível para contexto operacional.

Em modelos com janela de contexto menor, isso importa. Você está trocando flexibilidade por previsibilidade.

A minha posição é que o trade-off vale na maioria dos casos de produção. Flexibilidade sem previsibilidade em produção não é feature. É risco.

Mas há casos onde o objetivo é genuinamente aberto, onde você quer que o agente explore sem fronteiras rígidas. Nesses casos, o exit contract precisa ser mais leve, focado só nas falhas explícitas, e o monitoramento precisa ser mais rigoroso.

Não existe configuração universal. Existe a configuração certa para o comportamento que você quer em produção.

O que eu faria diferente hoje

Não teria tratado a condição de parada como uma preocupação de orquestração. Teria tratado como parte do design da tarefa, desde o início.

Antes de escrever uma linha de código de orquestração, a primeira pergunta deveria ser: sob quais condições este agente deve parar, em qualquer estado da execução?

Se você não consegue responder isso de forma verificável, o agente não está pronto para produção. Não importa o quão bem ele performa no caminho feliz.


O modelo persiste porque você disse para ele ser persistente. O problema é que você não disse até quando.

E "até terminar" não é uma condição de parada. É uma intenção. Em produção, intenção não é suficiente.