Lakshya

LLM Systems Engineering · Chapter 14 of 20

Cost optimisation, and the interview questions about it

The levers in the order you should pull them, the caching mechanics in detail, and the reasoning that separates a good answer from a list of tips.

11 min read2 diagramsAll 20 chapters

This comes up in interviews constantly, and most answers are a shopping list of techniques. What distinguishes a good answer is the order and the reasoning — which levers are free, which cost you quality, and how you would know whether any of it worked.

First, what you are actually paying for

  • Input tokens — everything you send: system prompt, tools, retrieved context, the conversation so far, the question.
  • Output tokens — what comes back. Priced several times higher than input on every model.
  • And the fact that surprises people: the API is stateless. In a ten-turn conversation, turn ten resends turns one through nine. Cost does not grow linearly with conversation length — it grows roughly with the square of it. This is the single biggest driver of a runaway agent bill, and it is invisible if you only look at per-request cost.

Anthropic's current published rates, as an example of the shape: Opus 5 at $5 input / $25 output per million tokens, Sonnet 5 at $2 / $10, Haiku 4.5 at $1 / $5. Note that the spread between tiers is about five times, while the levers below routinely save more than that without changing model at all — which is why ‘use a cheaper model’ is a weak first answer.

THE ORDER MATTERS — TAKE EVERY FREE WIN BEFORE YOU TRADE ANY QUALITY FREE WINS — same output, smaller bill. Do all of these first. 1. Prompt cachingReuse the stable prefix. Cached reads cost ~0.1× a fresh read.2. Input hygieneStop resending what the model does not need. Trim, do not pad.3. Output hygieneAsk for less. Output costs several times input on every model.4. Loop hygieneStop agent loops re-reading the same context every turn.5. Batch50% off, both directions, if you can wait. Most background work can. Stack these and the bill falls a long way before you have given up a single point of quality. TRADE-OFFS — you are now buying savings with quality. Measure each one. 6. Lower the effortSame model, less thinking. Try this BEFORE changing model.7. A smaller modelPer route, never globally. Measure on real traffic.8. Routing / cascadeCheap model first, escalate on low confidence. See the caveat below.9. Fine-tune a small oneOnly for a narrow, high-volume, stable task. And judge COST PER COMPLETED TASK, never per request. A cheap call that needs three retries is not cheap.
The order is the answer. Anyone can list these; the interview signal is knowing that steps 1–5 cost you nothing and 6–9 cost you quality.

Prompt caching — the biggest free win, and the one most often broken

Caching lets the provider keep the processed form of a repeated prefix so it does not have to reprocess it. The economics, using Anthropic's published multipliers: writing to the cache costs 1.25× a normal input token for a five-minute lifetime (or 2× for an hour), and reading from it costs 0.1×.

Work that through, because it sets the break-even. You pay a 25% premium once and then pay 10% forever. You are ahead from the second request onwards, and by the tenth you have paid roughly a fifth of what you would have. For a chatbot with a long system prompt, or a retrieval system pasting the same documents repeatedly, this is the difference between a viable product and an unviable one.

CACHING IS A PREFIX MATCH — THIS IS THE WHOLE MENTAL MODEL The request is assembled in a fixed order: tools → system → messages. The cache matches from the START of that. STABLE PREFIX — cached, ~0.1× cost on every reuse VOLATILE — full price Change ONE BYTE anywhere in the green part and everything after it is invalidated. Not just that block — everything after it. THE FOUR SILENT INVALIDATORS — each one makes your hit rate exactly zero • A timestamp in the system prompt — “Today is 2026-08-29 14:03” changes every request. Move it to the end, or drop the clock time.• A user or request ID early on — Same problem. Personalisation belongs after the last cache breakpoint.• Unsorted JSON — Serialising a dict without sorting keys produces a different byte string each run.• A tool list that varies — Tools render FIRST, so adding or reordering one invalidates the system prompt too.• Too short a prefix — Below the model’s minimum cacheable length it silently does not cache at all. HOW YOU KNOW — and this is a one-line check nobody runs. Every response reports cache_read_input_tokens. Log it. If it is zero across repeated similar requests, caching is not working. You will not feel this. There is no error, no warning, no degradation — only a bill several times larger than it should be. Writing to the cache costs slightly MORE than a normal read, so a broken cache is worse than no cache at all.
The bottom box is the practical content. A broken cache is worse than no cache — you pay the write premium and never collect the read discount.
pythonlog all three, on every request, from day one
# Structure for the cache: stable first, volatile last.
response = client.messages.create(
    model="claude-opus-5",
    max_tokens=16000,
    system=[{
        "type": "text",
        "text": STABLE_INSTRUCTIONS + POLICY_DOCUMENTS,   # never changes
        "cache_control": {"type": "ephemeral"},           # <-- breakpoint here
    }],
    messages=[
        # everything volatile lives AFTER the breakpoint
        {"role": "user", "content": f"Today is {today}. {user_question}"},
    ],
)

# The one line that tells you whether any of this is working:
print(response.usage.cache_read_input_tokens)      # ~0.1x cost -- want this large
print(response.usage.cache_creation_input_tokens)  # ~1.25x cost -- want this rare
print(response.usage.input_tokens)                 # full cost -- want this small

Batch processing

50% off both input and output, in exchange for asynchronous processing with results typically back within a few hours. It applies to work nobody is waiting for: nightly classification, bulk summarisation, backfills, evaluation runs, report generation.

And it stacks with caching. A batched request against a cached prefix pays half of a tenth. The reason to name this in an interview is that most people treat these as alternatives rather than as multiplicative — and the honest follow-up is that far more workloads are latency-tolerant than teams assume. ‘Real-time’ is usually a habit, not a requirement.

Then the trade-offs, in order

6. Lower the effort before you change the model. Current models expose an effort or thinking control that trades thoroughness against tokens within one model. This matters more than it sounds:

Before building a multi-model cascade, measure the simpler thing: the capable model at lower effort.

Lower effort on a current model often matches or beats a previous-generation model at high effort — so you may get the saving without the complexity.

And a cascade has a hidden cost almost nobody accounts for: caches are model-scoped. Routing across three models means three separate cache namespaces, so you forfeit cache reuse every time you escalate. A cascade can therefore be more expensive than one model at lower effort, and the arithmetic that justified it usually ignored caching entirely.

Which workloads repay higher effort is a property of the workload: coding and long-horizon agent work respond strongly; chat, classification and high-volume routes often do not, and run fine at low. Tune per route, not globally.

7. A smaller model, per route. Classification, routing, extraction, reformatting and simple summarisation rarely need a frontier model. Per route, not globally — and measured on your real traffic, not on the vendor's benchmark.

8. Routing and cascades. Run a cheap model first and escalate when confidence is low or the output fails a check. Genuinely effective, and subject to the caching caveat above. The hard part is not the routing; it is the confidence signal, and a bad one gives you both bills.

9. Fine-tune a small model. Only for a narrow, high-volume, stable task where the cost of the tuning run amortises. See the worked fine-tune — and note that the base model will be superseded within a year and your adapter will not transfer, so put that in the arithmetic.

The architectural decisions that dwarf all of this

  • Retrieval versus long context. Pasting a whole 200-page manual into every request is enormously more expensive than retrieving the three relevant pages — usually a hundredfold — and it typically produces worse answers, because the relevant passage is buried. Retrieval is a cost optimisation and a quality improvement at the same time, which is rare enough to be worth saying out loud.
  • Agent loop discipline. An agent that re-reads the same files every turn pays for them every turn. Context editing, compaction and simply not putting large tool results back into history are where agent bills are actually won.
  • Do not call a model at all. The cheapest token is the one you never send. A regular expression, a lookup table or a cached previous answer handles a startling share of production traffic, and a semantic cache — returning a stored answer when a new question is close enough in meaning to an old one — catches the repeats that exact-match caching misses.
  • Cap the output. A sensible max_tokens and an instruction to be brief. Runaway generations are a real cost line.

How you would know any of it worked

This is the part that separates a strong answer from a list, and it is where most candidates stop too early.

  • Log token counts per request, split into cached, uncached and output, and tag them by route. Without the split you cannot tell a caching problem from a context problem.
  • Track cost per completed task, not per request. A cheaper model that needs three attempts to get a usable answer is more expensive, and per-request metrics report it as a saving. This single reframing is the most useful sentence in the chapter.
  • Have an evaluation set before you optimise, so a quality regression from step 6 or 7 is a number rather than a complaint three weeks later.
  • Attribute cost to a feature or team, not one line item called ‘LLM API’. You cannot make a decision about a bill you cannot decompose.

The interview questions, and what a good answer contains

QuestionWhat they are listening for
“Our LLM bill tripled. How do you investigate?”Decompose before optimising. Which route, which changed. Then: is it more requests, longer inputs, longer outputs, or a cache that stopped hitting? Naming the fourth is the signal.
“How would you cut it in half?”The ladder, in order, with free wins first. A candidate who opens with ‘use a cheaper model’ has skipped everything that costs nothing.
“How does prompt caching work?”Prefix match, write premium, read discount, and the invalidators. Anyone can say ‘it caches the prompt’; knowing a timestamp at the top breaks everything is the differentiator.
“When would you fine-tune to save money?”Narrow, high-volume, stable task; small model replacing a large one; amortised against a base model that will be replaced within a year.
“RAG or a long context window?”Retrieval, on cost and quality — with the honest caveat that a bad index makes it worse than both.
“Why is our agent so expensive?”The history is resent every turn, so cost grows with the square of the conversation. Then: context editing, compaction, keeping large tool results out of history.

The one-sentence version, if you only have a minute: take every free win first — caching, input and output hygiene, batching — because they change the bill without changing the output; only then trade quality, starting with effort rather than model; and measure cost per completed task, because per-request cost will happily tell you a lie.

Check yourself

Why does an agent's cost grow faster than its conversation?The API is stateless, so every turn resends the whole history. Cost grows roughly with the square of the conversation length.
What is the order of levers?Free wins first — caching, input hygiene, output hygiene, loop hygiene, batching. Only then trade-offs: lower effort, smaller model, routing, fine-tuning.
What are the caching economics?Roughly 1.25x to write for a five-minute lifetime, 0.1x to read. You are ahead from the second request; by the tenth you have paid about a fifth.
What are the silent cache invalidators?A timestamp or request ID early in the prompt, unsorted JSON, a varying tool list, and a prefix below the minimum cacheable length.
How do you know caching is working?Log cache_read_input_tokens. Zero across repeated similar requests means it is not — and there is no error, just a bill several times too large.
What is the hidden cost of a model cascade?Caches are model-scoped, so routing across models forfeits cache reuse. A cascade can cost more than one model at lower effort.
Retrieval or a long context window?Retrieval — usually about a hundredfold cheaper and typically better answers, because the relevant passage is not buried. A cost and quality win at once.
What is the most important measurement?Cost per completed task, not per request. A cheaper call that needs three retries is more expensive, and per-request metrics report it as a saving.
← RAG in the enterpriseAn eval suite, start to finish →