Confiança quando a decisão estava errada
0,93
LLM
decidiu com quase certeza
0,71
Jev
ficou abaixo do limiar de 0,9
Marca laranja: limiar de 0,9 para ação irreversível · 82 casos de um agente de NFS-e

Um agente que executa ações precisa decidir, a cada mensagem, se entendeu o pedido e se o cliente autorizou. Testamos essas decisões com o GPT-5.4 mini e com o Jev num agente que emite e cancela NFS-e.

O cliente escreve “preciso anular aquela nota da Beta”. O agente propõe cancelar a nota 3350, de R$ 370,00, e pede confirmação. O cliente responde “manda ver”. Se o agente entender errado, cancela uma nota fiscal sem autorização, e cancelamento não tem volta.

Foram 82 casos rotulados à mão, com o mesmo catálogo de intenções nos dois lados. O acerto ficou igual. Mudaram o tempo, o custo e o quanto dá para confiar na confiança que cada modelo devolve.

Para quem prefere o formato em vídeo, a explicação completa está logo abaixo.

Neste artigo

1. O Jev em uma chamada

O Jev, da TypeSafe, é um modelo feito para decidir. Você envia os dados e uma pergunta com as opções possíveis. Ele devolve a opção escolhida, a probabilidade de cada uma e uma confiança, sem texto livre.

1. Você envia
state: os dados

“preciso anular aquela nota da Beta”
questions: a pergunta e as opções

O que o cliente quer fazer?
emitirconsultarcancelarcorrigir
Cada opção leva descrição e exemplos.
→
Jevescolhe uma das opções
→
2. O Jev devolve
choice

cancelar

confidence

0,97

probabilities

cancelar

0,97

corrigir

0,02

consultar

0,01

emitir

0,00

Uma chamada ao Jev: os dados e a pergunta entram, a escolha volta com probabilidade. Valores ilustrativos.

2. A mesma decisão com LLM e com Jev

Os dois lados usam o mesmo catálogo de intenções, com descrição e exemplos de cada uma. O que muda é a forma de perguntar.

Catálogomesmo nos dois lados
LLM

Texto no promptcatálogo + regras
→
LLM escreveum JSON
→
Parse defensivovazio? inválido?
→
Confiançaescrita pelo LLM
JEV

Opções tipadaswhat, examples
→
Jev escolheuma da lista
→
Sem parseresposta tipada
→
confiança < limiar?o código decide
Os dois caminhos devolvem o mesmo ResultadoIntencao.
O mesmo catálogo vira texto no prompt do LLM e vira opções no Jev. O resto do agente recebe o mesmo resultado.

Com o LLM, o código pede um JSON e interpreta o texto que voltou, com defesa para resposta vazia, inválida ou fora do catálogo.

// LLM: pede um JSON e interpreta o texto que voltou
var json = await llm.CompletarAsync(MontarPrompt(catalogo, conversa), MontarSchema());

if (!TentarLer(json, out var r) || !catalogo.Contem(r.Chave))
    return ResultadoIntencao.Seguro();   // vazio, inválido ou fora do catálogo

// confianca e precisaEsclarecimento foram escritos pelo próprio LLM
return new ResultadoIntencao(r.Chave, r.Confianca, r.PrecisaEsclarecimento);

Com o Jev, o código pede uma escolha entre as opções do catálogo.

// Jev: pede uma escolha entre as opções do mesmo catálogo
var resposta = await jev.PerguntarAsync(estado, new Choice(
    instrucao: "O que o cliente quer fazer na ultima_mensagem_do_cliente?",
    opcoes: catalogo.Select(i => new Opcao(i.Chave, i.Descricao, i.Exemplos, i.NaoE))));

// a confiança vem do modelo, a decisão vem do código
var precisaEsclarecimento = resposta.Confianca < limiar;
return new ResultadoIntencao(resposta.Escolha, resposta.Confianca, precisaEsclarecimento);

Trechos simplificados do agente.

3. Confiança escrita pelo modelo e confiança calibrada

No caminho do LLM, a confiança é um campo do JSON que o próprio LLM escreve. No caminho do Jev, ela vem do modelo e quem compara com o limiar é o código.

média quando acertoumédia quando errou82 casos
LLM
0,98
0,93
Jev
0,96
0,71
0,50,60,70,80,91,0
limiar 0,9
LLM: o menor erro teve confiança de 0,86.Jev: todos os erros ficaram abaixo de 0,9.
Confiança média nos 82 casos. Quando errou, o LLM marcou 0,93. Todos os erros do Jev ficaram abaixo de 0,9.

O LLM errou com quase a mesma certeza com que acertou: 0,93 contra 0,98. Um limiar sobre esse número deixaria passar quase todos os erros. No Jev, a confiança caiu para 0,71 nos erros, e o limiar separa os casos.

4. Três casos em que os dois discordaram

Mensagem do cliente LLM Jev
“preciso anular aquela nota da Beta” esclarecer, 0,86, errou cancelar_nfse, 0,99, acertou
“espera, antes me manda o pdf dela”, durante a confirmação de um cancelamento ajustar, 0,96, errou esclarecer, 0,53, acertou
“manda ver” confirmar, acertou fica em dúvida e pergunta de novo, errou

No terceiro caso o Jev perde. Mas o tipo de erro importa: quando ele erra numa confirmação, o efeito é uma pergunta a mais para o cliente, sem executar nada.

5. Trocar sem risco: modo sombra e fallback

O fluxo do agente chama DecidirAsync(...) e não sabe quem responde. Um executor escolhe o modo pela variável AI__JEV__MODO.

Fluxo do agentechama DecidirAsync(…)
→
Executorlê AI__JEV__MODO
→
Desligado: o LLM decide, como hoje
Ligado: o Jev decide
Sombra: o LLM decide, o Jev só é medido
Fallback
Jev deu timeout ou erro? O executor chama o LLM e o agente segue.
O tempo perdido entra na métrica do dashboard.
Num dos dias de teste, chamadas do Jev passaram de 3 s.
Cada decisão grava tempo, tokens e resposta dos dois caminhos.
Em Sombra, o cliente recebe a decisão do LLM e o Jev roda junto só para medir. Se o Jev falhar, o executor cai no LLM.

6. A decisão dentro de um fluxo durável no skail

Quando a confiança fica abaixo do limiar, o agente pergunta de novo e espera a resposta do cliente, que pode levar minutos ou horas. No skail, essa espera é durável: a chamada ao Jev fica num passo atômico e a resposta do cliente chega por evento. O padrão completo está em Construindo agentes de IA com skail.

[SkailFunction] ConfirmarCancelamento

Decidir com o Jev[SkailCommand] gravado
→
confiançacontra o limiar 0,9
→
acima: cancela a nota
abaixo: pergunta ao cliente de novo
muito baixa: pede revisão humana
↓
espera sem consumir CPUWaitForEvent + WhenAny
No replay, o skail devolve a decisão gravada. A confiança varia a cada chamada ao Jev, e a execução retomada segue o mesmo caminho.
Esboço de padrão com as primitivas do skail.
A decisão do Jev é um passo atômico com resultado gravado. Abaixo do limiar, o fluxo espera a resposta do cliente ou a revisão humana sem consumir CPU.
[SkailFunction]
public async SkailTask ConfirmarCancelamento(string conversaId, string notaId)
{
    var decisao = await DecidirConfirmacao(conversaId);   // chama o Jev

    if (decisao.Escolha == "confirmar" && decisao.Confianca >= 0.9)
    {
        await CancelarNota(notaId);                        // [SkailCommand]
        return;
    }

    await Responder(conversaId, $"Só para confirmar: cancelo a nota {notaId}?");

    var resposta = WaitForEvent<MensagemRecebida>("EVENTO_NOVA_MENSAGEM", conversaId);
    if (await WhenAny(resposta, DelayMinutes(15)) != resposta)
        await EncerrarConversa(conversaId);
}

Como o resultado do passo fica gravado, uma execução retomada depois de uma falha reutiliza a decisão já tomada, mesmo que uma nova chamada ao Jev devolvesse outra confiança.

7. Os números da rodada

Métrica LLM Jev
Acerto 95% 96%, igual dentro da margem
Erros críticos 0 0
Tempo mediano 1 s 258 ms, 4× mais rápido
Custo a cada 1.000 decisões US$ 0,92 US$ 0,048, 95% mais barato

O Jev usa uns 50% mais tokens, mas o token de entrada custa US$ 0,042 por milhão, contra US$ 0,75 do GPT-5.4 mini, e a saída é grátis.

Perguntas frequentes

O Jev substitui o LLM no agente?

Substitui as decisões. O narrador e a extração de dados continuam no LLM, porque o Jev não gera texto.

O que acontece se o Jev cair ou demorar?

O executor cai no LLM e registra o tempo perdido. O agente segue normalmente.

Preciso do skail para usar o Jev?

Não. O skail entra quando a decisão faz parte de um fluxo que precisa esperar o cliente, pedir revisão humana e sobreviver a reinícios.

Por onde começar no seu agente

  1. Escolha uma decisão com opções fechadas que hoje vai para o LLM. Confirmar uma ação irreversível é o melhor candidato.
  2. Monte casos rotulados à mão, incluindo respostas ambíguas como “manda ver”.
  3. Rode os dois caminhos em modo Sombra e compare acerto, tempo, custo e a confiança nos erros.
  4. Só depois deixe o Jev decidir, com fallback para o LLM.

Se você usa um assistente de IA para escrever esse código, entregue a ele as regras do skail antes.