Pré-requisitos
Conta, créditos e uma chave.
- Entre no painel com o login único e crie a sua organização.
- Em Cobrança, compre créditos (a partir de R$ 8,80 = 20 milhões de tokens de entrada; saída não é cobrada).
- Em Chaves de API, crie uma chave. O segredo aparece uma única vez: guarde num cofre.
export JEVAAAS_API_KEY="jev_sk_..." # painel → Chaves de APIEtapa 1 · Pergunte
Faça a primeira chamada com perguntas soltas.
O fanout julga perguntas tipadas sobre um estado, sem nada publicado antes. É o jeito mais rápido de ver se o JEV entende o seu problema. Três tipos cobrem quase tudo: choice (uma opção de um menu), score (nota numa rubrica) e noul (probabilidade de sim).
curl https://jev.api.br/v1/decisions/fanout \
-H "Authorization: Bearer $JEVAAAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "typesafe/jev-1.13.0",
"state": { "ticket": { "messages": [
{ "author": "customer", "text": "A fatura veio em dobro. Preciso disso ainda hoje." }
] } },
"questions": {
"urgente": { "type": "noul", "instructions": "O cliente pede resolução imediata?" },
"assunto": { "type": "choice", "instructions": "Qual é o assunto principal?",
"criteria": { "financeiro": "Fatura, cobrança ou pagamento.",
"suporte": "Falha de funcionamento ou configuração.",
"indeterminado": "Evidência insuficiente para escolher." } }
}
}'import { Jevaas } from '@jevaas/sdk';
const jevaas = new Jevaas({
apiKey: process.env.JEVAAAS_API_KEY!,
baseUrl: 'https://jev.api.br/v1',
});
const r = await jevaas.fanout({
model: 'typesafe/jev-1.13.0',
state: { ticket: { messages: [{ author: 'customer', text: 'A fatura veio em dobro.' }] } },
questions: {
urgente: { type: 'noul', instructions: 'O cliente pede resolução imediata?' },
},
});
console.log(r.answers, r.usage); // [{ question_id: 'urgente', type: 'noul', noul: 0.12 }] { input_tokens, output_tokens }choice precisa de uma saída de escape, como indeterminado. Sem ela, o modelo é obrigado a escolher uma fila mesmo quando nenhuma serve, e você perde o sinal mais útil: "o seu menu não cobre este caso".Etapa 2 · Contrate
Transforme as perguntas num contrato versionado.
Quando as perguntas funcionam, publique um contrato: as perguntas, as regras de rota e a barra de confiança passam a morar no JEVaaS, com versão. O seu código deixa de interpretar probabilidades e passa a receber uma rota: auto, human_review, collect_evidence ou abstain.
Comece sempre em "mode": "shadow": o julgamento vem completo, mas enforced é false e nenhuma rota autoriza ação.
{
"id": "ticket-router",
"description": "Roteia o ticket para a fila que deve tratá-lo.",
"owner": "[email protected]",
"mode": "shadow",
"action_class": "internal_write",
"bar": 0.75,
"collect_floor": 0.7,
"primary_question": "fila",
"default_route": "human_review",
"escalations": { "human_review": "fila-triagem-humana", "default": "fila-triagem-humana" },
"state_fields": [{ "path": "ticket.messages", "role": "evidence", "required": true }],
"questions": {
"fila": {
"type": "choice",
"instructions": "Qual equipe deve tratar a solicitação principal em ticket.messages?",
"criteria": {
"financeiro": "Fatura, cobrança ou pagamento.",
"suporte": "Falha de funcionamento, configuração ou integração.",
"indeterminado": "Evidência insuficiente ou nenhuma fila adequada."
}
}
},
"routes": [
{ "when": { "question": "fila", "equals": "indeterminado", "escape_hatch": true },
"route": "abstain", "reason": "o menu de filas não cobre o caso" },
{ "when": { "question": "fila", "confidence_below": 0.7 },
"route": "human_review", "reason": "confiança abaixo do piso" },
{ "when": { "question": "fila", "equals": "financeiro", "confidence_at_least": 0.75 },
"route": "auto", "allow": "ticket:route:financeiro", "reason": "financeiro com confiança" }
]
}curl https://jev.api.br/v1/contracts \
-H "Authorization: Bearer $JEVAAAS_API_KEY" \
-H "Content-Type: application/json" \
-d @ticket-router.json # o JSON acima; volta a versão 1const d = await jevaas.judge({
contract: 'ticket-router',
decision_id: ticket.id, // o mesmo id em retries: nunca julga duas vezes
state: { ticket: { messages: ticket.messages } },
});
switch (d.route) {
case 'auto': if (d.enforced) await moverPara(d.selected!.choice!); break;
case 'human_review': await filaHumana(ticket, d.escalation); break;
case 'abstain': await filaHumana(ticket, 'menu-de-filas-incompleto'); break;
case 'collect_evidence': await pedirMaisDados(ticket); break;
}
// guarde d.receipt_id junto do ticket: é por ele que você mede e auditaPUT /contracts/ticket-router). A anterior fica imutável, e cada recibo registra a versão que o julgou.Etapa 3 · Meça em sombra
Compare o JEV com a sua produção antes de confiar.
Em sombra, a sua operação continua decidindo como hoje. Para cada julgamento, devolva o que a produção de fato fez e, numa amostra, o rótulo de uma pessoa. Com isso a calibração mostra o acerto real em cada faixa de confiança do seu caso, não num benchmark.
// o que a produção de fato fez (ex.: a fila em que o atendente pôs o ticket)
await jevaas.receipts.outcome(d.receipt_id, {
production_answer: 'financeiro',
action_taken: true,
});
// amostra conferida por uma pessoa: é o que mede acerto × confiança
await jevaas.receipts.label(d.receipt_id, { human_label: 'financeiro', labeled_by: '[email protected]' });
const cal = await jevaas.calibration('ticket-router', { mode: 'shadow' });
console.log(cal); // acerto por faixa de confiança, divergência da produção, taxa de revisão humanaEtapa 4 · Promova
Passe a autorizar só quando a medição sustentar.
Rode o golden set do contrato (casos com resposta conhecida) e troque o modo para enforce. A partir daí, auto vem com allow e enforced: true. O resto continua indo para pessoas, como antes.
# chave com "pode promover contratos" (escopo contracts): rode o golden set (casos com a resposta esperada da pergunta decisiva)
curl -X POST https://jev.api.br/v1/evals/ticket-router/run \
-H "Authorization: Bearer $JEVAAAS_PROMOVER_KEY" -H "Content-Type: application/json" \
-d '[
{ "name": "fatura-dobrada", "expected": "financeiro",
"state": { "ticket": { "messages": [{ "author": "customer", "text": "A fatura veio em dobro." }] } } },
{ "name": "erro-login", "expected": "suporte",
"state": { "ticket": { "messages": [{ "author": "customer", "text": "Não consigo entrar, dá erro 500." }] } } }
]'
# relatório bom? então autorize
curl -X POST https://jev.api.br/v1/contracts/ticket-router/mode \
-H "Authorization: Bearer $JEVAAAS_PROMOVER_KEY" -H "Content-Type: application/json" \
-d '{ "mode": "enforce" }'contracts. No painel, marque pode promover contratos ao criar a chave, e use essa chave só no processo de publicação, não no serviço que julga.shadow é a mesma chamada. Faça isso ao primeiro sinal de deriva na calibração.Opere
Acompanhe consumo, recibos e erros.
const q = await jevaas.quota(); // teto e consumo do período
const u = await jevaas.usage(); // série diária de consumo
const recentes = await jevaas.receipts.list({ contract: 'ticket-router' });- 402: sem créditos. Compre no painel; a chamada não foi cobrada.
- 429: limite de uso ou teto diário. Espere a janela; não repita em laço.
- Timeout: a decisão pode ter acontecido. Repita com o mesmo
decision_id, nunca com um novo.
No painel, Uso mostra a série diária e Cobrança mostra saldo, estoque em tokens, histórico e recibos.
Boas práticas
O que separa um piloto de uma operação.
- Chave só no servidor, nunca no navegador ou no app. Uma chave por sistema, para revogar sem derrubar os outros.
- Envie ao estado só o que a decisão precisa. Menos dado pessoal, menos token, julgamento mais estável e menos viés: veja o que medimos.
- Toda pergunta
choicetem saída de escape, e toda saída de escape vai para uma pessoa. - A barra de confiança é da consequência (
action_class), não do modelo: mexer em dinheiro pede barra maior que mover um ticket. - Guarde o
receipt_idjunto do registro de negócio. É a trilha de auditoria da decisão. - Comece em sombra, sempre. Promova por contrato, não por projeto inteiro.
- Versione o contrato como código: revise a mudança, publique uma versão nova, compare a calibração das duas.
Próximos passos