- O Que Torna Algo um Agente de IA
- Passo 1: Planejamento — A Camada de Raciocínio
- Passo 2: Uso de Ferramentas — Conectando o Modelo ao Mundo
- Passo 3: Execução de Código — Executando Código com Segurança
- Passo 4: Avaliação — Sabendo Quando o Agente Está Correto
- Escolhendo um Modelo para Raciocínio de Agente
- Juntando Tudo — Um Agente Mínimo Funcional
- FAQ
- Artigos Recomendados
Construir um agente de IA começa com uma ideia simples: dar a um modelo a capacidade de executar ações, observar os resultados e entrar em loop. O modelo raciocina sobre um objetivo, decide o que fazer em seguida, chama uma ferramenta, recebe um resultado e continua até que a tarefa seja concluída. Esse loop de ler-avaliar-agir é o núcleo de todo agente que você construirá, seja um chamador de ferramenta de etapa única ou um sistema multi-etapas que escreve código, executa e corrige seus próprios erros.
Este tutorial aborda os quatro componentes que fazem um agente funcionar: planejamento, uso de ferramentas, execução de código e avaliação. Você sairá com um modelo mental funcional e uma implementação mínima que pode executar hoje.
O Que Torna Algo um Agente de IA
Um agente de IA difere de uma chamada básica de LLM de uma maneira crucial: ele executa ações e observa seus resultados.
Uma conclusão de chat simples produz texto. Um agente usa esse texto para chamar uma função, consultar uma API, executar um comando ou ler um arquivo — e alimenta o resultado de volta como entrada para a próxima etapa. Esse loop de feedback é o que torna os agentes úteis para tarefas que não podem ser respondidas em uma única resposta.
Três capacidades definem um agente:
- Chamada de ferramentas — invocar funções definidas por nome com argumentos estruturados
- Acúmulo de contexto — carregar os resultados das ferramentas adiante à medida que a conversa cresce
- Loop orientado a objetivos — continuar até que uma condição de parada seja atendida, não apenas após uma resposta
Se um sistema tem todas as três, você está construindo um agente. Se faltar acúmulo de contexto ou loop, é um chatbot aumentado com ferramentas.
A mesma arquitetura lida com uma ampla gama de tarefas: agentes de pesquisa que buscam e sintetizam, agentes de codificação que escrevem e executam código, agentes de dados que consultam bancos de dados e geram relatórios. O padrão de implementação é quase idêntico em cada caso.
Passo 1: Planejamento — A Camada de Raciocínio
A camada de planejamento é onde o modelo decide o que fazer em seguida. Você não precisa de um modelo separado de planejador — o mesmo modelo que gera texto pode raciocinar sobre um objetivo e produzir uma ação estruturada (uma chamada de ferramenta) a cada etapa.
O prompt de sistema faz a maior parte do trabalho. Um bom prompt de planejamento diz ao modelo:
- Qual objetivo ele está buscando
- Quais ferramentas estão disponíveis e o que cada uma faz
- Qual formato usar ao chamar uma ferramenta
- Quando parar (a condição de conclusão)
Com a API LLM da Novita — que é compatível com OpenAI — isso se parece com:
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.novita.ai/v3/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
tools = [
{
"type": "function",
"function": {
"name": "run_code",
"description": "Execute Python code e retorne stdout/stderr.",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Código Python para executar"
}
},
"required": ["code"]
}
}
}
]
messages = [
{
"role": "system",
"content": "Você é um assistente de codificação. Use a ferramenta run_code para executar Python e resolver tarefas. Pare quando tiver uma resposta final."
},
{
"role": "user",
"content": "Encontre todos os números primos abaixo de 50."
}
]
response = client.chat.completions.create(
model="meta-llama/llama-3.1-8b-instruct",
messages=messages,
tools=tools,
tool_choice="auto",
)
O modelo retorna uma chamada de ferramenta — um objeto JSON com o nome da função e os argumentos — em vez de uma resposta em texto simples. Seu código extrai isso, executa a função e faz o loop de volta com o resultado anexado a messages.
Passo 2: Uso de Ferramentas — Conectando o Modelo ao Mundo
Ferramentas são o que os agentes realmente fazem. Sem elas, o modelo só pode produzir texto. Com elas, ele pode ler arquivos, chamar APIs, executar código, pesquisar bancos de dados ou acionar qualquer função que você definir.
Definindo Ferramentas
Cada ferramenta é um esquema JSON que descreve uma função. O modelo lê esse esquema para formatar sua chamada corretamente. Algumas regras práticas:
- Mantenha as descrições curtas e precisas. O modelo as lê a cada etapa. Descrições verbosas desperdiçam tokens.
- Combine os nomes dos parâmetros com o que a função realmente aceita. Incompatibilidades entre o esquema e sua implementação causam falhas silenciosas.
- Retorne saída estruturada. Se sua ferramenta retornar HTML bruto ou prosa não formatada, o modelo desperdiça tokens analisando. Retorne strings limpas ou JSON com rótulos claros.
O Loop de Chamada de Ferramenta
Após o modelo fazer uma chamada de ferramenta, seu código:
- Analisa o nome e os argumentos da ferramenta a partir da resposta
- Localiza a função Python correspondente
- Chama-a com os argumentos analisados
- Anexa o resultado a
messagescomrole: "tool" - Faz outra solicitação de conclusão
O loop continua até que o modelo retorne uma mensagem simples sem chamada de ferramenta (a condição de conclusão).
import json
def run_agent_loop(client, model, messages, tools, tool_fns, max_steps=10):
for _ in range(max_steps):
response = client.chat.completions.create(
model=model,
messages=messages,
tools=tools,
tool_choice="auto",
)
choice = response.choices[0]
# Concluído — sem chamada de ferramenta
if choice.finish_reason == "stop":
return choice.message.content
# Executa a chamada de ferramenta
tool_call = choice.message.tool_calls[0]
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments)
result = tool_fns[fn_name](**fn_args)
# Anexa tanto a chamada de ferramenta do modelo quanto o resultado
messages.append(choice.message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": str(result),
})
return "Número máximo de etapas atingido sem uma resposta final."
Este é o loop completo do agente. O guarda max_steps previne loops infinitos — se o modelo ficar preso chamando a mesma ferramenta repetidamente, o loop termina em vez de rodar indefinidamente. Todo o resto — orquestração de múltiplas ferramentas, memória, streaming — é construído sobre esse padrão.
Padrões Comuns de Ferramentas
Acesso a arquivos: Leia e escreva arquivos dentro do diretório de trabalho do agente. Retorne conteúdos de arquivos como strings.
Pesquisa na web: Retorne resultados de pesquisa como uma lista de objetos {title, url, snippet}. Mantenha os trechos curtos.
Execução de código: Execute código em um ambiente isolado e retorne stdout/stderr (abordado na próxima seção).
Consulta de dados estruturados: Consulte um banco de dados ou API e retorne registros como JSON. O modelo lida com a sumarização.
Cada ferramenta deve fazer uma coisa e retornar saída determinística para as mesmas entradas. Agentes que tentam colocar muita lógica em uma única ferramenta se tornam mais difíceis de depurar e avaliar.
Passo 3: Execução de Código — Executando Código com Segurança
Se seu agente gera e executa código, você precisa de um ambiente isolado. Executar código gerado pelo modelo diretamente em sua máquina é um risco de segurança: o modelo pode produzir comandos de shell destrutivos, loops infinitos ou código que lê arquivos confidenciais que não deveria acessar.
A abordagem padrão é uma sandbox — um ambiente Linux de curta duração e isolado onde o agente pode executar código livremente sem afetar o sistema host. O Agent Sandbox da Novita foi projetado exatamente para este caso de uso: um ambiente de execução baseado em nuvem que é inicializado em menos de 200ms, executa qualquer código que o agente produza e descarta o ambiente quando a sessão termina.
Veja como a ferramenta run_code da seção de planejamento seria implementada usando o sandbox:
from novita_sandbox.code_interpreter import Sandbox
sandbox = Sandbox.create()
def run_code(code: str) -> str:
result = sandbox.run_code(code)
output = ""
if result.logs.stdout:
output += "\n".join(result.logs.stdout)
if result.logs.stderr:
output += "\nSTDERR: " + "\n".join(result.logs.stderr)
return output or "(sem saída)"
Quando o agente chama run_code, esta função executa o código no ambiente isolado e retorna stdout/stderr como uma string. O agente vê o resultado e decide o que fazer em seguida — seja corrigir um bug, continuar com a saída ou declarar a tarefa concluída.
Esta arquitetura separa claramente a camada de raciocínio da camada de execução. O LLM planeja e chama ferramentas; o sandbox executa código sem expor nada fora do contêiner.
O Que o Sandbox Lida
- Comandos de shell (
bash,git,python,node) - Operações de sistema de arquivos dentro do sandbox
- Requisições de rede a partir do contêiner
- Tarefas em segundo plano de longa execução com recuperação assíncrona de resultados
- Pausa e retomada para fluxos de trabalho de múltiplas sessões
O que ele não permite: acesso ao sistema host, a outros sandboxes ou a credenciais que você não passou explicitamente. Esse confinamento é o que torna seguro deixar o modelo escrever e executar código arbitrário.
Passo 4: Avaliação — Sabendo Quando o Agente Está Correto
A avaliação é a etapa mais ignorada no desenvolvimento de agentes. Um agente que produz saída não é o mesmo que um agente que produz saída correta.
Três Níveis de Avaliação
1. Verificações determinísticas
Para tarefas com respostas corretas conhecidas — analisar uma data, calcular um valor, extrair um campo — escreva testes unitários contra a saída do agente. Eles são baratos de executar e detectam regressões rapidamente.
2. Verificações baseadas em execução
Para agentes de codificação, o sinal mais confiável é: o código realmente é executado e produz o resultado correto? Execute a saída do agente no sandbox e compare stdout com os valores esperados. Isso é mais forte do que comparar texto de código porque testa o comportamento, não a sintaxe.
def evaluate_code_task(agent_code: str, expected_stdout: str) -> bool:
result = run_code(agent_code)
return result.strip() == expected_stdout.strip()
3. LLM como juiz
Para tarefas abertas — escrever um resumo, responder a uma pergunta de pesquisa — use uma segunda chamada de LLM para avaliar a saída com base em uma rubrica. Mantenha a rubrica específica: “A resposta incluiu um exemplo de código funcional?” é melhor do que “A resposta é boa?” Rubricas vagas produzem pontuações inconsistentes.
Rastreando Modos de Falha
Agentes falham em um conjunto recorrente de padrões:
| Falha | Causa | Correção |
|---|---|---|
| Loop de chamada de ferramenta | Modelo continua chamando a mesma ferramenta | Defina max_steps; registre etapas intermediárias |
| Incompatibilidade de argumentos | Esquema não corresponde à implementação | Valide argumentos da ferramenta antes de chamar |
| Estouro de contexto | Sessão cresce além do limite de contexto do modelo | Resuma ou trunque resultados de ferramentas mais antigas |
| Nome de ferramenta alucinado | Modelo inventa uma ferramenta que não existe | Use uma lista estrita de ferramentas com tool_choice: "auto" |
A avaliação não é uma fase separada no final — é parte do desenvolvimento. Toda vez que você estende as capacidades de um agente, escreva pelo menos um teste que capturaria uma regressão no novo comportamento.
Escolhendo um Modelo para Raciocínio de Agente
A maioria dos tutoriais usa modelos de código fechado para raciocínio de agente. Isso funciona bem para prototipagem, mas em produção — onde um agente pode fazer 20 chamadas de LLM por tarefa — o custo do modelo e os limites de taxa se tornam restrições reais.
Modelos de código aberto hospedados via API LLM da Novita cada vez mais correspondem ao desempenho de código fechado nas tarefas que os agentes realmente precisam: chamada de funções, saída estruturada e raciocínio multi-etapas. Modelos como Llama 3.3 70B e DeepSeek-V3 alcançam níveis de classe GPT-4 em codificação e seguimento de instruções, enquanto executam a uma fração do custo. Para cargas de trabalho agentivas de alta taxa de transferência, essa diferença se acumula rapidamente.
A API da Novita é totalmente compatível com OpenAI, portanto, mudar de um modelo de código fechado para um de código aberto é uma alteração de uma linha:
# Antes
model = "gpt-4o"
# Depois — mesmo contrato de API, custo menor
model = "meta-llama/llama-3.3-70b-instruct"
Para loops de agente sensíveis à latência, modelos menores como meta-llama/llama-3.1-8b-instruct respondem mais rápido e custam menos por etapa, o que importa quando cada tarefa do usuário envolve muitas chamadas sequenciais. Um padrão prático: use um modelo menor e mais rápido para etapas de roteamento e classificação, e um modelo maior para etapas de raciocínio complexo que precisam de mais capacidade.
A biblioteca de modelos da Novita também inclui opções robustas para subtarefas agentivas específicas — modelos de visão para resultados de ferramentas baseados em imagem, modelos de incorporação para recuperação e modelos especializados em código para agentes de codificação — todos acessíveis a partir do mesmo endpoint da API.
Juntando Tudo — Um Agente Mínimo Funcional
Aqui está um agente completo que pega um problema de matemática, gera código Python para resolvê-lo, executa em um sandbox e retorna o resultado verificado:
import os
import json
from openai import OpenAI
from novita_sandbox.code_interpreter import Sandbox
client = OpenAI(
base_url="https://api.novita.ai/v3/openai",
api_key=os.environ["NOVITA_API_KEY"],
)
sandbox = Sandbox.create()
def run_code(code: str) -> str:
result = sandbox.run_code(code)
stdout = "\n".join(result.logs.stdout)
stderr = "\n".join(result.logs.stderr)
return stdout or stderr or "(sem saída)"
tool_fns = {"run_code": run_code}
tools = [
{
"type": "function",
"function": {
"name": "run_code",
"description": "Execute código Python. Retorna stdout ou stderr.",
"parameters": {
"type": "object",
"properties": {
"code": {"type": "string"}
},
"required": ["code"]
}
}
}
]
messages = [
{
"role": "system",
"content": "Resolva a tarefa escrevendo e executando código Python. Use run_code para executá-lo. Pare quando tiver uma resposta verificada."
},
{
"role": "user",
"content": "Qual é a soma de todos os números pares de Fibonacci abaixo de 1.000.000?"
}
]
for _ in range(10):
response = client.chat.completions.create(
model="meta-llama/llama-3.1-8b-instruct",
messages=messages,
tools=tools,
tool_choice="auto",
)
choice = response.choices[0]
if choice.finish_reason == "stop":
print(choice.message.content)
break
tool_call = choice.message.tool_calls[0]
fn_args = json.loads(tool_call.function.arguments)
result = tool_fns[tool_call.function.name](**fn_args)
messages.append(choice.message)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"content": result,
})
sandbox.kill()
Isso cobre o loop completo do agente em aproximadamente 50 linhas: planejamento, chamada de ferramentas, execução, acúmulo de contexto, guarda de etapa máxima e desmontagem limpa do sandbox. O agente escreve código, executa, vê a saída e continua ou dá uma resposta final.
Para estendê-lo: adicione mais ferramentas a tool_fns e tools, troque por um modelo maior para tarefas mais difíceis, ou substitua o run_code do sandbox por qualquer outra função — uma pesquisa na web, uma consulta de banco de dados, uma chamada de API. O loop permanece o mesmo.
FAQ
Qual é a diferença entre um agente de IA e um chatbot?
Um chatbot gera respostas de texto. Um agente executa ações — chamar ferramentas, executar código, consultar APIs — e usa os resultados para continuar raciocinando em direção a um objetivo. A presença de chamadas de ferramentas e um loop de feedback é o que torna algo um agente.
Preciso de um framework como LangChain para construir um agente de IA?
Não. O loop central do agente é simples o suficiente para ser implementado diretamente contra qualquer API compatível com OpenAI. Frameworks agregam valor para orquestração complexa (sistemas multi-agente, memória persistente, saída em streaming), mas adicionam camadas de abstração que podem dificultar a depuração. Comece com a API pura e adicione um framework se você realmente precisar do que ele oferece.
Como evito que o agente execute código perigoso?
Use um ambiente de execução em sandbox. O código do agente é executado dentro de um contêiner isolado que não tem acesso ao seu sistema host ou a outros processos. O sandbox é descartado após o término da sessão, portanto, quaisquer alterações feitas pelo agente são limitadas a esse contêiner.
Modelos de código aberto conseguem lidar com chamada de funções de forma confiável?
Sim — com ressalvas quanto ao tamanho do modelo. Llama 3.3 70B, DeepSeek-V3 e Qwen 2.5 72B lidam bem com chamada de ferramentas estruturada. Modelos menores (7B–8B) funcionam para esquemas de ferramentas simples e bem definidos, mas podem ter dificuldades com instruções ambíguas ou cadeias de raciocínio complexas de múltiplas etapas. Para agentes em produção, teste em sua distribuição real de tarefas antes de se comprometer com um modelo.
Quantas etapas um agente deve executar por tarefa?
Defina um limite máximo — 10 a 20 etapas é típico para a maioria das tarefas. Registre as etapas intermediárias para que você possa ver exatamente o que o agente fez e depurar falhas. Tarefas que consistentemente precisam de mais etapas geralmente se beneficiam de serem divididas em subagentes ou subtarefas, em vez de estender o limite do loop.
