Cache

Visão geral

Cache é a prática de armazenar a saída de uma computação cara para que requisições futuras possam ser atendidas de forma mais rápida e barata. Em sistemas de IA generativa — especialmente aqueles que usam geração aumentada por recuperação (retrieval-augmented generation, RAG) e incorporações vetoriais (embeddings) — cache é uma das otimizações de maior alavancagem porque pode reduzir:

  • Latência (menos chamadas ao modelo e menos buscas vetoriais)
  • Custo (tokens e computação de incorporações vetoriais frequentemente são cobrados)
  • Carga em serviços downstream (BD vetorial, reranqueadores, ferramentas externas)
  • Variância na experiência do usuário (tempos de resposta mais estáveis)

Este artigo foca em cache de prompt/resultado, cache de incorporações vetoriais e invalidação: a parte difícil do cache não é escrever valores; é decidir quando um valor em cache ainda está correto.

Onde o cache se encaixa em uma pilha moderna de GenAI

Um pipeline típico no estilo RAG tem várias etapas cacheáveis:

  1. Normalização da requisição (sanitizar/padronizar a consulta)
  2. Geração de incorporações vetoriais (incorporação vetorial da consulta, incorporações vetoriais de documentos)
  3. Recuperação a partir de um banco de dados vetorial (Busca Vetorial e Incorporações Vetoriais) e/ou índice esparso (Busca Híbrida (Esparsa + Densa))
  4. Reranqueamento com um codificador cruzado (cross-encoder) (Reranqueamento)
  5. Montagem do prompt (template + contexto recuperado + instruções de sistema)
  6. Geração com LLM (resposta final, potencialmente com saídas estruturadas; veja Saídas Estruturadas)
  7. Opcional: chamadas de ferramentas (Chamada de Funções / Uso de Ferramentas) e loops de agentes (RAG Agêntico)

Cada etapa pode ser colocada em cache de forma independente, e caches podem ser em camadas (em memória → Redis → banco de dados) dependendo de necessidades de desempenho e durabilidade.

Conceitos centrais de cache (específicos de IA)

Chaves de cache e determinismo

Cache só funciona quando você consegue mapear com confiança “mesma requisição” → “mesma resposta”. Para LLMs, “mesma requisição” geralmente é mais do que o texto do usuário. Inclui:

  • Identificador do modelo (por exemplo, gpt-4.1-mini)
  • Parâmetros de amostragem (temperature, top_p, seed se houver suporte)
  • Prompt de sistema e instruções do desenvolvedor
  • Versão do template de prompt
  • Esquemas de ferramentas e disponibilidade de ferramentas
  • Contexto recuperado (documentos + versões)
  • Configurações de segurança e regras de pós-processamento

Se qualquer um desses mudar, o resultado em cache pode se tornar inválido ou enganoso.

TTL, frescor e correção

Caches equilibram dois objetivos concorrentes:

  • Frescor: refletir os dados, modelos e políticas mais recentes
  • Eficiência: reaproveitar trabalho agressivamente

Estratégias comuns:

  • TTL (tempo de vida) (time-to-live): expirar entradas após uma duração fixa
  • Versionamento: incorporar versões nas chaves de cache para que novas versões automaticamente “errem” (miss)
  • Invalidação orientada a eventos: remover ativamente ou marcar entradas como obsoletas quando os dados subjacentes mudam
  • Obsoleto-enquanto-revalida (stale-while-revalidate): servir dados em cache rapidamente enquanto recompõe em segundo plano

Cache de prompt/resultado

Cache de prompt/resultado armazena uma saída do LLM (e frequentemente metadados como tokens, citações ou rastros de ferramentas) indexada por uma representação do estado completo de entrada.

O que colocar em cache

Entradas comuns de cache de resultado incluem:

  • Resposta final em texto
  • Saída JSON estruturada (se estiver usando esquemas)
  • Rastros intermediários: chamadas de ferramentas, IDs de documentos recuperados, citações
  • Decisões de segurança ou resultados de recusa (cuidado: mudanças de política podem invalidá-los)

Cache por correspondência exata (mais confiável)

Cache por correspondência exata é o mais seguro: só reutiliza quando as entradas são idênticas após a normalização.

Princípio-chave de design: Sua chave de cache deve representar tudo que afeta a saída.

Exemplo (pseudocódigo estilo Python):

import hashlib
import json

def stable_json(obj) -> str:
    return json.dumps(obj, sort_keys=True, separators=(",", ":"))

def make_llm_cache_key(request: dict) -> str:
    # request should include model, messages, tools schema, params, template version, etc.
    payload = stable_json(request).encode("utf-8")
    return hashlib.sha256(payload).hexdigest()

# Example request object
req = {
  "model": "gpt-4.1-mini",
  "messages": [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "Summarize the caching strategy."}
  ],
  "temperature": 0.0,
  "top_p": 1.0,
  "prompt_template_version": "rag-v3.2",
  "tool_schema_hash": "b1d2...",
  "retrieval_snapshot": {"index_version": 42, "doc_ids": ["A", "B"], "chunk_ids": [7, 9]}
}

key = make_llm_cache_key(req)

Notas:

  • Se você usa recuperação, inclua o snapshot de recuperação (IDs de documentos + versões). Caso contrário, você pode retornar uma resposta fundamentada em documentos antigos.
  • Use temperature=0 (ou baixo) para aumentar o determinismo e a taxa de acerto do cache. Com alta amostragem, o cache ainda funciona, mas a reutilização pode parecer inconsistente com as expectativas do usuário.

Normalização para aumentar a taxa de acerto

Você pode normalizar antes de fazer o hash para melhorar o reaproveitamento, por exemplo:

  • Remover espaços em branco extras, unificar quebras de linha
  • Normalizar aspas, pontuação, capitalização (cuidado: capitalização pode importar)
  • Canonicalizar argumentos JSON para ferramentas
  • Remover metadados irrelevantes (IDs de requisição, timestamps)

Risco: Normalização excessiva pode causar acertos incorretos (dois prompts “diferentes o suficiente” mapeando para a mesma chave). Seja conservador.

Cache parcial: template + campos dinâmicos

Muitos sistemas constroem prompts a partir de templates mais contexto recuperado. Você pode colocar em cache pedaços:

  • O prompt de sistema renderizado (raramente muda)
  • O bloco de contexto recuperado formatado para um conjunto dado de IDs de chunks
  • O prompt final montado

Isso é mais útil quando a montagem do prompt é cara (por exemplo, formatação complexa de citações) ou repetida em múltiplas chamadas ao modelo.

Cache semântico (semantic caching) (correspondência aproximada)

Cache semântico reutiliza resultados para consultas que são significativamente semelhantes, não idênticas. Tipicamente:

  1. Gerar a incorporação vetorial da consulta do usuário.
  2. Buscar em um índice de cache de consultas passadas (busca vetorial).
  3. Se a similaridade exceder um limiar, reutilizar a resposta em cache.

Isso pode aumentar drasticamente a taxa de acerto em suporte ao cliente, FAQ e bases de conhecimento corporativas.

No entanto, isso introduz novos riscos:

  • Falsos positivos: perguntas similares que exigem respostas diferentes
  • Deriva de contexto: detalhes específicos do usuário (permissões, localidade, versão do produto) podem diferir
  • Deriva de política e segurança: saídas em cache podem não refletir guardrails atualizados

Se você usar cache semântico, considere:

  • Incluir filtros de metadados (tenant, localidade, versão do produto, papel do usuário)
  • Armazenar snapshots de recuperação e validá-los
  • Usar uma etapa de verificação (LLM barato ou regras) para confirmar aplicabilidade

Cache semântico frequentemente combina bem com RAG: reutilize os resultados de recuperação com mais facilidade do que a resposta final.

Cache de recuperação e reranqueamento

Em sistemas RAG, a “resposta” depende fortemente do contexto recuperado. Colocar em cache as etapas de recuperação pode reduzir latência sem “congelar” a saída final.

Colocar em cache resultados de recuperação (consulta → top-k IDs de documentos)

Coloque em cache a saída da sua busca vetorial:

  • Chave: incorporação vetorial da consulta (ou texto normalizado da consulta + versão do modelo de incorporações vetoriais)
  • Valor: lista de (doc_id, chunk_id, score, index_version)

Isso é especialmente eficaz quando:

  • Muitos usuários fazem as mesmas perguntas
  • O BD vetorial é um gargalo
  • Suas atualizações de índice são periódicas (por exemplo, jobs diários de ingestão; veja Ingestão e Chunking)

Requisito de invalidação: caches de recuperação precisam ser invalidados quando o índice muda (mais sobre isso abaixo).

Colocar em cache resultados de reranqueamento (consulta + candidatos → lista reranqueada)

Reranqueamento (pontuação com codificador cruzado) pode ser caro. Coloque em cache as saídas do reranqueador indexadas por:

  • Texto da consulta (ou incorporação vetorial)
  • IDs de chunks candidatos (ordenados ou como um conjunto)
  • Modelo/versão do reranqueador

Isso melhora o throughput em sistemas de alto tráfego e reduz pontuações repetidas.

Cache de incorporações vetoriais

Incorporações vetoriais são usadas em dois lugares principais:

  1. Ingestão offline: gerar incorporações vetoriais de chunks de documentos
  2. Consultas online: gerar incorporações vetoriais de consultas de usuários para recuperação ou cache semântico

Cache de incorporação vetorial de documentos (endereçado por conteúdo)

Para chunks de documentos, o cache frequentemente é implementado como armazenamento endereçado por conteúdo (content-addressed storage):

  • Chave: hash do texto normalizado do chunk + ID do modelo de incorporações vetoriais + versão de pré-processamento
  • Valor: vetor de incorporação vetorial + metadados (dimensões, timestamps)

Se você reexecutar a ingestão, chunks inalterados podem reutilizar incorporações vetoriais armazenadas.

Exemplo de componentes da chave:

  • embedding_model = "text-embedding-3-large"
  • preprocess_version = "chunk-v5" (captura regras de tokenização, remoção de boilerplate, etc.)
  • content_hash = sha256(normalized_text)

Isso evita recomputação desnecessária quando apenas partes não relacionadas do corpus mudam.

Cache de incorporação vetorial de consulta

Para consultas online, coloque em cache incorporações vetoriais de consulta por:

  • Texto normalizado da consulta
  • ID/versão do modelo de incorporações vetoriais
  • Opcional: localidade do usuário, domínio ou tenant (se o significado da consulta depender disso)

Cache de incorporação vetorial de consulta geralmente é seguro e eficaz porque incorporações vetoriais são determinísticas para um dado modelo e entrada (sem amostragem). Pode reduzir de forma relevante a latência em pipelines que fazem múltiplas buscas por requisição (por exemplo, decomposição de consulta em RAG Agêntico).

Armadilhas no cache de incorporações vetoriais

  • Atualizações de modelo: espaços de incorporações vetoriais mudam; você deve versionar seu cache.
  • Mudanças de dimensionalidade: um novo modelo pode produzir vetores de tamanho diferente.
  • Deriva de normalização: mudanças na limpeza de texto podem alterar sutilmente as incorporações vetoriais e a qualidade da recuperação.

Trate o pipeline de incorporações vetoriais como um artefato versionado.

Invalidação: o desafio central

Invalidação responde: quando uma entrada em cache não deve mais ser usada?

Em sistemas de IA, gatilhos de invalidação vêm de quatro fontes amplas:

1) Mudanças de modelo e parâmetros

Resultados em cache podem se tornar inválidos se você mudar:

  • Nome do modelo de LLM ou snapshot
  • Parâmetros de amostragem (temperature, top_p, max tokens)
  • Prompt de sistema ou políticas de segurança
  • Esquemas de ferramentas (assinaturas de função, restrições de schema JSON)
  • Regras de formatação de saída (Saídas Estruturadas)

Boa prática: incluir tudo isso na chave de cache (ou em um “carimbo de versão” incluído na chave). Assim, mudanças automaticamente direcionam para um novo namespace de cache.

2) Mudanças de dados (atualizações do corpus)

Se você usa RAG, sua resposta depende dos documentos subjacentes.

Mudanças de dados incluem:

  • Novos documentos adicionados
  • Documentos existentes editados ou removidos
  • Mudanças na estratégia de chunking (Ingestão e Chunking)
  • Mudanças de metadados que afetam filtros (ACLs, tags de departamento, versões de produto)

Abordagens:

  • Incluir um index_version nas chaves (simples, comum)
  • Rastrear versões no nível de documento e invalidar apenas entradas que referenciem documentos alterados (mais complexo, porém mais eficiente)

3) Mudanças de controle de acesso e personalização

Se as saídas dependem de identidade do usuário, papel, direitos, região ou preferências, você deve evitar vazamento entre usuários.

Opções:

  • Cache por usuário (seguro, menor taxa de acerto)
  • Cache por tenant (comum em SaaS)
  • Cache compartilhado com filtragem estrita de metadados (mais difícil de raciocinar)

No mínimo, inclua tenant_id e qualquer escopo relevante de autorização na chave, ou aplique filtragem antes de reutilizar.

4) Mudanças de política, segurança e conformidade

Políticas de segurança evoluem (conteúdo proibido, tratamento de PII, requisitos de citação; veja Fundamentação e Citações). Uma resposta em cache que era aceitável no mês passado pode não ser aceitável agora.

Mitigações:

  • Versione sua política de segurança e inclua-a na chave
  • Mantenha TTLs curtos para domínios de alto risco
  • Reexecute verificações de segurança em saídas em cache antes de servir (um “gate de cache”)

Estratégias comuns de invalidação (e quando usá-las)

Invalidação baseada em TTL

O que é: expirar entradas após uma janela de tempo (minutos a dias).

  • Prós: simples, obsolescência limitada
  • Contras: não garante correção após uma atualização crítica; pode causar misses desnecessários

Use TTLs quando:

  • Dados mudam frequentemente e rastrear dependências exatas é complexo demais
  • Você pode tolerar obsolescência leve (por exemplo, resumos genéricos)

Namespaces versionados (“aumente a versão”)

O que é: incluir um token de versão na chave: prompt_template_version, index_version, embedding_model_version, etc.

  • Prós: muito confiável; modelo mental simples
  • Contras: pode invalidar demais de uma vez (cache “frio” no início)

Use quando:

  • Implementar novos modelos/templates
  • Reconstruir índices
  • Alterar chunking ou algoritmos de recuperação

Invalidação baseada em dependências (direcionada)

O que é: armazenar dependências (por exemplo, quais doc_ids foram usados). Quando um documento muda, invalidar todas as entradas que o referenciaram.

  • Prós: precisa; preserva o cache quando possível
  • Contras: complexidade de engenharia; requer um índice reverso de doc → chaves de cache

Isso é valioso em bases de conhecimento corporativas onde:

  • Atualizações são frequentes
  • Requisitos de latência/custo são rigorosos
  • Respostas precisam estar frescas

Obsoleto-enquanto-revalida (SWR) (stale-while-revalidate)

O que é: servir o resultado em cache imediatamente se ele não estiver “velho demais”, mas disparar uma atualização em segundo plano.

  • Prós: baixa latência; rollouts suaves
  • Contras: clientes podem ver resultados desatualizados brevemente

SWR funciona bem para:

  • FAQs de alto tráfego
  • Resumos analíticos não críticos
  • Sugestões de autocompletar

Exemplos práticos

Exemplo 1: Colocar em cache uma resposta de RAG com segurança

Uma abordagem robusta é colocar em cache (recuperação → geração) com versões explícitas.

  • Cache de incorporação vetorial de consulta: indexado por (query_text_normalized, embedding_model_version)
  • Cache de resultados de recuperação: indexado por (query_embedding_hash, index_version, filters)
  • Cache da resposta final do LLM: indexado por (model_version, prompt_template_version, retrieval_result_hash, safety_policy_version)

Isso garante:

  • Se o índice atualizar, o cache de recuperação naturalmente falha (novo index_version)
  • Se o template do prompt mudar, o cache de resposta naturalmente falha
  • Se o mesmo snapshot de recuperação aparecer novamente, o cache de resposta acerta

Exemplo 2: Cache do resultado de chamadas de ferramentas

Se seu LLM usa ferramentas (clima, precificação, consulta a banco de dados), coloque em cache a saída da ferramenta separadamente da saída do LLM.

  • Resultados de ferramentas frequentemente têm TTLs mais claros (por exemplo, “cotação válida por 5 minutos”).
  • Se resultados de ferramentas estiverem em cache, a chamada ao LLM pode ser mais barata e mais estável.

A chave para uma chamada de ferramenta pode incluir:

  • Nome da ferramenta
  • JSON de argumentos canonicalizado
  • Escopo de usuário/tenant (se permissões se aplicarem)
  • TTL baseado em regras do domínio

Isso se encaixa naturalmente com Chamada de Funções / Uso de Ferramentas.

Exemplo 3: Cache de incorporações vetoriais para ingestão

Ao reindexar um corpus todas as noites:

  • Calcule content_hash para cada chunk
  • Se (content_hash, embedding_model_version) existir, reutilize a incorporação vetorial
  • Caso contrário, compute a incorporação vetorial e armazene

Isso reduz reprocessamento quando apenas uma pequena fração de documentos muda.

Considerações operacionais

Armazenamento, memória e políticas de expulsão

Políticas comuns de expulsão:

  • LRU (least recently used): bom padrão para caches com memória limitada
  • LFU (least frequently used): bom quando a frequência de acesso é altamente enviesada
  • Expulsão sensível a custo: manter entradas que são caras de computar (prompts longos, grandes conjuntos de reranqueamento)

Para saídas de LLM, considere armazenar:

  • Texto comprimido (gzip)
  • Contagens de tokens e métricas de latência (para quantificar economias)
  • Uma “impressão digital” curta do contexto usado (IDs de documentos) em vez de blocos completos de contexto

Observabilidade e avaliação

Acompanhe:

  • Taxa de acerto do cache por camada (incorporações vetoriais/recuperação/resposta)
  • Economias: tokens evitados, consultas ao BD vetorial evitadas
  • Incidentes de obsolescência: quando respostas em cache conflitam com novos documentos
  • Regressões de qualidade: teste A/B com e sem cache para consultas sensíveis

Em RAG, meça se o cache aumenta alucinações ao reutilizar respostas com contextos incompatíveis.

Segurança e privacidade

Tenha cautela ao colocar em cache:

  • Texto sensível fornecido pelo usuário
  • Saídas do modelo que podem conter PII
  • Saídas de ferramentas contendo segredos ou dados internos

Mitigações:

  • Criptografar em repouso
  • Evitar compartilhamento entre tenants a menos que seja comprovadamente seguro
  • Aplicar limites de retenção
  • Considerar não usar cache para categorias de “intenção sensível”

Quando *não* usar cache

Evite ou limite cache quando:

  • Respostas devem refletir dados em tempo real (trading, resposta a incidentes)
  • O sistema é altamente personalizado e o reaproveitamento por usuário é baixo
  • O modelo é intencionalmente estocástico (escrita criativa), onde a reutilização parece errada
  • Requisitos legais/conformidade exigem frescor estrito e auditabilidade

Checklist de design

  • Completude da chave: a chave inclui tudo que pode mudar a saída (modelo, parâmetros, versão do prompt, snapshot de recuperação, versão da política de segurança)?
  • Escopo: o cache é compartilhado entre usuários/tenants com segurança?
  • Estratégia de invalidação: TTL, incremento de versão, rastreamento de dependências ou SWR?
  • Camadas: você está colocando em cache incorporações vetoriais, recuperação, reranqueamento e geração separadamente onde isso ajuda?
  • Comportamento de fallback: o que acontece em caso de corrupção de cache, misses parciais ou entradas obsoletas?
  • Métricas: você consegue quantificar economias de custo/latência e impacto na qualidade?

Resumo

Cache em sistemas de IA generativa não é apenas um detalhe de infraestrutura; é uma preocupação de correção e qualidade de produto. Os designs mais robustos tratam cada etapa do pipeline como versionada, colocam em cache artefatos determinísticos (incorporações vetoriais, resultados de recuperação) agressivamente, e colocam em cache saídas finais do LLM com cuidado, usando chaves que codificam o estado completo (modelo, prompt/template, snapshot de recuperação, políticas). A invalidação — via TTL, versionamento, expurgos orientados a eventos ou obsoleto-enquanto-revalida — é o que transforma o cache de um hack de desempenho em um componente confiável do sistema.