No capítulo anterior, vimos que o agente de IA é feito dos 4 elementos cérebro (LLM), ferramentas, memória e laço (controle). Neste capítulo, finalmente colocamos a mão na massa. A meta é simples: montar "um agente que funciona" com esses 4 elementos na configuração mínima, rodá-lo de verdade e observar o comportamento. Ainda não vamos adicionar recursos vistosos. Primeiro, faremos girar com as próprias mãos o coração do agente — "fazer o LLM pensar, chamar uma ferramenta, devolver o resultado, pensar de novo e parar ao concluir".

A meta deste capítulo

"Montar e rodar você mesmo um agente mínimo"

Entender o laço
Saber explicar uma volta do agente: "pensar → chamar → devolver → pensar de novo → parar".
Entregar ferramentas
Entender a definição de ferramenta e os argumentos e dar uma ferramenta ao LLM.
Observar
Ver pelos logs onde ele decide e onde falha, ligando ao próximo capítulo.

O que é o agente mínimo

O "agente mínimo" é os 4 elementos do capítulo anterior montados cada um na sua forma mais enxuta. Não precisa de memória sofisticada, nem de muitas ferramentas, nem de vários agentes. Como na tabela a seguir, começamos por uma configuração cortada até o osso. Se você entender esta parte, o resto é só engordar cada elemento.

🧠 Cérebro

Basta chamar um único modelo Claude. No prompt, escreva uma linha de papel: "você é um assistente que sabe usar ferramentas".

🛠 Ferramentas

Só uma ferramenta. Pode ser busca ou cálculo. Comece por uma função simples de funcionamento garantido.

📝 Memória

Um único array de histórico de conversa basta. É só ir empilhando as trocas em ordem. Persistência, ainda não pense nela.

🔄 Laço

Um único while. Gire com "se houver pedido de ferramenta, execute; se não, conclua". Defina apenas o número máximo de voltas.

💡 A única diferença para "uma chamada de API" é o laço. Se você só pergunta uma vez ao LLM e recebe a resposta, isso é mero uso de API. No instante em que se adiciona a repetição de "executar a ferramenta, devolver o resultado e fazer pensar de novo", vira um agente. Na prática, o que se acrescenta é só esse laço. Por isso, entender o laço por completo primeiro é o atalho.

O que há dentro do laço do agente

O coração do agente é um único laço. A mesma coisa que um humano faz — "investigar, pensar, agir, ver o resultado e, se ainda não deu, repetir" — é o que rodamos em código. Vamos primeiro fixar o fluxo de uma volta em um diagrama.

PASSO 1
Fazer pensar

Entregar ao LLM o histórico da conversa e a lista de ferramentas. O LLM decide entre "responder" ou "usar uma ferramenta".

PASSO 2
Chamar a ferramenta

Se o LLM responder "quero usar esta ferramenta com estes argumentos", nós é que executamos de fato aquela função.

PASSO 3
Devolver o resultado

Anexamos o resultado da execução (o retorno da ferramenta) ao histórico e o entregamos de novo ao LLM. Aqui, volta ao PASSO 1.

PASSO 4
Parar ao concluir

Se o LLM devolver a resposta final sem usar ferramenta, damos o objetivo por cumprido e saímos do laço.

O ponto é que giramos os PASSOS 1 a 3 repetidamente e só saímos no PASSO 4. O LLM pode não terminar em uma volta. Como em "primeiro buscar, ver o resultado e buscar de novo", ele dá várias voltas até se aproximar do objetivo. Escrito em pseudocódigo, deve ficar surpreendentemente curto.

# Laço mínimo do agente (pseudocódigo)
messages = [ user("Descubra a temperatura média do mês passado e resuma") ]

while True:
    reply = llm.think(messages, tools=[search_tool])   # PASSO1 pensar

    if reply.wants_tool:                              # pediu ferramenta?
        result = run_tool(reply.tool_name, reply.args) # PASSO2 chamar
        messages.append(assistant(reply))
        messages.append(tool_result(result))           # PASSO3 devolver
    else:
        return reply.text                             # PASSO4 parar ao concluir

⚠️ Coloque sempre um limite de voltas. O pseudocódigo acima usa while True só para efeito de explicação, mas, no código de verdade, coloque sempre a trava de "até no máximo N voltas". O laço infinito em que o LLM fica chamando ferramentas sem parar é um acidente que acontece de verdade, e com frequência. Trataremos disso em detalhe em Pontos onde se tropeça.

Dar uma ferramenta ao agente

Em seguida, damos uma ferramenta ao LLM. O importante aqui é que o LLM não consegue executar código sozinho. O que o LLM consegue fazer vai só até pedir em palavras "quero usar esta ferramenta com estes argumentos". Executar de fato a função e devolver o resultado é sempre trabalho nosso (do seu programa).

Para isso, primeiro entregamos ao LLM a definição da ferramenta. A definição da ferramenta é o "manual" de "o que esta ferramenta faz e que argumentos recebe". O LLM lê isso e decide quando e como usá-la.

① Nome e descrição

Ex.: search_weather "recebe cidade e mês e devolve a temperatura média". Como a descrição é a base de decisão do LLM, escreva-a com cuidado.

② Formato dos argumentos

Especifique quais argumentos, em que tipo, por meio de um schema (definição em formato JSON). Ex.: city (string) e month (número).

③ A função de verdade

O corpo do código que roda de fato. Recebe os argumentos que o LLM devolveu e retorna o resultado. Aqui é uma função comum e simples.

Desse trio, o truque é entregar ① e ② ao LLM e manter ③ do seu lado. Quando o LLM pedir "quero usar search_weather(city=\"Paris\", month=6)", nós recebemos esse nome e argumentos, chamamos a função ③ e devolvemos o retorno à conversa como resultado da ferramenta. Vejamos em pseudocódigo.

# ① e ② … definição da ferramenta entregue ao LLM
search_tool = {
    "name": "search_weather",
    "description": "recebe cidade e mês e devolve a temperatura média (°C)",
    "input_schema": {
        "city":  "string",
        "month": "number",
    },
}

# ③ … a função de verdade, mantida do seu lado. Uma função comum
def run_tool(name, args):
    if name == "search_weather":
        return weather_db.lookup(args["city"], args["month"])
    return "unknown tool"

📊 A descrição faz parte do prompt. A description da ferramenta é a única pista que o LLM tem para decidir "quando usá-la". Se for vaga, ele não a usará, ou a usará em situações erradas. O truque é escrever como se fosse explicar a uma pessoa "o que ela retorna e quando deve ser usada". O desenho de ferramentas será aprofundado no próximo capítulo, sobre MCP.

Imagem da implementação — laço manual e SDK

Juntando as peças até aqui, o seu primeiro agente funciona. Há basicamente duas formas de implementar. Para aprender o funcionamento, o laço manual; para facilitar no trabalho real, o SDK — em ambos, o miolo é o mesmo laço. Vamos primeiro organizar as diferenças.

A. Laço manual
você mesmo gira o while

Você gira a chamada de API e a execução da ferramenta no seu próprio while. Como tudo o que acontece por dentro fica visível, é ótimo para aprender. E o controle é totalmente livre.

Quando usar: quer entender o funcionamento / quer controle fino.

B. Tool runner do SDK
delega o laço

Você registra as definições e as funções das ferramentas no laço de execução de ferramentas que SDKs como os do Claude oferecem, e delega. Dispensa escrever a parte repetitiva e é robusto.

Quando usar: quer construir rápido / quer delegar o encanamento de produção.

O panorama geral do A. laço manual é simplesmente ligar de forma direta as peças que vimos até aqui. Manter um histórico, chamar o LLM, executar se houver pedido de ferramenta e devolver o resultado, ou sair se não houver — é só isso.

# A. Laço manual (pseudocódigo / no estilo de mensagens do Claude)
messages = [ user(goal) ]

for step in range(MAX_STEPS):          # ← trava com um limite
    reply = client.messages.create(
        model="claude-…",
        messages=messages,
        tools=[search_tool],
    )
    log(step, reply)                       # ← registre sempre, para observar

    if reply.stop_reason == "tool_use":
        out = run_tool(reply.tool_name, reply.tool_args)
        messages += [ assistant(reply), tool_result(out) ]
    else:
        break                              # saiu a resposta final → encerrar

Ao usar o B. tool runner do SDK, a própria biblioteca cuida desse laço for. O que você escreve é só "a definição da ferramenta", "a função de verdade" e "o objetivo inicial". O laço, a gestão do histórico e a reinjeção do resultado da ferramenta são tratados internamente.

# B. Delegar ao tool runner do SDK (pseudocódigo)
runner = ToolRunner(
    model="claude-…",
    tools=[search_tool],       # definição
    handlers={"search_weather": run_tool},  # implementação
)
answer = runner.run(goal)      # o laço gira por dentro

💡 Os nomes exatos dos métodos estão na documentação oficial. Aqui, client.messages.create e ToolRunner são pseudocódigo para passar a ideia. Os nomes reais dos argumentos, o formato do retorno e os IDs de modelo variam de SDK para SDK e mudam com frequência, então confira sempre a versão mais recente na documentação oficial. Primeiro pegue o conceito e depois preencha os detalhes pela documentação — assim você não anda em círculos. Como introdução passo a passo, veja também Como construir um agente de IA (introdução).

A recomendação é "escrever manualmente uma vez com A e, dali em diante, facilitar a vida com B". Depois de girar o laço uma vez com as próprias mãos, o SDK deixa de ser uma caixa-preta e, quando você empacar, consegue imaginar o que está acontecendo lá dentro.

Rodar e observar

Montado, é hora de rodar. O importante aqui é não apenas rodar, mas observar "onde ele pensou o quê e onde falhou". No desenvolvimento de agentes, a qualidade dessa observação separa os resultados. Por isso, deixamos logs instalados desde o início.

No mínimo, registre o seguinte a cada volta. Num laço manual, basta a linha log(step, reply).

🧭 O que decidiu

Naquela volta, o LLM decidiu "usar uma ferramenta" ou dar a resposta final. É o ponto de bifurcação da decisão.

🛠 Com que argumentos chamou

O nome da ferramenta e os argumentos. Se isto estranhar, o resultado desanda. É onde mais aparece a interpretação errada do LLM.

📦 O que retornou

O retorno da ferramenta. Grande demais, vazio, com erro — o que vai desviar a próxima decisão pode se esconder aqui.

Ao rodar de verdade, surgem comportamentos interessantes (e incômodos). Por exemplo: o LLM chama a ferramenta sem precisar. Ou, ao contrário, não chama quando deveria e responde só de memória. Erra levemente o nome da cidade no argumento. Repete a mesma busca várias vezes quando uma bastaria. Esses "vícios de decisão" só ficam perceptíveis ao olhar os logs.

✅ Log não é "recurso de luxo" — é obrigatório desde o início. Como o interior do agente é pouco visível, sem log você não faz a menor ideia de "por que ele agiu assim". Por ora, até uma linha de print basta. A avaliação e a observabilidade, que sistematizam essa observação, serão tratadas a sério no Capítulo 5. Por enquanto, adquira aqui o "hábito de olhar".

Pontos onde se tropeça

Mesmo na configuração mínima, ao rodar você vai bater em alguns muros. Todos são tropeços clássicos do desenvolvimento de agentes, que vamos resolvendo capítulo a capítulo daqui em diante. Aqui, fixamos "o que é" e "o remédio possível agora" e deixamos o tratamento a sério para os capítulos seguintes.

🔁 Laço infinito

Fica chamando a mesma ferramenta sem parar e nunca conclui. Gira em falso sem conseguir julgar que o objetivo foi cumprido.

Remédio: imponha sempre um limite máximo de voltas. Detecte e interrompa chamadas consecutivas com os mesmos argumentos.

📦 Retorno grande demais

A ferramenta devolve um JSON enorme ou uma página inteira e, ao reinjetá-lo por completo na conversa, o custo e a latência incham.

Remédio: restrinja, do lado da ferramenta, só aos campos necessários / resuma antes de devolver. Não passe o conteúdo bruto.

🎈 Inchaço de contexto

Quanto mais voltas, mais o histórico se acumula, aproximando-se do limite e ficando lento, caro e instável.

Remédio: resuma trocas antigas para comprimir / descarte resultados de ferramenta desnecessários. Tratamento a sério nos próximos capítulos.

📊 Os três são problemas de "desenho de contexto". Laço infinito, retorno inflado e inchaço de contexto — a raiz de todos chega ao desenho de contexto: "o que, e quanto, entregar ao LLM". Essa forma de pensar está sistematizada como context engineering e é a espinha dorsal do desenvolvimento de agentes. Por ora, adote como lema "não entregar demais, não acumular demais".

Resumo do capítulo
  • Agente mínimo = os 4 elementos montados o menor possível. A diferença para um uso único de API é só o laço.
  • O laço repete a volta "pensar → chamar a ferramenta → devolver o resultado → pensar de novo → parar ao concluir". A trava de um limite de voltas é obrigatória.
  • Na ferramenta, a definição (nome, descrição, argumentos) vai ao LLM e a função de verdade é executada do seu lado. O LLM apenas pede.
  • A implementação tem 2 formas: laço manual (para aprender) e tool runner do SDK (para o trabalho real). O miolo é o mesmo.
  • Desde o início, observe pelos logs e encontre os tropeços clássicos: laço infinito, retorno inflado e inchaço de contexto.

Rodado o primeiro agente, é hora de aumentar as ferramentas de verdade. No próximo Capítulo 3 "MCP e conexão de ferramentas", vamos conectar as ferramentas a serviços e dados externos pela via padronizada (MCP) e ampliar de uma vez o alcance das mãos do agente. Quando quiser revisar o panorama do capítulo anterior, volte ao Capítulo 1.