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

Diagrama mostrando o cache de decisões do Jev montado pelo cliente com digest do state e schema da pergunta
Resposta rápida

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, com Authorization: Bearer <API_KEY>, Content-Type: application/json e corpo exigindo os campos model, state e questions
  • Existem três tipos de pergunta: noul (sim/não com probabilidade), choice (uma opção de uma lista) e score (posição numa escala). O campo instructions é opcional em todos eles
  • O state aceita string, object, array ou null. Número ou booleano isolado é rejeitado com 422, então não tente ser esperto mandando um true cru
  • 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 pra state + a pergunta mais longa
  • Os identificadores de modelo disponíveis hoje são o id versionado jev-1.13.0 e os aliases jev-latest (padrão dos SDKs) e jev-preview
Domine o Jev e coloque decisões de IA dentro do seu sistema
Pré-inscrição Curso Jev

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

  1. Normalize o state e 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

  1. 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

  1. 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

  1. 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

  1. Decida com cuidado o que fica de fora do hash, e nunca grave state bruto 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

  1. 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, state e questions
  • 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.




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 Claude Code

Formação Claude Code

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

  • 120 aulas
  • 4 projetos
  • 9h 45min

Blog | Mais populares