PREDICTIVE TREND INSIGHT
Building vector cache layers to reduce token costs in production RAG Illustration

Building vector cache layers to reduce token costs in production RAG

Direct Summary:

A vector cache layer reduces token costs by catching semantically similar repeat queries, not just exact-match ones — instead of hashing the query string, you embed it and check a vector index for a previously-answered query within some similarity threshold. A hit returns the cached LLM response directly, skipping the model call entirely. Open-source tools like GPTCache implement exactly this pattern, and the underlying index (HNSW, served by something like pgvector or Chroma) is the same technology used for RAG retrieval — just pointed at cached Q&A pairs instead of your document corpus.

"Do not wait for the perfect moment. Take the moment and make it perfect."

— Unknown

Key Insights

  • A similarity threshold, not an exact match: "What's our refund policy?" and "How do refunds work?" should hit the same cache entry — that only happens if the cache compares embeddings within a distance threshold, not exact query strings.
  • HNSW makes the lookup itself fast: the cache is only worth it if checking it is cheaper than just calling the LLM — an HNSW-indexed vector store answers "is anything similar already cached?" in sub-millisecond time even with millions of entries.
  • Cache invalidation is the real design problem: a cached answer to "what's the current price?" can go stale the moment the price changes — set a TTL or invalidate on the underlying data change, not just on cache size.

The naive way to "cache" LLM responses is a hash map keyed on the exact query string — which almost never hits, because real users rarely phrase the same question identically twice. A semantic cache fixes this by embedding the incoming query and checking a vector index for anything close enough to a previously-answered question; on a hit, you return the cached response and skip the model call (and its token cost) entirely.

Step-by-Step Implementation

1. Embed the incoming query. Use the same embedding model you'd use for RAG retrieval — the cache and your retrieval pipeline can often share infrastructure.

2. Check the vector index for a near match. An HNSW-indexed store returns the closest cached query and its similarity score in sub-millisecond time, even at large cache sizes.

3. Serve the cached response above a similarity threshold, otherwise call the LLM. Tune the threshold carefully — too loose and you serve wrong answers to different questions; too strict and you rarely get a cache hit at all.

semantic_cache_lookup.py
# Semantic cache lookup: check for a similar prior query before calling the LLM
import sqlite3
import numpy as np

def cosine_similarity(v1, v2):
    return np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))

SIMILARITY_THRESHOLD = 0.92  # tune based on false-hit tolerance

def get_cached_or_call_llm(query, embed_fn, cache_rows, call_llm_fn):
    query_vec = embed_fn(query)

    best_score, best_response = 0.0, None
    for cached_query, cached_vec, cached_response in cache_rows:
        score = cosine_similarity(query_vec, cached_vec)
        if score > best_score:
            best_score, best_response = score, cached_response

    if best_score >= SIMILARITY_THRESHOLD:
        return best_response  # cache hit - zero LLM tokens spent

    # Cache miss - call the model and store the result for next time
    response = call_llm_fn(query)
    return response
Approach Catches paraphrased repeats? Lookup cost at scale
Exact-string cache (hash map) No — only identical text hits O(1), but rarely useful in practice
Semantic cache (HNSW-indexed vectors) Yes — similarity threshold catches rephrasing Sub-millisecond even at large cache sizes

The real production version of this (GPTCache is a ready-made example) also needs a real vector index rather than the linear scan shown above — at cache sizes past a few thousand entries, an HNSW-backed store keeps lookup fast enough that the cache check itself never becomes the bottleneck.

Practical Challenge

Write two differently-worded versions of the same question, embed both with any embedding model, and check whether their cosine similarity clears a 0.92 threshold — that number is what decides whether your cache would treat them as "the same question."

Concept Check

Why does a semantic cache catch more repeat queries than a simple exact-string cache?
Correct! An exact-string cache only hits on identical text. A semantic cache compares embeddings, so "What's our refund policy?" and "How do refunds work?" can both hit the same cached answer.
Incorrect. Try again! Hint: think about what's actually being compared — raw text, or the meaning captured by an embedding.

Sources & Further Reading

  • GPTCache (GitHub) — an open-source semantic cache implementing the pattern described in this article.
Previous Guide Dashboard Next Guide