Skip to content
Chapter 6 of 8

Prompt caching and cost

You pay per token, and most apps quietly waste money. They resend the same long system prompt on every single request and pay full price for it every time. Prompt caching fixes this. The model stores the stable prefix of your prompt after the first call, and later calls that reuse that prefix only pay for the new part. The answers do not change, only the cost. In this lab you run one stable system prompt across several queries, count the tokens billed with and without caching, and watch the repeated cost drop by most of the bill while the answers stay identical.

The lab: read it, then run it

labs/prompt-engineering/pe6-prompt-caching.py
#!/usr/bin/env python3
"""
LAB PE6: Prompt caching and cost.

You pay per token. Most apps resend the SAME long system prompt on every single
request, paying full price for it every time. Prompt caching stores that stable
prefix after the first call so later calls only pay for the new part. In this lab
you run one stable system prompt across several queries, count the tokens billed
with and without caching, and prove caching cuts the repeated cost while the
answers stay identical.

Run: python3 modules/academy-content/labs/prompt-engineering/pe6-prompt-caching.py
"""
import sys, os
_cands = [os.path.join(os.path.dirname(__file__), "..") if "__file__" in globals() else None,
          os.path.join(os.getcwd(), "..", "labs"), os.path.join(os.getcwd(), "labs")]
for _c in _cands:
    if _c and os.path.exists(os.path.join(_c, "academy_llm.py")):
        sys.path.insert(0, os.path.abspath(_c)); break
from academy_llm import complete, chat, embed, cosine, tool_route

def ntokens(s):
    return len(s.split())

# A big stable system prompt reused on every request (the expensive, repeated part).
SYSTEM = ("You are a careful sentiment classifier. " * 12).strip()
queries = [
    "Classify the sentiment: I love this",
    "Classify the sentiment: this is terrible",
    "Classify the sentiment: it is good and nice",
    "Classify the sentiment: a broken awful mess",
]
sys_tok = ntokens(SYSTEM)
print(f"stable system prompt = {sys_tok} tokens, reused across {len(queries)} queries")
print("")

# 1. NO CACHE: every request pays for the full system prompt + its query.
no_cache = 0
answers_a = []
for q in queries:
    no_cache += sys_tok + ntokens(q)
    answers_a.append(complete(SYSTEM + "\n" + q))
print(f"STEP 1: no cache, tokens billed = {no_cache}")

# 2. CACHE: pay the system prompt ONCE, then only the per-query tokens after that.
cache = sys_tok
answers_b = []
for q in queries:
    cache += ntokens(q)          # only the new suffix is billed on a cache hit
    answers_b.append(complete(SYSTEM + "\n" + q))
print(f"STEP 2: with cache, tokens billed = {cache}")

saved = no_cache - cache
print("")
print(f"STEP 3: tokens saved = {saved} ({100 * saved // no_cache}% cheaper)")

# 4. Caching must not change the answers, only the cost.
same = (answers_a == answers_b)
ok = (cache < no_cache) and same
print("")
print(f"        answers identical with and without cache : {same}")
print(f"PROMPT CACHING CUTS REPEATED TOKENS: {'YES' if ok else 'NO'}")
if not ok:
    sys.exit(1)
print("Cache the stable prefix, pay for it once. Next: context engineering.")
Runnable lab
pe6-prompt-caching.py

Run a stable system prompt across queries and prove caching cuts the repeated token cost.

Proves: PROMPT CACHING CUTS REPEATED TOKENS: YES

Open the notebook

Runs in your browser via Pyodide. First run loads the runtime once; no install, no server.

Same answers, a fraction of the cost. Without caching you paid for the full system prompt on all four queries. With caching you paid for it once and then only for each new query. The rule in production is simple: put the stable, reused material, your system prompt and long shared context, at the front where it can be cached, and put the part that changes at the end. Then measure the savings, because unmeasured optimization is just a guess. Next you learn to decide what belongs in the context at all.
Check your understanding
  1. 1. What does prompt caching do?

  2. 2. Where should you put the stable, reusable content to benefit from caching?

  3. 3. In the lab, what stayed the same when caching was applied?