Aula 01 · Fundamentos

Sem o loop, não existe agent

Todo o segredo do Claude Code cabe em uma linha: while stop_reason == "tool_use"

⏱ ~10 min · 📝 3 componentes interativos · 🧑‍💻 Baseado em shareAI-lab · s01_agent_loop.py

O que um agent faz, de verdade?

Quando você roda o Claude Code no terminal e pede para ele "organizar todos os comentários TODO em uma lista", você vê: ele decide por conta própria fazer grep, depois cat em alguns arquivos e então gerar Markdown. O modelo não executa código diretamente — ele apenas solicita que seja executado. O que o faz parecer "ter mãos e pés" são as cerca de 30 linhas de código de cola ao redor dele.

Esta lição desmonta essa cola e a examina de perto. Ela se chama agent loop (laço do agente), e sua estrutura é:

while response.stop_reason == "tool_use":
    response = LLM(messages, tools)      # 1. ask the model
    execute_tools(response.tool_calls)   # 2. run what it asked for
    messages.append(tool_results)        # 3. feed results back

Só isso. Nada mais. O Claude Code em produção empilha permissões, hooks, sub-agents, isolamento com worktree e compressão de memória em cima disso — mas o núcleo ainda são essas quatro linhas.

Intuição-chave: toda resposta do modelo é ou "quero chamar uma ferramenta" ou "terminei de falar". Enquanto estiver no primeiro estado, o loop continua; assim que entra no segundo, o loop sai. O critério é o campo stop_reason da resposta.

Veja o messages[] crescer passo a passo

O loop abaixo simula a tarefa: "Quais arquivos existem no diretório atual? Depois leia o package.json". Clique em Step; a cada clique você avança uma ação dentro do ciclo. À esquerda está a visão em bolhas de conversa, à direita está o array messages[] real enviado ao modelo — observe como ele cresce.

Os resultados das ferramentas precisam voltar ao histórico de mensagens

O erro mais comum de iniciantes é tratar "executar a ferramenta" como um efeito colateral — executou, acabou. Mas o modelo só vê messages[] na próxima rodada de inferência. O que não está lá, para o modelo nunca aconteceu. Esquecer o append quebra o loop inteiro.

Os erros não são todos iguais. Dois tipos comuns:

  • Esquecer o append: o modelo não vê o resultado na próxima rodada, então repete o mesmo tool_use — e você tem um loop infinito.
  • Fazer o append mas perder o tool_use_id: a API da Anthropic retorna diretamente o erro tool_result must have tool_use_id, e o loop trava na chamada de API.

A mesma tarefa: "Contar quantos arquivos Python existem no projeto". Dois versões lado a lado — veja a diferença no resultado final.

Interpretando o stop_reason

Toda resposta do modelo traz um stop_reason. Para decidir se o loop continua, basta olhar esse campo:

  • tool_use — o modelo quer chamar uma ferramenta, continue o loop.
  • end_turn — o modelo considera que terminou, saia do loop.
  • max_tokens — atingiu o limite de tokens e foi truncado, saia do loop (trate como exceção e informe o usuário que a saída está incompleta).
  • stop_sequence — encontrou uma sequência de parada personalizada, saia do loop.

Escrever "continuar enquanto não for tool_use", ou ignorar max_tokens tratando-o como end_turn normal, são bugs frequentes.

Rode você mesmo

Clone localmente shareAI-lab/learn-claude-code:

git clone https://github.com/shareAI-lab/learn-claude-code
cd learn-claude-code
pip install -r requirements.txt
cp .env.example .env  # fill in ANTHROPIC_API_KEY
python agents/s01_agent_loop.py

Depois peça para ele fazer algo real: Conta quantos arquivos .py tem neste repositório. Você verá ele enviar um ls -R, ver a saída, enviar um find . -name "*.py" | wc -l e então dar a resposta. Todo esse processo é esse loop em ação.

Interativo

Widget 1 · Loop Stepper · crescimento do messages[]

Entenda: os resultados das ferramentas são reinseridos no messages em um formato especial (bloco tool_result, com tool_use_id); esse é o único caminho pelo qual o modelo sabe, na próxima rodada, que "a ferramenta executou e o resultado foi X".

Visão da conversaNão iniciado
Clique em Step para começar →
messages[] JSONlength: 0
[]
stop_reason:
Interativo

Widget 2 · Break the Loop · Implementação correta vs esqueceu o append

Entenda: basta omitir uma linha messages.append(tool_results) para que o agent inteiro deixe de ser um agent — ele vira uma máquina que esquece a rodada anterior a cada ciclo.

while True:
    resp = LLM(msgs, tools)
    msgs.append({"role":"assistant",
                 "content": resp.content})
    if resp.stop_reason != "tool_use":
        return
    results = execute(resp.tool_uses)
    msgs.append({"role":"user",
                 "content": results})
Simulação
while True:
    resp = LLM(msgs, tools)
    msgs.append({"role":"assistant",
                 "content": resp.content})
    if resp.stop_reason != "tool_use":
        return
    results = execute(resp.tool_uses)
    # BUG: forgot to append results
    # msgs.append(...)
Simulação
Interativo

Widget 3 · Identifique o stop_reason · Avalie 4 respostas reais

Cada uma é um trecho real de uma possível resposta do modelo. Clique em "Continuar loop" ou "Sair do loop" e veja se acertou.

Acertos: 0 / 4