Why Cache LLM Responses?
Think of LLM caching like a restaurant kitchen. If ten customers order the same dish, a smart kitchen does not cook it from scratch each time — it prepares a batch and plates from that. LLM caching works the same way: identical or semantically similar requests get served from a stored result instead of burning GPU cycles and dollars on a fresh inference. LLM calls are expensive and slow:OpenAI Prompt Caching (Built-in)
OpenAI automatically caches prompts with shared prefixes:Maximizing Prompt Cache Hits
Exact Match Caching
Cache identical requests with deterministic settings.Semantic Caching
Cache based on meaning, not exact match:Production Semantic Cache with Redis
Multi-Layer Caching
Combine caching strategies for maximum efficiency:Cache Invalidation Strategies
Key Takeaways
Use OpenAI's Cache
Layer Your Caches
Semantic for Flexibility
Invalidate Smartly
What’s Next
Embeddings Deep Dive
Interview Deep-Dive
Explain the difference between exact-match caching, semantic caching, and prompt prefix caching for LLMs. When would you use each?
Explain the difference between exact-match caching, semantic caching, and prompt prefix caching for LLMs. When would you use each?
- These are three fundamentally different caching strategies operating at different layers, and a strong production system usually combines all three.
- Exact-match caching hashes the entire request (model, messages, temperature, all parameters) and returns a stored response for identical requests. The hit rate depends entirely on how often users send exactly the same query. For internal tools, customer support bots with common FAQs, and batch processing with repeated inputs, exact-match cache hit rates can reach 30-50%. The critical constraint is to only cache deterministic requests where temperature equals 0. If you cache responses from temperature 0.7 calls, you are serving stale creative output and killing the intended variety. I have seen this exact bug in production — users complained the chatbot gave the same answer to every question because someone cached non-deterministic responses.
- Semantic caching uses embedding similarity to match queries by meaning rather than exact text. “What is machine learning?” and “Can you explain ML to me?” would hit the same cache entry if their embedding similarity exceeds a threshold (typically 0.92-0.95). This dramatically increases hit rates for user-facing applications where people phrase the same question differently. The tradeoff is the cost of an embedding call per cache lookup (though embeddings are 100x cheaper than completions), the risk of false positives (returning a cached answer for a question that is similar but meaningfully different), and the O(N) comparison against the cache for each lookup unless you use a vector index.
- Prompt prefix caching is a provider-side optimization (OpenAI offers this natively). When multiple requests share the same long prefix (same system prompt, same few-shot examples), the provider caches the KV-cache for that prefix and gives you a 50% discount on cached input tokens. You do not manage this cache yourself — you just structure your prompts so the static content comes first and the dynamic content comes last. The optimization is: put your 2000-token system prompt and 1000-token few-shot examples at the top, and the user’s 50-token question at the bottom. Every request after the first gets 3000 tokens at half price.
- My production stack layers all three: prompt prefix caching reduces per-request cost at the provider level, exact-match caching eliminates redundant API calls entirely for repeated queries, and semantic caching catches the paraphrased queries that exact-match misses.
You are building a multi-layer cache for an LLM application: L1 in-memory, L2 Redis, L3 semantic. Walk me through the read path, the write path, and how you handle cache consistency.
You are building a multi-layer cache for an LLM application: L1 in-memory, L2 Redis, L3 semantic. Walk me through the read path, the write path, and how you handle cache consistency.
- The read path checks layers in order of speed. L1 (in-memory dictionary with TTL) is checked first — sub-millisecond latency, lives in the application process, perfect for hot queries. If L1 misses, check L2 (Redis) — 1-5ms latency, shared across all application instances, persists across restarts. If L2 misses, check L3 (semantic cache backed by a vector store) — 10-50ms latency because it requires an embedding call plus similarity search. If all three miss, call the LLM, get the response, and populate all three layers on the write path.
- The write path writes to all layers simultaneously after an LLM call. L1 gets the exact query-response pair with a short TTL (60-300 seconds for hot data). L2 gets the same pair with a longer TTL (1-24 hours). L3 gets the query embedding and response, stored until eviction.
- The critical detail most people miss is the backfill on read. If L2 hits but L1 missed, I backfill L1 from L2 so subsequent requests for the same query are served from the fastest layer. Same for L3 to L2 backfill. This is the same principle CPU caches use — a slower cache hit should populate all faster caches above it.
- Cache consistency is managed through TTLs and event-based invalidation. TTLs handle gradual staleness: L1 has the shortest TTL so stale data ages out fastest from the hottest cache. Event-based invalidation handles immediate changes: when a product price changes, I publish an invalidation event that clears all cache entries matching a pattern (any query containing that product name) across all three layers. The invalidation fan-out goes from bottom to top: clear L3 first (most entries), then L2, then broadcast to all instances to clear L1.
- One gotcha: the semantic cache (L3) cannot be invalidated by exact key because it is similarity-based. For event-based invalidation of L3, I either clear the entire context partition or re-embed the invalidation pattern and clear all entries within a similarity radius.
Cache invalidation is the hardest problem in computer science. How do you invalidate LLM caches when the underlying knowledge changes?
Cache invalidation is the hardest problem in computer science. How do you invalidate LLM caches when the underlying knowledge changes?
- The classic quote is that there are two hard problems in computer science: cache invalidation, naming things, and off-by-one errors. For LLM caches, this is especially hard because the relationship between source data changes and cached responses is fuzzy — a product price change does not just invalidate the one cached response about that product’s price; it potentially invalidates any response that mentioned pricing.
- I use a three-strategy approach. First, time-based TTLs as the baseline guarantee. Every cache entry has a maximum TTL that ensures it is eventually refreshed even if I miss an invalidation event. For rapidly changing data (stock prices, inventory levels), TTLs are minutes. For slow-changing data (company policies, product descriptions), TTLs are hours to days. This is the safety net.
- Second, event-driven invalidation for known change events. When the product catalog updates, the pricing API changes, or a new policy document is published, the system publishes an invalidation event. The cache listener receives the event and invalidates all entries matching the affected domain. The tricky part is mapping a data change to the right cache entries. I use cache tags: every cached response is tagged with the data sources it depends on (e.g., tags
["product:123", "pricing:q4-2025"]). When product 123 changes, I invalidate all entries tagged withproduct:123. - Third, versioned caching for prompt and model changes. When I update a system prompt or switch models, the entire cache is logically stale because a new model or prompt would generate different responses. I include a prompt version hash and model identifier in the cache key, so a prompt change automatically starts a new cache namespace without requiring explicit invalidation.
- The thing most people get wrong is trying to be too precise with invalidation. If you spend more engineering time on surgical cache invalidation than you save from caching, you have over-optimized. For most LLM applications, aggressive TTLs (1-4 hours) plus event-driven invalidation for the highest-impact changes (pricing, availability, critical policies) covers 95% of cases.
Your LLM application costs $50,000/month in API calls. The CTO wants this halved. Design a caching strategy to get there.
Your LLM application costs $50,000/month in API calls. The CTO wants this halved. Design a caching strategy to get there.
- First, I would profile the spend to understand where the money goes. I would break down costs by: endpoint (which features use the most tokens), model (GPT-4o vs GPT-4o-mini), request type (how many are unique vs repeated), and token composition (how much is system prompt vs user input vs output). At most companies I have seen, 60-70% of the token spend is the system prompt being resent identically on every request.
- Quick win number one: prompt prefix caching. If 70% of our token spend is the system prompt, and OpenAI gives 50% off cached prefix tokens, that is an immediate 35% cost reduction with zero code changes beyond restructuring our prompts (static prefix first, dynamic content last). On 17,500.
- Quick win number two: exact-match caching with Redis. I would analyze request logs and identify the repeat rate. For internal tools and support bots, 20-40% of queries are repeats. Implementing exact-match caching with a 24-hour TTL on deterministic (temperature=0) requests would eliminate those API calls entirely. Conservatively, if 25% of requests are cacheable repeats, that saves another 32,500.
- Medium-term win: semantic caching for the remaining non-exact-repeat traffic. With a 0.93 similarity threshold, I would expect an additional 10-15% cache hit rate on queries that are paraphrases of cached queries. That saves another $2,500-3,500.
- Model optimization: audit which requests actually need GPT-4o versus GPT-4o-mini. For straightforward classification, formatting, and simple Q-and-A, GPT-4o-mini at 0.60 per million tokens is 17x cheaper than GPT-4o at 10.00. If 40% of current GPT-4o requests can be downgraded to mini without quality loss, that saves significantly.
- Combined realistic projection: prefix caching (8,000) plus semantic (28,500 in savings, which is a 57% reduction. Adding model downgrading pushes it past 60% comfortably. Total cost of implementation: one Redis instance ($50/month), engineering time for caching layer (1-2 weeks), and ongoing monitoring.