In March 2024, engineers running a large customer-support deployment on top of an LLM noticed something odd in their GPU utilization graphs: the same 1,200-token block of instructions and worked examples was being fed through all 32 transformer layers, from scratch, on every single incoming ticket — thousands of times an hour — even though that block had not changed since the last deploy. The model was re-deriving the same keys and values for the same tokens again and again, burning compute on work it had already done. The fix was not a better sentence in the prompt. It was recognizing that a prompt is not just text a model reads; it is a sequence of tokens that gets compiled into cached tensors inside a specific architecture, and that architecture has exploitable structure. This chapter treats prompt engineering the way the rest of your Class 12 year has treated transformers: as an artifact with a mechanism, not a bag of tricks. You already know attention, the KV cache, and in-context learning from this year's Advanced AI & Mathematics track. Here we push on three questions a "better wording" treatment does not answer: what physically happens to a prompt inside the model that makes reusing it valuable at production scale, what formal claim can be made about why in-context examples change model behaviour at all, and how do you force a model's output into an exact schema rather than hoping it cooperates.
Recap: what a prompt becomes inside the model
Recall the mechanics. For every token position t, each self-attention layer computes a query vector q_t, a key vector k_t, and a value vector v_t by projecting the token's residual-stream representation through learned matrices W_Q, W_K, W_V. Attention output at position t is a weighted sum of the value vectors of all earlier positions, weighted by the softmax-normalized dot products of q_t against every k_{\le t}. Because generation is autoregressive, a serving engine caches every (k_i, v_i) pair it has already computed, at every layer, so that producing token t+1 does not require recomputing attention over tokens 1..t from scratch — this is the KV cache. The crucial fact for this chapter: k_i and v_i for a given token depend only on that token and everything before it, and on the frozen model weights. Nothing about them depends on which API call you happen to be inside. If two different requests share an identical prefix — the same system prompt, the same few-shot exemplars, byte for byte — the K,V tensors for that prefix are mathematically identical both times. Prompt engineering, at the systems level, is partly the discipline of designing prompts so that the expensive, reusable part (instructions, exemplars, schema definitions) is a stable prefix, and the cheap, per-request part (the user's actual question) is a short suffix appended at the end.
Prefix caching: turning a stable prompt into a production asset
This is exactly what systems like vLLM's automatic prefix caching and the Claude API's prompt-caching feature exploit: the first request pays the full cost of running the shared prefix through the network and stores the resulting K,V tensors; every subsequent request with the same prefix skips that computation entirely and only runs a forward pass over its own short suffix, attending back over the cached K,V for everything before it. Let's derive what that actually buys you, with a concrete architecture so every number is checkable rather than asserted.
Take an illustrative 7B-parameter-class transformer with the well-documented Llama-2-7B configuration: L = 32 layers, d_model = 4096, 32 attention heads (head_dim = 128), fp16 storage (2 bytes/element). Suppose a support-bot deployment uses a reusable prefix of P = 1200 tokens (system instructions plus a handful of few-shot exemplars) and the average user query is Q = 40 tokens.
>>> L, d_model, bytes_fp16 = 32, 4096, 2
>>> P, Q = 1200, 40
>>> # KV cache size: 2 tensors (K and V) x L layers x d_model x P tokens x bytes/elem
>>> kv_cache_bytes = 2 * L * d_model * P * bytes_fp16
>>> kv_cache_bytes
629145600
>>> kv_cache_bytes / 1024**2 # MiB
600.0
>>> # Forward-pass cost per token ~ 2N FLOPs (N = non-embedding params),
>>> # the standard estimate from Kaplan et al. (2020), "Scaling Laws for
>>> # Neural Language Models," arXiv:2001.08361.
>>> N_params = 7e9
>>> flops_per_token = 2 * N_params
>>> flops_per_token
14000000000.0
>>> flops_no_cache = flops_per_token * (P + Q) # cold start: recompute P+Q every time
>>> flops_no_cache
17360000000000.0
>>> flops_with_cache = flops_per_token * Q # cache hit: only Q is fresh
>>> flops_with_cache
560000000000.0
>>> savings = (flops_no_cache - flops_with_cache) / flops_no_cache
>>> savings * 100
96.7741935483871
So a 1,200-token reusable prefix costs 600 MiB of GPU memory to keep resident as a KV cache — a real, budgetable cost — and buys a 96.8% reduction in the FLOPs spent per request once it is warm, because the 40-token suffix is all that needs a fresh forward pass. This is the diagram below: Request 1 runs the full prefix cold and writes it into a shared cache; Request 2 reads that cache instead of recomputing it, and pays only for its own new tokens.
Two things follow directly from this mechanism, and they are prompt-engineering decisions, not infrastructure decisions. First, put everything stable — role instructions, formatting rules, few-shot exemplars, retrieved-but-fixed reference documents — at the front of the prompt, and everything that varies per request at the end. If a single per-request field (a timestamp, a user ID) is interleaved in the middle of an otherwise-static block, it breaks the prefix match and the entire cache is invalidated on every call, because caching works on exact token-sequence prefixes, not on semantic similarity. Second, there is a real, quantifiable tradeoff in exemplar count: more few-shot examples usually improve accuracy but linearly grow the cache's memory footprint, so "how many examples should I put in the prompt" is now also a capacity-planning question with a formula behind it, not just an accuracy question.
Why in-context examples work at all: the implicit-optimization view
Sibling material on this site walks through zero-shot, few-shot, and chain-of-thought prompting as techniques you apply. A fair question those techniques leave open: what is a few-shot exemplar actually doing to the network, mechanically, that a plain instruction is not? Dai, Sun, Dong, Hao, Ma, Sui, and Wei, in "Why Can GPT Learn In-Context? Language Models Secretly Perform Gradient Descent as Meta-Optimizers" (Findings of ACL 2023, pp. 4005–4019), give a precise answer for a simplified but instructive case.
Strip the softmax out of self-attention and keep only the linear part (the paper does this as a tractable approximation of the real mechanism, then validates the conclusion empirically on real, softmax-based transformers). For a query vector q attending over demonstration key/value pairs {(k_i, v_i)} drawn from the in-context examples, linear attention computes:
Attention_linear(q) = Σ_i v_i (k_i∙q) = (Σ_i v_i k_iᵀ) q = W_ICL · q
where W_ICL = Σ_i v_i k_iᵀ is an outer-product sum built entirely from the demonstration tokens. Now compare this to one step of gradient descent on a linear layer with weight matrix W, trained on input/error pairs (x_i, e_i): the accumulated update is ΔW = -η Σ_i e_i x_iᵀ — also an outer-product sum. W_ICL and ΔW have identical algebraic structure: a sum of outer products between a "signal" vector (v_i or the error e_i) and an "input" vector (k_i or x_i). The paper's reading is that each demonstration in the prompt produces a "meta-gradient," and the attention mechanism applies the sum of these meta-gradients to the query representation before the model ever samples a token — functionally equivalent to fine-tuning a linear layer for one step on the demonstrations, except the "training" happens inside the forward pass, and the "weight update" is discarded the instant the context window changes. Their empirical section shows the analogy holds on real models too: attention patterns induced by in-context demonstrations correlate with attention patterns induced by actual gradient-descent fine-tuning on the same examples, and a momentum-based attention variant they designed by analogy with momentum-based SGD outperforms vanilla attention — evidence for the mechanism, not just a metaphor for it.
This reframes what a prompt engineer is actually doing when they add exemplars: they are handing the model synthetic training signal that gets consumed as an implicit, single-step, in-forward-pass optimization update — bounded by the size of the context window and confined to that one call, never touching the stored weights.
Forcing exact structure: constrained decoding versus asking nicely
A second production-grade prompt-engineering problem the sibling chapters don't cover: getting a model to emit output that a downstream program can parse without a try/except wrapped around every call. The naive approach — "respond only in JSON matching this schema" — still leaves the model free to sample any token at every position; a schema-violating token can win at any step if it happens to have higher probability than the valid options, and it usually does not fail loudly, it fails by producing plausible-looking but unparseable text on some fraction of calls.
Grammar-constrained decoding, used in JSON-mode/tool-use features and libraries like Outlines and llama.cpp's GBNF grammars, does something structurally different: at every decoding step it compiles the target schema into a finite-state grammar, checks which vocabulary tokens are legal continuations given everything generated so far, and sets the logits of every illegal token to -∞ before the softmax runs. The model's raw preferences over illegal tokens become irrelevant; probability mass is redistributed only over the tokens that keep the output schema-valid.
Trace it. Suppose the schema is {"verdict": "PASS" | "FAIL"} and the model has already emitted {"verdict": ". The raw logits over five candidate next tokens are:
>>> import math
>>> tokens = ["PASS", "FAIL", "MAYBE", "true", "1"]
>>> logits = [2.1, 1.8, 3.5, 0.5, -1.0]
>>> exps = [math.exp(z) for z in logits]
>>> Z = sum(exps)
>>> probs = [e / Z for e in exps]
>>> for t, p in zip(tokens, probs): print(t, round(p, 4))
PASS 0.1655
FAIL 0.1226
MAYBE 0.6711
true 0.0334
1 0.0075
Unconstrained, the model's top pick is "MAYBE" — a fluent, high-probability token that is invalid under the schema. It happens because "MAYBE" is a common, unremarkable word in the model's training distribution; nothing in the raw logits knows or cares that the surrounding JSON only permits two literals. Now apply the grammar mask: the parser has already determined that, having consumed {"verdict": ", only "PASS" or "FAIL" are legal continuations, so every other logit is set to -∞ and the softmax is renormalized over just those two:
>>> valid = ["PASS", "FAIL"]
>>> valid_exps = [math.exp(logits[tokens.index(t)]) for t in valid]
>>> Zc = sum(valid_exps)
>>> for t, e in zip(valid, valid_exps): print(t, round(e / Zc, 4))
PASS 0.5744
FAIL 0.4256
The output is now guaranteed to be schema-valid on every single call, not merely likely to be — and note the ranking of PASS over FAIL is preserved from the model's own relative preference (2.1 > 1.8), just renormalized; constrained decoding does not override the model's judgment among valid options, it only removes the option to be invalid. This is the real engineering lever behind "structured outputs": the prompt still needs to communicate the schema and the task, but reliability at the tail comes from the decoding-time mask, not from phrasing the instruction more persuasively.
A common misconception, corrected
Students who have only seen prompt engineering framed as wording tricks often conclude that a prompt is a suggestion the model may or may not follow, and that better results come from finding more persuasive phrasing — "please," "you must," "this is very important." That is backwards for both mechanisms in this chapter. Prefix caching does not care about persuasion at all: it cares whether the token sequence is byte-identical to one seen before, a strictly mechanical property. And in-context learning, on the implicit-optimization account, is not about the model being convinced by wording; the exemplars act as training signal processed through a fixed mathematical operation (attention as outer-product accumulation) regardless of how politely they are introduced. Rewording an instruction can change results because it changes the token sequence the model conditions on, not because the model is more or less "willing" to comply. Prompt engineering is the design of a token sequence with predictable computational consequences, not a negotiation.
Active recall
Attempt each question before reading its answer.
Q1. Why can the K,V tensors for a fixed prompt prefix be cached and reused across independent API calls, but a model's previously generated output tokens generally cannot be reused the same way if the same question is asked twice?
Q2. A team uses a model with L=40 layers and d_model=5120 (fp16 KV cache), with a reusable system prompt of P=2,000 tokens. Compute the KV cache size in MiB and GiB.
Q3. Using the chapter's numbers (L=32, d_model=4096, 7B params, Q=40), the team shrinks the few-shot block from P=1200 to P=300 tokens to reduce memory. What happens to (a) the cache's memory footprint and (b) the percentage of FLOPs saved by caching? Which degrades faster, and why?
Q4. After generating {"status": " under the schema {"status": "OK" | "BAD"}, a model produces raw logits OK=7.5, BAD=7.4, N/A=7.6. With grammar-constrained greedy decoding, which token is emitted, and what are the renormalized probabilities of the two valid options?
Q5. In the implicit-gradient-descent view of in-context learning (Dai et al., 2023), what plays the role of the "input" vector x_i from ordinary gradient descent, and what plays the role of the "error" vector e_i?
Q6. A classmate argues "prompting is basically fine-tuning without updating weights, so results should always match what fine-tuning would give." Using the Dai et al. (2023) framing itself, give one concrete reason this is false.
A1. k_i and v_i at every layer are a deterministic function of token i, every token before it, and the frozen model weights — nothing about a repeat call changes any of those, so an identical prefix produces bit-identical K,V. Generated output, in contrast, is typically produced by sampling (temperature > 0, top-p, etc.), so two calls with the same prompt can legitimately produce different continuations; there is no guarantee of an identical token sequence to key a cache on, so naive prefix caching does not apply to generated text the way it does to a fixed prompt (systems that want this instead use exact-match or semantic response caching, a different technique).
A2. bytes = 2 × L × d_model × P × 2 = 2 × 40 × 5120 × 2000 × 2 = 1,638,400,000 bytes = 1,562.5 MiB ≈ 1.53 GiB.
A3. Memory scales linearly in P: 150 MiB at P=300 versus 600 MiB at P=1200 — a clean 4× drop, matching the 4× drop in P. FLOPs saved: at P=1200, savings = (17.36×10¹³ − 0.56×10¹¹)/17.36×10¹³ = 96.77%; at P=300, flops_no_cache = 1.4×10¹⁰ × 340 = 4.76×10¹², flops_with_cache is unchanged at 5.6×10¹¹ (Q didn't change), giving savings = (4.76×10¹² − 0.56×10¹¹)/4.76×10¹² = 88.24%. Memory drops by 75% but the caching benefit only drops from 96.8% to 88.2% — a much smaller relative change. The reason: percentage savings is bounded by P/(P+Q); shrinking P shrinks the numerator of that ratio, but Q=40 stays fixed, so the fixed per-request cost becomes a proportionally larger slice of a smaller total. Memory footprint and caching benefit do not degrade in lockstep — memory is the more sensitive lever here.
A4. The grammar mask removes N/A (logit 7.6) regardless of its raw score, leaving OK (7.5) and BAD (7.4). Renormalizing: exp(7.5)/(exp(7.5)+exp(7.4)) ≈ 0.5250, exp(7.4)/(exp(7.5)+exp(7.4)) ≈ 0.4750. Greedy decoding among the valid set picks the higher one: OK. Note the mask changed which tokens are eligible, not the relative ranking between OK and BAD, which was already OK > BAD before masking.
A5. The demonstration key vectors k_i (derived from each exemplar's input tokens) play the role of the gradient-descent input x_i; the demonstration value vectors v_i play the role of the error signal e_i that gets multiplied against the input in the outer-product update. Both W_ICL = Σ_i v_i k_iᵀ and ΔW = −ηΣ_i e_i x_iᵀ are sums of outer products between a "what changed" vector and a "what was seen" vector; the paper's claim is that attention over the prompt's demonstrations is structurally the same operation as accumulating a gradient-descent update from those demonstrations, under the linear-attention approximation.
A6. The paper's own derivation only claims a single, implicit gradient step, confined to whatever examples fit inside one context window, applied inside one forward pass under a linear-attention approximation of the real (softmax) mechanism — it is not claiming equivalence to full optimization. Explicit fine-tuning runs many gradient steps over an arbitrarily large dataset, using the exact (non-linearized) computation graph, and the resulting weight changes persist in the model after training and apply to every future call, whereas the "meta-gradient" from in-context examples is recomputed from scratch and then discarded the moment the context changes. A single bounded, transient, approximate update is not interchangeable with an unbounded, persistent, exact one, even though both have the outer-product structure described above.
Think About It
Think about this: How would you explain prompt engineering: the art and science of ai interaction to a friend who has never seen a computer? What real-world analogy would you use? Imagine you had to build a system using these concepts — what would be your first step? Try this: before moving on, write down three things you learned and one question you still have.
Practice Exercises
Now it is time to practice! Complete these challenges to solidify your understanding:
- Exercise 1: Write a short program that demonstrates the core concept from this chapter. Test it with at least 3 different inputs.
- Exercise 2: Find a real-world example where prompt engineering: the art and science of ai interaction is used in an Indian company (like TCS, Infosys, Flipkart, or ISRO). Write a paragraph explaining the connection.
- Exercise 3: Create a mind-map connecting prompt engineering: the art and science of ai interaction to at least 3 other topics you have studied.