All Resources

R-23

Intelligence (AI)

Production-Grade LLM Applications: Prompt Engineering Best Practices

Prompt wording is a tenth of the work. What actually ships a feature: context assembly, output contracts, an evaluation harness, and designed failure.

PAR2 Labs

August 12, 2024

14 min

Production-Grade LLM Applications: Prompt Engineering Best Practices

Prompt engineering is the part of an LLM application you can see, which is why it collects both the credit and the blame. In production it is perhaps a tenth of the work and almost none of the variance. What separates a feature that ships from a demo that never does is the machinery around the prompt: what goes into the context window and how it was chosen, whether the output is a contract or a paragraph somebody downstream has to parse, whether a regression is caught by a test or by a customer, and what the whole thing costs per thousand calls at the latency the interface already promised. This is how we build each of those.

Watch the explainer

5:17

Narrated, with every diagram built on screen: the compiled prompt, context is the lever, scoring every change and designing for failure.

01

The demo works because you were holding it right

A demo is one prompt, one happy path, and an operator who already knows the phrasing that works. Production is thousands of people who phrase it wrong, a proportion who phrase it adversarially, a model provider who will deprecate the version you tuned against, and a page that has to render inside the latency budget the rest of the product already committed to.

The variance that kills the feature is almost never in the instruction. It is in the input distribution. The same prompt that scores ninety per cent on the twelve examples you wrote by hand will score sixty on the first thousand real requests, because real requests contain abbreviations, three questions at once, pasted tables, the wrong language, and a fair number of people testing whether it will swear.

Teams routinely spend weeks on wording, days on retrieval, hours on evaluation and nothing at all on failure design. The right ratio is close to the reverse of that.

02

What a prompt actually is once it ships

By the time a request leaves your server, the prompt is not a string somebody wrote. It is a compiled artefact, assembled per request out of half a dozen parts that were each decided by different code.

Seeing it that way changes the question. It stops being what should I write and becomes what should the compiler put in, in what order, and under what budget. Every one of those layers is testable on its own, and most of the quality lives in the two that nobody writes tests for.

WHAT IS ACTUALLY IN THE CONTEXT WINDOWSystem contractRole, scope, refusal rules, output schema. Stable across calls — so it can be cachedTool definitionsTyped, with descriptions the model reads as docs. Half of tool accuracy is written hereFew-shot exemplarsSelected, not pasted. Chosen for the shape of this request, not the average requestRetrieved contextPassages, with their source. The largest slice, and where the answer's truth comes fromConversation stateSummarised past turns, not the raw transcript — how a window silently fillsThe user's turnUntrusted input. Never concatenated into the instruction blockOutput schemaThe shape the caller validates against — including a value meaning 'cannot answer'

Fig 1 — the instruction is one of seven layers, and not the one carrying the risk.

03

Make the output a contract, not a hope

The single highest-return change in most LLM features is to stop asking for an answer and start asking for a structure. Structured output, JSON schema, or a tool call — the mechanism differs by provider, the discipline does not. An unstructured answer makes every consumer downstream a parser of prose, and parsers of prose fail silently and at the worst possible moment.

Validate the result against a schema you own, in your own language, on your own side of the network. Do not trust that the provider enforced it; enforce it again. If validation fails, send one repair turn carrying the validation error, and if that fails, fail loudly. A retry loop with no ceiling is how a feature that cost two paise a call starts costing eleven.

Give the schema somewhere to put uncertainty. A field for confidence, an enumerated value for insufficient evidence, a list of the sources used. A model with no legal way to say it does not know will invent something that satisfies the schema, and you will have built a machine for producing well-formed nonsense.

Rules for output contracts

01

Never parse free text where a schema would do. If you find yourself writing a regular expression against model output, the contract is missing.

02

Validate on your side, every time, even when the provider claims to have enforced the schema.

03

One repair turn, then fail. Pass the validator's own error message into the repair — the model fixes what it is shown.

04

Include an explicit 'cannot answer' value and treat it as a success, not an error. Abstention is a feature you have to design in.

05

Version the schema alongside the prompt. A schema change is an API change to everything downstream.

04

Context is the lever; the instruction is only the handle

In any retrieval-grounded system, retrieval quality dominates everything else, and it is not close. Most of what gets reported as hallucination is a retrieval miss the model papered over — the right passage was never in the window, and the model answered from its weights rather than admitting the gap. No amount of instruction wording fixes a document that is not there.

Position inside the window matters too. Models attend unevenly across long contexts, and material buried in the middle is used least reliably — the effect is well enough established to have its own name in the literature. Put the decisive passage at the start or the end. If you have twenty candidate passages and room for six, the job is to pick the right six, not to send all twenty and hope.

Chunk on structure rather than on a fixed token count. A chunk that begins mid-sentence and ends mid-table has lost the thing that made it answerable. Carry the heading path with each chunk, so the model knows that this paragraph is under Returns, under Policy, under the 2024 edition, and can say so when it cites.

Then give the model permission to come back empty-handed. An assistant that says the policy does not cover this is more useful than one that produces a confident paragraph about a policy that does not exist, and it is the only version you can put in front of a customer.

05

The techniques that survive contact with production

There is a long list of prompting techniques and a much shorter list that still earns its cost at scale. The difference is usually that the technique bought accuracy on a benchmark and buys latency in a product.

TechniqueWhat it actually buysWhat it costsWhen it stops helping
Few-shot exemplarsFormat and edge-case conformance far more than reasoning. The model copies the shape it is shown.Tokens on every single call, and quiet drift as the examples age out of date with the product.When the task needs novel reasoning rather than a pattern to imitate.
Explicit step-by-step reasoningAccuracy on multi-step arithmetic and constrained logic.Latency and output tokens, plus reasoning text you must not render to the user raw.On grounded extraction, where the answer is in the context and there is nothing to reason about.
Task decompositionEach step becomes separately testable and cheap to fix. This is the one that scales.More calls, more orchestration, and latency that compounds down the chain.When the sub-tasks are not genuinely separable and you have only added handoffs.
Self-consistency / n-samplingVariance reduction where there is exactly one right answer.Literally n times the cost and n times the latency unless parallelised.On open-ended generation, where there is no majority to take.
Reranking retrieved passagesPrecision at the top of the context, which is the part the model actually uses.One extra, usually small, model call per query.When your corpus is small enough that recall was never the problem.
Prompt cachingMost of the input cost of a long, stable prefix — often the largest line on the bill.Nothing, if the prompt is ordered so the stable part comes first.When the prefix varies per user, which is usually a sign it should not.

← Swipe for the rest of the table →

← Drag for the rest of the table →

Order every prompt stable-prefix-first — system contract, tools, exemplars, then retrieved context, then the user turn. Caching only works on a prefix that does not move, and the ordering is free to get right and expensive to change later.

Most of what gets called a hallucination is a retrieval miss the model was too polite to mention.

06

Version the prompt like code, because it is code

A prompt that lives in a database, edited through an admin screen by whoever is awake, is an unversioned production dependency. Prompts belong in the repository, in the same commit as the code that compiles them and the tests that score them, with a diff that a reviewer can read.

Pin the model version explicitly. A prompt is not portable across models and barely portable across versions of the same model; the pair is the artefact, not the string. When a version is deprecated — and it will be, on the provider's schedule rather than yours — you are migrating a program, not swapping a constant. Budget for a re-evaluation run and a fix window every time, and keep the previous version routable until the new one clears the same bar.

07

You cannot improve what you have not scored

Everything above is guesswork until there is a harness. The golden set is the real specification of the feature — more so than any document, because it is the only description of the behaviour that can fail a build.

01

Freeze a golden set drawn from real traffic

  • GET

    A versioned, reviewed case set

Between one and three hundred cases, taken from what users actually sent, deliberately over-weighted towards the ones that failed. Write the expected behaviour for each — not necessarily the exact expected string, but the property that must hold. Freeze it and version it with the code.

Why: A set written from imagination tests the feature you imagined. A set drawn from traffic tests the one you have.

02

Write the grader before the fix

  • GET

    A scorer that runs unattended

Decide how a case is scored before you start changing the prompt. Exact match where the answer is closed. A schema check and a groundedness check where it is open. A rubric where judgement is genuinely required.

Why: A grader written after the fix will score the fix well. That is not a test, it is a mirror.

03

Use a model as a judge, and distrust it correctly

  • USE

    Rubric plus a reference answer

Model-graded evaluation is the only affordable way to score open-ended output at volume, and it carries known biases: it prefers the first option shown, prefers longer answers, and prefers output from its own family. Force a written rubric, randomise the order, and calibrate against human labels on a sample every time the grader changes.

Why: An uncalibrated judge moves your metric without moving your product, which is worse than having no metric.

04

Run it on every change and gate the merge

  • GET

    A pass/fail that blocks

Prompt edits, retrieval changes, model bumps, chunking changes, schema changes — all of them run the full set. A drop beyond the agreed threshold on the aggregate, or any regression on the safety subset, fails the build.

Why: Without a gate, the harness is a dashboard. Dashboards get admired; gates change what ships.

05

Feed production failures back in

  • GET

    A set that grows with the product

Every escalation, thumbs-down and support ticket about the feature becomes a candidate case. Triage weekly, add the real ones, and never delete a case because it became inconvenient.

Why: The set is only as good as the last surprise it absorbed.

08

The bill nobody costs at demo time

Cost and latency are product decisions disguised as infrastructure ones, and they should be written down before the prompt is. A feature that answers beautifully in nine seconds has been designed for a user who does not exist.

DriverWhy it bitesWhat to do about it
Input tokens dominate in retrieval systemsYou pay for the entire assembled context on every turn, and context grows quietly as the product gains features.Cache the stable prefix. Retrieve fewer passages and rank them better. Summarise conversation state instead of replaying it.
Output tokens dominate in generationThey are also the slow ones: output is produced serially, input is not.Cap max tokens deliberately. Ask for the structure, not the essay. Move prose the user does not read out of the model's job.
Time to first token is the number people feelTotal latency barely registers if the first words arrive fast. Silence is what registers.Stream. Run retrieval and any independent tool calls in parallel. Render the frame before the content arrives.
Repair loops multiply everythingAn unbounded validate-and-retry can triple the cost and the latency of a single request without anyone noticing.One repair, then fail. Alarm on the repair rate — a rising one is a schema or prompt regression showing up early.
Long context is not free even when it fitsCost scales with what you send, and recall degrades as the window fills.Treat the window as a budget with a line per layer. Precision beats stuffing, every time.

← Swipe for the rest of the table →

← Drag for the rest of the table →

09

Failure has to be designed, not discovered

Every production LLM feature needs an answer to four questions before launch, and finding the answers after launch is considerably more expensive.

The first is abstention: what does it do when it does not know, and does that path look like a deliberate product state rather than an error. The second is escalation: at what confidence, on which intents, does a human take over, and does the handover carry the context or make the customer start again.

The third is injection. Retrieved documents and user messages are data, never instruction. A support article that contains the words ignore previous instructions must not be able to act as one, which means privilege separation in the prompt structure, not a sentence asking the model to be careful. Anything the model can do with a tool, an attacker who can write into your corpus can attempt.

The fourth is observability. Log the compiled prompt with secrets and personal data redacted, the model and version, the retrieved document identifiers, the raw response, the validation result and the latency. Without that, a complaint about one bad answer is unreproducible, and unreproducible failures never get fixed.

Before this goes in front of a customer

01

There is a legal, well-presented way for the system to say it does not know, and a test that proves it uses it.

02

There is a confidence threshold per intent, not one global number, and a human path behind it that carries the context across.

03

User and retrieved content are structurally separated from instructions, and an injection suite runs in the harness alongside the accuracy set.

04

Every call is reconstructable from logs — prompt, model version, sources, response, validation, latency — with personal data redacted at the boundary.

05

The latency and cost budget per call was written down before launch, and something alerts when either is exceeded.

10

Where this leaves prompt engineering

It is still worth doing well. A clear system contract, a schema that leaves room for uncertainty, exemplars chosen rather than pasted, and an ordering that lets the cache work — these are real gains and they are cheap. They are simply not the project.

The project is the compiler around the prompt, the harness that scores it, and the failure paths that catch it. Build those and the wording becomes a tuning exercise. Skip them and no amount of wording will get the thing past its first thousand real users.

Worth reading properly

OpenAI — Structured Outputs

Schema-constrained generation, and the constraints the schema itself has to satisfy.

Anthropic — Prompt engineering

The techniques, with the trade-offs stated rather than implied.

Anthropic — Prompt caching

Why prompt ordering is a cost decision.

Lost in the Middle: How Language Models Use Long Contexts

The position effect, measured. The reason ranking matters more than recall.

Harness engineering: making AI quality measurable

The evaluation loop above, in more detail.

Key Takeaways

01

Treat the prompt as a compiled artefact, not a string. Seven layers go into the window and the instruction is the least risky of them.

02

Ask for a schema, validate it on your own side, allow exactly one repair turn, and give the model a legal way to say it does not know.

03

Retrieval quality dominates. Rank harder, send less, and put the decisive passage at an edge of the context rather than the middle.

04

Order every prompt stable-prefix-first. Caching is usually the largest single reduction available on the bill and it is free to design in.

05

A golden set drawn from real traffic, scored by a calibrated grader and gating the merge, is the only thing that turns prompt changes into engineering.

06

Design abstention, escalation, injection separation and logging before launch. Each one is an order of magnitude cheaper than retrofitting it.


PAR2 Labs · Intelligence (AI)

Work With Us

Have a problem worth solving?