Como desenhar o schema de saída de uma decisão do Jev?

diagrama representando o schema de saída do Jev com campos de decisão
Resposta rápida

O schema de saída do Jev é a própria decisão: você define antecipadamente as opções e a estrutura, e a correspondência de schema é garantida, então a resposta não inventa campo nem devolve tipo errado. Modelar bem significa quebrar a decisão em fatores independentes, escolher a primitiva certa em cada campo (Noul para sim/não, Choice para um entre N, Score para nível em escala), nomear opções pelo significado e escrever rubricas com fronteira clara. Depois o código combina os sinais, lê probabilities e confidence e decide o que faz sozinho e o que vai pra revisão humana.

No Jev, o schema não é enfeite em volta da resposta, ele é a decisão

Se você ainda não brincou com o bicho: o Jev é o primeiro System One Model da TypeSafe AI, apresentado por Diogo Almeida, ex-pesquisador da OpenAI. Ele recebe um estado (texto ou JSON) e responde perguntas tipadas devolvendo valores estruturados com probabilidades, em vez de gerar texto

E aqui está o pulo do gato: quem define as opções e a estrutura da saída é você, antes da chamada. A correspondência de schema é garantida por construção, ou seja, a resposta não pode inventar um campo nem retornar o tipo de dado errado

Traduzindo: a qualidade e a usabilidade da resposta são decididas no momento em que você desenha o schema, não depois

Bora modelar isso direito? 🙂

O que você precisa saber antes de modelar o schema

Antes de sair escrevendo JSON, tenha o mapa na cabeça. A API do Jev define três tipos de pergunta, chamados de primitivas, e cada uma devolve uma coisa diferente

Primitiva Pergunta que ela responde O que volta na resposta Limites
Noul Sim ou não Um único número, noul, que é a probabilidade da resposta ser sim Sem campo confidence separado
Choice Um entre N choice, probabilities (cada opção mapeada pra sua probabilidade, somando 1) e confidence Até 255 opções
Score Nível em escala ordenada score, uma probabilidade por nível e confidence De 2 a 10 níveis

Repare no Noul: ele não tem confidence porque a própria probabilidade já descreve a incerteza. Faz sentido, né? Um 0,51 num sim/não já grita "eu não faço ideia"

Formação Claude Code
Formação Recomendada

Formação Claude Code

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

  • 120 aulas
  • 4 projetos
  • 9h 45min

Como a requisição é montada: o corpo carrega state, model e um mapa de questions, onde cada chave é o ID da pergunta e o valor é a definição dela. Na volta, cada resposta preserva o ID da pergunta e corresponde ao tipo daquela pergunta

Modelo e orçamento: o modelo atual documentado é o jev-1.13.0, com contexto de requisição de 64k e orçamento de 32k para o state mais a pergunta mais longa, entrada somente texto. Os aliases jev-latest (release estável e padrão do SDK) e jev-preview apontam pro modelo atual

Preço: US$ 0,042 por milhão de tokens de entrada, com tokens de saída gratuitos. Como a saída não custa, empilhar muitas perguntas na mesma requisição é barato do ponto de vista de output, o peso fica no state e no texto das perguntas

Guarde esse número do 32k, ele volta lá embaixo na seção de erros 😛

Passo a passo para desenhar o schema de uma decisão

1. Escreva a decisão em UMA frase

Antes de qualquer campo, escreve em português o que o software precisa decidir

"Este ticket vai pro time de billing ou pro de suporte técnico?"

"Este texto viola a política de spam?"

Se a sua frase tem um "e" no meio, ou se ela precisa de um parágrafo pra ser explicada, ela não é uma pergunta só

O erro comum deste passo: achar que "avalie este pitch de startup" é uma pergunta. Não é. É uma pilha de perguntas disfarçada

2. Decomponha em fatores independentes

A documentação é bem direta nisso: se a decisão exigiria raciocínio extenso ou pesa vários fatores independentes, pergunte cada fator separadamente e combine os resultados com lógica no seu código

O exemplo que a própria doc usa é exatamente o do pitch: em vez de "avalie este pitch de startup", pergunte separadamente sobre tamanho de mercado, viabilidade técnica e diferenciação, e depois combine os scores com uma fórmula sua

Isso muda o papel de cada lado: o modelo vira o sensor, o seu código vira o juiz

O erro comum deste passo: delegar a fórmula pro modelo. A ponderação entre fatores é regra de negócio, e regra de negócio tu quer no repositório, versionada, não escondida dentro de uma rubrica

3. Escolha a primitiva campo por campo

Com os fatores na mão, cada um vira uma pergunta com um tipo

  • É sim ou não? Noul
  • É um entre N rótulos que não se sobrepõem? Choice
  • É um nível numa escala que tem ordem real (baixo, médio, alto)? Score

Se você está em dúvida entre Choice e Score, pergunta: as opções têm uma ordem natural? Se "reembolso" não é maior nem menor que "cancelamento", é Choice

O erro comum deste passo: usar Score pra coisa que não é escala, só porque parece mais informativo

4. Nomeie as opções sem ambiguidade

A doc orienta usar os mesmos nomes de campo entre as opções, pro modelo comparar diretamente, e descrever o que pertence a cada opção, o que pertence à opção vizinha e alguns exemplos representativos

Traduzindo pra prática: nome de opção precisa dizer o que a opção significa, e as descrições precisam ter o mesmo formato entre si

Se a opção A fala de "tipo de cliente" e a opção B fala de "quando usar", o modelo está comparando maçã com bicicleta

O erro comum deste passo: nomes vazios tipo opcao_a, tipo_1, outro. O modelo só tem aquele rótulo e a rubrica pra trabalhar, nome genérico é informação jogada fora

5. Escreva os criteria no formato certo de cada primitiva

Aqui o formato muda conforme o tipo, e vale decorar:

  • Choice: criteria é um mapa de opção para descrição (a rubrica), e aceita null quando aquela opção não precisa de detalhe extra
  • Score: criteria é um array ordenado de descrições de nível, da ponta baixa para a ponta alta da escala
  • Noul: é definido pelas instructions (a pergunta sim/não a avaliar) e pode opcionalmente receber criteria com descrições para true e false, quando a fronteira entre sim e não é sutil

Um esqueleto ilustrativo de questions, só pra você sentir a forma (os nomes exatos dos campos estão na doc das primitivas):

{
  "state": "Assunto: cobrança duplicada no cartão...",
  "model": "jev-latest",
  "questions": {
    "categoria_ticket": {
      "type": "choice",
      "instructions": "Para qual fila este ticket deve ser roteado?",
      "criteria": {
        "cobranca": "Problemas de fatura, cobrança duplicada, estorno e reembolso. Não inclui falha de login no portal de faturas, isso é acesso_conta",
        "acesso_conta": "Login, senha, 2FA e permissão de usuário. Não inclui valores cobrados, isso é cobranca",
        "bug_produto": "Comportamento errado do produto reproduzível pelo usuário",
        "duvida_uso": null
      }
    },
    "precisa_resposta_hoje": {
      "type": "noul",
      "instructions": "O cliente precisa de resposta ainda hoje?",
      "criteria": {
        "true": "Serviço parado, cobrança em curso ou prazo legal citado no texto",
        "false": "Pedido de informação sem impacto imediato no uso"
      }
    },
    "severidade": {
      "type": "score",
      "instructions": "Qual a severidade do impacto relatado?",
      "criteria": [
        "Incômodo cosmético, cliente segue trabalhando",
        "Funcionalidade secundária afetada, existe contorno",
        "Funcionalidade principal afetada, sem contorno",
        "Operação parada ou dinheiro saindo errado"
      ]
    }
  }
}

O erro comum deste passo: rubrica que só diz o que a opção É e nunca diz o que ela NÃO é. A fronteira é a parte que resolve os casos difíceis

6. Use objetos e arrays quando a orientação tem vários tipos

Quando as instruções ou os critérios precisam carregar vários tipos de orientação, a doc recomenda usar objetos ou arrays com campos nomeados, em vez de achatar tudo numa string densa de prosa

Ou seja: definição num campo, fronteira em outro, exemplos num array. Cada coisa no seu lugar, que é justamente o que evita o modelo misturar tudo

O erro comum deste passo: o parágrafão. Aquela rubrica de oito linhas onde definição, exceção e exemplo estão todos grudados

7. Defina IDs de pergunta estáveis

A resposta vem indexada pelo ID da pergunta, com o type correspondente. Então o ID não é decoração, é a chave que o seu código vai usar pra sempre

Escolhe nomes que descrevem o fator (severidade, categoria_ticket) e trata mudança de ID como mudança de contrato

O erro comum deste passo: ID descartável tipo q1, q2. No dia que alguém reordenar as perguntas, seu if vai ler o campo errado e ninguém vai perceber

8. Agrupe muitas perguntas estreitas na mesma requisição

A recomendação da doc é fazer muitas perguntas estreitas e independentes sobre o mesmo estado numa única requisição, porque elas rodam em paralelo e o seu código combina os sinais

Não existe limite fixo de quantidade de perguntas: o limite é o orçamento de tokens da requisição, compartilhado entre o state e as perguntas

O erro comum deste passo: mandar uma requisição por fator, repetindo o mesmo state gigante em todas. Você paga o state de novo a cada chamada, e o state é justamente a parte cara (entrada é o que é cobrado, US$ 0,042 por milhão de tokens)

Como fazer o schema devolver algo que o código consiga usar

Schema bom não é o que responde bonito, é o que o if do outro lado consegue consumir sem chutar

A resposta de um Choice traz choice (a opção de maior probabilidade), probabilities (cada opção mapeada pra sua probabilidade, somando 1) e confidence (número de 0 a 1 calculado a partir da dispersão das probabilidades). O Score traz score, probabilidade por nível e confidence

Forma ilustrativa do answers:

{
  "answers": {
    "categoria_ticket": {
      "type": "choice",
      "choice": "cobranca",
      "probabilities": {
        "cobranca": 0.71,
        "acesso_conta": 0.18,
        "bug_produto": 0.08,
        "duvida_uso": 0.03
      },
      "confidence": 0.62
    },
    "precisa_resposta_hoje": {
      "type": "noul",
      "noul": 0.88
    }
  }
}

Agora o que fazer com isso:

  1. Leia probabilities e confidence, não só o vencedor. O confidence colapsa a distribuição num único número de 0 a 1 justamente pro seu código aplicar um limiar sem refazer a conta na mão
  2. Aplique as três faixas de confiança. A doc sugere alta (segue sem humano), média (pede confirmação, marca pra revisão ou coleta mais informação) e baixa (não age, roteia pra humano ou cai em outro sistema). Isso é o padrão de confidence-gated routing
  3. Aceite que a aplicação não precisa agir sobre a opção vencedora. Um exemplo da própria doc retorna uncertain quando a probabilidade do topo fica abaixo de 0,60 e manda o caso pra um humano
  4. Lembre que o Noul não tem confidence. Ali o limiar você aplica direto na probabilidade: 0,88 é uma coisa, 0,52 é outra bem diferente

O erro comum aqui é o schema que só expõe o vencedor pro resto do sistema. Você joga fora o sinal de incerteza e transforma um modelo calibrado (o Jev foi treinado com RLCD, Reinforcement Learning for Calibrated Decisions, método criado pra produzir probabilidades calibradas junto de cada decisão) num chute com cara de certeza

Se você vem do mundo de LLM generativa, é a mesma dor de exigir JSON com schema entre agentes, só que aqui a garantia é estrutural, e sobra energia pra pensar no conteúdo dos campos em vez de no parse

Três schemas de exemplo para decisões comuns

São modelagens ilustrativas, pra você ver a FORMA dos campos. Adapte às suas regras

Triagem de ticket

  • categoria_ticket: Choice, com as filas reais como opções e rubrica dizendo o que é de cada fila e o que é da fila vizinha
  • precisa_resposta_hoje: Noul, com criteria de true/false porque a fronteira do "urgente" é sutil e cada time entende uma coisa
  • severidade: Score, com 3 a 5 níveis descritos em ordem crescente de impacto

Três perguntas estreitas, uma requisição, e o roteamento final sai de uma regra sua do tipo "cobrança + severidade alta + urgente = fila prioritária"

Moderação de conteúdo

Aqui a tentação é fazer um Choice gigante de "tipo de violação". Só que um conteúdo pode violar duas políticas ao mesmo tempo, e Choice devolve uma opção só

Então: um Noul independente por política (viola_spam, viola_discurso_odio, viola_dados_pessoais, por aí vai), cada um com instructions próprio e criteria de true/false quando a linha é fina

O código junta: qualquer Noul acima do limiar alto derruba, faixa média manda pra revisão

Avaliação de candidatura ou pitch

É o exemplo da doc levado ao pé da letra: em vez de uma nota geral, um Score por fator independente (tamanho de mercado, viabilidade técnica, diferenciação) e a combinação por uma fórmula sua

O ganho não é só precisão, é auditoria: quando alguém perguntar por que o caso X foi reprovado, tu mostra qual fator puxou pra baixo, em vez de dar de ombros pra uma nota única

Erros de modelagem que quebram o schema na prática

Sintoma: as probabilidades ficam sempre empatadas entre duas opções

Causa: opções ambíguas ou nomeadas genericamente, que se sobrepõem no significado

Correção: renomeie pelo significado e escreva na rubrica o que pertence a cada opção e o que pertence à vizinha

Como prevenir: leia só os nomes das opções, sem a rubrica. Se VOCÊ não consegue classificar três casos reais, o modelo também não vai

Sintoma: a resposta parece "média demais", nunca crava nada

Causa: uma pergunta gigante pesando vários fatores de uma vez

Correção: decompor em perguntas independentes e combinar no código

Como prevenir: toda vez que a instructions precisar da palavra "considere também", nasceu uma segunda pergunta ali

Sintoma: o Score se comporta de forma errática entre níveis

Causa: níveis que não formam uma ordem real, tipo misturar "técnico", "urgente" e "caro" na mesma escala

Correção: ou vira Choice, ou vira vários Scores separados, cada um com escala própria (lembrando: mínimo de 2 e máximo de 10 níveis)

Como prevenir: leia o array de criteria de cima pra baixo. Se não dá pra dizer "este é mais que o anterior", não é escala

Sintoma: o modelo ignora parte da instrução

Causa: rubrica escrita como um parágrafo denso, com definição, exceção e exemplo grudados

Correção: objetos ou arrays com campos nomeados, separando os tipos de orientação

Como prevenir: uma orientação por campo, sempre

Sintoma: o sistema erra feio em casos raros, com toda a confiança do mundo

Causa: o código ignora confidence e age sempre no topo

Correção: faixas de confiança, com a baixa indo pra humano ou pra outro sistema

Como prevenir: no code review, procure qualquer lugar que lê choice sem olhar confidence ou probabilities

Sintoma: a requisição não cabe

Causa: state gigante empilhado com pergunta longa, estourando o orçamento de 32k pro state mais a pergunta mais longa

Correção: enxugar o state pro que a decisão realmente precisa e encurtar as rubricas mantendo a fronteira

Como prevenir: trate o state como payload de API, não como "joga o banco inteiro e reza". Já vi gente se ferrar mandando log completo quando três campos resolviam 🙂

Quando o schema é um modelo Python: o caso do Pydantic AI

Se você prefere descrever o schema como tipo em vez de JSON na mão, o mesmo desenho aparece na integração do Pydantic AI com a TypeSafe

Ali, cada campo do output_type vira uma pergunta, e a anotação define o tipo:

  • Enum ou Literal vira Choice
  • IntEnum vira Score
  • bool ou float limitado vira Noul

E tem um detalhe que reforça tudo que falamos sobre nomes: nessa integração, o Jev enxerga as opções de Literal e Enum apenas pelo nome. Então o nome precisa dizer o que a opção significa, sem depender de comentário no código

Quando a diferença entre duas opções precisa de explicação, usa-se um Enum com UseEnumDocstrings e uma docstring sob cada membro. É a mesma rubrica de antes, só que morando no tipo

Importante deixar claro: isso é detalhe do Pydantic AI, não da API REST do Jev. Na API você continua montando state, model e o mapa de questions na mão

Vídeo: IA lendo código e desenhando estrutura sozinha

Pra quem quer entrar no clima de IA que lê contexto e devolve estrutura em vez de textão, este vídeo do canal mostra uma skill que faz a IA ler seu código e desenhar a arquitetura sozinha

Conclusão

Schema bem desenhado no Jev é decisão decomposta: fatores independentes, cada um num campo tipado, com nomes que dizem o significado e rubricas que marcam a fronteira entre as opções

O modelo devolve o sinal com probabilidade, e a combinação, o peso de cada fator e o limiar ficam no seu código, que é onde regra de negócio deve morar

Próximo passo é bem chato e bem eficiente: escreve a decisão no papel, lista os fatores independentes e monta uma primeira requisição com várias perguntas estreitas sobre o mesmo state

Depois é só olhar as probabilidades e ver onde o modelo hesita, porque é exatamente ali que sua rubrica está frouxa 😀

até o próximo post!

Perguntas frequentes

Quantas opções uma pergunta Choice do Jev pode ter?

Uma pergunta Choice aceita até 255 opções. Cada opção entra no mapa de criteria, que pode ter uma descrição por opção ou null quando aquela opção não precisa de detalhe extra.

Qual a diferença entre confidence e probabilities na resposta do Jev?

probabilities mostra cada opção (ou nível) mapeado pra sua chance, somando 1 no total. confidence é um número único de 0 a 1 que colapsa essa distribuição, pensado pra você aplicar um limiar no código sem refazer a conta.

Por que a resposta de uma pergunta Noul não tem campo confidence?

Porque o Noul retorna só um número, noul, que já é a probabilidade da resposta ser sim. Como esse número sozinho já descreve a incerteza (um 0,51 é tão incerto quanto parece), não existe um confidence separado pra essa primitiva.

Existe limite de quantas perguntas posso colocar numa requisição do Jev?

Não tem um limite fixo de perguntas. O que limita é o orçamento de tokens compartilhado entre o state e as questions, e no modelo jev-1.13.0 esse orçamento é de 32k pra state mais a pergunta mais longa.

Quando usar null no criteria de uma pergunta Choice?

Você usa null quando a opção é autoexplicativa pelo nome e não precisa de uma descrição extra na rubrica. As demais opções do mesmo Choice podem ter descrição normal, o mapa aceita misturar.

Quando vale a pena decompor uma decisão em várias perguntas no Jev?

Sempre que a decisão exigiria raciocínio extenso ou pesaria vários fatores independentes. A documentação recomenda perguntar cada fator separadamente e combinar os resultados com lógica no seu código: em vez de "avalie este pitch de startup", pergunte sobre tamanho de mercado, viabilidade técnica e diferenciação, e junte os scores com uma fórmula sua. Como as perguntas rodam em paralelo na mesma requisição, dá pra empilhar várias perguntas estreitas sobre o mesmo state e ainda manter a regra de negócio versionada no seu repositório.




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 Vibe Coding

Formação Vibe Coding

Do Prompt ao Produto: Crie Software Real com IA

  • 474 aulas
  • 20 projetos
  • 39h 27min

Blog | Mais populares