@andreias.ai · por baixo do capô

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));
    }
  }
}