Prompt Caching Is an Architecture Problem, Not a Parameter

If you are running autonomous agents in production and caching feels like a nice-to-have, you are probably leaving 40 to 80 percent of your API spend on the table. A typical eight-step research agent processing 5,000 tokens of context per step incurs 40,000 input tokens without caching. With the right structure, that drops to 8,000. That is not a configuration tweak; it is an architectural decision you have to get right before you write your first cache directive.

This article walks through how prompt caching actually works at the infrastructure level, why naive full-context caching can make things worse, and what a cache-aware agent architecture looks like from structural placement through multi-turn decay and batch processing.


Caching Is Prefix Matching, Full Stop

The most important thing to understand about prompt caching is what it is not: it is not a smart similarity search, and it does not "remember" semantically equivalent prompts. It is a byte-exact prefix match. If the beginning of your current request is identical to the beginning of a previous cached request, the cached tokens are reused. The moment any byte in that prefix changes, the cache misses entirely.

This has an immediate architectural consequence: static content must come first, dynamic content must come last. Always. Without exception.

System prompts, tool definitions, and large reference documents are static. User queries, tool results, and accumulated conversation turns are dynamic. If your system prompt contains a timestamp, a session ID, or any per-request variable, the cache will never hit. If tool definitions are interspersed with dynamic context, the prefix invalidates on every call.

Most cache hit rate failures in production trace back to this single structural error, not to cache configuration or provider limits.


The Trap of Full-Context Caching

Once developers understand prefix matching, the instinct is to cache everything: system prompt, tool definitions, conversation history, and all prior tool outputs. In theory, caching a longer prefix saves more tokens. In practice, research on long-horizon agentic tasks has found that naive full-context caching can reduce overall cache hit rates and, in measured deployments, increase end-to-end latency compared to selectively caching only the stable prefix.

Why? Full-context caching ties the cache to the most volatile part of the prompt. Tool outputs change on every iteration. When they do, the entire cached prefix misses, and you have paid both the cost of checking the cache and the cost of regenerating input tokens from scratch. On a large full-context prefix, that miss penalty is substantial.

The consistently safer strategy is to cache only the stable layer: the system prompt and static context. Dynamic content sits outside the cache boundary. This way, even when tool outputs change, the stable prefix remains valid and the recomputed delta is small.

Structurally, this means placing a cache_control marker at the end of your static system content, not at the end of the full message history.

messages = [
    {
        "role": "user",
        "content": [
            {
                "type": "text",
                "text": STATIC_SYSTEM_CONTEXT,  # large, stable reference material
                "cache_control": {"type": "ephemeral"}  # cache boundary here
            },
            {
                "type": "text",
                "text": current_user_query  # dynamic, NOT cached
            }
        ]
    }
]

Contrast this with the problematic pattern of placing the cache marker at the very end of a fully assembled message that includes tool outputs. Every tool call invalidates the whole thing.


Multi-Turn Decay: The Problem That Grows Slowly and Hurts Late

Single-turn caching is relatively forgiving. Multi-turn agents accumulate state, and that is where cache efficiency quietly collapses if you are not watching for it.

Research on long-horizon agentic tasks found that a naive agent appending every tool output and assistant response to a growing conversation history can show roughly 90 percent cache hits on turn two. By turn 20, that number can fall below 15 percent. The reason is straightforward: the growing history keeps pushing dynamic content earlier in the prefix, eventually overtaking the static cache block from the perspective of byte-exact matching.

The fix requires treating cache management as an active task rather than a passive feature. Three approaches work well in practice:

History pruning. Periodically drop the oldest turns from the conversation history, keeping only recent context and the permanent static header. The static prefix stays valid; only recent dynamic content is resent.

Summarization rotation. When conversation history grows past a threshold, summarize older turns into a compact paragraph and append that summary to the static context (outside the cache boundary). This preserves information while keeping the dynamic section small.

Tiered caching. Keep two cache points: one at the end of the immutable system prompt, and a second "sliding window" cache point at a recent-but-stable point in the history. The first always hits; the second has moderate hit rates but reduces recomputation costs even on partial misses.

def build_agent_messages(system_prompt, history, max_history_turns=10):
    # Static layer: always cached (can also be placed in the system parameter)
    cached_block = {
        "type": "text",
        "text": system_prompt,
        "cache_control": {"type": "ephemeral"}
    }

    # Dynamic layer: prune to last N turns to prevent decay
    pruned_history = history[-max_history_turns:]

    return [
        {"role": "user", "content": [cached_block]},
        *pruned_history  # appended outside the cache boundary
    ]

The specific threshold depends on your use case, but the pattern is the same: enforce a ceiling on the dynamic section so the cached prefix always starts at the same byte offset.


Tool Definitions Are Cache Infrastructure

Tool definitions are part of your system prompt prefix. Changing them in any way, including reordering them, adding a parameter, or renaming a field, breaks the cache for the entire session.

For agents with a fixed, known tool set, this is manageable: freeze your tool definitions and treat them like configuration code. No silent mutations between turns.

For extensible agents that need to dynamically add tools based on context, there is a better pattern: deferred tool definitions. Rather than including every possible tool in the system prompt, include only the core tools in the cached prefix and append specialized tool definitions after the cache boundary when they are needed. The model discovers these through a lightweight tool-search step and receives the definition inline in the dynamic portion of the message.

This means adding a new specialized tool does not invalidate the existing cache. Your permanent tool set stays stable; your extended tool set lives in the dynamic layer where invalidation is cheap.


Know Your Provider's Minimum: Caching Has an Entry Cost

Prompt caching does not activate below a minimum prefix size. On Anthropic's API, that floor is 1,024 tokens; other providers set it as high as 4,096 tokens. Requests below the threshold are checked against the cache, find nothing, and proceed normally, but you have paid the overhead of the cache lookup with no benefit.

The economics scale with how large your cacheable prefix is relative to your total input. Cache-read prices on major providers are typically 80 to 90 percent cheaper than standard input token prices, so savings are proportional to how much of each request can actually be served from cache. A request where a small static prefix sits atop a large dynamic payload captures limited value. A request where 48,000 tokens of static context precede a 2,000-token dynamic section, repeated across dozens of turns, can reduce input token costs by 60 to 90 percent.

Knowledge-intensive agents with large system prompts, dense tool definitions, or extensive reference documents benefit most. Lightweight agents with minimal system prompts may see negligible ROI and should not engineer around a cache that rarely activates.


Cache Duration and Batch Alignment

Cached entries have a minimum lifetime of five minutes on the standard tier and up to one hour on extended tiers (availability and pricing vary by provider). For long-running agents with infrequent turns, this creates a practical risk: if the gap between turns exceeds the cache lifetime, the cached prefix expires and must be rebuilt on the next call.

For batch processing workflows, there is a compounding benefit worth noting: batch API discounts and prompt caching discounts stack. A batch of requests that all share an identical system prompt can receive both the per-token batch discount and the cache write/read discount on those shared tokens. The constraint is that requests in the batch must share byte-identical cache blocks and must execute quickly enough that the first-written cache entry is still valid when later requests in the batch read from it.

For jobs running longer than five minutes, the extended cache tier is worth its additional write cost, because a cache miss on a large prefix can wipe out more savings than the tier upgrade costs.


Anti-Patterns That Break Caches Silently

Cache failures are silent by default. There is no error, no warning, and no obvious performance cliff. Your cost monitoring shows elevated token counts, your latency numbers creep up, and nothing in the logs explains why. These are the most common culprits:

Timestamps or session IDs in the system prompt. Every request generates a new prefix. Cache hit rate is zero. This is the most common cause of "caching seems to not work" reports.

Re-injecting standing instructions each turn. Many agent frameworks append a reminder like "You are an assistant that..." to every user turn. If that reminder ends up before the cache boundary, it silently prevents cache reuse.

Extended thinking settings that vary by turn. If you enable or disable extended thinking mid-session, the entire prompt structure changes. Keep extended thinking mode consistent throughout a session.

Model switching between turns. Cache entries are scoped to a specific model version. Switching models, even between versions of the same family, invalidates all prior cache entries.

Reordering tool definitions. Tool definitions are byte-compared as part of the prefix. Alphabetizing them differently on one call invalidates the cache. Build your tool list once, sort it once, and treat the order as immutable.


Semantic Caching: The Emerging Next Layer

Everything covered so far is prefix caching, which operates at the token level. A complementary technique is gaining traction for agents that deal with high prompt variation: semantic caching uses embeddings to identify conceptually equivalent requests and return stored responses even when the exact wording differs.

For autonomous agents, semantic caching adds two benefits beyond cost reduction. First, it enables cache hits on reformatted or paraphrased context that a prefix cache would miss entirely. Second, for agents running repetitive tasks over a narrow domain, reusing a previously computed and validated response reduces output variation across equivalent queries: the model returns a stored answer rather than generating a new one that may diverge. This is a narrower claim than "reducing hallucinations" in the general sense. The stored response carries whatever errors the original contained; the benefit is consistency across equivalent queries, not guaranteed correctness.

Semantic caching does not replace prefix caching. It sits above it, catching the cases where the static prefix is valid but the dynamic query has minor variations. For agents running at scale over narrow task domains, the two layers together can push effective token reuse well above what either approach achieves alone.


Conclusion

Prompt caching is not a configuration option you enable at the end of development. It is an architectural constraint that shapes how you structure every prompt in your system. Get the structure right first: static content at the top with the cache boundary immediately after it, dynamic content below. Then manage multi-turn decay actively, freeze your tool definitions, stay above the minimum prefix threshold, and audit your prompts for silent mutations.

A production agent that treats caching as infrastructure, rather than an afterthought, routinely achieves 60 to 80 percent reductions in input token costs. At scale, that is the difference between a sustainable cost model and one that quietly erodes your margins with every API call.

Subscribe to Marvin G6 | Engineering, AI & Automation

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe