Dá para cachear decisões do Jev? Como montar a chave e quando recalcular

O cache de decisões do Jev é 100% do lado do cliente: a API não expõe cache nativo nem campo cached_tokens, e a cobrança é só por token de entrada (state + questions), a US$ 0,042 por 1 milhão. A chave precisa carregar o digest do state, o identificador de modelo (jev-latest e jev-1.13.0 são entradas diferentes), o schema completo da pergunta e a identidade real do item avaliado. Reaproveite quando nada disso mudou, recalcule quando qualquer parte mudou, e nunca memoize decisão de aprovação em silêncio
Fala aí, beleza? A API do Jev não tem cache nativo
Não existe cache de prompt do lado do fornecedor e não existe campo cached_tokens no Usage da resposta
Ou seja: se você quer reaproveitar decisão, o trabalho é todo do seu código
E isso importa porque o Jev cobra só token de entrada, e o que entra na conta é o state mais as questions (incluindo instructions e descrições de opções). Saída custa zero. O preço publicado hoje é de US$ 0,042 por 1 milhão de tokens de entrada, o equivalente a US$ 42 por bilhão
A latência que a própria TypeSafe informou no material de lançamento é de 70 a 500 ms ponta a ponta (número do fornecedor, não benchmark independente). Então sim, tem espaço pra ganhar tempo e dinheiro reaproveitando decisão
O problema é o outro lado: chave mal montada não deixa o sistema mais lento, ela devolve a decisão ERRADA com cara de decisão certa
Neste post a gente vê como montar a chave, quando reaproveitar com segurança e quando é obrigatório recalcular
O que você precisa saber antes de cachear decisões do Jev
Antes de sair hasheando coisa, é bom ter esses pontos firmes na cabeça:
- O formato da chamada é único:
POST https://api.typesafe.ai/v1/systemone, comAuthorization: Bearer <API_KEY>,Content-Type: application/jsone corpo exigindo os camposmodel,stateequestions - Existem três tipos de pergunta:
noul(sim/não com probabilidade),choice(uma opção de uma lista) escore(posição numa escala). O campoinstructionsé opcional em todos eles - O
stateaceita string, object, array ou null. Número ou booleano isolado é rejeitado com 422, então não tente ser esperto mandando umtruecru - Cada pergunta é avaliada isolada e em paralelo contra o mesmo
state, num único passe. Isso muda tudo no desenho da chave: a unidade que você cacheia é pergunta + state, não request inteiro - O consumo de entrada vem na resposta, no campo
usage.input_tokens. É com ele que tu mede se o cache está valendo a pena - Os limites de contexto são dois: 64k tokens pra
state+ todas as perguntas somadas, e 32k prastate+ a pergunta mais longa - Os identificadores de modelo disponíveis hoje são o id versionado
jev-1.13.0e os aliasesjev-latest(padrão dos SDKs) ejev-preview

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!
Esse último ponto merece um aviso: alias e id versionado não são a mesma entrada de cache
Se você guarda decisão sob jev-latest hoje e o alias aponta pra outra versão amanhã, seu store continua servindo a decisão velha achando que está tudo certo
Um detalhe honesto pra fechar: a TypeSafe não publica garantia de determinismo. Não tem temperature, não tem seed, não tem promessa de reprodutibilidade na doc. A ideia de que a decisão é quase uma função pura de model + schema + state aparece em projetos da comunidade, não no material do fornecedor. Trate cache aqui como uma aposta sua, feita com os olhos abertos
Como montar a chave de cache passo a passo
Bora ver na prática? A chave boa carrega quatro coisas: o estado, o modelo, o schema da pergunta e a identidade do item avaliado
- Normalize o
statee gere um digest
Serialização canônica, com ordenação estável de chaves em qualquer profundidade. Sem isso, o mesmo objeto vira dois hashes diferentes só porque a ordem das chaves mudou
import { createHash } from "node:crypto"
// ordena as chaves em qualquer profundidade
function canonical(value) {
if (Array.isArray(value)) return value.map(canonical)
if (value && typeof value === "object") {
return Object.keys(value)
.sort()
.reduce((acc, key) => {
acc[key] = canonical(value[key])
return acc
}, {})
}
return value
}
const digest = (v) =>
createHash("sha256").update(JSON.stringify(canonical(v))).digest("hex")
const stateDigest = digest(state)
O erro comum deste passo: hashear JSON.stringify(state) direto. Ordem de chave varia conforme quem montou o objeto, e aí teu hit rate despenca sem motivo nenhum
- Coloque o identificador de modelo na chave
E trate jev-latest e jev-1.13.0 como entradas DISTINTAS, porque é exatamente o que elas são
const modelKey = "jev-1.13.0" // id versionado em cache de vida longa
O erro comum deste passo: cachear sob alias. O alias é ótimo pra chamar a API e péssimo como namespace de cache, já que ele muda de versão embaixo de você sem avisar
- Inclua o schema COMPLETO da pergunta, não só o nome
Tipo, opções, descrições das opções e instructions. Tudo isso entra no julgamento e tudo isso é cobrado como entrada
const question = {
type: "choice",
options: [
{ value: "keep", description: "manter o conteúdo integral" },
{ value: "drop", description: "descartar o conteúdo" }
],
instructions: "avalie se o resultado ainda é útil pro objetivo atual"
}
const schemaDigest = digest(question)
O erro comum deste passo: guardar só o nome da pergunta na chave. Aí tu edita uma instructions ou a descrição de uma opção, o julgamento muda, e o cache segue devolvendo a decisão do texto velho sem piscar
- Acrescente a identidade real do item avaliado
Esse é o passo que quase todo mundo pula. A correção aplicada naquele projeto passou a chavear no formato ${stateDigest}:${name}:${identity}, com identity sendo o digest da tool call original, do input completo, do resultado completo e do estado de erro
const identity = digest({
call: toolCall.name,
input: toolCall.input, // input completo, não truncado
result: toolCall.result, // resultado completo
errored: toolCall.isError
})
const cacheKey = `${modelKey}:${schemaDigest}:${stateDigest}:${name}:${identity}`
O erro comum deste passo: usar ID temporário como identidade. Nome tipo t1 NUNCA é identidade, porque ele reinicia. Já volto nisso
- Decida com cuidado o que fica de fora do hash, e nunca grave
statebruto no store
Redigir credencial antes de hashear é bom. Redigir campo que distingue sujeito é catástrofe
// ok: tira segredo
const REDACT = ["api_key", "authorization", "password"]
// NÃO tire daqui: são o que separa uma decisão da outra
const IDENTITY_FIELDS = ["user_id", "order_id", "tenant_id"]
O erro comum deste passo: achar que user_id e order_id são ruído e apagar antes do hash. Aí dois sujeitos diferentes passam a dividir a mesma decisão cacheada
- Meça o efeito com
usage.input_tokens
Compare o consumo de entrada nas chamadas que aconteceram (miss) com o volume que você evitou (hit) e passe a régua do preço: US$ 0,042 por 1 milhão de tokens de entrada
curl -X POST https://api.typesafe.ai/v1/systemone \
-H "Authorization: Bearer $TYPESAFE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "jev-1.13.0",
"state": {"order_id": "A-921", "itens": 3, "risco": "medio"},
"questions": {
"vale_revisar": {
"type": "noul",
"instructions": "o pedido precisa de revisão manual?"
}
}
}'
O erro comum deste passo: ligar o cache e celebrar sem linha de base. Se tu não registrou usage.input_tokens ANTES, não tem com o que comparar depois. Esse contador é a mesma matéria-prima de monitorar as decisões em produção, então instrumente uma vez e use pros dois fins
E um aviso pra fechar a seção: não vou te dar percentual de economia nem hit rate esperado. Nenhuma fonte oficial ou independente publica isso, e chutar número aqui seria só enfeite
Três formas de chave que quebram o cache na prática
Caso 1: a decisão de uma tool call vaza pra outra sem relação
Sintoma: o veredito de uma chamada de ferramenta aparece aplicado a outra ferramenta, sem nenhuma relação entre elas
Causa: chave montada em cima do identificador temporário da tool call, tipo call_t1 e result_t1. Esses IDs reiniciam em t1 a cada compactação e a cada janela de histórico, então o t1 de agora não é o t1 de antes. É exatamente o que a issue #5 do omp-jev-compaction descreve
Solução: o formato do PR #6, ${stateDigest}:${name}:${identity}, com identidade derivada da tool call original, input completo, resultado completo e estado de erro
Prevenção: regra dura no código, nome temporário nunca vira identidade
Caso 2: dois usuários diferentes recebem a mesma decisão
Sintoma: a decisão sai coerente, mas é do outro sujeito
Causa: campos de identidade descartados antes do hash. A issue #2 do jevcache aponta isso direto: user_id e order_id saem na redação, e aí dois sujeitos diferentes compartilham a mesma entrada de cache. A chave lá é sha256(model ⊕ schema ⊕ canonical(redact(state))), e o buraco está dentro do redact
Solução: separar redação de segredo (tirar) de campo de identidade (manter, obrigatoriamente)
Prevenção: teste automatizado que muda só o user_id e exige chaves diferentes. É barato e pega a regressão no berço
Caso 3: credencial vazando num store legível
Sintoma: segredo aparecendo em arquivo que meio mundo consegue abrir
Causa: persistir um preview do state. A issue #1 do jevcache aponta que o state_preview guarda credenciais não redigidas num ledger legível por todos
Solução: guardar digest, só digest. Preview de state é conveniência de debug que cobra caro depois
Prevenção: o store do cache entra na sua superfície de segurança, não é pasta temporária
E já que estamos falando dele: o jevcache é distribuído como binário estático único, e o repositório tem a issue #3 apontando que não há código-fonte publicado, além da ausência de LICENSE na issue #2. São duas ressalvas de adoção pra pesar com calma antes de colocar isso no caminho de uma decisão
Vale lembrar também que nenhum desses projetos (omp-jev-compaction, jevcache, jevvc, typesafe-laravel, jev-sdk-java) aparenta ter vínculo oficial com a TypeSafe AI. É tudo comunidade
Quando reaproveitar e quando recalcular
O resumo da ópera fica assim:
| Situação | O que fazer |
|---|---|
state idêntico bit a bit, schema igual, identidade estável |
Reaproveitar |
Qualquer campo do state que entra no julgamento mudou |
Recalcular |
| Trocou o identificador de modelo (alias ou versão) | Recalcular |
Texto de instructions ou descrição de opção editado |
Recalcular |
| Decisão de aprovação (allow/deny) | Sempre recalcular |
O reaproveitamento é seguro quando três coisas seguram juntas: o state é idêntico, o schema das perguntas não mudou e o item avaliado tem identidade estável (mesma tool call, mesmo input, mesmo resultado, mesmo estado de erro)
Tirou uma dessas três pernas, o banco cai
E tem o caso especial: aprovação nunca é memoizada em silêncio. A issue #6 do jevvc resolve isso de um jeito limpo, com um CachingDecider que envolve outro Decider mas deixa a aprovação de fora da memoização. Mesmo que a decisão do Jev seja perfeitamente memoizável em teoria, um allow/deny reaproveitado é o tipo de bug que ninguém vê até ser tarde
Se o recálculo cair num momento ruim de rede ou de carga, a conversa vira outra: aí o assunto é timeout e fallback no Jev, não cache
A alternativa que muitas vezes resolve antes do cache
Se liga nisso, porque é a orientação oficial de eficiência e costuma ser ignorada: agrupe perguntas que compartilham o mesmo state num único request
Lembra que cada pergunta é avaliada de forma isolada e em paralelo contra o mesmo state? Então mandar cinco perguntas juntas é bem diferente de mandar cinco requests
E no limite de 32k, só a pergunta mais longa conta junto do state. O limite de 64k é que cobre state + todas as perguntas somadas
Muita gente monta cache pra resolver um problema que era só request mal agrupado
Implementações de referência pra olhar
Duas abordagens da comunidade que mostram caminhos diferentes:
- marcemarin/typesafe-laravel: SDK não oficial de PHP/Laravel que guarda a resposta sob uma chave derivada do hash de
model,stateequestions - luigivis/jev-sdk-java: cliente Java em que
JevRequesté um record de records, compara estruturalmente e pode ser usado direto como chave de mapa
A segunda é elegante pra caramba: a linguagem faz a canonicalização por você. A primeira é a rota clássica do hash explícito. As duas ignoram a camada de identidade do item avaliado, então se teu caso tem tool call ou item com ID temporário, volta pro passo 4
Pra começar do zero no assunto decisão
Pra quem está chegando agora na ideia de decidir de forma mais estruturada como dev, este vídeo do canal traz o pontapé inicial do tema:
Conclusão
Recapitulando o que importa:
Enquanto não existir cached_tokens na API, o cache de decisões do Jev é 100% do lado do cliente. Nenhum desconto vem de graça do fornecedor, o ganho é o request que você não fez
A chave precisa carregar estado E identidade, mais o modelo e o schema completo da pergunta. Nome de pergunta sozinho não é chave, ID temporário não é identidade, e alias não é versão
Aprovação fica fora do cache. Ponto
Próximo passo: instrumente usage.input_tokens antes de ligar qualquer cache, pra ter linha de base de verdade, e acompanhe a issue #10 do typesafe-sdk-js, que é o pedido aberto pra reuso de critérios idênticos e exposição de cache hits no SDK oficial em JavaScript
Se isso virar realidade, metade deste post vira legado, e vai ser ótimo 🙂
até o próximo post!
Perguntas frequentes
O Jev tem cache de prompt nativo como outras APIs de IA?
Não, a API do Jev não publica nenhuma dimensão de cache: não existe cache de prompt do lado do fornecedor nem campo cached_tokens na resposta. Isso é inclusive um pedido em aberto no SDK oficial em JavaScript, a issue #10 no repositório typesafe-ai/typesafe-sdk-js, pedindo reuso de critérios idênticos e exposição de cache hits. Enquanto isso não existe, todo cache que você usa é feito no seu próprio código.
Vale a pena cachear decisões do Jev pra economizar dinheiro?
Vale, porque o Jev cobra só entrada: US$ 0,042 por 1 milhão de tokens de entrada, o equivalente a US$ 42 por bilhão, com saída sempre gratuita. E o que entra nessa conta é o state mais as questions, incluindo instructions e descrições de opções. Pra medir se o cache está compensando, olhe o campo usage.input_tokens que vem em cada resposta.
jev-latest e jev-1.13.0 podem compartilhar a mesma chave de cache?
Não devem. Os identificadores disponíveis hoje são o id versionado jev-1.13.0 e os aliases jev-latest (padrão dos SDKs) e jev-preview. Como o alias pode apontar pra outra versão sem aviso, cachear sob jev-latest arrisca servir uma decisão de um modelo diferente do que está ativo agora.
Por que cachear decisões do Jev pelo ID temporário da tool call (tipo call_t1, result_t1) é perigoso?
Porque IDs temporários como t1 reiniciam a cada compactação e a cada janela de histórico, então eles não identificam de fato o item avaliado. Foi exatamente esse o bug relatado na issue #5 do projeto omp-jev-compaction, onde uma resposta sobre uma tool call acabava sendo reaproveitada em outra tool call sem relação nenhuma. A correção trocou a chave pra ${stateDigest}:${name}:${identity}, usando o digest da tool call, input, resultado e estado de erro como identidade real.
Existe algum cache pronto pra usar com o Jev, sem eu precisar montar a chave do zero?
Existe o jevcache, mantido no repositório hyperspaceai/jevcache, que chaveia como sha256(model ⊕ schema ⊕ canonical(redact(state))) e roda como binário estático com store local append-only. Antes de adotar, vale ler as issues abertas: a #3 aponta ausência de código-fonte no repositório, a #2 mostra que user_id e order_id são descartados antes do hash (o que pode juntar sujeitos diferentes na mesma decisão cacheada) e a #1 aponta que state_preview grava credenciais não redigidas num ledger legível por todos. Também há SDKs de comunidade com cache embutido, como o PHP marcemarin/typesafe-laravel e o Java luigivis/jev-sdk-java.
Decisões de aprovação (allow/deny) do Jev podem ser cacheadas normalmente?
Segundo a issue #6 do projeto jevvc (hexuria), não devem ser cacheadas silenciosamente, mesmo quando a decisão do Jev em si seria memoizável. O CachingDecider daquele projeto envolve outro Decider justamente pra separar esse caso, deixando a aprovação sempre fora do cache automático. É uma regra de segurança adotada pela comunidade, não uma imposição da API do Jev.
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.

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.

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.
