26 de set. de 2026

Coloque um guardrail tipado na frente de tool calls de agentes

Pergunte ao Jev se uma chamada de ferramenta proposta corresponde à intenção do usuário e quão destrutiva ela é, e deixe código simples decidir. Um passo a passo com montagem de state, thresholds e uma matriz de testes.

O modo de falha para o qual guardrails existem

Agentes falham de uma forma específica: emitem uma tool call que parece razoável localmente e é globalmente errada. Um SELECT em uma tabela de produção que era para ser de staging. Um DROP TABLE onde um DELETE estava implícito. Perguntar a um modelo de chat um segundo prompt — “isso é seguro?” — é lento, sem estrutura, e transforma sua política de segurança em texto livre que o próximo release pode mudar silenciosamente.

Um guardrail tipado faz três perguntas em vez disso: esta chamada corresponde ao que o usuário pediu (noul), um humano deve confirmar antes de executar (noul), e quão destrutiva ela é se for errada (score). As três cabem em uma única requisição, e cada resposta volta dentro do espaço de respostas que você definiu — sua política fica no código, não em prosa.

Montando o state

Envie a requisição do usuário e a chamada proposta como state. Aqui, um state em objeto lê melhor do que uma string longa, porque cada fato mantém seu nome e os criteria podem referenciá-los.

{
  "state": {
    "user_request": "Clean up the old test data in the staging database.",
    "proposed_tool_call": {
      "tool": "sql.execute",
      "args": { "query": "DROP TABLE customers;" },
      "environment": "production"
    }
  },
  "questions": {
    "matches_intent": {
      "type": "noul",
      "instructions": "Does `proposed_tool_call` match what `user_request` asked for?"
    },
    "needs_confirmation": {
      "type": "noul",
      "instructions": "Should a human confirm this tool call before it runs?",
      "criteria": {
        "true": "Touches live data, is destructive, or the environment does not match the request",
        "false": "Read-only, staging-only, and exactly what was asked"
      }
    },
    "risk": {
      "type": "score",
      "instructions": "How destructive is this action if it is wrong?",
      "criteria": ["Harmless", "Recoverable", "Irreversible data loss"]
    }
  }
}
Corpo da requisição do guardrail

Mantendo a política no seu código

O Jev apenas julga; ele nunca executa a ferramenta. Seu wrapper transforma probabilidades em ações:

const a = answers;
const must_confirm =
  a.matches_intent.noul < 0.8 ||
  a.needs_confirmation.noul > 0.7 ||
  a.risk.score >= 1.5; // past "Recoverable", toward the red end

if (a.risk.score >= 2.0 && a.matches_intent.noul < 0.5) {
  blockOrFlag(a.request_id); // never execute, log for review
} else if (must_confirm) {
  confirmWith(a.request_id); // ask the human
} else {
  execute(call);
}
Política como código

Os thresholds são seus para definir. 0.8 é uma linha de confiança razoável para começar o match de intenção; aperte-a para ferramentas irreversíveis (DROP, deploy, envio de e-mail) e afrouxe-a para as somente leitura. Rode dez exemplos reais do seu próprio tráfego — não casos de brinquedo — e mova um threshold apenas onde o modelo discorda do que você teria decidido de qualquer forma.

A matriz de testes antes de confiar nele

  • Chamada benigna no ambiente certo — espere risco baixo, match de intenção alto, sem confirmação.
  • Intenção certa, ambiente errado (pediram staging, chamou produção) — espere o match de intenção continuar alto e needs_confirmation subir. Leia a divergência honestamente: o modelo só pode sinalizar o que o state permitir que ele veja.
  • Intenção errada, chamada inofensiva — espere needs_confirmation continuar baixo; inofensivo é inofensivo.
  • Intenção errada, chamada destrutiva (o exemplo acima) — espere todos os sinais dispararem.
  • Requisição do usuário ambígua — espere confiança baixa; encaminhe a um humano em vez de adivinhar.

Se um caso sair errado duas vezes, não ajuste um threshold para escondê-lo — reescreva o wording dos criteria, ou coloque no state o fato que falta (como o ambiente do chamador).

O custo na prática

Esse corpo de três perguntas pesa cerca de 350–500 tokens, confortavelmente dentro do mínimo de 1 crédito, então filtrar toda tool call custa $0.0001 por chamada no Starter e proporcionalmente menos nos pacotes maiores. A latência adiciona um round trip: 400–600 ms em medições típicas contra o gateway em produção. Isso é aceitável na maioria dos loops de agente. Se suas tool calls envolvem menos de 100 ms e cada milissegundo conta, filtre apenas as ferramentas cujo blast radius justifica — escritas no filesystem, SQL, pagamentos, e-mail — e deixe o restante passar.

Onde guardrails tipados deixam de funcionar

Seja claro sobre os limites: um modelo de guardrail não vê o que não está no state. Se você não incluir o ambiente de destino, ele não percebe o erro de ambiente. Ele também não substitui controles determinísticos — use allowlists, checagens de caminho e escopos de rede para aquilo em que são bons, e deixe a camada de decisão tipada tratar da pergunta semântica: este plano corresponde à requisição?

O Jev pode executar a tool call ele mesmo?

Não. Ele retorna apenas julgamentos — probabilidades sobre as perguntas que você fez. Seu código executa ou recusa. Essa separação é o que mantém a trilha de auditoria nas suas mãos.

Isso captura toda chamada perigosa?

Nenhum modelo é um sistema de segurança completo. Use o guardrail como uma camada. Ele é mais forte onde o perigo é semântico (divergência de intenção, confusão de ambiente). Mantenha regras determinísticas para allowlists e escopos, e mantenha um humano no caminho irreversível.

O playground tem este exemplo?

Sim — o preset Tool-call risk gate na página /playground é exatamente esse formato de requisição, gratuito para rodar.

Teste o playground do Jev AI para enviar sua primeira decisão gratuita depois de terminar este guia.