Um cache semântico responde "como eu redefino minha senha" com a resposta que já gerou para "esqueci meu login", porque as duas significam a mesma coisa. Em vez de comparar a string exata de uma requisição, ele transforma a requisição em embedding e procura uma requisição salva cujo embedding esteja próximo o suficiente, e então retorna essa resposta em cache sem chamar o modelo. Quando funciona, você pula uma chamada de modelo inteira: sem tokens, sem latência, sem custo. Quando erra o julgamento de "próximo o suficiente", ele serve uma resposta confiante para uma pergunta que o usuário não fez.

O reenquadramento central: um cache normal chaveia por identidade, um cache semântico chaveia por similaridade, e similaridade é um limiar que você define, não um fato. Esse limiar é toda a superfície de risco. Deixe frouxo e você serve respostas erradas; deixe apertado e você praticamente não guarda nada em cache. Tudo em rodar bem um cache semântico está em gerenciar esse dial.

Como funciona, sem a mágica

O mecanismo tem três passos. Transforme a requisição recebida em um vetor usando um modelo de embeddings como o Amazon Titan Text Embeddings ou o Cohere no Bedrock. Busque em um vector store pela requisição anterior mais próxima. Se a similaridade de cosseno do vizinho mais próximo estiver acima do seu limiar, retorne a resposta em cache; caso contrário, chame o modelo, e então salve o novo embedding da requisição e a resposta para a próxima vez.

vec = embed(request)
hit = vector_store.nearest(vec)
if hit and hit.similarity >= THRESHOLD:
    return hit.cached_response      # no model call
answer = model.invoke(request)
vector_store.put(vec, answer)
return answer

O vector store pode ser qualquer coisa que você já opera: OpenSearch, Aurora com pgvector, ou um índice em memória para um conjunto pequeno e quente. A parte difícil nunca é o armazenamento. É o valor de THRESHOLD e o que você permite entrar no cache em primeiro lugar.

O risco de falso hit é o jogo inteiro

Um falso hit é quando duas requisições estão próximas no espaço de embeddings mas deveriam ter respostas diferentes. "Qual é nossa política de reembolso para clientes da UE" e "qual é nossa política de reembolso para clientes dos EUA" podem ficar perigosamente próximas uma da outra no espaço vetorial, exigindo respostas diferentes. Um limiar frouxo retorna a resposta da UE para um usuário dos EUA, e faz isso com total confiança, porque o modelo nunca foi consultado.

Isso é pior do que um cache miss normal, que só custa uma chamada de modelo. Um falso hit custa uma resposta errada que parece certa. Então o limiar precisa ser ajustado contra tráfego real, não chutado, e deveria ser enviesado para o apertado: um miss é barato, um falso hit pode virar um ticket de suporte ou um problema de compliance. Meça a taxa de falso hit em uma amostra rotulada antes de confiar o cache com qualquer coisa que importe.

O que não colocar em cache

Cache semântico se encaixa em conhecimento estável, geral e compartilhado e não se encaixa em mais nada. Coloque em cache a resposta para "como eu exporto meus dados", que é a mesma para todo mundo. Não coloque em cache nada que dependa de quem está perguntando ou de quando. Regras práticas que vale a pena impor no código:

  • Nunca faça cache entre fronteiras de identidade. Chaveie o cache por tenant, ou exclua qualquer coisa personalizada. Um cache compartilhado que ignora quem chama vai eventualmente servir a resposta de um cliente para outro.
  • Nunca faça cache de respostas sensíveis ao tempo. Saldos, status de pedido e estoque mudam sem você saber. Um hit desatualizado aqui é um bug novinho em folha.
  • Coloque em cache o que independe de retrieval, não o que depende dele. Se a resposta depende de documentos que mudam, coloque em cache a busca do embedding, não a resposta final.

Invalidação é a parte que todo mundo pula

A piada mais antiga da computação é que invalidação de cache é um dos dois problemas difíceis, e um cache semântico não ganha isenção. Quando a verdade subjacente muda, quando uma política é atualizada, um preço se move, um documento é revisado, toda resposta em cache que se apoiava na verdade antiga agora está errada e vai continuar sendo servida até você removê-la.

Dê às entradas um time-to-live curto o suficiente para que respostas desatualizadas expirem sozinhas, e adicione um caminho de purga explícito ligado aos eventos que mudam a verdade de base: publicar uma nova versão de política deveria invalidar a fatia do cache construída sobre a antiga. Sem uma estratégia de invalidação, um cache semântico não só economiza chamadas, ele silenciosamente prende seu produto aos fatos de ontem.

A conclusão

Cache semântico é uma vitória real em custo e latência para perguntas que são a mesma pergunta em palavras diferentes, e é uma arapuca para qualquer coisa personalizada, sensível ao tempo ou sujeita a mudança. O mecanismo é trivial: transforme em embedding, busque, aplique limiar. A engenharia está inteiramente no limiar, nas regras de escopo e no caminho de invalidação. Ajuste o limiar contra tráfego rotulado, coloque em cache só conhecimento compartilhado estável, e invalide nos eventos que movem a verdade. Faça essas três coisas e o cache se paga sozinho. Pule-as e ele serve respostas erradas mais rápido do que o modelo teria servido respostas certas.

Leia isso a seguir

Para o lado de plataforma de rodar um vector store e sua remoção sob carga, as notas de campo de cloud estão em ercan.cloud, e o hub fica em ercanermis.com.