
Como Usar o Jev na Prática: Chave, Primeira Chamada em TypeScript e os 3 Tipos de Pergunta com Código
Entre na sua conta para curtir e guardar o que gostou.
Como Usar o Jev na Prática: Chave, Primeira Chamada em TypeScript e os 3 Tipos de Pergunta com Código
Entre na sua conta para curtir e guardar o que gostou.
Como Usar o Jev na Prática: Chave, Primeira Chamada em TypeScript e os 3 Tipos de Pergunta com Código
0:00 / 21:40
Guia prático para usar o Jev, explicando configuração da chave, chamadas em TypeScript e os três tipos de pergunta (noul, choice, score). Detalha usos reais, custos e limitações, focando em decisões fechadas.
Nesta semana eu conferi 82 frases de um post de 2.300 palavras contra a ficha de fatos dele. A conta deu US$ 0,0015, e o Jev marcou 3 frases sem fonte, que eu corrigi antes de publicar. Nenhum modelo escreveu nada nessa revisão: cada frase virou uma pergunta de sim ou não.
Esse é o lado do Jev que ainda faltava aqui no blog: o tutorial com código. Sem repetir o que ele é. Chave, primeira chamada em TypeScript, os três tipos de pergunta com o JSON de ida e de volta, e três usos reais, com o custo de cada um.
Criar a chave e guardar em variável de ambiente
Primeira chamada com fetch em TypeScript, sem SDK
Noul, choice e score com entrada e saída explicadas
Três usos reais no blog, com o custo de cada um
Onde o Jev erra e não deve entrar
Antes do código: chave e ambiente
Para usar o Jev você precisa de uma conta na TypeSafe e de uma chave de API gerada no painel dela. A chave vai numa variável de ambiente chamada TYPESAFE_API_KEY, lida só no servidor. Ela nunca entra no código, no repositório, no navegador nem numa variável NEXT_PUBLIC_.
Se você ainda não sabe o que é um modelo System One, comece pelo post que explica o que é o Jev e de onde ele vem. Aqui eu parto do ponto em que você já decidiu testar.
Crie a conta no site da TypeSafe e gere a chave no painel. O guia de início rápido da TypeSafe manda exportar a chave como
TYPESAFE_API_KEY.No seu computador, coloque a chave no
.env.local(que fica fora do git).Em produção, cadastre a mesma variável no painel do seu servidor ou da sua hospedagem, nunca num arquivo versionado.
# .env.local (fora do git)
TYPESAFE_API_KEY="cole-a-chave-aqui"
O motivo é simples: no Next.js, tudo que começa com NEXT_PUBLIC_ vai parar no JavaScript do navegador. Chave exposta é crédito queimado por qualquer um que abrir o DevTools. Se uma rota pública do seu site chama o Jev, ela também precisa de limite de requisições por visitante.
A primeira chamada em TypeScript
O Jev tem um endpoint só: POST https://api.typesafe.ai/v1/systemone, com a chave no cabeçalho Authorization. O corpo leva três campos: model (a versão), state (os dados que ele vai julgar) e questions (as perguntas, cada uma com um nome seu e um tipo). A resposta traz answers, model e usage.
Dá pra usar o SDK oficial, mas fetch direto resolve e não adiciona dependência ao projeto. Este é um cliente mínimo, para rodar só no servidor (rota de API, Server Action, script ou cron):
// lib/jev.ts: roda só no servidor
const URL_JEV = "https://api.typesafe.ai/v1/systemone";
export const MODELO_JEV = "jev-1.13.0";
export type PerguntaJev =
| { type: "noul"; instructions: string; criteria?: { true?: string; false?: string } }
| { type: "choice"; instructions: string; criteria: Record<string, string> }
| { type: "score"; instructions: string; criteria: string[] };
export async function perguntarAoJev(
state: unknown,
questions: Record<string, PerguntaJev>,
) {
const chave = process.env.TYPESAFE_API_KEY;
if (!chave) throw new Error("TYPESAFE_API_KEY ausente");
const resposta = await fetch(URL_JEV, {
method: "POST",
headers: {
Authorization: `Bearer ${chave}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ model: MODELO_JEV, state, questions }),
signal: AbortSignal.timeout(8000),
});
if (!resposta.ok) throw new Error(`Jev respondeu ${resposta.status}`);
return resposta.json();
}
Três decisões desse código merecem explicação, porque são as que evitam dor de cabeça depois:
Versão fixa do modelo.
jev-1.13.0, nuncajev-latest. O alias troca sozinho quando sai versão nova, e o limiar que você calibrou ("acima de 0,7 eu aceito") deixa de valer sem aviso.Timeout em toda chamada. Sem
AbortSignal, uma conexão pendurada trava o fluxo inteiro esperando uma resposta que não vem.Erro tratado por status. Em produção, 429 e 529 merecem nova tentativa com espera crescente, respeitando o cabeçalho
retry-after. Já 401 e 422 não melhoram tentando de novo: é chave errada ou pergunta mal montada.
Guarde também o usage.input_tokens de cada resposta. O Jev cobra US$ 0,042 por milhão de tokens de entrada, e a saída não é cobrada, segundo a página de modelos da TypeSafe. É barato, mas gasto que ninguém registra vira surpresa. Os números de uso real estão no post sobre quanto o Jev custa com dados reais.
Noul, choice e score na prática
Os três tipos de pergunta do Jev respondem coisas diferentes. Noul devolve a probabilidade de uma afirmação ser verdadeira, de 0 a 1. Choice escolhe uma opção de uma lista que você define. Score posiciona a resposta numa escala de níveis que você descreve. O tipo certo depende do que a resposta significa para o seu código.
Tipo | Quando usar | O que você manda | O que volta |
|---|---|---|---|
noul | Uma condição é verdadeira ou não | instructions e, se quiser, criteria com true e false | noul (0 a 1), sem confiança separada |
choice | Uma opção entre várias definidas | instructions e criteria com uma descrição por opção | choice, probabilities e confidence |
score | Grau numa escala com níveis | instructions e criteria com a lista de níveis | score, legend, probabilities e confidence |
Duas regras valem para os três. O nome da pergunta (a chave no objeto questions) serve só para o seu código e não chega ao modelo, então o texto da pergunta precisa ter o sentido completo. E perguntas independentes sobre o mesmo state vão juntas na mesma chamada: rodam em paralelo e uma não vê a resposta da outra.
Noul: a frase é verdadeira?
Entrada: o state traz a ficha de fatos e a frase; a pergunta diz o que é "sim" e o que é "não".
{
"model": "jev-1.13.0",
"state": {
"ficha": [
"O Jev cobra US$ 0,042 por milhão de tokens de entrada.",
"Tokens de saída não são cobrados."
],
"frase": "O Jev cobra US$ 0,042 por milhão de tokens de entrada e de saída."
},
"questions": {
"sustentada": {
"type": "noul",
"instructions": "A `frase` é sustentada pela `ficha`?",
"criteria": {
"true": "Tudo o que a frase afirma está na ficha",
"false": "A frase afirma algo que a ficha não diz ou contradiz"
}
}
}
}
Saída (os números aqui são ilustrativos, para mostrar o formato):
{
"model": "jev-1.13.0",
"answers": {
"sustentada": { "type": "noul", "noul": 0.08 }
},
"usage": { "input_tokens": 120, "output_tokens": 4 }
}
O noul é a resposta e a certeza ao mesmo tempo. Perto de 1 é um sim forte, perto de 0 é um não forte, e perto de 0,5 quer dizer que o modelo acha sim e não igualmente prováveis. Não existe confiança separada no noul. Quem decide o corte (0,5, 0,7, 0,9) é o seu código, conforme o custo de errar.
Choice: qual das opções?
{
"model": "jev-1.13.0",
"state": { "mensagem": "Quanto custa uma loja virtual com Pix?" },
"questions": {
"assunto": {
"type": "choice",
"instructions": "Qual é o assunto da `mensagem` enviada pelo formulário de contato?",
"criteria": {
"orcamento": "Pede preço ou proposta de um serviço",
"suporte": "Já é cliente e relata um problema",
"duvida": "Pergunta como algo funciona, sem pedir preço",
"outro": "Nenhuma das opções acima"
}
}
}
}
Saída (ilustrativa):
{
"model": "jev-1.13.0",
"answers": {
"assunto": {
"type": "choice",
"choice": "orcamento",
"probabilities": { "orcamento": 0.91, "suporte": 0.01, "duvida": 0.07, "outro": 0.01 },
"confidence": 0.84
}
},
"usage": { "input_tokens": 150, "output_tokens": 6 }
}
Repare na opção outro. Toda choice que pode não ter resposta certa precisa de uma saída "nenhuma" ou "outro", senão o modelo é obrigado a escolher uma opção errada. A confidence mede quão concentrada está a distribuição, não se o seu fluxo inteiro está certo.
Score: em que grau?
No score, os níveis são uma lista, e o número de cada nível é a posição dele na lista, começando em 0. O exemplo abaixo é o da documentação do score, com os rótulos traduzidos:
{
"model": "jev-1.13.0",
"state": { "mensagem": "Já é a terceira vez que eu peço o reembolso." },
"questions": {
"frustracao": {
"type": "score",
"instructions": "Quão frustrado está o cliente na `mensagem`?",
"criteria": ["Calmo", "Frustrado", "Muito irritado"]
}
}
}
Saída, com os números do exemplo oficial:
{
"model": "jev-1.13.0",
"answers": {
"frustracao": {
"type": "score",
"score": 1.05,
"legend": { "0": "Calmo", "1": "Frustrado", "2": "Muito irritado" },
"probabilities": { "0": 0.0, "1": 0.95, "2": 0.05 },
"confidence": 0.92
}
}
}
O score é a média dos níveis pesada pela probabilidade: 0 × 0,0 + 1 × 0,95 + 2 × 0,05 = 1,05. A legend devolve o texto de cada nível, para o seu código não depender da ordem que ficou na memória de alguém.
Três usos reais no blog esta semana
Nesta semana o Jev entrou em três tarefas do blog, todas de decisão fechada: conferir frases contra uma ficha de fatos, escolher pautas e calcular o lado das apostas do placar de previsões. Em nenhuma delas ele escreveu uma linha. O texto continuou com quem escreve; o Jev só julgou.
1. Revisar um post contra a ficha de fatos
O post tinha uma ficha de fatos: cada fato com a fonte e o trecho de onde saiu. Na revisão, o código separa as frases do texto que têm número ou data, e cada uma vira uma pergunta noul: "a frase é sustentada pela ficha?". O que fica abaixo do corte volta para revisão.
// frases com número ou data escrita com dígitos
const frases = separarFrases(texto).filter((f) => /\d/.test(f));
const questions = Object.fromEntries(
frases.map((frase, i) => [
`f${i}`,
{
type: "noul" as const,
instructions: `A frase a seguir é sustentada pela \`ficha\`? Frase: "${frase}"`,
criteria: {
true: "Tudo o que a frase afirma está na ficha",
false: "A frase afirma algo que a ficha não diz ou contradiz",
},
},
]),
);
const r = await perguntarAoJev({ ficha }, questions);
const semFonte = frases.filter((_, i) => r.answers[`f${i}`].noul < 0.5);
Num post de 2.300 palavras foram 82 frases conferidas por US$ 0,0015. O Jev marcou 3 frases sem fonte, e eu corrigi as três. Repare que a frase vai dentro do texto da pergunta: o nome f0, f1 não chega ao modelo. É o mesmo padrão da receita de checagem de citação da TypeSafe.
O Jev não corrigiu nada. Ele apontou onde olhar, e a correção foi minha.
2. Escolher pautas
Dez notícias candidatas entram no state, cada uma com título e resumo curto. Uma pergunta choice escolhe a melhor pauta para o blog, com a opção "nenhuma" incluída. Junto, na mesma chamada, vão duas perguntas noul por pauta: as pessoas vão pesquisar esse assunto? E ele continua sendo buscado depois da semana da notícia?
const questions: Record<string, PerguntaJev> = {
melhor: {
type: "choice",
instructions: "Qual das `pautas` rende o melhor post de tecnologia para leitores brasileiros?",
criteria: {
...Object.fromEntries(pautas.map((p, i) => [`p${i}`, p.titulo])),
nenhuma: "Nenhuma pauta serve",
},
},
};
pautas.forEach((p, i) => {
questions[`p${i}_busca`] = {
type: "noul",
instructions: `Brasileiros vão pesquisar no Google sobre: "${p.titulo}"?`,
};
questions[`p${i}_dura`] = {
type: "noul",
instructions: `O assunto "${p.titulo}" continua sendo buscado meses depois da notícia?`,
};
});
São 21 perguntas numa chamada, e custou cerca de US$ 0,0001. O código cruza as três respostas; a decisão final de pauta continua sendo de quem escreve.
3. Calcular as apostas do placar de previsões
O blog tem um placar público de previsões, com apostas datadas que depois são conferidas. Para cada aposta, o Jev recebe uma pergunta noul do tipo "até 31/12, X acontece?", com o contexto da notícia no state. A aposta é escrita pelo lado mais provável.
const r = await perguntarAoJev(
{ contexto: resumoDaNoticia },
{
acontece: {
type: "noul",
instructions: "Até 31/12/2026, a empresa lança o recurso descrito no `contexto` para usuários no Brasil?",
},
},
);
const lado = r.answers.acontece.noul >= 0.5 ? "sim" : "não";
Dois cuidados aqui. O Jev não pesquisa na web: ele julga o que está no state, então o contexto precisa ser bom. E a data entra como parte do enunciado, não como conta: a pergunta não pede para ele comparar datas, coisa em que ele é fraco.
Quando não usar o Jev
O Jev não gera texto, erra em conta e lê datas como texto, não como quantidade. Pergunta que depende de somar, contar, comparar valores ou decidir qual data vem antes fica no código. E state grande, cheio de campos que não importam para a pergunta, piora a resposta em vez de ajudar.
A própria TypeSafe documenta esses pontos fracos na página de limitações do jev-1.13. Na prática, fica de fora:
Gerar texto. Resposta de atendimento, post, resumo, e-mail. O jev-1.13 não foi treinado para isso.
Conta e número exato. Soma, porcentagem, comparação de valor: código.
Datas. Qual vem antes, quantos dias entre elas, se cai dentro de um prazo. Com formatos misturados, piora.
Contagem. Divida em perguntas individuais e deixe o código contar, como na revisão acima: o Jev julga frase por frase, o código conta quantas falharam.
State inchado. Mande só os campos que a pergunta precisa. O teto é de 64 mil tokens por chamada, sendo 32 mil para o state mais a maior pergunta. E dado pessoal (e-mail, telefone, CPF) sai antes, por LGPD.
Regra que protege dinheiro ou acesso. Permissão, bloqueio, segurança: regra determinística no código, não probabilidade.
Imagem e áudio. O Jev lê texto.
E um lembrete que vale para qualquer modelo: resposta tipada garante o formato, não a verdade. Antes de deixar o Jev decidir sozinho, rode em paralelo com o que você usa hoje e compare.
O Jev decide, outro modelo escreve
Minha posição: o Jev serve para decisão fechada e barata ao lado do código, e não substitui o modelo que escreve. Ele brilha onde existe uma pergunta clara, um conjunto fechado de respostas e um custo de errar que você consegue medir. Para escrever, resumir ou conversar, continue com um modelo de linguagem.
O contra-argumento óbvio: "dá pra fazer tudo isso pedindo JSON a um modelo grande". Dá. Mas você paga por cada token de saída, precisa validar o texto que volta e recebe uma resposta sem probabilidade calibrada, ou seja, sem um número honesto para decidir o corte. Para 82 frases por US$ 0,0015, a conta não fecha a favor do modelo grande. Se você quer comparar preços de assinatura e de API dos modelos que escrevem, o post sobre quanto vale pagar por IA em 2026 faz essa conta. E se a sua dúvida é rodar algo parecido no próprio servidor, sem mandar dado para fora, leia a comparação entre o Jev e o Laya local.
O próximo passo prático é pegar uma decisão que hoje é um if frágil ou um prompt caro no seu sistema e trocar por uma pergunta tipada. Se quiser fazer isso com alguém olhando a arquitetura junto, a consultoria técnica 1:1 serve exatamente para desenhar onde o Jev entra e onde ele não deve entrar.
Perguntas frequentes
Como criar a chave de API do Jev?
Crie uma conta no site da TypeSafe e gere a chave no painel. Guarde a chave numa variável de ambiente chamada TYPESAFE_API_KEY, no .env.local em desenvolvimento e no painel da hospedagem em produção. Nunca coloque a chave no código, no repositório ou numa variável NEXT_PUBLIC.
Quanto custa cada chamada ao Jev?
O Jev cobra US$ 0,042 por milhão de tokens de entrada, e os tokens de saída não são cobrados. Uma revisão de 82 frases de um post saiu por US$ 0,0015, e uma escolha de pauta com 21 perguntas, por cerca de US$ 0,0001. Registre o usage.input_tokens de cada resposta para acompanhar o gasto.
Qual a diferença entre noul, choice e score?
Noul responde se uma condição é verdadeira, com uma probabilidade de 0 a 1. Choice escolhe uma opção entre as que você definiu e devolve a probabilidade de cada uma. Score posiciona a resposta numa escala de níveis numerados a partir de 0. Escolha pelo que a resposta significa para o seu código.
Posso chamar o Jev direto do navegador?
Não. A chamada precisa sair do servidor, porque a chave de API daria acesso ao seu crédito para qualquer visitante. No Next.js, use uma rota de API ou uma Server Action, e coloque limite de requisições se a rota for pública.
Por que fixar jev-1.13.0 em vez de jev-latest?
O alias jev-latest aponta sozinho para a versão nova quando ela sai. Se o seu código decide com um corte calibrado, como aceitar acima de 0,7, a troca de versão pode mudar as probabilidades sem aviso. Com a versão fixa, você atualiza quando quiser e testa antes.
Golber Dóriaquem escreve este blog
Receba os posts novos no seu email
IA aplicada ao trabalho, stack e o negócio de uma pessoa só, direto na sua caixa de entrada. Sem spam, e sair é um clique em qualquer email.