Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Semantic Caching for API Optimization

Hello! In our last module, we focused on building and securing a dynamic context system using the Model Context Protocol (MCP). You successfully designed a server that is not just functional but also adheres to robust security principles, a crucial step toward building production-grade AI systems.

Now, we shift our focus from providing context to optimizing the performance and efficiency of the core AI application itself. This brings us to our new module, "Scaling AI Systems with Async Processing." Today's lesson tackles a fundamental challenge in production LLM applications: the high cost and latency of API calls. We will explore and implement semantic caching, a powerful technique to reduce redundant LLM calls, dramatically lower costs, and improve response times for your users.

By the end of this lesson, you will have implemented a complete semantic caching layer, understanding not just the "how" of its code but the "why" behind its architecture. This is a key pattern that any AI Engineer building scalable systems must master.

The Problem: Redundant, Expensive LLM Calls

In any real-world application, users will ask similar questions. A traditional, exact-match cache might handle identical queries, but it fails the moment a user rephrases their question.

  • "How do I return an item?"
  • "What's your return policy?"
  • "Help me with a product return."

To a traditional system, these are three distinct queries, each triggering a new, expensive call to the LLM. Semantic caching is designed to solve exactly this problem.

This diagram illustrates the core value proposition of semantic caching. On the left, paraphrased queries lead to multiple, redundant LLM calls. On the right, a semantic cache identifies the shared intent and serves a cached response for subsequent similar queries, reducing latency and cost.

The Core Idea: Caching Meaning, Not Just Strings

Semantic caching works by understanding the meaning or intent behind a user's query, not just the literal string of text. Let's start with a quick video from Redis that explains the concept at a high level.

What is a semantic cache?

This short video provides a clear and concise overview of what semantic caching is, the problem it solves, and the high-level workflow involving embeddings and a vector database.

Watch the entire video. Pay attention to the two main benefits it highlights: saving time and LLM calls, and how the process works under the hood by converting queries to vectors and comparing them.

As the video explains, the magic lies in converting text into embeddings—numerical representations (vectors) that capture semantic meaning. Queries with similar meanings will have vectors that are close to each other in a high-dimensional space. The cache can then find and return a previously stored answer if a new query's vector is "close enough" to a cached one.

A Layered Caching Architecture

While semantic matching is powerful, generating embeddings for every single request is computationally expensive. A robust system design, one you'd be expected to discuss in a system design interview, uses a layered approach to optimize for the most common and cheapest cases first.

The following article provides a superb, in-depth guide to building a semantic cache. We will use it as the foundation for our implementation.

Semantic Caching for LLMs: FastAPI, Redis, and Embeddings

This article from PyImageSearch introduces the problem and explains the core concepts and architecture of a layered semantic cache. It sets the stage for the implementation we'll be building.

First, read the introduction to solidify your understanding of the problems semantic caching solves. Then, focus on the section How Semantic Caching Works. This section is crucial as it explains the three-layer strategy that forms the basis of our architecture: Exact Match (Fast Path) Semantic Match (Flexible Path) LLM Fallback (Slow Path) Pay close attention to this layered model; it's a classic engineering pattern that prioritizes performance by handling the cheapest operations first.

This layered approach is the key to an efficient implementation. We only escalate to more expensive operations (embedding generation, semantic search) after the cheaper ones (exact-match hash lookup) have failed.

The End-to-End Request Flow

Let's visualize this layered architecture with a concrete request flow.

This flowchart details the decision-making process within a semantic caching system. A user query is first embedded and then compared against existing cache entries. A similarity score above a certain threshold (e.g., 0.90) results in a fast "Cache Hit." Otherwise, it's a "Cache Miss," triggering a full LLM generation cycle, with the new result being cached for future use.

The flowchart clearly distinguishes between the "Cache Hit" and "Cache Miss" paths and even provides indicative performance numbers. Notice the significant difference in latency and cost: ~200ms for a hit versus ~2,800ms for a miss. This is the practical impact you're aiming for as an AI Engineer.

Implementing the Semantic Cache

Now, let's move from theory to practice. We will walk through the implementation of a semantic cache using Python, FastAPI, and Redis, as detailed in the PyImageSearch article. Your background in software engineering will be valuable here, as we'll be looking at project structure, API endpoints, and class design.

Semantic Caching for LLMs: FastAPI, Redis, and Embeddings

This reading block guides you through the core implementation details of the semantic caching system. We will focus on the architecture, orchestration logic, and the cache implementation itself.

First, review the system components and the step-by-step process. This maps the conceptual layers to concrete software components like FastAPI, Redis, and Ollama. Next, look at the Project Structure section. As an engineer, you'll appreciate how concerns are separated into distinct modules (api/, cache/, llm/), making the system maintainable and easy to reason about. The heart of the orchestration is the /ask endpoint. Read the section FastAPI Ask Endpoint carefully. This is the "control plane" that implements our layered caching logic: it attempts an exact match, then a semantic match, and finally falls back to the LLM. Notice how it prioritizes cheap operations. Now, let's go one level deeper into the cache itself. Read the section on The Semantic Cache. This explains the SemanticCache class, how it uses Redis for storage, and how it performs a linear scan with cosine similarity for semantic search. Finally, understand what gets stored by reading Cache Entries. The CacheEntry schema is designed to support both exact-match (with query_hash) and semantic-match (with embedding) lookups efficiently.

After studying these sections, you should have a clear mental model of how the code works. The orchestration in the FastAPI endpoint is the brain, deciding the path a request takes, while the SemanticCache class is the workhorse, handling storage and retrieval from Redis.

Production Hardening: Thinking Like a Senior AI Engineer

You've now implemented a functional semantic cache. For many tutorials, this is the end. But your goal is to be a top AI engineer, and that means building systems that are not just functional but also robust, safe, and production-ready.

A naive cache can introduce subtle but dangerous failure modes over time. What happens if the cached information becomes outdated? What if you cache an error response from the LLM?

The second article in the series addresses exactly these production-hardening concerns. We won't implement all of these today, but being aware of them is critical for system design interviews and real-world application development.

Semantic Caching for LLMs: TTLs, Confidence, and Cache Safety

This article is about moving from a working prototype to a production-ready system. It introduces safeguards that are essential for long-term reliability.

First, read the introduction to understand the types of "silent correctness issues" that a simple cache can introduce. Read about Time-To-Live (TTL) in the section Cache TTL. This explains why LLM responses become stale and how TTLs act as a crucial safety mechanism to prevent the cache from becoming a "museum of old truths." High similarity is not always enough. Read the section on Confidence Scoring. This introduces the idea of combining semantic similarity with freshness to make a more informed decision about reusing a response. Finally, read about Preventing Cache Poisoning. This section discusses the importance of never caching or reusing erroneous or unsafe LLM responses, which could otherwise be amplified by the cache.

These hardening concepts—TTL, confidence scoring, and poison prevention—transform a cache from a simple performance optimization into a reliable component of your AI system. While our current implementation uses a simple linear scan over Redis, which has O(N) complexity, be aware that for large-scale systems, you would replace this with a dedicated vector database (like Pinecone, Weaviate, or Redis itself with the RediSearch module) for efficient approximate nearest neighbor (ANN) search. The hardening principles, however, remain the same regardless of the backend technology.

Conclusion

In this lesson, you have dived deep into one of the most practical and impactful techniques for scaling AI applications. You've learned how to design and implement a semantic cache to significantly reduce API costs and improve user-perceived latency.

Key Takeaways:

  • Semantic caching solves the problem of redundant LLM calls for paraphrased or semantically similar queries by caching "meaning" instead of exact strings.
  • A robust implementation uses a layered architecture, prioritizing cheap exact-match lookups before escalating to more expensive embedding generation and semantic search.
  • The core components include an API orchestrator, an embedding model, and a cache store (like Redis) that holds query embeddings and responses.
  • For production systems, hardening is non-negotiable. You must consider staleness (with TTLs), confidence (similarity + freshness), and cache poisoning to ensure the cache remains a source of truth, not a source of amplified errors.

You are now equipped with a powerful tool for building more efficient and scalable AI systems. In our next lesson, we will continue our exploration of performance and user experience by looking at how to stream LLM responses to a client using Server-Sent Events (SSE), providing users with instant feedback instead of making them wait for the full response to be generated.

Can't find a good explanation? Sign up and we'll make it for you

Sign up