0Pricing
AI Prompt Engineering · Aula

Estratégias de cache para prompts

Cache semântico, cache por correspondência exata e cache de prompts da Anthropic.

Estratégias de cache para prompts é uma aula grátis de AI Prompt Engineering no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de AI Prompt Engineering, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de AI Prompt Engineering inclui 4 aulas no total.

Por que armazenar os resultados das instruções em cache?

As chamadas à API de LLM são caras e lentas. Muitas aplicações em produção enviam as mesmas instruções, ou instruções muito semelhantes, repetidamente. O armazenamento em cache retorna resultados armazenados para consultas repetidas, eliminando chamadas redundantes à API e reduzindo drasticamente tanto o custo quanto a latência.

Armazenamento em cache por correspondência exata com chaves de hash

O cache mais simples: gere o hash da string exata da instrução e armazene o resultado. Se a mesma string de instrução aparecer novamente, retorne o resultado armazenado em cache sem chamar a API.

import hashlib
import json
from functools import lru_cache

class ExactMatchCache:
    def __init__(self, backend=None):
        # backend: a dict (in-memory) or Redis client
        self.store = backend or {}

    def _key(self, messages, model, max_tokens):
        content = json.dumps({'messages': messages, 'model': model,
                               'max_tokens': max_tokens}, sort_keys=True)
        return 'llm:' + hashlib.sha256(content.encode()).hexdigest()

    def get(self, messages, model, max_tokens):
        key = self._key(messages, model, max_tokens)
        return self.store.get(key)

    def set(self, messages, model, max_tokens, result, ttl_seconds=3600):
        key = self._key(messages, model, max_tokens)
        self.store[key] = result
        # In Redis: self.store.setex(key, ttl_seconds, json.dumps(result))

cache = ExactMatchCache()

# Usage
messages = [{'role': 'user', 'content': 'What is the capital of France?'}]
cached = cache.get(messages, 'gpt-4o-mini', 100)
if cached:
    print('Cache HIT:', cached[:50])
else:
    print('Cache MISS — calling API...')

Cliente LLM com cache integrado

Envolva a chamada à API de LLM com um decorador de cache para que todos os chamadores obtenham o armazenamento em cache de forma transparente, sem alterar o próprio código.

import openai
from typing import Optional

client = openai.OpenAI(api_key='YOUR_API_KEY')
cache = ExactMatchCache()

def cached_completion(messages, model='gpt-4o-mini', max_tokens=500,
                       temperature=0.0, use_cache=True) -> str:
    if use_cache and temperature == 0.0:
        # Only cache deterministic requests (temperature=0)
        cached = cache.get(messages, model, max_tokens)
        if cached:
            return cached

    response = client.chat.completions.create(
        model=model,
        messages=messages,
        max_tokens=max_tokens,
        temperature=temperature
    )
    result = response.choices[0].message.content

    if use_cache and temperature == 0.0:
        cache.set(messages, model, max_tokens, result)

    return result

# Important: only cache temperature=0 responses
# Non-deterministic responses (temp>0) may return stale results
print('Cache wrapping: only deterministic (temp=0) calls are cached.')

Armazenamento em cache semântico com incorporações

O armazenamento em cache semântico retorna resultados armazenados para consultas semelhantes em significado, não apenas para strings idênticas. Ele usa vetores de incorporação e similaridade de cosseno para encontrar consultas quase duplicadas.

import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

class SemanticCache:
    def __init__(self, similarity_threshold=0.95):
        self.entries = []  # [(embedding, query, result)]
        self.threshold = similarity_threshold

    def embed(self, text):
        '''Get embedding for text using OpenAI embeddings API.'''
        response = client.embeddings.create(
            model='text-embedding-3-small',
            input=text
        )
        return np.array(response.data[0].embedding)

    def get(self, query):
        if not self.entries:
            return None
        query_emb = self.embed(query)
        for emb, stored_query, result in self.entries:
            sim = cosine_similarity([query_emb], [emb])[0][0]
            if sim >= self.threshold:
                print(f'Semantic cache HIT (similarity={sim:.3f}): {stored_query[:40]}...')
                return result
        return None

    def set(self, query, result):
        emb = self.embed(query)
        self.entries.append((emb, query, result))

sem_cache = SemanticCache(similarity_threshold=0.95)
print('Semantic cache ready. Threshold: 0.95 cosine similarity.')

Biblioteca GPTCache

O GPTCache é uma biblioteca de código aberto para armazenamento em cache semântico que oferece suporte a vários modelos de incorporação, mecanismos de similaridade (FAISS, Redis) e estratégias de remoção. Ela se integra diretamente a clientes OpenAI e LangChain.

# pip install gptcache
# GPTCache integration example

# from gptcache import cache
# from gptcache.adapter import openai
# from gptcache.embedding import Onnx
# from gptcache.manager import CacheBase, VectorBase, get_data_manager
# from gptcache.similarity_evaluation.distance import SearchDistanceEvaluation

# Initialize GPTCache
# onnx = Onnx()
# data_manager = get_data_manager(
#     CacheBase('sqlite'),
#     VectorBase('faiss', dimension=onnx.dimension)
# )
# cache.init(
#     embedding_func=onnx.to_embeddings,
#     data_manager=data_manager,
#     similarity_evaluation=SearchDistanceEvaluation(),
# )

# After init, use openai from gptcache.adapter instead of standard openai
# response = openai.ChatCompletion.create(
#     model='gpt-4o-mini',
#     messages=[{'role': 'user', 'content': 'What is Python?'}]
# )
# Same API, but cache is checked first

print('GPTCache: drop-in semantic cache for OpenAI API calls.')
print('Supports: FAISS, Redis, SQLite, Milvus as vector backends.')

Armazenamento nativo de instruções em cache da Anthropic

A Anthropic oferece armazenamento nativo de instruções em cache, que armazena o processamento da instrução do sistema nos próprios servidores. Quando há um acerto no cache, você paga apenas 10% do preço normal dos tokens de entrada. Isso é separado do armazenamento de respostas em cache no nível da aplicação.

import anthropic

client = anthropic.Anthropic(api_key='YOUR_API_KEY')

LONG_SYSTEM_PROMPT = '''You are an expert financial analyst with 20 years of experience.
''' + 'Domain knowledge: ' + 'analysis context...' * 500  # large system prompt

# Enable prompt caching with cache_control
response = client.messages.create(
    model='claude-opus-4-5',
    max_tokens=1024,
    system=[
        {
            'type': 'text',
            'text': LONG_SYSTEM_PROMPT,
            'cache_control': {'type': 'ephemeral'}  # cache this prefix
        }
    ],
    messages=[{'role': 'user', 'content': 'Analyze Q3 2024 earnings.'}]
)

print('Cache write tokens:', response.usage.cache_creation_input_tokens)
print('Cache read tokens: ', response.usage.cache_read_input_tokens)
print('Regular input tokens:', response.usage.input_tokens)
# On cache HIT: cache_read_input_tokens shows the cached tokens
# Cost: cached tokens charged at 10% of normal rate

TTL do cache e estratégias de remoção

Os resultados armazenados em cache ficam obsoletos quando o conhecimento subjacente muda ou o modelo é atualizado. O TTL (tempo de vida) e as estratégias de remoção gerenciam a atualidade dos dados.

import time
from collections import OrderedDict

class TTLCache:
    def __init__(self, max_size=1000, default_ttl=3600):
        self.store = OrderedDict()  # key: (value, expire_at)
        self.max_size = max_size
        self.default_ttl = default_ttl

    def set(self, key, value, ttl=None):
        ttl = ttl or self.default_ttl
        expire_at = time.time() + ttl
        if key in self.store:
            del self.store[key]
        self.store[key] = (value, expire_at)
        # LRU eviction: remove oldest if over capacity
        if len(self.store) > self.max_size:
            self.store.popitem(last=False)

    def get(self, key):
        if key not in self.store:
            return None
        value, expire_at = self.store[key]
        if time.time() > expire_at:
            del self.store[key]
            return None  # expired
        # Move to end (LRU update)
        self.store.move_to_end(key)
        return value

# TTL strategy guidelines
ttl_guidelines = {
    'Static knowledge': 86400,  # 24h (facts, definitions)
    'Semi-static': 3600,        # 1h (product info, FAQs)
    'Dynamic content': 300,     # 5min (news, prices)
    'Personalized': 0           # no cache (user-specific)
}
for k, v in ttl_guidelines.items():
    print(f'{k}: {v}s TTL')

Padrões de invalidação de cache

A invalidação de cache — saber quando limpar dados obsoletos — é um dos problemas mais difíceis da computação. Para caches de LLM, estes padrões tratam das necessidades de invalidação mais comuns.

class InvalidationAwareCache(TTLCache):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.tags = {}  # key: set of tags
        self.tag_index = {}  # tag: set of keys

    def set_with_tags(self, key, value, tags, ttl=None):
        self.set(key, value, ttl)
        self.tags[key] = set(tags)
        for tag in tags:
            self.tag_index.setdefault(tag, set()).add(key)

    def invalidate_by_tag(self, tag):
        keys_to_delete = self.tag_index.pop(tag, set())
        for key in keys_to_delete:
            self.store.pop(key, None)
            self.tags.pop(key, None)
        print(f'Invalidated {len(keys_to_delete)} entries with tag={tag}')

# Usage: tag cache entries by data source
cache = InvalidationAwareCache()
cache.set_with_tags('product_faq_123', 'Product FAQs...', tags=['product:123', 'faqs'])
cache.set_with_tags('product_spec_123', 'Spec sheet...', tags=['product:123', 'specs'])

# When product 123 is updated, invalidate all its cache entries
cache.invalidate_by_tag('product:123')  # Invalidated 2 entries

Medição do desempenho do cache

Acompanhe as métricas de desempenho do cache para entender o impacto do armazenamento em cache sobre o custo e a latência. Um cache bem ajustado deve atingir uma taxa de acertos >50% na maioria dos casos de uso em produção.

class CacheMetrics:
    def __init__(self):
        self.hits = 0
        self.misses = 0
        self.total_latency_saved_ms = 0
        self.total_cost_saved_usd = 0
        self.avg_api_latency_ms = 1500  # typical LLM call latency
        self.avg_api_cost_usd = 0.002   # typical cost per call

    def record_hit(self):
        self.hits += 1
        self.total_latency_saved_ms += self.avg_api_latency_ms
        self.total_cost_saved_usd += self.avg_api_cost_usd

    def record_miss(self):
        self.misses += 1

    def report(self):
        total = self.hits + self.misses
        hit_rate = self.hits / total if total else 0
        return {
            'hit_rate': f'{hit_rate:.1%}',
            'total_requests': total,
            'cache_hits': self.hits,
            'latency_saved_sec': round(self.total_latency_saved_ms / 1000, 1),
            'cost_saved_usd': round(self.total_cost_saved_usd, 2)
        }

metrics = CacheMetrics()
for i in range(100):
    if i % 3 == 0:  # simulate 33% hit rate
        metrics.record_hit()
    else:
        metrics.record_miss()
print(metrics.report())

Cache com Redis para produção

Os caches em memória são perdidos após uma reinicialização e não podem ser compartilhados entre instâncias do servidor. O Redis fornece um cache persistente e compartilhado, que funciona em vários servidores de API em uma implantação de produção.

import redis
import json
import hashlib

class RedisLLMCache:
    def __init__(self, host='localhost', port=6379, db=0, default_ttl=3600):
        self.client = redis.Redis(host=host, port=port, db=db,
                                   decode_responses=True)
        self.default_ttl = default_ttl

    def _key(self, messages, model):
        content = json.dumps({'messages': messages, 'model': model},
                              sort_keys=True)
        return 'llmcache:' + hashlib.sha256(content.encode()).hexdigest()

    def get(self, messages, model):
        key = self._key(messages, model)
        value = self.client.get(key)
        if value:
            self.client.expire(key, self.default_ttl)  # refresh TTL on hit
            return json.loads(value)
        return None

    def set(self, messages, model, result, ttl=None):
        key = self._key(messages, model)
        self.client.setex(key, ttl or self.default_ttl, json.dumps(result))

    def stats(self):
        keys = self.client.keys('llmcache:*')
        return {'cached_entries': len(keys),
                'memory_bytes': self.client.memory_usage('llmcache:') or 0}

# Usage: drop-in replacement for in-memory cache
# cache = RedisLLMCache(host='redis.internal', port=6379)
print('RedisLLMCache: shared across all server instances, survives restarts.')

Quando não usar cache

O armazenamento em cache não é adequado para todas as chamadas a LLM. Entender quando ignorar o cache evita o fornecimento de resultados desatualizados ou incorretos.

DONT_CACHE_WHEN = {
    'High temperature': (
        'temperature > 0 produces different outputs for the same input. '
        'Caching would always return the first generation, defeating the purpose.'
    ),
    'Real-time data required': (
        'Queries about current prices, live news, or real-time status '
        'must always hit the API and live data source.'
    ),
    'Personalized responses': (
        'Responses that depend on user_id, session context, or personal data '
        'should not be shared across users.'
    ),
    'Safety-critical': (
        'Medical, legal, or financial responses where staleness could cause harm '
        'require fresh responses with the most current model version.'
    ),
    'Non-deterministic tools': (
        'If the prompt includes a current timestamp or random seed, '
        'the response is by design non-repeatable.'
    )
}

for condition, reason in DONT_CACHE_WHEN.items():
    print(f'Skip cache: {condition}')
    print(f'  Reason: {reason[:60]}...')
    print()

Verificação rápida

Qual é a principal diferença entre o armazenamento em cache por correspondência exata e o armazenamento em cache semântico para respostas de LLM?

Resumo das estratégias de cache

Um armazenamento eficaz de instruções em cache combina várias estratégias:

  • Correspondência exata: baseado em hash, sem sobrecarga quando há correspondência e com baixa taxa de correspondência para formulações variadas
  • Cache semântico: a similaridade entre representações vetoriais encontra correspondências de paráfrases, proporcionando uma taxa de correspondência maior
  • GPTCache: biblioteca de código aberto que combina as duas estratégias com sistemas de retaguarda FAISS/Redis
  • Cache nativo da Anthropic: armazenamento no servidor das instruções do sistema em cache, a 10% do custo dos tokens
  • Expiração TTL + LRU: atualização baseada em tempo + gerenciamento de capacidade
  • Invalidação baseada em tags: invalida entradas relacionadas quando os dados de origem mudam
  • Quando não usar cache: temperatura diferente de zero, dados em tempo real, dados personalizados ou situações críticas para a segurança

Perguntas Frequentes

A aula “Estratégias de cache para prompts” é grátis?

Sim — o texto completo de “Estratégias de cache para prompts” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de AI Prompt Engineering, atualize para CoddyKit PRO. O curso de AI Prompt Engineering inclui 4 aulas no total.

O que vou aprender em “Estratégias de cache para prompts”?

Cache semântico, cache por correspondência exata e cache de prompts da Anthropic. Você pratica AI Prompt Engineering com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar AI Prompt Engineering?

Nenhuma experiência prévia é necessária. AI Prompt Engineering no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “Estratégias de cache para prompts”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de AI Prompt Engineering?

Sim. Cada aula de AI Prompt Engineering inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Estratégias de cache para prompts
  2. Processamento em lote e execução assíncrona
  3. Balanceamento de carga entre modelos
  4. Monitoramento e alertas para pipelines de prompts
← Voltar para AI Prompt Engineering