Blog

Cache semântico para reduzir custo de LLM

Duas pessoas perguntam a mesma coisa de dois jeitos diferentes: "como cancelo meu pedido?" e "quero desistir da compra, o que faço?". Para um cache tradicional, são duas chaves distintas, dois cache miss, duas chamadas ao modelo, duas faturas. Mas a intenção é idêntica, e a resposta poderia ser a mesma. O cache semântico existe para explorar exatamente isso: em vez de casar a pergunta por texto exato, casa por significado. Ele transforma a pergunta em embedding, procura no histórico a pergunta mais parecida e, se a similaridade passar de um limiar, devolve a resposta já gerada sem chamar o modelo. Bem calibrado, um cache semântico corta uma fatia grande do custo de LLM e derruba a latência de perguntas repetidas de segundos para milissegundos. Mal calibrado, ele entrega a resposta de uma pergunta para outra parecida mas diferente, o que é pior do que não ter cache nenhum. Este artigo mostra como construir essa camada com a régua no lugar certo: o limiar de similaridade, o TTL e a invalidação, o que nunca deve ir para o cache e a métrica que diz se ele está ajudando ou atrapalhando.

2026-07-12 / IA Aplicada / 12 min

01

Cache exato não serve para linguagem natural

Um cache tradicional casa a chave por igualdade byte a byte: mesma string, mesmo hash, cache hit. Isso funciona para consultas estruturadas (id de produto, url, parâmetro numérico), onde duas requisições iguais são de fato a mesma requisição. Mas linguagem natural quebra essa premissa. A mesma intenção aparece em infinitas formas: sinônimos, ordem das palavras, gírias, erros de digitação, pontuação. "Qual o prazo de entrega?" e "em quantos dias chega?" têm hash completamente diferente e resposta idêntica. Num cache exato, cada variação é um miss, e o modelo é chamado de novo para responder algo que ele já respondeu com outras palavras.

O cache semântico troca o critério de comparação. Em vez de perguntar "essa string é idêntica?", ele pergunta "essa pergunta significa a mesma coisa que alguma que já respondi?". A tabela abaixo separa os dois para deixar claro em que cada um serve.

DimensãoCache exatoCache semântico
Critério de acertoString idêntica (hash)Similaridade de significado (embedding)
Serve bem paraChave estruturada, id, urlPergunta em linguagem natural
Variação de formaCada variação é um missFormas equivalentes casam
Risco principalBaixa taxa de acertoCasar perguntas parecidas mas diferentes

O ganho é claro: onde o cache exato acerta quase nada em linguagem natural, o semântico captura a família inteira de variações de uma mesma pergunta. O preço é uma nova classe de erro que o cache exato nunca teve: o falso positivo, quando duas perguntas parecidas mas com respostas diferentes acabam casando. Todo o resto do artigo gira em torno de manter esse ganho sem pagar esse preço.

02

Como funciona: embedding, similaridade e limiar

O mecanismo tem três peças. Primeiro, o embedding: a pergunta vira um vetor de números que representa seu significado, e perguntas com sentido parecido geram vetores próximos no espaço. Segundo, a busca por similaridade: a nova pergunta é comparada com as perguntas já em cache, e a mais próxima (maior similaridade de cosseno) é a candidata. Terceiro, o limiar: só é cache hit se a similaridade da candidata passar de um corte que você define. Abaixo do limiar, trata como miss, chama o modelo e guarda a nova resposta. O fluxo abaixo mostra o caminho completo de uma pergunta.

Caminho de uma pergunta pelo cache semântico

  pergunta do usuário
        |
        v
  gera embedding da pergunta
        |
        v
  busca a pergunta mais parecida no cache
        |
        v
  [ similaridade >= limiar? ]
     |  sim                    |  não
     v                         v
  CACHE HIT               CACHE MISS
  devolve resposta        chama o modelo
  guardada (sem LLM)      guarda pergunta+resposta+embedding
     |                         |
     v                         v
  responde em ms          responde e popula o cache

  hit = economia de token + latência baixa
  miss = custo normal, mas alimenta o próximo hit
// cache/semantic.js
// Cache semantico minimo: casa a pergunta por significado, nao por texto.
// So devolve do cache se a similaridade passar do limiar calibrado.

export function createSemanticCache({ embed, callModel, store, threshold = 0.92 }) {
  return async function ask(question) {
    const vector = await embed(question);

    // Procura a pergunta mais parecida ja respondida.
    const best = await store.nearest(vector); // { score, answer } | null

    // So e hit se a similaridade passar do limiar. Abaixo dele, e miss:
    // perguntas "parecidas" nao sao garantia de mesma resposta.
    if (best && best.score >= threshold) {
      return { answer: best.answer, cached: true, score: best.score };
    }

    // Miss: chama o modelo e alimenta o cache para o proximo acerto.
    const answer = await callModel(question);
    await store.put({ question, vector, answer });
    return { answer, cached: false };
  };
}

Repare que o miss não é desperdício: ele paga o custo normal da chamada, mas grava a pergunta, o embedding e a resposta, de modo que a próxima variação equivalente vira hit. O cache aquece com o uso. O ponto sensível é um só, o valor de threshold, e é ele que decide se a camada ajuda ou entrega resposta trocada. É o que a próxima seção detalha.

03

Calibrar o limiar: o único parâmetro que importa

O limiar de similaridade é o único botão de verdade do cache semântico, e ele governa um trade-off direto. Limiar alto demais e quase nada casa: o cache vira um cache exato caro, com pouca economia. Limiar baixo demais e coisas parecidas mas diferentes casam: "como cancelo o pedido?" recebe a resposta de "como cancelo a assinatura?", e o usuário leva informação errada com a confiança de uma resposta pronta. O erro perigoso é o segundo, porque é silencioso: não estoura exceção, não aparece em log de erro, só entrega a resposta errada com cara de certa.

  • Não chute o limiar: colete pares de perguntas reais rotuladas como "mesma resposta" e "resposta diferente" e meça a similaridade de cada par. O limiar certo é o que separa as duas nuvens.
  • Prefira errar para o lado seguro: em domínio sensível (cobrança, saúde, jurídico), um limiar mais alto perde alguns hits, mas evita o falso positivo, que ali é caro. Miss custa token; falso positivo custa confiança.
  • Calibre por domínio, não global: perguntas de FAQ toleram limiar mais baixo; perguntas com dado específico (valor, prazo, id) exigem limiar alto porque uma palavra muda a resposta inteira.
  • Revise o limiar com dado, não com sensação: o baseline de hoje só se compara ao de amanhã se você tiver a métrica; sem medir, você ajusta no escuro.

A regra que resume: o limiar não é uma constante universal, é uma decisão de risco por domínio. Um assistente de FAQ genérico e um bot de suporte financeiro não podem ter o mesmo corte, porque o custo de uma resposta trocada é radicalmente diferente entre eles. Comece conservador (limiar alto), meça a taxa de acerto e de falso positivo, e só então afrouxe onde o risco permite.

04

O que nunca deve ir para o cache

Cache semântico assume uma coisa: a mesma pergunta tem a mesma resposta. Quando essa premissa não vale, o cache não só deixa de ajudar, ele mente. O caso mais óbvio é a pergunta cuja resposta depende de dado vivo ou de contexto do usuário. "Qual o status do meu pedido?" é a mesma frase para dois usuários, casaria no cache com similaridade quase perfeita, e serviria o status de um para o outro. A resposta não pode ser cacheada porque não é função só da pergunta, é função da pergunta mais o estado do usuário mais o instante.

// cache/policy.js
// Nem toda pergunta pode ser cacheada. Antes de consultar ou gravar,
// decide se aquela resposta e estavel o suficiente para virar cache.

export function isCacheable(question, ctx) {
  // Resposta depende do usuario: status do pedido, saldo, dado pessoal.
  if (ctx.usesUserData) return false;

  // Resposta depende do agora: preco, estoque, disponibilidade, cotacao.
  if (ctx.usesLiveData) return false;

  // Pergunta que dispara acao (cancelar, reembolsar) nunca e "resposta cacheavel".
  if (ctx.triggersAction) return false;

  // FAQ estavel, explicacao de politica, definicao: pode cachear.
  return true;
}

// No fluxo: so passa pelo cache semantico se isCacheable() for true.
// Caso contrario, vai direto ao modelo (ou a uma tool de dado vivo).

A separação prática é entre pergunta de conhecimento estável e pergunta de dado dinâmico. FAQ, definição, explicação de política, passo a passo de um processo: são estáveis, a resposta hoje é a resposta amanhã, e cachear é seguro e rentável. Status, saldo, preço, disponibilidade, qualquer coisa por usuário ou por instante: são dinâmicas, e ali o cache semântico é um vetor de vazamento e de resposta obsoleta. A porta de entrada do cache tem que ter esse filtro, senão a economia vem às custas de servir dado de um usuário para outro, um erro muito mais caro do que a chamada que você economizou.

05

TTL, invalidação e quando a resposta certa vira errada

Mesmo uma pergunta cacheável tem validade. A política de reembolso muda, o prazo de entrega é revisado, o texto da FAQ é reescrito: a resposta que estava correta ontem passa a estar errada hoje, e um cache que a serve indefinidamente propaga informação desatualizada com a autoridade de uma resposta pronta. Por isso o cache semântico precisa de dois mecanismos de expiração. O TTL (time to live) dá um prazo de validade a cada entrada: passou do prazo, a entrada expira e a próxima pergunta força um miss que regenera a resposta. A invalidação explícita permite derrubar entradas quando você sabe que a fonte mudou, sem esperar o TTL.

  1. Defina TTL por natureza do conteúdo: uma definição estável pode viver dias; uma política que muda com frequência merece TTL curto, de horas, para não servir versão vencida por muito tempo.
  2. Invalide na fonte, não no cache: quando o texto da FAQ ou a política de reembolso é editada, dispare a invalidação das entradas relacionadas nesse momento, em vez de confiar só no relógio do TTL.
  3. Trate expiração como miss normal: entrada vencida não é erro, é um miss que chama o modelo, regenera a resposta e reaquece o cache com a versão nova.
  4. Não deixe cache crescer sem limite: além do TTL, imponha um teto de tamanho com despejo do menos usado, senão o custo de memória e de busca corrói a economia que o cache trouxe.

O erro clássico é montar o cache semântico e nunca pensar em expiração, tratando toda resposta como eterna. O resultado é sutil e corrosivo: o sistema fica cada vez mais rápido e mais barato, mas também cada vez mais desatualizado, respondendo com políticas e prazos que não valem mais. TTL e invalidação são o que mantém o cache alinhado com a verdade da fonte. Sem eles, você troca custo de token por dívida de correção, que cobra juros em forma de reclamação de cliente.

06

Medir se o cache está ajudando ou atrapalhando

Um cache semântico sem métrica é um palpite caro. Você precisa de números para saber se ele está entregando economia real e, mais importante, se não está servindo resposta trocada. Quatro sinais bastam para operar com confiança. A taxa de acerto (hit rate) diz quanto do tráfego o cache está poupando de chamar o modelo, e é a medida direta da economia. A economia estimada traduz o hit rate em token e custo evitados. A latência compara o tempo de um hit (milissegundos) com o de um miss (a chamada inteira ao modelo). E a taxa de falso positivo, a mais importante e a mais ignorada, mede quantos hits entregaram a resposta errada.

MétricaO que revelaQuando agir
Hit rateFração de perguntas servidas do cacheMuito baixo: limiar alto demais ou pouco tráfego repetido
Custo evitadoToken e dinheiro poupados pelos hitsJustifica (ou não) manter a camada
Latência hit vs missGanho de tempo por resposta cacheadaConfirma o benefício de experiência
Falso positivoHits que serviram resposta erradaQualquer valor relevante: suba o limiar já

A taxa de falso positivo é a que separa um cache que você confia de um que assusta, e é a mais difícil de medir porque não estoura sozinha. Uma amostragem periódica de hits revisada por humano ou por um modelo juiz revela se a resposta servida do cache batia com a que o modelo teria dado. Falso positivo subindo é ordem direta para elevar o limiar, mesmo que isso derrube um pouco o hit rate. A hierarquia é clara: economia é bom, mas resposta certa vem primeiro. Um cache que economiza 40% mas serve 3% de respostas trocadas não vale a pena; um que economiza 25% com falso positivo perto de zero é ouro.

07

Montar a camada sem virar risco novo

O cache semântico é uma das poucas otimizações de LLM com retorno imediato: reduz custo e latência ao mesmo tempo, e o esforço de implementação é modesto. Mas é uma otimização que introduz uma classe de erro nova, então a ordem de implantação importa tanto quanto o código. O caminho é começar seguro e afrouxar com dado na mão.

  1. Comece pelo filtro de cacheabilidade: antes de tocar em embedding, garanta que pergunta com dado vivo ou por usuário nunca entra no cache. Esse filtro previne o erro mais caro (dado de um usuário servido a outro).
  2. Ligue o cache só na leitura primeiro, com limiar alto: observe o hit rate e amostre os hits para medir falso positivo antes de confiar na camada para valer.
  3. Instrumente as quatro métricas desde o dia um: hit rate, custo evitado, latência e falso positivo. Sem elas você não sabe se está economizando ou entregando resposta trocada.
  4. Adicione TTL e invalidação antes de escalar: sem expiração, o cache fica desatualizado silenciosamente, e o problema só aparece em reclamação de cliente.
  5. Afrouxe o limiar por domínio, com base na métrica: onde o falso positivo é baixo e o hit rate baixo, dá para descer o corte; onde o risco é alto, mantenha conservador.

A diferença entre um cache semântico que corta a fatura sem ninguém perceber e um que serve resposta trocada está inteira na régua e no filtro, não no algoritmo de embedding. O embedding é commodity; o limiar calibrado por domínio, o filtro de cacheabilidade, o TTL e a métrica de falso positivo são o que transforma uma ideia atraente numa camada de produção confiável. Bem feito, é uma das melhores relações custo-benefício de um sistema com LLM: poucas centenas de linhas que pagam token, latência e experiência, sem trocar economia por resposta errada.

FAQ

Perguntas frequentes

Cache semântico não é a mesma coisa que um cache normal com a pergunta como chave?

Não. Um cache normal casa a chave por igualdade exata: mesma string, mesmo hash. Em linguagem natural isso quase nunca acerta, porque a mesma intenção aparece em infinitas formas ("qual o prazo?" e "em quantos dias chega?" têm hash diferente e resposta idêntica). O cache semântico troca o critério: transforma a pergunta em embedding e casa por significado, usando similaridade de cosseno com um limiar. Assim ele captura a família inteira de variações de uma mesma pergunta, algo impossível para o cache exato. O preço é uma classe de erro nova, o falso positivo, que o cache exato não tem.

Qual o maior risco do cache semântico e como controlar?

O maior risco é o falso positivo: casar duas perguntas parecidas mas com respostas diferentes e servir a resposta de uma para a outra. É um erro silencioso, não estoura exceção, só entrega informação errada com cara de resposta pronta. Controla-se com o limiar de similaridade calibrado por domínio (mais alto em domínio sensível como cobrança ou saúde), com o filtro de cacheabilidade (dado vivo e por usuário nunca entram no cache) e com a métrica de falso positivo medida por amostragem. A regra é simples: economia é bom, mas resposta certa vem primeiro; falso positivo subindo é ordem direta para elevar o limiar.

Toda pergunta pode ser cacheada?

Não. O cache semântico assume que a mesma pergunta tem a mesma resposta, e isso só vale para conhecimento estável (FAQ, definição, política, passo a passo). Perguntas cuja resposta depende de dado vivo (preço, estoque, cotação) ou de contexto do usuário (status do pedido, saldo, dado pessoal) não podem ser cacheadas, porque a resposta não é função só da pergunta, é função da pergunta mais o estado mais o instante. Cachear "qual o status do meu pedido?" serviria o status de um usuário para outro. Por isso a porta de entrada do cache precisa de um filtro que barra pergunta dinâmica antes de qualquer busca por similaridade.

Cache semântico corta custo de LLM quando a régua está no lugar certo

Casar pergunta por significado economiza token e latência de perguntas repetidas, mas só é seguro com limiar calibrado por domínio, filtro do que não pode ser cacheado, TTL com invalidação e métrica de falso positivo. Posso desenhar essa camada no seu sistema de IA, do embedding à observabilidade, cortando fatura sem trocar economia por resposta errada.