Como usar o Jev para escolher qual técnico atende cada chamado de campo?

O Jev é o modelo System One da TypeSafe AI: você manda um estado e perguntas tipadas, ele devolve valores tipados com probabilidade e confiança, sem texto solto. Para despacho de campo isso vira uma pergunta Choice: o state descreve o chamado e as equipes candidatas, o criteria descreve cada equipe, e a resposta traz a equipe escolhida, a probabilidade de cada uma e a confiança. Filtro duro (equipe de folga, fora de área) fica em código, o Jev entra só no julgamento, e a confiança decide se a decisão segue automática ou vai pra um humano.
Despacho de campo é aquele lugar onde ou existe uma planilha e um coordenador decidindo na mão, ou existe uma regra rígida do tipo "pega a equipe mais próxima" que ignora metade do contexto do chamado
E aí você fica preso entre um humano que não escala e um if que não julga
O Jev é o modelo System One da TypeSafe AI, e ele resolve exatamente esse meio de campo: em vez de gerar texto, ele recebe um estado e perguntas tipadas e devolve valores tipados, com distribuição de probabilidade e confiança
Ou seja: a saída não é um parágrafo bonito que você precisa parsear, é um valor dentro do conjunto que VOCÊ definiu, pronto pro software consumir
Neste post a gente monta a decisão de despacho do zero, trata o caso chato de quando a lista de equipes muda, e fecha com o que gravar pra auditoria
Bora? 🙂
O que você precisa antes de começar
Pouca coisa, se liga:
- Chave de API, criada no
console.typesafe.ai. Desde 21 de setembro de 2026 o acesso está aberto pra todo mundo, sem lista de espera - Variável de ambiente
TYPESAFE_API_KEY, que é de onde o cliente do SDK Python lê a chave - SDK Python instalado, com
pip install typesafe-sdkouuv add typesafe-sdk - A lista de equipes vindo do seu sistema de escala, não de um arquivo estático copiado na mão
pip install typesafe-sdk
export TYPESAFE_API_KEY="sua-chave-aqui"
E aqui vai o princípio que segura o projeto inteiro em pé, direto da orientação da TypeSafe: fluxo de controle, regra determinística e efeito colateral ficam em código, e o System One entra só onde existe julgamento de verdade
Traduzindo pro despacho: equipe de folga, equipe fora da área de atendimento, equipe sem a certificação obrigatória pro tipo de serviço… tudo isso é filtro duro, é WHERE no seu banco
O Jev não deve "adivinhar" que a equipe está de folga, ele deve escolher entre as que JÁ estão elegíveis
Tome cuidado com isso, é o erro número um de quem começa: jogar a escala inteira no state e esperar que o modelo respeite regra de negócio

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!
Passo a passo: montando a decisão de despacho no Jev
- Modele o state com o chamado e as equipes candidatas
O state é o que dá contexto ao modelo, e ele aceita tanto texto puro quanto dados estruturados (objeto ou lista)
Pra despacho, estruturado é bem mais confortável, porque você já tem os dados no formato de registro:
state = {
"chamado": {
"id": "CH-48211",
"tipo_servico": "reparo de elevador",
"descricao": "Cabine parada entre andares, sem passageiros presos",
"prioridade_contratual": "SLA 4h",
"endereco": "Centro, Sao Paulo - SP",
"abertura": "2026-09-24T09:12:00-03:00"
},
"equipes_candidatas": [
{"id": "EQ-01", "especialidade": "elevadores", "distancia_km": 3.2,
"chamados_na_fila": 1, "certificacoes": ["NR-12", "NR-35"]},
{"id": "EQ-07", "especialidade": "elevadores", "distancia_km": 11.8,
"chamados_na_fila": 0, "certificacoes": ["NR-12"]},
{"id": "EQ-14", "especialidade": "geral predial", "distancia_km": 1.4,
"chamados_na_fila": 3, "certificacoes": ["NR-35"]}
]
}
O erro comum deste passo: mandar o histórico inteiro da empresa no state achando que "mais contexto é melhor". A cobrança do Jev é por token de entrada (US$ 0,042 por 1 milhão de tokens de entrada, com saída gratuita), então state inchado custa dinheiro e ainda dilui o que importa pra decisão
- Escreva a pergunta Choice com criteria descrevendo cada equipe
Choice seleciona uma única opção do conjunto definido em criteria, e aceita de 1 até 255 opções
O pulo do gato tá na descrição: cada opção do criteria pode ser string, objeto ou lista. Usando objeto, tu consegue dizer o que a equipe cobre, o que ela NÃO cobre e dar exemplos:
questions = {
"equipe_despacho": {
"type": "Choice",
"instructions": (
"Escolha a equipe que deve atender este chamado, "
"considerando especialidade, distancia e fila atual"
),
"criteria": {
"EQ-01": {
"cobertura": "Especialista em elevadores, certificada NR-12 e NR-35",
"exclusoes": "Nao atende rede eletrica de media tensao",
"exemplos": "Cabine travada, porta desalinhada, troca de cabo"
},
"EQ-07": {
"cobertura": "Especialista em elevadores, fila vazia no momento",
"exclusoes": "Sem NR-35, nao faz trabalho em altura externa",
"exemplos": "Manutencao preventiva, diagnostico de painel"
},
"EQ-14": {
"cobertura": "Generalista predial, mais proxima do endereco",
"exclusoes": "Nao faz reparo estrutural de elevador",
"exemplos": "Hidraulica, alvenaria leve, iluminacao"
}
}
}
}
Repara que a chave de cada opção é o ID da equipe, não o nome bonitinho
Isso é de propósito: a resposta volta com essa chave, e tu quer algo que casa direto com o seu banco, sem tabela de tradução no meio
O erro comum deste passo: descrever só o que a equipe faz e esquecer as exclusões. É a exclusão que impede o modelo de mandar a equipe generalista pro reparo estrutural só porque ela está a 1,4 km
- Monte o body e chame a API
A API do System One tem um endpoint só: POST https://api.typesafe.ai/v1/systemone, com headers de Authorization e Content-Type, e corpo com state, model e questions
No cru fica assim:
curl -X POST https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"state": { "chamado": { "id": "CH-48211" } },
"model": "jev-latest",
"questions": { "equipe_despacho": { "type": "Choice", "criteria": {} } }
}'
E no SDK Python, que é onde tu vai viver de verdade:
from typesafe import TypeSafeClient
client = TypeSafeClient() # le TYPESAFE_API_KEY do ambiente
resposta = client.system_one(
state=state,
questions=questions,
)
Tem cliente síncrono (TypeSafeClient) e assíncrono (AsyncTypeSafeClient), então se o teu despacho roda dentro de um worker assíncrono, é só trocar a classe
O model padrão do SDK é jev-latest, mesmo valor que aparece nos exemplos da doc
O erro comum deste passo: mandar a chave hardcoded no código porque "é só um teste". Credencial esquecida em script de teste tem a mania de virar produção sozinha 😅
- Leia a resposta sob a mesma chave da pergunta
Aqui o negócio fica bonito: questions é um mapa em que VOCÊ escolhe cada chave, e as respostas voltam sob as mesmas chaves
A resposta do endpoint traz model, answers e usage (com usage.input_tokens e usage.output_tokens)
E a resposta de um Choice traz a opção escolhida, uma probabilidade pra CADA opção e um valor de confiança:
decisao = resposta.answers["equipe_despacho"]
equipe = decisao.choice # ex: "EQ-01"
probabilidades = decisao.probabilities # probabilidade por opcao
confianca = decisao.confidence
modelo_usado = resposta.model # ID versionado do modelo que respondeu
A distribuição de probabilidade não é enfeite, viu? Ela é o que te conta se a escolha foi tranquila ou se o modelo ficou dividido entre duas equipes quase empatadas
O erro comum deste passo: usar só a opção escolhida e jogar probabilidade e confiança no lixo. Aí tu perdeu justamente o sinal que diferencia um modelo de decisão tipada de um if com ares de IA
- Aplique o roteamento por confiança em código
A documentação chama isso de confidence-gated routing: tu divide a confiança em faixas, alta age automaticamente, média age com cautela, baixa não age, e os casos incertos escalam pra uma pessoa ou pra um modelo de raciocínio mais caro
Detalhe importante: esse escalonamento é lógica da SUA aplicação sobre a probabilidade retornada, não uma nova chamada à API
if confianca >= FAIXA_ALTA:
despachar(equipe, chamado)
elif confianca >= FAIXA_MEDIA:
despachar(equipe, chamado, exige_confirmacao=True)
else:
enviar_para_coordenador(chamado, sugestao=equipe, probabilidades=probabilidades)
E o limiar não é um número universal: ações diferentes dentro do mesmo sistema devem ter limiares diferentes, conforme a consequência do erro
Mandar a equipe errada num preventivo de rotina custa uma viagem perdida
Mandar a equipe errada num chamado de emergência com risco de vida custa MUITO mais
Então o limiar de cada um tem que ser diferente, e quem define isso é o teu time, não a doc
Esse mesmo raciocínio de faixa aparece quando o assunto é moderação de conteúdo com escalonamento humano, onde o custo de errar pra mais e pra menos também não é simétrico
O erro comum deste passo: fixar um limiar único global e sair replicando ele em todo lugar do sistema
Quando a lista de equipes muda: o que fazer
Esse é o ponto que derruba a maioria das implementações de despacho, então vamos por sintoma
Sintoma: equipe nova nunca é escolhida
Você cadastrou a EQ-22 no sistema de escala semana passada e o Jev continua ignorando ela
Causa: o criteria viaja dentro do corpo de cada requisição, junto com state e questions. Ele não é pré-registrado no serviço
Se a tua função que monta o payload tem um dicionário de equipes escrito na mão, a equipe nova simplesmente não existe pro modelo
Solução: gerar o criteria a partir da fonte de verdade (o sistema de escala) a cada chamada, sempre:
def montar_criteria(equipes_elegiveis):
return {
eq.id: {
"cobertura": eq.descricao_cobertura,
"exclusoes": eq.restricoes,
"exemplos": eq.exemplos_servico,
}
for eq in equipes_elegiveis
}
Sintoma: equipe desligada continua aparecendo na escolha
Mesma causa, espelhada: alguém saiu da escala e o payload não soube
Solução: o filtro de elegibilidade roda ANTES de montar o criteria, em código puro. Equipe inativa, de folga, fora de área ou sem certificação nem chega a virar opção
O Jev só vê a lista já filtrada, e é isso que garante que ele nunca escolha alguém impossível
Sintoma: probabilidades diluídas quando tem muita equipe
Choice aceita até 255 opções, mas "aceita" não é o mesmo que "é uma boa ideia"
Quando você joga 80 equipes numa pergunta só, a probabilidade se espalha e a confiança tende a cair, porque várias opções são genuinamente parecidas
Solução: aperte o filtro em código antes. Raio de deslocamento, especialidade obrigatória, janela de disponibilidade… corta pro conjunto que faz sentido de verdade e deixa o julgamento pra um punhado de candidatos reais
E os casos de borda?
Dois casos que o teu código precisa tratar antes de chamar a API:
- Nenhuma equipe elegível: não chame o Jev. Não existe julgamento a fazer, existe um problema de escala. Isso vai direto pro coordenador
- Uma equipe só elegível: também não precisa chamar. Choice funciona com 1 opção, mas você estaria pagando token pra confirmar o óbvio. Despacha direto e registra que foi por regra, não por modelo
Como registrar a decisão para auditoria
Despacho de campo tem consequência física: alguém sobe num elevador, alguém vai pra uma subestação
Então registrar o porquê da escolha não é capricho
O que dá pra gravar, usando só o que a API devolve:
- O
stateexato que você enviou, serializado. É o contexto que produziu a decisão, e sem ele o resto não se explica - O
criteriadaquela requisição específica, porque ele muda a cada chamada conforme a escala. Guardar "as equipes de hoje" não reconstrói uma decisão de três meses atrás - A opção escolhida, o ID da equipe que saiu no Choice
- As probabilidades por opção, que é onde mora a diferença entre uma escolha folgada e um quase empate
- O valor de confiança e a faixa que a tua aplicação aplicou em cima dele (agiu sozinho? pediu confirmação? escalou?)
- O campo
modelda resposta, que informa o ID versionado do modelo que respondeu. Mesmo você mandandojev-latestna requisição, a resposta te diz qual versão efetivamente decidiu, e isso é ouro pra explicar mudança de comportamento entre períodos usage.input_tokenseusage.output_tokens, pro seu controle de custo por chamado
Na prática:
registro = {
"chamado_id": state["chamado"]["id"],
"state_enviado": state,
"criteria_enviado": questions["equipe_despacho"]["criteria"],
"equipe_escolhida": equipe,
"probabilidades": probabilidades,
"confianca": confianca,
"faixa_aplicada": faixa,
"modelo": resposta.model,
"input_tokens": resposta.usage.input_tokens,
"output_tokens": resposta.usage.output_tokens,
"decidido_em": datetime.now(timezone.utc).isoformat(),
}
salvar_auditoria(registro)
E o log do SDK?
O log do SDK Python é configurado pelo logging padrão ou pela variável TYPESAFE_LOG_LEVEL, que aceita debug, info, warning, error e off
Detalhe que pega muita gente: ela precisa ser definida ANTES de importar o SDK
import os
os.environ["TYPESAFE_LOG_LEVEL"] = "info"
from typesafe import TypeSafeClient # import depois
Em info sai uma linha de resumo por requisição, ótimo pra produção
Em debug saem headers e corpos de requisição e resposta, o que é excelente pra investigar um despacho estranho, mas pensa duas vezes antes de deixar isso ligado num ambiente que trafega endereço de cliente
Tome cuidado: log de aplicação é diagnóstico, não é trilha de auditoria. A trilha é o registro estruturado que você mesmo grava, com retenção que você controla
Outras decisões de campo que cabem no mesmo padrão
Aqui tá a parte que deixa o padrão realmente barato: questions é um mapa, então você faz várias perguntas na mesma chamada, e elas são respondidas numa passada paralela
Os três tipos disponíveis:
| Tipo | O que devolve | Uso no despacho de campo |
|---|---|---|
| Choice | Uma opção do conjunto, com probabilidade por opção e confiança | Qual equipe atende, faixa de prioridade, tipo de serviço a registrar |
| Score | Uma nota dentro dos níveis definidos no criteria | Urgência do chamado, complexidade estimada do reparo |
| Noul | Sim ou não | "Precisa de segunda visita?", "Exige peça especial?" |
Na prática, uma chamada só já resolve o triângulo inteiro:
questions = {
"equipe_despacho": {"type": "Choice", "criteria": criteria_equipes},
"urgencia": {"type": "Score", "criteria": niveis_urgencia},
"precisa_peca_especial": {"type": "Noul",
"instructions": "A descricao indica necessidade de peca fora do kit padrao?"},
}
Com a latência típica de 70 a 500 milissegundos por chamada, isso cabe tranquilo dentro do fluxo de abertura do chamado, sem o usuário sentir
E o padrão é o mesmo de qualquer decisão tipada: é a mesma estrutura que aparece quando o assunto é priorizar leads no funil, só muda o domínio
Vale lembrar também que o Jev é treinado com RLCD (Reinforcement Learning for Calibrated Decisions), método que otimiza calibração em vez de fluência: a ideia é que a confiança declarada acompanhe a acurácia real
É justamente isso que dá sentido a montar faixa de roteamento em cima do número, em vez de só olhar pra ele de longe
Vídeo: automatizando o gatilho do chamado
Na vida real essa chamada ao Jev não sai de um script rodando na mão, ela sai de um gatilho: chamado aberto no sistema, formulário enviado, webhook do app do cliente
Pra começar do zero com essa parte de disparo, este vídeo do canal explica a função de cada trigger do n8n e como eles funcionam:
Conclusão
O padrão todo cabe em três linhas, e é bom guardar assim:
Código decide o que é elegível. Folga, área, certificação, equipe inativa… isso é regra determinística e nunca deve virar responsabilidade do modelo
O Jev decide o julgamento. Uma pergunta Choice, criteria gerado na hora a partir da escala real, e uma resposta tipada com opção, probabilidades e confiança
A confiança decide se escala. Faixas definidas pelo teu time, limiar diferente por ação, e o caso incerto indo pra um coordenador sem drama
Próximo passo, bem concreto: cria a chave no console, monta UMA pergunta Choice com as equipes reais de um turno e roda os chamados de uma semana que já aconteceram
Depois compara a escolha do modelo com o despacho manual que foi feito naquela semana
Onde bateu, ótimo
Onde não bateu, olha a probabilidade e a confiança antes de julgar quem errou… às vezes o modelo estava dividido e o teu limiar é que precisa de ajuste 🙂
Até o próximo post!
Perguntas frequentes
Quantas opções o Jev consegue comparar numa decisão de despacho com Choice?
O tipo Choice aceita de 1 até 255 opções dentro do criteria, então mesmo uma central com dezenas de equipes candidatas cabe numa única pergunta. No exemplo do post usamos só três equipes, mas dá pra escalar bem além disso sem trocar de estrutura.
O Jev decide sozinho se a equipe está de folga ou fora da área?
Não. A orientação da TypeSafe é manter regra determinística e efeito colateral em código, e deixar o Jev só pro julgamento entre opções já elegíveis. Filtro de folga, área de atendimento e certificação obrigatória é WHERE no seu banco, antes de montar o state.
Quanto custa rodar uma decisão de despacho por chamado no Jev?
O Jev cobra US$ 0,042 por 1 milhão de tokens de entrada, e a saída sai de graça. Como o state de um chamado é pequeno (um registro de ordem de serviço e algumas equipes candidatas), o custo por decisão individual fica bem baixo.
Dá pra usar o resultado do Jev direto num sistema de despacho em produção, sem parsear texto?
Sim, essa é a ideia central do System One: a resposta já vem como valor tipado, com a opção escolhida, probabilidade por opção e confiança, sob a mesma chave que você definiu em questions. Não tem parágrafo pra interpretar, é campo estruturado pronto pro seu código consumir.
Quanto tempo leva pra receber a resposta de uma chamada de despacho no Jev?
A latência típica fica entre 70 e 500 milissegundos por chamada, com todas as perguntas da mesma requisição respondidas numa passada paralela. Isso importa em despacho porque o chamado geralmente está esperando alguém decidir em tempo real.
O que fazer quando a confiança da resposta do Jev vem baixa pra um despacho?
A documentação recomenda o padrão de confidence-gated routing: você define faixas de confiança na própria aplicação, sem nova chamada à API, e escala os casos de confiança baixa pra uma pessoa ou pra um modelo de raciocínio mais caro. O limiar não é fixo, cada ação (tipo de chamado, SLA envolvido) pode ter um limiar diferente conforme o time definir.
Formações
Formação Claude Code
Domine Claude Code do absoluto zero até o avançado
- 120 aulas
- 4 projetos
- 9h 45min
Blog | Mais populares

Como montar um workflow de automação combinando decisões do Jev em código
Aprenda a montar um workflow com Jev: decisões tipadas encadeadas em código, alta confiança agindo sozinha e casos incertos escalando para revisão.

Jev decide, LLM escreve: como dividir os papéis dentro de um agente de IA
Jev é o modelo que decide, não escreve: entenda como dividir papéis entre Jev e LLM dentro de um agente de IA e quando usar cada um.

Para quem o Jev serve (e para quem não serve)?
Jev serve pra roteamento, scoring e guardrails em IA, não pra texto ou código. Veja pra quem o Jev serve e quando evitar.
