Agentes de IA: construa o loop sem perder o controle
Entenda o que é um agente de IA, implemente o ciclo entre modelo e ferramentas, limite passos e mantenha autorização, estado e erros no código.
Um agente de IA é um programa em ciclo: o modelo recebe uma tarefa, pode pedir uma ferramenta, observa o resultado e continua até responder ou atingir um limite. Nesta lição, você vai implementar esse loop, tornar os passos visíveis e interromper uma sequência que não consegue terminar.
O nome técnico agent não significa uma mente trabalhando sozinha. Na prática, é uma combinação de modelo, instruções, ferramentas, estado e regras de parada. O programa continua responsável por autenticação, efeitos colaterais, orçamento e decisões que exigem uma pessoa.
O código daqui usa decisões em fixtures locais para tornar o controle de fluxo determinístico. Ele foi executado no Node, mas não chamou a API OpenAI. Essa separação permite provar o loop sem fingir que uma lista escrita no código mede o comportamento de um modelo real.
O despachante escolhe a próxima parada, mas não dirige sem estrada
Imagine uma central de entregas. O despachante recebe o destino, consulta o estoque, manda um motorista buscar o pacote e confere o comprovante antes de encerrar. Há uma sequência, um registro e um limite de tentativas. Se a estrada não existe, insistir dez vezes não cria uma rota.
No paralelo, o modelo é o despachante; as ferramentas são veículos autorizados; os resultados são comprovantes; o histórico é o quadro da operação; e a regra de parada define quando a tarefa termina. O limite da analogia: o modelo não observa o mundo por conta própria. Ele conhece apenas o contexto e os resultados que a aplicação devolve.
Uma execução pode ser resumida assim:
tarefa
-> modelo decide
-> resposta: encerra
-> ferramenta: aplicação valida e executa
-> resultado volta ao contexto
-> modelo decide novamenteAntes de criar o ciclo, estude o protocolo da lição de function calling. Agente não corrige uma ferramenta insegura; ele apenas pode chamá-la mais vezes.
Estado explícito transforma conversa em execução auditável
Modele as duas decisões que interessam ao nosso laboratório:
type Decisao =
| { tipo: "ferramenta"; nome: "buscar_pedido"; argumentos: string }
| { tipo: "resposta"; texto: string };
type Passo = {
numero: number;
tipo: Decisao["tipo"];
detalhe: string;
};
const trajetoria: Passo[] = [];
console.log(trajetoria);O array vazio é o começo da trajetória. Em produção, um item de
response.output com type: "function_call" representa a primeira decisão;
uma mensagem final representa a segunda. Registrar o passo ajuda a explicar
qual ferramenta foi usada e onde o sistema parou.
Estado não precisa significar guardar conversas para sempre. Preserve apenas o
necessário, defina retenção e retire dados sensíveis dos logs. Na Responses API,
você pode continuar com itens anteriores no input ou encadear uma resposta
anterior, conforme o desenho de privacidade e estado do produto.
O loop mínimo precisa de uma porta de saída
Primeiro crie uma versão independente de provedor. As decisões entram como fixture para que a execução seja repetível:
function executarLoop(decisoes: Decisao[], limite = 3) {
const passos: string[] = [];
for (let indice = 0; indice < limite; indice += 1) {
const decisao = decisoes[indice] ?? decisoes.at(-1);
if (!decisao) throw new Error("SEM_DECISAO");
passos.push(decisao.tipo);
if (decisao.tipo === "resposta") {
return { passos, resposta: decisao.texto };
}
// Aqui a aplicação validaria e executaria a ferramenta.
}
throw new Error("LIMITE_DE_PASSOS_ATINGIDO");
}Passe uma trajetória que usa ferramenta e depois responde:
const concluido = executarLoop([
{
tipo: "ferramenta",
nome: "buscar_pedido",
argumentos: '{"pedidoId":1042}',
},
{ tipo: "resposta", texto: "Pedido enviado." },
]);
console.log(concluido);O primeiro passo não produziu uma frase ao cliente. Ele indicou que faltava um
dado externo. A aplicação obteria esse dado, acrescentaria
function_call_output ao contexto e faria a próxima chamada.
A Responses API ocupa o lugar das decisões da fixture
Num projeto novo da OpenAI, use a Responses API. O esqueleto do ciclo fica assim, com histórico carregado de maneira explícita e sem armazenamento remoto:
const input: OpenAI.Responses.ResponseInput = [
{ role: "user", content: pergunta },
];
for (let passo = 1; passo <= maxPassos; passo += 1) {
const response = await client.responses.create({
model: "gpt-5.6-luna",
instructions,
input,
tools,
store: false,
});
input.push(...(response.output as OpenAI.Responses.ResponseInput));
const chamadas = response.output.filter(
(item) => item.type === "function_call",
);
if (chamadas.length === 0) return response.output_text;
// Validar, executar e anexar cada function_call_output.
}Evite começar um projeto novo pela antiga Assistants API. A documentação atual recomenda Responses API para novas integrações e oferece uma migração para quem mantém sistemas anteriores. Isso não significa reescrever uma aplicação em produção sem planejamento; significa não copiar um fluxo legado num projeto que ainda nem começou.
Quando uma chamada aparece, a aplicação executa uma allowlist, confere a
identidade real da sessão e devolve o mesmo call_id:
for (const chamada of chamadas) {
const output = executarFerramenta(
chamada.name,
chamada.arguments,
usuarioIdDaSessao,
);
input.push({
type: "function_call_output",
call_id: chamada.call_id,
output,
});
}O modelo pode selecionar uma ferramenta; ele não decide autorização. Também não entregue uma conexão de banco, um shell genérico ou um cliente HTTP sem restrições apenas para tornar o agente “mais capaz”. Capacidade sem fronteira aumenta a superfície de erro.
Três orçamentos impedem autonomia sem fim
Número de passos é apenas um limite. A execução também deve controlar tempo e operações com efeito:
const limites = {
maxPassos: 4,
tempoTotalMs: 15_000,
maxAcoesComEfeito: 0,
};
console.log(limites);Para um assistente que apenas consulta pedido, zero ações com efeito faz sentido. Se o sistema puder cancelar uma compra, crie uma etapa separada de confirmação, idempotência e auditoria. Não deixe o mesmo prompt que interpreta a intenção autorizar a consequência.
Você também pode limitar ferramentas por etapa. Um classificador não precisa
ver cancelar_pedido; um fluxo de suporte financeiro talvez só possa abrir uma
solicitação para uma pessoa. Privilégio mínimo continua sendo regra de software,
mesmo quando um modelo está no meio.
O loop infinito aparece como erro útil, não como conta surpresa
Nossa função repete a última fixture quando as decisões acabam. Forneça apenas uma chamada de ferramenta, sem resposta final:
try {
executarLoop([
{
tipo: "ferramenta",
nome: "buscar_pedido",
argumentos: '{"pedidoId":1042}',
},
], 3);
} catch (erro) {
console.log({ erro: (erro as Error).message, limite: 3 });
}A mensagem traduz o problema: três decisões ocorreram sem estado final. Em uma integração real, guarde a trajetória, ids de resposta, ferramentas chamadas e motivo da interrupção. Mostre ao usuário uma saída segura, como “não consegui concluir; encaminhei para revisão”, sem expor detalhes internos.
Retentar cegamente o mesmo contexto pode repetir a mesma falha. Uma retentativa precisa de condição: erro transitório, limite de tentativas, intervalo e idempotência. Erro lógico ou falta de permissão deve parar imediatamente.
Quando um agente é exagero
Se a ordem das etapas já é conhecida — validar formulário, consultar pedido, calcular frete e responder — escreva um fluxo determinístico. Ele é mais simples de testar e explicar. Use decisão do modelo quando a linguagem realmente muda o caminho e há mais de uma próxima ação plausível.
Compare as escolhas:
| problema | desenho inicial | motivo |
|---|---|---|
| extrair três campos | uma chamada estruturada | não há ciclo |
| consultar pedido quando citado | modelo + uma ferramenta | caminho depende da mensagem |
| aprovação de reembolso | workflow com humano | consequência financeira |
| pesquisa em várias fontes | agente com limites | próximos passos variam |
Frameworks e SDKs de agentes podem ajudar com tracing, handoffs e orquestração. Adote-os quando essas necessidades aparecerem, não para esconder o loop que a equipe ainda não entende. O tutorial de assistente com Responses API mantém o ciclo visível justamente para você conseguir depurá-lo.
Missão: encerre por falta de permissão
Acrescente uma terceira decisão bloqueio e faça o loop terminar sem chamar
outra ferramenta:
type ResultadoBloqueado = {
passos: string[];
resposta: string;
precisaDeHumano: true;
};
const bloqueado: ResultadoBloqueado = {
passos: ["ferramenta", "bloqueio"],
resposta: "Esta ação precisa de revisão humana.",
precisaDeHumano: true,
};
console.log(bloqueado);O critério de sucesso é objetivo: uma tarefa de consulta ainda conclui; uma
ação financeira encerra em bloqueio; e a fixture que só chama ferramenta
continua falhando no limite. Em seguida, coloque os três caminhos numa
avaliação de aplicações de IA para
que uma mudança de prompt não remova silenciosamente a parada humana.
Perguntas frequentes
O que transforma uma chamada de modelo em agente?
Um agente de IA trabalha sozinho?
Preciso de um framework para criar um agente?
Como impedir um agente de entrar em loop infinito?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
Todo o código deste artigo foi executado em Node 26.3.0 e TypeScript 7.0.2; loop, limites e fixtures executados localmente, sem chamada à API, e as saídas exibidas são as reais — como produzimos este conteúdo.
Fontes consultadas
- OpenAI Docs — Agents — developers.openai.com
- OpenAI Docs — Function calling — developers.openai.com
- OpenAI Docs — Migrate to the Responses API — developers.openai.com
- OpenAI Docs — Safety best practices — developers.openai.com


