Agent Mentor Learn
Fundamentos do Harness de Agente: Laços e Controle · Lição 2 de 6

Lição 2: O loop central: de uma ida e volta à operação contínua

Objetivos de aprendizado:

  • Recitar os quatro passos do loop de múltiplos turnos que o stop_reason dirige, e conectar uma ida e volta de chamada de ferramenta em um while loop que segue rodando
  • Usar o valor de stop_reason (tool_use / end_turn) para decidir se o loop continua ou para, e explicar por que esse campo é a condição do while do loop
  • Apontar quais fronteiras faltam a este loop esqueleto, e explicar por que o histórico cresce a cada turno e por que você não pode confiar só no modelo dizendo "terminei"

Pré-requisitos: Você leu a Lição 1 e sabe que um harness é a camada de código de controle ao redor do modelo; você consegue ler o tool_use / tool_result de uma única ida e volta de chamada de ferramenta | Anterior: Lição 1 << | Próxima: Lição 3 >>

Uma ida e volta deixa de bastar

A Lição 1 já desmontou uma ida e volta completa de chamada de ferramenta: o modelo devolve stop_reason: "tool_use" junto com um bloco tool_use, o seu código do host lê o name e o input, de fato roda a coisa, empacota a saída em um tool_result e a envia de volta, e só então o modelo dá a resposta final. Três pedaços de JSON, uma viagem, pronto.

Tarefas reais raramente são tão educadas. Mude o cenário: você está escrevendo um bot de plantão, e um usuário diz "reinicie o serviço api para mim, depois cheque se os logs ainda têm erros, e cole-os se tiverem". Essa única frase embute dois trabalhos, e o segundo depende do primeiro — checar os logs não significa nada até o reinício ter terminado. O modelo não consegue fazer os dois no primeiro turno. Tudo o que ele pode fazer é isto:

  1. O turno um devolve tool_use, chamando restart_service. Você o roda e envia "reinício bem-sucedido" de volta.
  2. O turno dois devolve tool_use de novo, desta vez chamando read_logs. Você o roda e envia o conteúdo dos logs de volta.
  3. O turno três finalmente devolve stop_reason: "end_turn", com uma linha do tipo "Reinício concluído; os logs têm dois erros de timeout, colados abaixo".

Uma requisição do usuário, três idas e voltas. O que o modelo consegue ver em cada passo, e o que ele faz em seguida, depende do que voltou no tool_result anterior — que é exatamente a definição de agente da Anthropic: um LLM usando ferramentas com base em feedback do ambiente, em um loop.1 A ida e volta única da Lição 1 é só o caso especial em que esse loop por acaso girou uma vez só. O que esta lição faz é conectar "uma ida e volta" em "idas e voltas que seguem indo", e dar uma olhada clara em o que é o loop no meio, o que o dirige, e onde ele precisa de um freio.

Os quatro passos do loop

Transformar uma única ida e volta em um loop não exige inventar nada novo. Você só repete os movimentos que já conhece. A documentação da API Claude escreve esse processo de múltiplos turnos como uma sequência fixa:2

  1. Você envia uma requisição carregando messages e o manifesto tools (tools tem que ir junto em cada turno).
  2. O modelo devolve uma resposta. Se ele ainda precisa de uma ferramenta, stop_reason é "tool_use" e content carrega um ou mais blocos tool_use.
  3. Você executa cada bloco tool_use e transforma cada saída em um bloco tool_result. A palavra-chave desse passo é cada: quantos blocos tool_use tenham voltado em uma resposta, a próxima mensagem user precisa desse tanto de blocos tool_result correspondentes, cada um reivindicado pelo seu tool_use_id, todos empacotados naquela única mensagem user imediatamente seguinte. A documentação põe a regra assim: "Whichever strategy you use, return one tool_result for each tool_use block, all together in the next user message. Match each result to its call with tool_use_id, and put every tool_result block before any text content in that message."3 (Qualquer que seja a estratégia usada, devolva um tool_result para cada bloco tool_use, todos juntos na próxima mensagem do usuário. Case cada resultado com a sua chamada usando o tool_use_id, e ponha todo bloco tool_result antes de qualquer conteúdo de texto naquela mensagem.)
  4. Você anexa a messages tanto a resposta completa do modelo daquele turno (papel assistant) quanto o lote de tool_result que você montou (papel user), e então envia outra requisição.

Aí vem a frase que mais importa: repita a partir do passo 2 enquanto stop_reason for "tool_use".2 Esse "repita enquanto" é o eixo que estica uma única ida e volta em um loop. O exemplo da Lição 1 parou no primeiro end_turn porque uma viagem era tudo o que aquela tarefa precisava; o bot de plantão roda os passos 2 a 4 três vezes, até o turno três voltar end_turn.

Vale lembrar: os campos dos blocos tool_use e tool_result não mudaram em nada. Um bloco tool_use carrega id / name / input; um bloco tool_result carrega tool_use_id / content, mais um is_error opcional quando a chamada falhou.3 O loop não reescreve o que qualquer um desses campos significa. Ele só faz o mesmo conjunto de campos ser preenchido e enviado de volta, repetidamente.

stop_reason é a condição do while do loop

Aquela linha acima — "repita enquanto stop_reason ainda for tool_use" — se traduz em código como o teste de um while loop. E a única frase que você deveria levar desta lição é esta: decidir se o loop continua ou para se resume a esse único campo, stop_reason. Ele tem muitos valores possíveis, mas para controle de loop, distinguir dois deles já basta para começar:

  • "tool_use": o modelo ainda quer uma ferramenta. Ele entrega a requisição a você e espera que você execute e envie o resultado de volta antes de continuar. O loop gira mais uma rodada.
  • "end_turn": o modelo não quer mais uma ferramenta; ele acha que disse o que tinha a dizer. O loop termina por conta própria e você entrega o texto final ao usuário.

Um roadmap de código aberto sobre engenharia de harness põe essa camada de controle de forma seca: o que dirige um harness é o while loop rodando modelo→ferramentas→modelo.4 E o que fica na expressão de condição desse while é o stop_reason. Mesmo modelo, mesmo conjunto de ferramentas — quantas rodadas ele gira e quando para é decidido inteiramente por como o host lê aquele campo e como escreve aquela condição. É por isso que a Lição 1 disse "Same model, different harness, completely different result."4 (Mesmo modelo, harness diferente, resultado completamente diferente.)

Uma coisa a acertar de saída, para você não ler o sinal ao contrário: stop_reason: "tool_use" significa que o modelo quer usar uma ferramenta, não que uma ferramenta foi usada. O modelo nunca executa nada por conta própria. Ele emite uma requisição estruturada; o que de fato roda a ferramenta é o seu código do host (ou os servidores da Anthropic), e o resultado só volta para a conversa depois.2 Então, no instante em que tool_use aparece no loop, nada aconteceu ainda. A ação acontece nas poucas linhas do seu código que leem o name e o input e vão fazer o trabalho. Tratar "recebi um tool_use" como "a ferramenta terminou de rodar" é o tropeço mais fácil ao passar de uma única ida e volta para um loop — faz você errar em qual passo o loop de fato está agora.

Escrito por extenso, são só algumas linhas

Ponha aqueles quatro passos e a condição do while do stop_reason em JavaScript e o esqueleto é surpreendentemente curto:

Percorra os quatro passos mais uma vez contra o código: a linha while é "repita enquanto ainda for tool_use"; dentro do corpo, tanto a resposta assistant quanto a mensagem user de blocos tool_result levam push para dentro de messages, e então response é reatribuído. Essa última atribuição é o que torna possível parar — remova-a, e response.stop_reason mantém o valor antigo para sempre, então o while loop nunca sai (esse sabor de loop infinito é a estrela da Lição 4).

Este código roda, mas é um esqueleto — simples o bastante para tornar o próprio loop visível, e nem de longe seguro para entregar à produção. Ele assume que o modelo sempre voltará com end_turn em algum turno, assume que cada ferramenta executa sem falha, e assume que não importa quão longo o histórico fica. Essas três suposições são exatamente o que as próximas lições desmontam uma de cada vez.

Cada turno a mais deixa o histórico mais longo

Olhe de novo aquelas linhas messages.push: cada turno do loop enfia mais duas mensagens em messages — a resposta assistant do modelo e o lote de tool_result que você enviou de volta. E o próximo callModel tem que despachar todo o messages de novo, sem mudança. Então, quanto mais tempo esse loop roda, mais histórico cada requisição carrega, e ele só cresce.

Isso não é um descuido da implementação. É uma propriedade inerente do loop como estrutura: um agente rodando em loop gera cada vez mais dados que podem ser relevantes para o próximo turno de inferência.5 Três turnos do bot de plantão só empilham um resultado de reinício mais um punhado de logs. Mas uma tarefa que precisa de dezenas de turnos rola o histórico em algo enorme.

Escondido aqui há um problema que só é aberto no curso "Memória e Estado de Agente", mas que precisa ser plantado agora: modelos têm um "orçamento de atenção", e cada novo token introduzido esgota esse orçamento em alguma medida.5 Histórico mais longo significa mais tokens, e conforme o número de tokens na janela de contexto aumenta, a capacidade do modelo de recuperar com precisão a informação daquele contexto diminui.5 Note que isso é um gradiente de desempenho que desce suavemente com o comprimento, não um penhasco de que você despenca ao passar de algum limiar5 — não leia como "passou do limite, virou inútil". Mas a direção é inequívoca: o contexto tem que ser tratado como um recurso finito com retornos marginais decrescentes.5 Aquele messages.push sem cerimônia no loop esqueleto não faz nada a respeito disso. Ele assume que o histórico pode crescer para sempre — e "Memória e Estado de Agente" é o curso que volta para acertar essa conta.

Um loop sozinho não basta; fronteiras têm que ser adicionadas

Você agora tem um loop que gira. Mas "consegue girar" e "gira com segurança" são duas coisas diferentes. O loop esqueleto entrega a decisão de parar-ou-continuar inteiramente ao modelo: o turno em que ele devolve end_turn é o turno em que o loop para. Só que agentes são sistemas em que o modelo dirige dinamicamente seu próprio processo e uso de ferramentas.1 Essa autonomia é precisamente de onde vem a utilidade, e precisamente onde mora o risco: autonomia significa custos maiores e o potencial de erros que se acumulam ao redor de turno após turno do loop.1 O modelo vai potencialmente operar por muitos turnos, e você tem que ter algum nível de confiança na tomada de decisão dele antes de deixá-lo rodar.1

O detalhe é que confiança não é o mesmo que ausência de supervisão. Se o modelo empaca em algum passo, ou é puxado para fora do rumo pelo que uma ferramenta devolveu, e simplesmente nunca volta com end_turn, um loop que vigia nada além do stop_reason seguirá girando junto com ele indefinidamente. Então, além do próprio sinal de conclusão do modelo, você normalmente adiciona condições de parada explícitas também — um teto no número máximo de iterações, por exemplo, para manter o controle do seu lado.1 O nu while (response.stop_reason === "tool_use") do esqueleto não tem tal fusível: ele confia no modelo sem deixar para si mesmo uma saída.

Isso planta os dois preparativos desta lição: este loop precisa de fronteiras (você não pode depender do modelo dizer end_turn; você precisa de condições de parada explícitas — Lição 3), e o histórico que este loop produz precisa ser gerenciado (tokens são um recurso finito, então você não pode simplesmente jogar coisas para dentro — deixado para o curso Memória e Estado de Agente). Como um loop de fato parece quando sai dos trilhos, e como pegá-lo, é o assunto da Lição 4. Para esta lição basta acertar o eixo: como o loop gira, e que stop_reason é o que o dirige.

Recapitulação

  • Conectar uma ida e volta de chamada de ferramenta em um loop não precisa de mecanismo novo, só de quatro passos repetidos: envie a requisição → leia stop_reason e os blocos tool_use → rode as ferramentas e empacote os blocos tool_result → anexe ao histórico e envie de novo; repita enquanto stop_reason ainda for tool_use2
  • stop_reason é a condição do while deste loop: tool_use significa que o modelo ainda quer uma ferramenta e o loop continua, end_turn significa que o modelo está encerrando e o loop termina por conta própria — o eixo modelo→ferramentas→modelo é dirigido por aquele campo4
  • tool_use é o sinal de que o modelo quer uma ferramenta, não um comprovante dizendo que uma rodou; o modelo nunca executa nada por si só, e a ação acontece onde o host lê o name e o input e vai trabalhar2
  • Cada turno do loop deixa o histórico mais longo e nunca mais curto, já que um agente em loop segue gerando mais dados que podem ser relevantes5; e com um orçamento de atenção finito, a recuperação piora conforme o contexto cresce, então tokens têm que ser tratados como um recurso finito com retornos marginais decrescentes5
  • Um loop sozinho não basta: autonomia traz custos maiores e erros que se acumulam, e o modelo pode operar por muitos turnos1, então, além do próprio end_turn do modelo, você normalmente adiciona condições de parada explícitas (um número máximo de iterações, digamos) para manter o controle do seu lado1 — como configurá-las é o assunto da Lição 3

>> Lição 3: Condições de parada: quando um agente deve desistir

Footnotes

  1. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2 3 4 5 6 7

  2. How tool use works — Claude API — https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works 2 3 4 5

  3. Handle tool calls — Claude API — https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls 2

  4. The 2026 Agent Engineering Roadmap — GitHub (codejunkie99/agent-roadmap-2026) — https://github.com/codejunkie99/agent-roadmap-2026 2 3

  5. Effective context engineering for AI agents — Anthropic Engineering — https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents 2 3 4 5 6 7

Exercícios

01

Um agente assistente de agenda é montado com duas ferramentas, search_calendar (olhar a agenda) e create_event (marcar algo). O usuário diz "veja se estou livre na quinta à tarde, e se estiver, marque uma revisão de 30 minutos". O host o dirige com o loop esqueleto desta lição, e a sequência de stop_reason que de fato acontece é:

Nível 1: Conte quantas vezes este loop gira
  • callModel retorna → stop_reason: "tool_use" (um bloco tool_use, chamando search_calendar)
  • callModel retorna → stop_reason: "tool_use" (um bloco tool_use, chamando create_event)
  • callModel retorna → stop_reason: "end_turn" (texto: "Marquei a revisão para quinta às 14:00")

Responda: (1) Quantas vezes callModel foi chamado no total? (2) Quantas vezes executeTool foi chamado no total? (3) Quantas vezes o corpo do while loop executou? (4) Ao ler qual stop_reason o loop saiu?

Critérios de conclusão · marcado localmente
02

Um colega tentou transformar uma única ida e volta em um loop de múltiplos turnos e escreveu o código abaixo. Ele "parece funcionar" em tarefas que precisam de apenas uma chamada de ferramenta, mas no momento em que uma tarefa precisa que o modelo chame ferramentas duas vezes seguidas, o processo trava, recursos são consumidos, e os logs mostram a mesma ferramenta sendo chamada repetidamente. Encontre a causa raiz e corrija.

Nível 2: Por que este loop nunca para
Critérios de conclusão · marcado localmente