Funcionar é uma coisa. Operar em produção é outra.
O mapa das 10 peças do vídeo, na ordem de construção. As 6 primeiras fazem o agente funcionar; as 4 últimas deixam ele operar em produção. Marque o que o seu já tem: o que ficar em branco é por onde começar. No fim, quatro blocos de código pra copiar.
01System prompt
Vai em toda chamada, antes de qualquer pedido.
02Decisão
O caminho sai de opções fixas, sem o modelo grande improvisando.
03Tool calling
O modelo pede, com nome e argumentos. Quem executa é o seu código.
04Memória
O modelo não lembra de nada. Quem lembra é o sistema.
05Orquestração
Um orquestrador divide o pedido entre especialistas.
06Humano no loop
Ação irreversível nunca sai sozinha.
07Observabilidade
Quando quebra, você segue o fio até o passo exato.
08Evals
Trocou de modelo? Os evals dizem se piorou.
09Guardrails e segurança
Conteúdo de fora é dado, nunca instrução.
10Retry, timeout e idempotência
A API do financeiro vai cair. Planeje pra isso.
11Exceção não vira regra
O erro do João: a exceção de julho foi lida como regra permanente. Aqui ela vale só para o pedido dela e até a validade. O teto fica no código.
// A regra mora no código. Exceção só vale para o pedido dela e dentro da validade.
export function limiteDe(politica, excecoes, pedido, hoje = new Date()) {
const valida = excecoes.find((e) => e.pedidoId === pedido.id && new Date(e.validaAte) >= hoje);
return valida ? valida.limite : politica.teto;
}
export const precisaAprovar = (pedido, limite) => pedido.valor > limite;
12Decisão por opção fixa
Liste as opções do seu fluxo. Confiança abaixo do limiar ou opção fora da lista vai para uma pessoa.
// O classificador devolve uma opção fixa e a confiança. Quem decide o caminho é o código.
const OPCOES = ["reembolso", "duvida", "fora_de_escopo"];
export function rota({ opcao, confianca }, limiar = 0.8) {
if (!OPCOES.includes(opcao) || confianca < limiar) return "pessoa";
return opcao;
}
13Loop com limite
Ligue ao seu modelo: ele recebe as mensagens e devolve custo, mais o texto final ou a ferramenta pedida.
// O modelo pede a ferramenta, o seu código executa. O loop guarda turnos e gasto e para no limite.
export async function rodar(modelo, ferramentas, mensagens, { maxTurnos = 10, maxUSD = 0.5 } = {}) {
let gasto = 0;
for (let turno = 1; turno <= maxTurnos; turno++) {
const r = await modelo(mensagens);
gasto += r.custo;
if (!r.ferramenta) return r.texto;
if (gasto > maxUSD) throw new Error("limite de gasto");
const executar = ferramentas[r.ferramenta.nome];
if (!executar) throw new Error(`ferramenta fora da lista: ${r.ferramenta.nome}`);
mensagens.push({ papel: "ferramenta", conteudo: await executar(r.ferramenta.argumentos) });
}
throw new Error("limite de turnos");
}
14Pagamento sem duplicar
A chave só protege se a API de destino aceitar idempotência: confira antes e mande a chave no formato dela.
// Timeout corta a espera, retry tenta de novo, e a mesma chave impede pagar duas vezes.
export async function pagar(pedido, chamarApi, tentativas = 3) {
const chave = `reembolso-${pedido.id}`; // igual em todas as tentativas
for (let i = 1; ; i++) {
try {
return await chamarApi(pedido, { chave, signal: AbortSignal.timeout(5000) });
} catch (e) {
if (i >= tentativas || e.status < 500) throw e; // erro 4xx não melhora repetindo
await new Promise((r) => setTimeout(r, 2 ** i * 200));
}
}
}