Semantic Cache: Como Otimizar Aplicações RAG com Cache Semântico
Source: Dev.to
Disclaimer
Este texto foi inicialmente concebido pela IA Generativa em função da transcrição de um vídeo do canal de Daniel Romero (a pessoa que lidera nossa especialização em Engenharia de IA). Se preferir acompanhar por vídeo, é só dar o play.
Introdução
Ao trabalhar com aplicações baseadas em RAG (Retrieval‑Augmented Generation), um dos desafios é lidar com o custo e a latência de processar perguntas repetidas. Cada vez que um usuário faz uma pergunta, todo o pipeline precisa ser executado novamente, mesmo que a resposta já tenha sido gerada anteriormente.
Neste post, vamos explorar como o Semantic Cache pode resolver esse problema de forma elegante e eficiente.
O que é um cache?
Um cache é uma memória de alta velocidade que armazena de forma eficiente os dados acessados com frequência. A ideia é simples: em vez de recalcular ou buscar uma informação toda vez que ela é solicitada, armazenamos o resultado para uso futuro.
RAG tradicional vs. cache
Em um RAG tradicional, quando um usuário faz uma pergunta, o sistema executa várias etapas:
- Embedding da pergunta.
- Busca em um vector database (palavra‑chave, semântica ou híbrida).
- Recuperação dos documentos relevantes.
- Envio do contexto + prompt + pergunta ao LLM.
- Retorno da resposta ao usuário.
Se o usuário fizer a mesma pergunta repetidamente, todo esse processo ocorre a cada requisição, o que é custoso em tempo e dinheiro.
Cache simples (key‑value)
Podemos armazenar as perguntas e suas respectivas respostas em um cache simples:
- Chave: a pergunta literal.
- Valor: a resposta gerada.
Exemplo:
- Pergunta: “Qual é a capital do Brasil?”
- Resposta: “Brasília.”
Na próxima vez que a mesma pergunta for feita, recuperamos a resposta do cache.
Problema
Pequenas variações na formulação geram hashes diferentes.
- “Qual é a capital do Brasil?”
- “Você pode me dizer a capital do Brasil?”
Embora semanticamente equivalentes, o cache key‑value não as reconhece.
Cache semântico
Para melhorar a precisão, usamos um cache semântico, que armazena:
- Pergunta
- Resposta
- Embedding da pergunta
Quando o usuário faz uma nova pergunta, realizamos uma busca semântica entre o embedding da consulta e os embeddings armazenados no cache. Se a similaridade estiver acima de um limite predefinido, retornamos a resposta correspondente.
Diferenças principais
| Característica | Cache tradicional (Exact Match) | Cache semântico |
|---|---|---|
| Busca | Combinação exata da chave | Busca por significado (similaridade) |
| Tolerância a variações | Não reconhece variações | Reconhece variações semânticas |
| Exemplo | “Qual é a capital do Brasil?” → falha para “Me diga a capital do Brasil” | Ambas retornam “Brasília” |
Por que usar cache semântico?
- Redução de custos: menos chamadas à API do LLM.
- Velocidade: respostas instantâneas a consultas frequentes.
- Consistência: mesma resposta para perguntas equivalentes.
Estratégias de otimização
- Patches ad‑hoc: identificar perguntas frequentes e criar respostas pré‑escritas.
- Persistência: usar o mesmo vector database (ex.: Qdrant) tanto para documentos quanto para o cache.
Fluxo de operação
- Embedding da pergunta – sempre necessário, independentemente do uso de cache.
- Busca no cache semântico (comparação de embeddings).
- Se houver correspondência acima do limiar → retorna a resposta armazenada.
- Caso contrário → segue o pipeline RAG completo.
- Armazenamento: a nova pergunta, sua resposta e o embedding são salvos no cache para futuras consultas.
Métricas de similaridade
- Distância Euclidiana: quanto menor, maior a similaridade (próximo de zero).
- Similaridade de cosseno: valores maiores indicam maior similaridade.
Implementação prática (exemplo resumido)
def semantic_cache(query, cache_db, rag_pipeline, threshold=0.85):
# 1. Embedding da consulta
q_emb = embed(query)
# 2. Busca semântica no cache
similar, score = cache_db.search(q_emb, top_k=1)
if similar and score >= threshold:
return similar.answer # Resposta do cache
# 3. Pipeline RAG tradicional
docs = rag_pipeline.search(q_emb)
answer = rag_pipeline.generate(query, docs)
# 4. Salva no cache
cache_db.upsert(query, answer, q_emb)
return answer
Conclusão
O Semantic Cache oferece uma solução elegante para reduzir custos, melhorar a latência e garantir consistência em sistemas RAG. Ao combinar embeddings e buscas semânticas, conseguimos reutilizar respostas já geradas mesmo quando a formulação da pergunta varia ligeiramente. Essa abordagem é especialmente valiosa em aplicações de chatbot e Q&A que dependem de chamadas frequentes a LLMs.
Otimização de Aplicações RAG
A ca poderosa para otimizar aplicações RAG permite uma economia significativa de tempo e custos ao evitar processamentos redundantes. A chave está em entender que perguntas semanticamente equivalentes devem retornar a mesma resposta, mesmo quando formuladas de maneiras diferentes.
Ecossistema Dev+ Eficiente
Este conteúdo faz parte do ecossistema Dev+ Eficiente, mantido por Alberto, Maurício Aniche e Rafael Ponte. O ecossistema inclui:
- Jornada Dev+ Eficiente – foco em capacitar você a entregar software que realmente gera valor, com o máximo de qualidade e eficiência.
- Especialização em Engenharia de IA – parceria com Daniel Romero, cujo objetivo é habilitar a entrega de software de excelência, integrando sistemas com LLMs.