Prompt Caching and Semantic Caching Economics
**Pricing snapshot dated 2026-04.** Numeric claims below reflect vendor rate cards captured at this lesson's publication; verify against the linked docs before quoting them downstream.
Learning Objectives
- Distinguish L2 prompt/prefix caching (KV reuse at provider) from L1 semantic caching (LLM bypass on similar prompts).
- Explain Anthropic's `cache_control` explicit marking and the two TTL options (5-min vs 1-hour) with their price multipliers.
- Compute expected monthly savings given hit rate, prompt/response mix, and token prices.
- Name the parallelization anti-pattern that inflates bills by 5-10x and the dynamic-content anti-pattern that collapses hit rate.
The Problem
You add prompt caching to your RAG service and the bill stays flat. You measure the hit rate and it is 7%. The prompts look static, but they are not. The system prompt includes the current date formatted to the minute, a request ID, and a randomized example reorder for diversity, so each request writes a new cache entry and reads nothing.
Separately, your agent runs ten parallel tool calls per user question. All ten reach the provider before the first cache write completes, which gives you ten writes and no reads. The bill lands at 5-10x what "with caching" was supposed to cost.
Caching is a protocol, not a flag. There are two layers, and each fails in its own way.
The Concept
L2: provider prompt/prefix caching
Provider stores the attention KV for a cacheable prefix and reuses it on the next request that matches the prefix. You pay a write cost once and reads are nearly free.
Anthropic (Claude 3.5 / 3.7 / 4 series): explicit cache_control marker in the request. You tag which blocks are cacheable. TTL: 5-minute (write costs 1.25x base) or 1-hour (write costs 2x base). Cache reads: $0.30/M on Claude 3.5 Sonnet vs $3.00/M fresh, which is 10x cheaper (docs.anthropic.com, as of 2026-04). Rates differ per model (Opus and Haiku are published separately), so check the live pricing page.
OpenAI: automatic caching for prompts ≥1024 tokens (platform.openai.com, 2026-04). No explicit flag. Cached input is roughly 10x cheaper than fresh on current gpt-4o/gpt-5 rate cards. Neither the docs nor the release notes publish an official hit-rate baseline. Community reports cluster around 30–60% with careful prompt design. Monitor usage.cached_tokens to measure your own.
Google (Gemini): context caching via explicit API; the 1M-token context means caching pays off even more.
Self-hosted (vLLM, SGLang): Phase 17 · 06 covers RadixAttention, the same pattern on your own compute.
L1: app-level semantic caching
Before calling the LLM at all, hash the prompt, embed it, and look for a similar cached request (cosine similarity above a threshold, typically 0.95+). On a hit, return the cached response. On a miss, call the LLM and cache the result.
Open-source: Redis Vector Similarity, GPTCache, Qdrant. Commercial: Portkey Cache, Helicone Cache.
Vendor accuracy claims measure how often the returned cached response was semantically appropriate, which is a different number from how often you hit. Production hit rates run like this:
- Open-ended chat: 10-15%.
- Structured FAQ / support: 40-70%.
- Code questions: 20-30% (small variants kill hits).
- Voice agents repeating prompts: 50-80% (voice normalization fixed set).
The parallelization anti-pattern
Your agent makes 10 tool calls in parallel, and all 10 share the same 4K-token system prompt. The first cache write completes around 300 ms after the provider sees the prompt. Requests 2-10 arrive in the same millisecond window, so each one sees a miss. You pay 10 write premiums and collect 0 read discounts.
Fix: go sequential-first. Make request 1 alone, then fire 2-10 once its cache has populated. That adds about 300 ms to the first tool call and saves 5-10x on the bill.
The dynamic content anti-pattern
Your system prompt looks like:
You are a helpful assistant. The current time is 14:32:17.
User ID: abc123. Today is Tuesday...
Each request carries different bytes near the top, so each one writes a new entry and none of them hit.
Fix: put everything static in the cacheable prefix and append dynamic content after the cache boundary:
[cacheable]
You are a helpful assistant. [rules, examples, instructions]
[/cacheable]
[dynamic, not cached]
Current time: 14:32:17. User: abc123.
ProjectDiscovery moved from 7% to 74% cache hit rate this way and published the breakdown.
Stack batch + cache for overnight workloads
Batch APIs (Phase 17 · 15) give a 50% discount at 24-hour turnaround. Cached input stacks another ~10x on top. Overnight classification, labeling, and report generation can drop to ~10% of synchronous-uncached cost this way.
Numbers to remember
Pricing points are captured 2026-04 from the linked vendor docs and drift every few months, so re-check before relying on them.
- Anthropic cached read: $0.30/M on Claude 3.5 Sonnet, roughly 10x cheaper than fresh input (docs.anthropic.com).
- Anthropic cache write premium: 1.25x (5-min TTL) or 2x (1-hour TTL).
- OpenAI auto-cache: applies to prompts ≥1024 tokens; cached input priced at roughly 10% of fresh input on current rate cards (platform.openai.com).
- Semantic cache hit rate (community-reported): ~10% open chat, up to ~70% structured FAQ. Not a vendor-documented baseline.
- ProjectDiscovery: 7% → 74% hit rate by moving dynamic content out of the prefix (project blog, 2025-11).
- Parallelization anti-pattern: typical reports of 5–10x bill inflation when N parallel requests all miss the first cache write.
Use It
code/main.py simulates L1 and L2 caching on a mixed workload of about 1,300 requests (500 arrival events, 40% of them parallel waves of five) and prints cost, L1 hits, L2 reads, and L2 writes for five configs: no caching, L2 with the parallel penalty, L2 with the penalty fixed, and L2 with a 30% and a 70% semantic-cache hit rate. Compare the second and third rows to see what the parallelization penalty costs you.
Ship It
This lesson produces outputs/skill-cache-auditor.md. Give it a prompt template and a traffic pattern, and it audits cacheability and recommends a restructure.
I'd run main.py before trusting any of the dollar figures above. It prices about 1,300 requests (500 arrival events, 40% of them waves of five) at the Sonnet-class rates in the constants at the top of the file, and the second and third rows differ only in the parallel-penalty flag, so the gap between them is the cost of the N-write trap on that workload. It comes to about $1 on a $6 bill, nowhere near 5-10x, because only the first touch of each of the 12 prefixes can miss.
Keep the simulator in proportion, though. The L1 layer is a coin flip at a fixed hit probability, so the 0.95 threshold is passed in but never used, and nothing is embedded. The L2 set also has no expiry, which means the 5-minute and 1-hour TTLs only change the write price here and never evict anything. Exercise 3 is where TTL choice gets real, and you have to bring your own arrival rate.
My bet is that the dynamic-content fix is the one to do first. It costs a prompt edit, no new infrastructure, and the 7% to 74% move in the concept section came from exactly that. I'd reach for a semantic cache only after the prefix is clean, and I'd measure usage.cached_tokens or the Anthropic equivalent before and after rather than trusting my read of the template.
Exercises
- Run
code/main.py. Toggle the parallelization flag. How much does the bill change? - Your system prompt has a date. Move it out. Show before/after hit rate math.
- Calculate break-even for 1-hour TTL (2x write) vs 5-minute TTL (1.25x write) given your request arrival rate.
- Semantic cache at 0.95 threshold hits 20%. At 0.85 it hits 50% but you see incorrect cached responses. Pick the right threshold and justify.
- You batch 10 parallel sub-queries per user question. Rewrite for cache-friendliness without adding end-to-end latency.
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| L2 prompt cache | "prefix cache" | Provider stores KV for repeated prefix |
cache_control | "Anthropic cache marker" | Explicit attribute marking cacheable blocks |
| Cache write premium | "write tax" | Extra cost for first miss-to-cache (1.25x or 2x) |
| L1 semantic cache | "embedding cache" | App-level hash-and-embed before calling LLM |
| GPTCache | "LLM caching lib" | Popular OSS L1 cache library |
| Cache hit rate | "hits / total" | Fraction of requests served from cache |
| Parallelization anti-pattern | "the N-write trap" | N parallel requests miss cache N times |
| Dynamic content trap | "the time-in-prompt trap" | Dynamic bytes in prefix kill hit rate |
| RadixAttention | "intra-replica cache" | SGLang's prefix-cache implementation |
Further Reading
- Anthropic Prompt Caching: official
cache_controlsemantics and TTLs. - OpenAI Prompt Caching: automatic caching behavior and eligibility.
- TianPan: Semantic Caching for LLMs Production
- ProjectDiscovery: Cut LLM Costs 59% With Prompt Caching
- DigitalOcean / Anthropic: Prompt Caching