Como usar o Jev para decidir qual ferramenta o agente vai chamar?

diagrama mostrando o Jev decidindo qual ferramenta o agente de IA vai chamar
Resposta rápida

O Jev é o primeiro modelo público da classe System One da TypeSafe AI: ele avalia um estado e devolve resposta tipada com probabilidades e confiança, em vez de gerar texto. Para roteamento de ferramentas, isso vira uma pergunta do tipo choice: você define as opções antes, monta o state como objeto JSON, faz um POST em https://api.typesafe.ai/v1/systemone e lê choice, probabilities e confidence. Com uma opção de escape tipo "other" e um limiar de confiança por risco, o dispatcher para de torcer pra string bater e passa a decidir com número na mão

Fala aí, beleza? Roteamento de ferramenta em agente ainda é, na maioria dos projetos, uma aposta: o LLM escreve o nome da tool em texto livre e o dispatcher fica torcendo pra string bater com alguma chave do dicionário

Quando bate, ótimo

Quando não bate, você descobre em produção, com um KeyError bonito no log ou, pior, com a ferramenta errada rodando calada

O Jev ataca exatamente essa etapa. Ele é o primeiro modelo público da classe System One da TypeSafe AI: avalia um estado e devolve respostas tipadas com probabilidades e confiança, em vez de gerar texto

Ou seja: a escolha da ferramenta deixa de ser uma frase que você precisa parsear e vira uma decisão entre opções que VOCÊ definiu antes, com um número dizendo o quanto o modelo confia naquilo

Bora montar isso na prática?

O que você precisa antes de começar

A barreira de entrada aqui é baixa. O Jev entrou em early access com lista de espera em 15 de setembro de 2026, mas a waitlist já foi removida e o acesso está aberto pra qualquer pessoa: cadastro no console em console.typesafe.ai, com crédito inicial gratuito de US$ 5

O checklist é curto:

  • Conta criada no console e uma chave de API gerada
  • A chave exportada na variável de ambiente TYPESAFE_API_KEY (é dela que o client dos SDKs oficiais lê)
  • SDK instalado, se você for usar um: pip install typesafe-sdk (ou uv add typesafe-sdk), que pede Python >= 3.10, ou npm install @typesafe-ai/sdk no lado JS/TS

Sobre custo, pra você dimensionar o roteador antes de sair plugando em tudo: o Jev cobra US$ 0,042 por milhão de tokens de entrada, e os tokens de saída não são cobrados

Isso muda o cálculo mental. Um roteador que roda em TODO turno do agente normalmente é a parte que você tem medo de deixar ligada… aqui o peso vai quase todo pro tamanho do state e das perguntas que você manda

Passo a passo: roteando a chamada de ferramenta com uma pergunta choice

A API do System One expõe três tipos de pergunta, definidos pelo campo type: noul (sim/não), choice (uma opção de um conjunto) e score (posição em uma escala)

Os três compartilham type e instructions, e cada um acrescenta o seu próprio criteria

Domine o Jev e coloque decisões de IA dentro do seu sistema
Pré-inscrição Curso Jev

Domine o Jev e coloque decisões de IA dentro do seu sistema

Você vai aprender a usar o Jev, o System One Model da TypeSafe AI, pra automatizar decisões com resposta tipada e confiança medida, sem depender de chat nem de alguém revisando cada passo. Entre na lista de espera para garantir a condição de lançamento!

Pro nosso caso, escolha de ferramenta é choice. Sempre

  1. Desenhe o state como objeto JSON, com subcampos nomeados

O campo state aceita string, objeto JSON ou array de textos. A documentação recomenda objeto na maioria das requisições, pra cada parte do estado ter um nome descritivo, e orienta passar os subcampos relevantes em vez de serializar tudo em um template de string

{
  "user_turn": "quanto eu gastei em anuncios no mes passado?",
  "last_tool_used": "none",
  "user_plan": "pro",
  "has_connected_billing_account": true
}

O erro comum deste passo: pegar todo o histórico da conversa, jogar dentro de uma f-string gigante e mandar como state. Além de ficar caro à toa, o Jev 1.13 degrada quando há contexto irrelevante no state. Nomeie e recorte

  1. Escreva a pergunta choice com instructions e criteria

Em um Choice, criteria é um mapa de opção para descrição do critério. A documentação permite passar null quando a opção não precisa de detalhe extra, mas guarde isso pra opção realmente autoexplicativa

Opção de escape (aquela que recebe tudo que não coube nas outras) é o caso oposto: ela SEMPRE merece descrição, e eu volto nesse ponto mais pra frente

"tool": {
  "type": "choice",
  "instructions": "Qual ferramenta atende o pedido do usuario neste turno?",
  "criteria": {
    "get_ad_spend": "Gasto com anuncios em um periodo, por conta ou campanha",
    "get_invoice": "Faturas emitidas, segunda via, status de pagamento",
    "search_docs": "Duvida conceitual sobre o produto, sem dado de conta",
    "handoff_human": "Pedido fora do escopo das ferramentas ou que exige atendimento humano"
  }
}

O erro comum deste passo: encurtar a lista de opções achando que "menos é mais". Um Choice aceita até 255 opções, e cada opção adicional custa poucos tokens, então a própria documentação orienta passar a lista completa em vez de uma lista curta. Se o agente tem 40 tools, mande as 40

  1. Monte a requisição

O endpoint é POST https://api.typesafe.ai/v1/systemone, com header Authorization: Bearer <API_KEY> e Content-Type: application/json. O corpo tem três campos: state, model e questions

O questions é um mapa de perguntas tipadas cujas chaves você escolhe, e as respostas voltam sob as mesmas chaves. Isso é ótimo: o nome da chave é o seu contrato

curl -X POST https://api.typesafe.ai/v1/systemone \
  -H "Authorization: Bearer $TYPESAFE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "jev-latest",
    "state": {
      "user_turn": "quanto eu gastei em anuncios no mes passado?",
      "user_plan": "pro",
      "has_connected_billing_account": true
    },
    "questions": {
      "tool": {
        "type": "choice",
        "instructions": "Qual ferramenta atende o pedido do usuario neste turno?",
        "criteria": {
          "get_ad_spend": "Gasto com anuncios em um periodo, por conta ou campanha",
          "get_invoice": "Faturas emitidas, segunda via, status de pagamento",
          "search_docs": "Duvida conceitual sobre o produto, sem dado de conta",
          "handoff_human": "Pedido fora do escopo das ferramentas ou que exige atendimento humano"
        }
      }
    }
  }'

No campo model, jev-latest é o padrão do SDK, e IDs versionados como jev-1.13.0 também são aceitos. Em roteador de produção eu fixaria a versão, pra sua decisão não mudar de comportamento sem você saber

O erro comum deste passo: ignorar que a ordem das opções faz parte do que o Jev enxerga. Reordenar as opções pode mudar a resposta, então não gere esse mapa a partir de um set do Python nem de um dicionário que você monta em ordem aleatória a cada deploy

  1. Leia a resposta

A resposta de um Choice traz a opção escolhida em choice, a distribuição de probabilidade em probabilities (uma por opção) e um confidence entre 0 e 1 para a opção selecionada

{
  "tool": {
    "choice": "get_ad_spend",
    "probabilities": {
      "get_ad_spend": 0.91,
      "get_invoice": 0.05,
      "search_docs": 0.02,
      "handoff_human": 0.01
    },
    "confidence": 0.91
  }
}

Repare na soma do exemplo: ela não fecha em 1.0, e isso não é bug

O erro comum deste passo: tratar probabilities como se fosse uma distribuição fechada. A própria TypeSafe documenta que probabilidades complementares não somam necessariamente 1.0 no Jev 1.13, então não escreva lógica do tipo "pego 1 menos a soma das outras"

  1. Despache

Agora o dispatcher fica chato de tão simples: ele recebe uma chave que só pode ser uma das que você mesmo escreveu

HANDLERS = {
    "get_ad_spend": get_ad_spend,
    "get_invoice": get_invoice,
    "search_docs": search_docs,
    "handoff_human": handoff_human,
}

answer = response["tool"]
HANDLERS[answer["choice"]](state)

Sem regex, sem "será que ele escreveu get_ad_spend ou getAdSpend?", sem retry porque o modelo inventou uma tool que não existe

Como tratar o caso em que nenhuma ferramenta serve

Esse é o ponto onde a maioria dos roteadores quebra na vida real. O usuário manda "bom dia", ou pede uma coisa que nenhuma tool cobre, e o modelo escolhe a opção menos pior porque você o obrigou a escolher alguma

  1. Coloque uma opção de escape na lista

A documentação do Choice recomenda adicionar uma opção do tipo other ou none of the above quando a lista pode não cobrir todos os casos, justamente pro modelo poder dizer que nenhuma das outras serve

"criteria": {
  "get_ad_spend": "Gasto com anuncios em um periodo",
  "get_invoice": "Faturas, segunda via, status de pagamento",
  "search_docs": "Duvida conceitual, sem dado de conta",
  "none": "Nenhuma ferramenta se aplica: conversa social, ruido ou pedido fora de escopo"
}

O erro comum deste passo: deixar o escape sem descrição. Lembra do null lá do passo 2? Ele serve pra opção autoexplicativa, não pra essa aqui. Um other com null vira lixeira de tudo que o modelo não entendeu. Descreva QUANDO ele deve ser usado

  1. Corte por confiança

O padrão documentado de roteamento por intenção é direto: cheque o confidence da escolha e roteie para um humano quando estiver abaixo de um limiar (o exemplo da documentação usa 0.5), senão despache pro handler correspondente

answer = response["tool"]

if answer["confidence"] < 0.5:
    handoff_human(state)
else:
    HANDLERS[answer["choice"]](state)
  1. Use um limiar por ação, proporcional ao risco

Um limiar único pra tudo é preguiça. A documentação traz o padrão de limiar por ação: mais baixo pra ações reversíveis e somente leitura, mais alto pra ações arriscadas

Tipo de ação Comportamento sugerido na documentação
Reversível / somente leitura Limiar mais baixo, despacha direto
Arriscada Abaixo de 0.6, roteia para humano
Alto risco Abaixo de 0.5 vai para humano, acima de 0.9 segue com confirmação, no meio verifica antes

É a mesma lógica de graduar decisão por consequência que aparece quando você usa decisão tipada para qualificar leads: errar pra baixo custa pouco, errar pra cima custa caro

  1. Escale o caso incerto, não force o palpite

A documentação de arquitetura recomenda escalar casos incertos para uma pessoa ou para um modelo de raciocínio mais caro

E recomenda testar os limiares plotando confiança contra acurácia nos seus próprios dados. Não herde o 0.5 do exemplo e durma tranquilo: o número certo é o que sai do SEU histórico

Escolher a ferramenta e os argumentos na mesma requisição

Aqui fica mais interessante. O cookbook oficial de function calling mostra que a escolha da função E os argumentos de conjunto fechado podem virar perguntas tipadas com confiança, tudo na mesma ida

O desenho é: uma requisição carrega a escolha da função e os argumentos de TODAS as funções, e o dispatcher lê apenas as respostas da função escolhida

Parece desperdício? É o contrário de um round-trip extra por argumento

  1. Mapeie cada argumento de conjunto fechado pro primitivo certo

O cookbook mapeia argumento de conjunto fechado como choice (um valor), set (vários valores) ou flag (booleano)

Se o argumento é texto aberto, ele não cabe nesse desenho: lembre que o Jev não gera texto

  1. Monte o mapa de perguntas com prefixo por função

Como as chaves de questions são suas, dá pra namespacear e depois filtrar só o ramo vencedor

chosen = response["function"]["choice"]
args = {
    key.split(".", 1)[1]: value["choice"]
    for key, value in response.items()
    if key.startswith(f"{chosen}.")
}
  1. Leia a confiança por argumento, não só a da função

O exemplo numérico do cookbook deixa isso claro: a frase "is amd tracking nvidia lately" retorna rolling_correlation(symbol='AMD', benchmark='NVDA') com confiança geral de 0.82, sendo p 0.87 para symbol igual a ‘AMD’ e p 0.78 para benchmark igual a ‘NVDA’

Repare que a confiança não é uniforme dentro da mesma chamada. Dá pra confirmar só o argumento fraco em vez de descartar a chamada inteira

O erro comum deste passo: tentar preencher todo argumento opcional na marra. No cookbook, argumentos opcionais que não foram citados simplesmente ficam de fora, e a função usa seus defaults

Quando o catálogo de ferramentas é grande: o padrão de duas requisições

Mandar 40 tools em um Choice é tranquilo. E quando o roster tem centenas?

O cookbook oficial de sugestão de skill descreve exatamente o problema do roteamento por lista de nomes e propõe um padrão de duas requisições:

  1. Primeira requisição: ranqueia o roster inteiro e pergunta se precisa de alguma skill

No exemplo do cookbook são 182 skills. A primeira ida ranqueia todas e também responde se o turno precisa de alguma skill

  1. Segunda requisição: relê só o topo, com descrição completa

A segunda pega apenas as três primeiras, agora com a descrição completa de cada uma, e pode rejeitar todas

O resultado publicado no cookbook: as cargas de skill errada e as cargas quando nada serve caem mais da metade

  1. Respeite o orçamento de contexto

Esse padrão existe porque contexto não é infinito. No Jev 1.13, 64k cobre o state somado a todas as perguntas, e 32k se aplica ao state somado à pergunta mais longa

Ou seja: a lista completa de nomes curtos cabe fácil na primeira requisição, as descrições longas só cabem quando você já filtrou o topo. O padrão de duas idas não é firula, é o jeito de caber

Onde esse roteamento tipado já está plugado (Pydantic AI, LangChain, Cloudflare)

Se você não quer montar o cliente na mão, já tem caminho pronto em três lugares bem diferentes da stack

Pydantic AI

O Pydantic AI tem integração oficial com o Jev via TypeSafeModel, instalada pelo extra typesafe, que traz a dependência typesafe-sdk

O perfil do modelo declara supports_text_output=False, o que é honesto e evita que você tente usar aquilo como chat

O detalhe elegante: um output_type com vários tipos estruturados funciona como conjunto de rotas, e uma segunda requisição pergunta apenas os campos do tipo escolhido. É o mesmo padrão de duas idas, só que embrulhado

LangChain

A LangChain publicou um middleware que usa o Jev dentro do harness do agente: o AutoModeMiddleware usa o Jev pra checar chamadas de ferramenta arriscadas e bloquear a chamada antes da execução

Tome cuidado com um detalhe importante: ele RECUSA, não pede aprovação. Se o que você quer é aprovação humana, precisa combinar com o human-in-the-loop middleware

No mesmo material, a LangChain reporta os números divulgados pela TypeSafe: inferência até 200x mais rápida e custo até 400x menor que LLMs comparáveis em tarefas de classificação

Cloudflare

O Jev também está na plataforma de IA da Cloudflare, com model ID typesafe/jev, invocável pelo binding AI com env.AI.run(). A versão documentada lá é jev-1.13.0

Pra quem já roda o agente em Worker, isso tira uma chamada externa do caminho

Limites: onde o roteamento pelo Jev quebra

Agora a parte honesta, porque esse desenho tem custo

A própria TypeSafe publica as fraquezas do Jev 1.13, e várias delas batem em cheio em roteamento de ferramenta:

  • Leitura literal: negações, escopo e condições implícitas são lidas ao pé da letra. "Não quero ver a fatura, quero o gasto" é o tipo de turno que merece teste
  • Matemática e contagem fracas, e ordenação de datas fraca. Se a sua escolha de tool depende de comparar períodos, não delegue a comparação pro modelo
  • Indireção multi-hop é difícil pra ele
  • Degradação com contexto irrelevante no state: mais um motivo pra recortar o state em vez de despejar a conversa inteira
  • Quebra sob instruções contraditórias
  • Sem geração de texto: argumento de texto livre não sai daqui
  • Probabilidades complementares que não somam necessariamente 1.0

E tem o ponto de segurança, que é o mais sério quando você usa isso como guarda. Conteúdo escrito de forma adversarial pode mover a resposta: injeção de instrução, enquadramento enganoso ou texto que argumenta pela própria classificação conseguem mudar a decisão

Soma isso ao fato de que reordenar as opções pode mudar a resposta, e a conclusão é uma só: uma guarda construída sobre o Jev fica AO LADO de checagens determinísticas, nunca no lugar delas

Vale a pena onde? Roteamento de conjunto fechado, com opções que você define, com confiança lida e limiar por risco. É o mesmo terreno de quando o Jev é usado pra moderar conteúdo enviado por usuários: decisão repetitiva, opções conhecidas, volume alto

Não vale a pena onde? Quando a decisão exige contar, calcular, ordenar data, seguir uma cadeia de referências ou produzir texto. Aí o trabalho continua sendo de outro modelo, e o Jev no máximo decide se vale chamar esse outro modelo

Conclusão

O resumo do desenho cabe em quatro linhas:

  • state como objeto JSON com subcampos nomeados, sem contexto irrelevante
  • lista COMPLETA de opções no Choice, em ordem estável (cabem até 255)
  • uma opção de escape tipo other ou none, com descrição de quando usar
  • limiar de confiança por ação, proporcional ao risco, com escalonamento pra pessoa ou pra um modelo mais caro

Próximo passo concreto: cria a conta no console, que o acesso está aberto e vem com US$ 5 de crédito grátis, e porta UM único ponto de roteamento do seu agente, não o agente inteiro

Deixa rodar, guarda o confidence junto com o acerto real, plota um contra o outro e só depois escolhe o seu limiar de verdade

Aí sim tu expande pro resto…

até o próximo post! 😀

Perguntas frequentes

Como decidir o limiar de confiança pra rotear pra um humano em vez da ferramenta escolhida pelo Jev?

A documentação traz um padrão de corte: você checa o confidence da escolha e, se estiver abaixo de um limiar, exemplo 0.5, roteia pra humano em vez de disparar a ferramenta. Esse limiar não é fixo pra todo caso, ele deve ser proporcional ao risco da ação. Ações reversíveis e de somente leitura aceitam limiares mais baixos, enquanto ações arriscadas pedem limiares mais altos, como abaixo de 0.6 rotear pra humano, ou abaixo de 0.5 rotear e acima de 0.9 seguir com confirmação em ação de alto risco. A recomendação é testar isso nos seus próprios dados, plotando confiança contra acurácia.

Dá pra usar o Jev pra escolher os argumentos da ferramenta, não só o nome dela?

Sim, esse é justamente o padrão do cookbook oficial de function calling da TypeSafe. A escolha da função e os argumentos de conjunto fechado viram perguntas tipadas com confiança, tudo numa única requisição que carrega a escolha da função e os argumentos de todas as funções disponíveis. O dispatcher lê só as respostas da função que foi escolhida, e argumentos de conjunto fechado são mapeados como choice para um valor, set para vários valores, ou flag pra booleano.

O Jev funciona bem quando o agente tem centenas de ferramentas ou skills pra escolher?

A TypeSafe documenta esse cenário no cookbook de sugestão de skill, com um roster de 182 skills no exemplo. O padrão é duas requisições: a primeira ranqueia todas as skills e responde se o turno precisa de alguma delas, e a segunda relê apenas as três primeiras com descrição completa, podendo até rejeitar todas. Segundo o cookbook, esse desenho reduz mais da metade das cargas de skill erradas e das cargas quando nada realmente serve.

O Jev substitui checagem de segurança antes de rodar uma ferramenta arriscada?

Não, e a própria TypeSafe é clara sobre isso: conteúdo escrito pra direcionar o modelo de forma adversarial pode mover a resposta, então uma guarda construída sobre o Jev deve ficar ao lado de checagens determinísticas, nunca no lugar delas. A LangChain segue essa linha no AutoModeMiddleware, que usa o Jev pra checar chamadas de ferramenta arriscadas e bloquear a execução antes dela acontecer. Só que ele recusa, não pede aprovação, então se você quer aprovação humana precisa combinar com o middleware de human-in-the-loop.

Existe alguma ferramenta ou framework de agente com integração pronta pro Jev?

Sim, o Pydantic AI tem integração oficial via TypeSafeModel, instalada pelo extra typesafe, que já traz a dependência typesafe-sdk. O perfil desse modelo declara supports_text_output=False, então quando você passa um output_type com vários tipos estruturados, isso funciona como um conjunto de rotas, e uma segunda requisição pergunta só os campos do tipo que foi escolhido. O Jev também está disponível na plataforma de IA da Cloudflare, com model ID typesafe/jev, invocável pelo binding AI usando env.AI.run(), na versão jev-1.13.0.

Quais os limites de contexto que preciso respeitar ao montar o state e as perguntas pro Jev 1.13?

O orçamento documentado pro Jev 1.13 é de 64k somando o state com todas as perguntas da requisição, e de 32k somando o state com a pergunta mais longa isoladamente. Vale lembrar que o modelo degrada quando há contexto irrelevante no state, então o limite técnico não é motivo pra empurrar histórico inteiro de conversa pra dentro do campo.



Escrito por | Matheus Battisti

Matheus Battisti
Fundador da Hora de Codar

Programador apaixonado pelo mundo das tecnologias, sempre buscando em aprender e se aprofundar em linguagens, frameworks e o que mais for necessário para executar um bom trabalho. Agora tem uma nova missão que é de passar seu conhecimento adiante para formar novos programadores e especializar mais os que já são.

Subscribe
Notify of
guest

0 Comentários
Oldest
Newest Most Voted

Formações

Formação Claude Code

Formação Claude Code

Domine Claude Code do absoluto zero até o avançado

  • 120 aulas
  • 4 projetos
  • 9h 45min

Blog | Mais populares