Read our operating thesis — Machines work better in tandem
JOURNALCOST · JULY 2026

Semantic caching for AI agents

Semantic caching is designed for repeated questions, and an agent loop almost never repeats a prompt. The wins are on the tool side — and the cache key has to carry the requester's authorization scope, or the cache becomes a data-leak channel.

WRITTEN BYTandem Machines
Coordinated Intelligence
PUBLISHED
READING TIME13 minutes
SHARE
LinkedInX
KEY TAKEAWAYS
  • 01Prompt caching is an exact prefix match run by the provider. Semantic caching is an approximate match you run. They are not alternatives.
  • 02Semantic caching on the model call rarely fires in an agent loop, because the context grows every turn.
  • 03The wins are on the tool side, where the same read genuinely repeats within and across runs.
  • 04The cache key must carry the requester's authorization scope. Without it, the cache is a data-leak channel.
  • 05A false hit is worse than a miss: you pay nothing and return a confidently wrong answer.

What semantic caching is

Semantic caching stores responses keyed by the meaning of a request rather than its exact text, so a paraphrase can hit a cached answer. The mechanism is: embed the incoming request, search a vector store for similar past requests, and return the stored response when similarity exceeds a threshold.

The motivation is sound. Exact-match caching performs poorly on natural language because users phrase the same question forty ways, so hit rates stay near zero. Semantic matching is the obvious fix, and for FAQ-shaped traffic it works well — reported gains for the pattern run to an order of magnitude on cost and considerably more on latency.

The architecture is consistent across implementations, and worth knowing because each component is a place the design can go wrong: an embedding generator to vectorise the request, a vector store to find the nearest past requests, a cache storage layer holding the responses, a similarity evaluator deciding whether a candidate counts as a hit, and a cache manager handling eviction.

Three kinds of caching, three jobs

Prompt caching is provider-side and keys on an exact byte-for-byte prefix. Exact-response caching keys on the literal request. Semantic caching keys on approximate similarity. They sit at different points in the request path and solve different problems — using one does not substitute for another.
Keys onRun bySaves
Prompt cacheExact prefix bytesThe model providerCost of re-processing a long stable prefix
Exact response cacheLiteral request stringYouThe whole call, when input repeats verbatim
Semantic cacheEmbedding similarityYouThe whole call, when input means the same thing
THE FIRST ROW IS THE ONE MOST AGENTS SHOULD FIX BEFORE CONSIDERING THE THIRD

Conflating the first and third is the most common mistake in this area, and it leads teams to build a semantic cache while their prompt cache is silently broken — which is both harder and far less valuable than fixing the prompt cache.

The prefix rule
Prompt caching invalidates on any byte change anywhere in the prefix. Render order is tools, then system, then messages — so a timestamp in the system prompt sits at the front and makes everything after it uncacheable. If cache-read tokens are zero across apparently identical requests, that is what to look for.

Fix that first. It is a one-line change with no accuracy risk, and it frequently delivers more than a semantic cache would.

Why it usually fires on the wrong layer

In an agent loop, no two model requests are similar enough to hit, because each turn appends the previous turn’s actions and results to the context. The requests diverge monotonically. Semantic caching assumes a population of independent, comparable questions — which is chat, not an agent loop.

Picture the actual sequence. Turn one is the objective. Turn two is the objective plus a tool call plus its result. Turn three adds two more. By turn six the request is substantially unlike anything that came before it in the same run, let alone in a different one.

“Semantic caching assumes a population of comparable questions. An agent loop is a single question that grows.”

There is a second, worse problem. Even where similarity does cross the threshold, the thing being cached is a decision about what to do next — and the correct next action depends on details the embedding flattens away. Two nearly identical states can require different actions, and a cache that cannot tell them apart will confidently supply the wrong one. A near-miss on a factual answer is a wrong sentence. A near-miss on a tool selection is a wrong action.

Where it does work

Semantic caching earns its place on the edges of an agent system rather than inside the loop:

LAYERS WHERE A SEMANTIC CACHE ACTUALLY HITS
01Front-door triageClassifying an incoming request before any loop begins
02Standalone answersFAQ-shaped questions resolved without tools
03Sub-queriesA retrieval or rewrite step invoked with a short, self-contained input
ALL THREE SHARE A PROPERTY: MANY INDEPENDENT REQUESTS, NOT ONE GROWING ONE

Cache tool results instead

The repetition in an agent run is on the tool side. An agent re-reads the same file, re-queries the same record, re-fetches the same page — within a run and across runs. Those calls have small, well-structured inputs and deterministic outputs, which makes them cacheable by exact key with no similarity risk at all.

This is the recommendation this article exists to make. It is less interesting than semantic caching and considerably more effective.

Model call in a loopTool call in a loop
Repeats?Almost never — context growsFrequently, within and across runs
Input shapeLarge, unbounded, uniqueSmall, structured, bounded
Match type neededSimilarity (risky)Exact (safe)
Cost of a wrong hitA wrong actionA stale read — bounded by your TTL

Read-only tools are the target: a document fetch, a schema lookup, a reference-data query. Key on the tool name plus the canonicalised arguments, set a TTL that reflects how fast the underlying data actually changes, and you get most of the available saving with none of the similarity exposure.

Exact-key tool cache
// Only read-only tools. Only if the tool declares itself cacheable.
function cacheKey(tool: string, args: Record<string, unknown>, scope: AuthScope) {
  // Canonicalise: sorted keys, so {a,b} and {b,a} are one entry.
  const canonical = JSON.stringify(args, Object.keys(args).sort());
  // Scope is part of the key — see the authorization section below.
  return `tool:${tool}:${scope.tenantId}:${scope.principalId}:${sha256(canonical)}`;
}

async function callTool(tool: ReadOnlyTool, args: Args, scope: AuthScope) {
  const key = cacheKey(tool.name, args, scope);
  const hit = await cache.get(key);
  if (hit) return hit;

  const result = await tool.run(args);
  await cache.set(key, result, { ttlSeconds: tool.cacheTtlSeconds });
  return result;
}
NOTE WHAT IS ABSENT: NO EMBEDDING, NO THRESHOLD, NO FALSE-HIT RISK

The key must carry the authorization scope

A cache keyed on request meaning alone will serve one principal’s data to another. Two users can ask a semantically identical question and be entitled to different answers, so the authorization scope — tenant, principal, role — has to be part of the key, not a check applied afterwards.

This is the failure that turns a performance optimisation into an incident, and it is absent from most writing on the subject because most writing on the subject assumes a single-tenant FAQ bot.

The mechanism is unglamorous. User A at Acme asks “what’s our outstanding balance?”. User B at Globex asks the same words. The embeddings are identical — the sentences areidentical — so the second request hits the cache and receives Acme’s balance. No vulnerability was exploited. The cache simply did what it was built to do.

Key dimensionWhyOmitting it causes
TenantData is partitioned by customerCross-tenant disclosure
Principal / roleTwo users in one tenant see different subsetsPrivilege escalation via cache
LocaleThe correct answer is language-specificWrong-language responses
Data versionThe answer was derived from a snapshotConfidently stale answers
ANYTHING THAT CHANGES WHAT A CORRECT ANSWER IS MUST BE IN THE KEY

There is a real tension here and it should be named: every dimension you add to the key partitions the cache and lowers the hit rate. A per-principal key on a system with many users may hit almost never, at which point the honest conclusion is that this workload is not cacheable at the response layer — not that the scope should come out of the key.

This is the same class of problem as a retrieval layer that returns content the requester could not otherwise read: authorization applied at the wrong boundary. See the agent threat model for the confused-deputy pattern it belongs to.

The threshold problem

Similarity scores are not calibrated. A cosine similarity of 0.95 means something different on each embedding model and on each corpus, so no threshold transfers between systems. Set it empirically against labelled pairs from your own traffic, and set it conservatively.

Implementations acknowledge this directly: a semantic cache produces false positives on hits and false negatives on misses. Those two errors are not symmetric, and the asymmetry should drive the threshold.

ErrorWhat happensCost
False negative (missed hit)You make a call you could have skippedThe price of one call. Bounded and visible
False positive (wrong hit)You return a stored answer to a different questionA confidently wrong answer, attributed to your system, invisible in cost metrics

A false negative shows up as spend. A false positive shows up as a support ticket, weeks later, with no trace explaining it — because from the system’s point of view nothing happened. Tune for precision and accept the lower hit rate.

How to actually set it

Take a few hundred real request pairs from your own traffic and label them: same answer, or different answer. Compute similarity for each, plot the two distributions, and pick a threshold above the overlap rather than through the middle of it. If the distributions overlap heavily, that is the finding — this traffic is not semantically cacheable, and no threshold rescues it.

What never to cache

Three categories, and the rule for each is absolute rather than a matter of tuning: anything with a side effect, anything whose correct answer depends on current state, and anything scoped to a principal whose scope is not in the key.
NEVER CACHE
01Side effectsA cached write means the second write never happened
02Live stateBalances, inventory, availability — true once, then wrong
03Unscoped answersAnything whose correctness depends on who asked
THE FIRST ONE IS NOT A CACHE AT ALL — IT IS A SILENTLY DROPPED ACTION

The first deserves its own sentence because it is a real thing people do. Caching a mutating tool call does not save a duplicate — it suppresses a genuine second action, which is the opposite problem from the one idempotency solves and needs the opposite treatment. If the concern is duplicate effects, the answer is an intent-derived key at the action layer, not a cache at the transport layer; see idempotency for agent actions.

Measuring whether it helped

Four numbers: hit rate, cost per resolved request, p95 latency, and the false-hit rate measured by sampling. The fourth is the one nobody instruments, and without it the first three will happily tell you that a cache serving wrong answers is a success.

A semantic cache optimises a metric that improves as correctness degrades. Loosen the threshold and hit rate rises, cost falls, latency falls — every dashboard turns green. The only counterweight is a deliberate audit: sample served hits, compare the stored response against what a fresh call returns, and track the disagreement rate as a first-class metric alongside the savings.

Report the savings and the false-hit rate together or not at all. A 40% hit rate at a 3% false-hit rate is not a 40% saving; it is a trade whose second term someone needs to have agreed to. For where this instrumentation belongs, see agent observability.

The short version: fix the prompt cache first, cache read-only tool results by exact key, and reach for semantic matching only at the edges where requests are genuinely independent — with the authorization scope in the key and a precision-biased threshold you derived from your own traffic.

Frequently asked questions

What is semantic caching?

Semantic caching stores responses keyed by the meaning of a request rather than its exact text, so a paraphrased question can hit a cached answer. It works by embedding the incoming request, searching for similar past requests in a vector store, and returning the stored response when similarity exceeds a threshold.

How is semantic caching different from prompt caching?

Prompt caching is provider-side and keys on an exact byte-for-byte prefix match — one changed character invalidates everything after it. Semantic caching is yours to run and keys on approximate similarity. They are complementary and solve different problems: prompt caching reduces the cost of a long stable prefix, semantic caching avoids the call entirely.

Does semantic caching work for AI agents?

Rarely on the model call. An agent's context grows every turn, so no two requests in a loop are similar enough to hit, and a near-miss on a tool-selection decision is dangerous rather than merely wrong. The wins in an agent are on the tool side, where the same read genuinely repeats within and across runs.

What should be in a semantic cache key?

The request embedding plus every dimension that changes what a correct answer would be: the requesting principal's authorization scope, tenant, locale, and the version of any underlying data. Omitting the authorization scope turns the cache into a mechanism for serving one user's data to another.

What similarity threshold should I use for a semantic cache?

There is no portable number, because similarity scores are not calibrated across embedding models or corpora. Set it empirically against labelled pairs from your own traffic, and set it conservatively — a false hit serves a confidently wrong answer, which costs more than the call you avoided.

What should never be semantically cached?

Anything with a side effect, anything whose correct answer depends on current state, and anything scoped to a principal whose scope is not in the key. Caching a write is not a cache, it is a dropped action; caching a balance or an inventory count returns a confident answer that used to be true.

Sources

  1. 01Prompt caching Anthropic, 2026
  2. 02GPTCache: a semantic cache for LLMs Zilliz, 2026
Tandem Machines
Notes on coordinated intelligence — one essay a month
Subscribe
PREVIOUS

Mastra vs LangGraph vs CrewAI