Chapter 1 of 7 · 20 min read
The six parts of a prompt
On this page
A prompt is the complete instruction handed to a large language model on a single call: the standing directions about how to behave, whatever facts the model needs for this particular request, and the input itself, assembled into one piece of text and sent to the provider. Almost none of that text is written fresh per request. What lives in a codebase is a template with a placeholder in it, and what goes out on the wire is that template with the current input substituted into the placeholder, which is why one file is responsible for every call the service makes. Models carry nothing over between calls, so everything the reply has to obey must be present in the assembled text every time it is assembled. You meet prompts in practice as a string in application code, a file the service loads at boot, or a text box in a provider console, sitting immediately upstream of code that has to do something with whatever comes back.
The anatomy of a prompt, which is the subject of this topic, is the observation that whatever a prompt looks like on the surface, it is answering a small fixed set of questions on the model's behalf, and that those questions can be named. Naming them solves a specific problem. A prompt written as prose can only be argued about as prose, so one engineer finds it clear, another finds it thin, and neither can point at the part of it responsible for a third of calls failing to parse. A prompt read as six parts behaves like an interface, whose slots are present, absent, or wrong, which is what lets a complaint about the output be traced to something a person can edit rather than left as an opinion about the model.
Six questions, asked whether or not you answer them
The model answers any of the six questions you leave blank, using the statistics of its training data and whatever the rest of your prompt implies, and those answers vary from call to call in exactly the way a sampled decision varies. That sentence is the whole of why prompt wording has any effect at all, and it is worth taking apart in steps before the six slots are worth naming, because every technique in this topic is a consequence of it.
A language model, as topic 1.0 established, is a scoring function with a loop around it: text goes in, and out comes a score for every fragment of text that could come next, after which a separate step of ordinary code picks one fragment, appends it, and calls the function again on the now longer text. Nothing in that arrangement distinguishes your instructions from the transcript you pasted in or from the reply the model has produced so far. There is one string, and the function's only job is to extend it. A prompt is therefore not a command that gets executed; it is the opening of a document, and the reply is the rest of that document.
What those scores are made of is the next link. The function was shaped by an enormous quantity of text, and the score it assigns a candidate reflects how often text of the shape now in front of it was continued that way. The question to ask of any prompt is therefore not what you told the model to do. It is what followed text like this, across everything the model was built from.
Ask that of the four-line specimen printed further down, whose whole instruction is
the sentence Summarise the earnings call below. and a transcript inside tags.
Documents of that shape exist in very large numbers, and they were continued in every
way a person might summarise something: a paragraph of continuous prose, a bulleted
list of takeaways, a numbered list under headings, and a conversational opener of the
form "Certainly! Here's a summary". Each of those continuations is well attested, so
each receives a substantial score. The distribution over the next fragment at that
point is genuinely broad, and it is broad correctly — an accurate report that text of
this shape was continued several different ways, rather than a confusion the model is
having about what you meant.
The picking step is the last link, and it is where two calls part company. That
step draws one fragment from the distribution, independently on each call, with
no memory of what it drew the time before. A draw that lands on the fragment
opening a bulleted list starts a bulleted list; a draw that lands on Certainly
starts a preamble. Because the loop only ever moves forward and never revisits an
earlier choice, everything after that first fragment is generated in its presence
and follows it. Two calls on the same transcript produce two different shapes,
from one unchanged prompt sent to one unchanged model.
Read backwards, that chain says something more useful than "the model is
inconsistent". The slot was never blank. A question about output shape was put to the
model on every call, the model answered it honestly with a spread of plausible
shapes, and the sampler picked one of them. Filling a slot means changing the text so
that the spread is narrow at the position where the answer gets committed. After a
sentence reading Return a single JSON object and nothing else, the fragments that
followed such text are overwhelmingly an opening brace, and there is very little
probability mass left anywhere else for the sampler to find.
The tempting repair, and the one reached for in the incident below, is to turn the temperature down instead. Temperature is the decoding setting that governs how far down the ranked candidates the sampler is allowed to reach, and at zero it makes the picking step take the highest-scoring fragment every time. That does produce the same shape on every call, which is why it looks like a fix. What it cannot do is change what the distribution is about. A broad distribution produced by an ambiguous instruction is still broad at temperature zero — the sampler simply always takes its favourite, and the model's favourite shape is whichever one was most common in text like yours rather than the one your parser wants. You buy stability and you do not buy a contract, and the difference surfaces the first time the favourite moves, on a new model version or on a transcript phrased slightly differently.
One step in that account needs defending, because it is the step a careful reader
will not grant. If the function only continues text, why should the sentence
Return a single JSON object and nothing else be obeyed rather than merely
continued — why not answer it with a discussion of JSON formatting? On a raw
model trained only to continue text, that is frequently what happened. What
changed is a further stage of training called instruction tuning, in which
the scores are adjusted on a large collection of written instructions paired with
responses that carry them out. After it, text following an instruction is text
that obeys the instruction, because that is now the continuation the training
material overwhelmingly demonstrates. Instructions work by the same statistics as
everything else in the prompt, which is why they can be outweighed by other
statistics in the prompt — the subject of chapter 3.
Each of the six slots is worth pinning to the failure it produces when nobody fills it, and every one of those failures is the mechanism above with a different question left to the draw.
| Slot | The question it answers | What you see when it is absent |
|---|---|---|
| Role | Who is answering | Register and depth drift; a reply pitched at the wrong reader |
| Task | What they are doing with the input | The model answers an adjacent question — explains when you wanted a decision |
| Context | What they know that the model does not | Confident invention, or a generic answer that ignores your situation |
| Constraints | What they may not do | Values outside your vocabulary, forbidden content, unbounded length |
| Output format | What the reply must physically look like | Shape drift between calls; downstream parse failures |
| Examples | What a correct one looks like in practice | Shape roughly right, details and edge cases wrong |
The slots are not sections, and they are not an order. A well-written prompt often satisfies two of them in one sentence — "Return one of the six categories below and nothing else" is a constraint and a format at once — and the strongest production prompts rarely have a heading called "Constraints". What matters is that each question has an answer somewhere in the request, written by you rather than inferred.
Here is a prompt that fills exactly one slot, of the kind that survives in production for months because nothing about it looks wrong:
You are a senior financial analyst. Summarise the earnings call below.
<transcript>
{transcript}
</transcript>Role was covered structurally in topic 2.6, on where an instruction belongs, where the question was placement: standing behaviour goes in the system prompt, per-request facts go in the turn. Here the question is different. Role is doing work when it changes what counts as a correct answer — "you are triaging for an on-call engineer who will act on this in the next five minutes" moves the reply toward actionability. It is doing no work at all when it is decoration. In the prompt above it is the only slot filled, and it is the slot that matters least, which is a pattern you will see repeatedly: role is the easiest part to write, so it is the part people write when they are not sure what else to say.
Why role is the easiest is worth naming, because it explains why role keeps being the part that gets improved. Of the six slots it is the only one that reads like writing rather than like specification. It takes one sentence, it requires no knowledge of the failure data, it can be made to sound more expert by adding an adjective, and a reviewer can hold an opinion about it within four seconds. The other five demand something specific that has to be found out first: what the operation is, what the downstream parser accepts, which values are legal, which case broke last week. Somebody who does not yet know which of those is wrong will reliably improve the one that can be improved without finding out, and a prompt maintained that way accumulates seniority in its role line while the format slot stays empty.
What a role line actually does is narrower than the folk version of it, and the triage line above marks the boundary. A role line moves the scores the way any other text in the prompt moves them, so it changes the reply exactly to the extent that the role it describes predicts different continuations. Text written for a reader under time pressure genuinely is different text — shorter, ordered by what to do first, stripped of background — so naming that reader moves the distribution somewhere you can point at. "You are a world-class expert in this domain" names a reader whose text is not reliably different from anybody else's, so it moves nothing you could measure, and there is no failure you could name in advance that the sentence removes.
A task a reviewer can fail
The task slot answers what is being done to the input, and the test of a task line is whether two competent people reading it would produce recognisably the same output. "Summarise the earnings call" fails that test badly, because a summary for an equity analyst, a summary for a compliance archive, and a summary for a push notification share almost no content. Specificity here is not about length; it is about naming the operation, the scope and the unit of the answer.
The vague version leaves three decisions open — how long, what to include, what to leave out. The specific version closes them:
Extract, from the transcript, the single guidance change management
announced (if any), the three risks management named explicitly, and a
one-sentence headline stating whether the quarter beat, met or missed
the company's prior guidance. Do not include analyst questions.That version can be wrong in ways a reviewer can point at. If the model returns four risks, or a risk that management did not name, or a headline about revenue growth when the prior guidance was about margin, someone can say so without appealing to taste. A vague task produces outputs that are only ever arguably worse, and arguable is exactly the state in which prompts sit unimproved for months.
Context is the slot with the opposite failure mode, because it is the one people overfill. It exists to supply what the model cannot know — the transcript itself, a customer's order history, the internal definition of "active user" that your company uses and nobody else does, the fact that this quarter's numbers are unaudited. Each of those changes what a correct answer is. The two paragraphs of company background that get pasted in because they were in the onboarding doc change nothing, and they are not free: they occupy the input the model is attending over, they cost tokens on every call, and they give a distractible model something adjacent to talk about. The test for a candidate piece of context is whether removing it would make some correct answer unreachable. If you cannot name the answer it unlocks, it is decoration in the same way an unused role line is.
The same discipline applies to constraints, which are the slot people skip most
often because they feel like the model's job rather than yours. A constraint is a
statement about what is not allowed, and it earns its place when the disallowed
thing is reachable — the model can easily produce it, and producing it is a real
failure. "Never name a competitor" is a constraint. "Be accurate" is not, because
there is no output the model would recognise as violating it. The most valuable
constraints in production prompts tend to be closure statements: the six
categories are the complete set, the only allowed values for urgency are low,
medium and high, an unknown value must be reported as null rather than
guessed. Each of those turns a class of plausible-looking output into an output
you can reject on sight.
The gap between a constraint and a slogan follows from where a constraint acts. A sentence in a prompt changes the reply only by changing the scores over next fragments, and it changes them at the positions where the fragments it forbids would otherwise have been the leading candidates. "Never name a competitor" picks out a class of strings — the names themselves — and there are identifiable points in a draft where one of those names is what comes next, so the sentence has somewhere to bear. "Be accurate" picks out no class of strings at all. Accuracy is a relation between the finished reply and the world, and no fragment is more or less accurate than another at the instant it is being scored, so there is no position at which the sentence can move anything. It reads as a constraint and behaves as decoration.
The closure statements above are the strongest form available, and they repay being
looked at as something other than a longer list. Naming low, medium and high
tells the model which strings are legal and says nothing about the region outside
them, so a ticket fitting none of the three still has plausible continuations:
critical and urgent are perfectly good English urgency levels and the model has
met a great many of both. Saying that the three are the complete set, and that an
unknown value must be reported as null, supplies a continuation for that region as
well. That is what removes the pressure to invent one. A constraint that forbids
without supplying an alternative leaves the model choosing between two violations,
and it will choose.
Rejection is the property you are engineering for
The output format slot is the one that ends parser incidents, and the reason is worth stating carefully. When you name the fields, the container, and what may not appear around them, you are not only telling the model what to produce. You are creating the possibility of a reply being invalid — a category that did not exist when the prompt said "summarise", because every string of English is a summary of something. Validity is what lets you retry, alert, route to a human, or count failures in a dashboard.
That claim sounds like wordplay and is a mechanical fact about what a program can compute, so it is worth taking apart. Invalidity is not a property a string carries on its own. It is a relation between a string and a stated set of legal strings — a reply is invalid when it is not a member of that set — so the predicate needs the set to exist before it can be evaluated at all. Where the prompt says only "summarise", no set was ever stated, and a program has nothing to test membership against. The four-hundred-word prose reply in the incident below is, under that prompt, outside the reach of the question entirely. The parser did not judge it and lose. There was no judgement available to make.
Stating a format is what brings the set into existence, and from there the rest follows without any further cleverness. Membership in a stated set is decidable, so a program can compute an answer for every reply. A computed answer can be recorded, so failures can be counted rather than estimated. A count can carry a reason, since the membership test fails at an identifiable clause and the clause has a name. And a decidable failure can be acted on inside the request that produced it — retried, alerted on, routed to a human — because the calling code knows it has one before it does anything with the result. Every operation a team wants when a reply goes wrong rests on that first step, and no amount of improving the wording of the task supplies it.
Here is the same prompt with the format slot filled, and the constraint that closes off the preamble parsers most often trip over:
You are summarising an earnings call for an equity research database.
Extract the single guidance change management announced, the three
risks management named explicitly, and a one-sentence headline stating
whether the quarter beat, met or missed prior guidance.
Return a single JSON object and nothing else. No preamble, no code
fence, no commentary after the object.
{
"headline": string, // one sentence, max 25 words
"guidance": string | null, // null if management announced no change
"risks": string[] // exactly 3 items, each max 15 words
}And here is the receiving end, which is now allowed to have an opinion:
from pydantic import BaseModel, Field, ValidationError
class CallSummary(BaseModel):
headline: str = Field(max_length=200)
guidance: str | None
risks: list[str] = Field(min_length=3, max_length=3)
def parse(raw: str) -> CallSummary:
return CallSummary.model_validate_json(raw)
try:
summary = parse(completion)
except ValidationError as e:
metrics.increment("summary.invalid", tags=[f"reason:{e.errors()[0]['type']}"])
raiseThe raise is what changes. Before the format was stated, a four-hundred-word
prose reply was an output the system had no vocabulary for — it was neither valid
nor invalid, it was just text the parser silently produced nothing from. After
the format is stated, that same reply increments a counter with a reason
attached, and the reason tells you which part of the contract broke. missing on
risks means the model returned two; json_invalid means it wrapped the object
in a code fence despite being told not to; string_too_long on headline means
the length constraint is being ignored and needs a different treatment. Three
failure modes that were one undifferentiated blob of "quality problems" are now
three separate numbers.
That prompt also demonstrates something about the examples slot by leaving it empty. The schema block above is a specification of shape, not a demonstration of it, and those two things behave very differently under pressure. A specification says what the space of legal outputs is; a demonstration shows one point inside that space, along with a great deal it never intended to show. Chapter 3, on what happens when an example contradicts a rule, is about the disagreement between them, which resolves in a direction most people find surprising and resolves that way reliably.
Full slots, bad prompts
The temptation, having learned six names, is to treat them as a checklist that guarantees a good prompt. It does not. A prompt with all six slots filled can still be bad, in ways the checklist is blind to: the context can be irrelevant and merely dilute the input, the constraints can contradict each other, the examples can undercut the constraints. Chapters 3 and 4, on demonstrations that outrank rules and on rules that contradict each other, are about precisely those failure modes, and both occur in prompts where nothing is missing.
The framework is also not a claim that structure must be visible. There is no requirement that a prompt carry headed sections, and no evidence that a model prefers XML tags to markdown headings to plain paragraphs in any way that survives a real eval. The systematic surveys of production prompt templates — the arXiv paper From Prompts to Templates catalogues a large sample of them — find enormous surface variation and much less variation in which questions get answered. Structure helps you and your reviewers find things; it is not incantation.
The nearest neighbour that gets confused with the format slot is constrained
decoding, which providers expose as structured outputs or JSON mode. That
machinery masks the token sampler so the completion is guaranteed to match a
schema, and it genuinely eliminates the class of failure that breaks a parser. It
does not eliminate the reason you write a format slot. A constrained decoder will
happily give you a syntactically perfect object in which risks contains three
risks the model invented because the task never said they had to be named
explicitly by management, and it will give you urgency: "high" on every ticket
if nothing told the model what high means. Structured output guarantees shape.
Everything about content — what goes in the fields, what is forbidden, what
counts as unknown — still lives in the six slots, which is why prompts written
well before JSON mode existed are mostly still correct.
The reason that guarantee stops exactly where it does is visible in what the mask
can be computed from. At each step the decoder holds two things: the schema, and
the partial string produced so far. It works out which fragments would keep that
string a legal prefix of some instance of the schema, and it sets the score of
every other fragment to nothing, so the sampler cannot reach one however the draw
falls. Both inputs to that calculation are properties of the string. A fact about
the transcript is not among them. So the mask can rule out a reply with four
entries in risks, because arity is a fact about the string's structure, and it
cannot rule out three entries naming risks nobody on the call mentioned, because
that is a fact about the earnings call. The mask answers a question about form
and the question you had was about content, and no grammar closes that gap.
Six questions, then, and the discipline is not to answer all of them at length but to know which one you have left to the model. An unanswered question is not a gap in a document; it is a decision delegated to a sampler, made again on every call, and it produces exactly the class of failure that gets filed under model quality and investigated as if it were.
You can now write a prompt from a blank page and check that six questions have answers. Almost nothing you do in production starts from a blank page. You inherit a prompt that someone wrote a year ago, it underperforms, and the only evidence you have is a folder of bad outputs — which means the working skill is the inverse of this chapter: reading an output backwards to the slot that was never filled.
Anatomy of a prompt — read the current version at https://daviddominguez.dev/learn/en/anatomy-of-a-prompt/the-six-parts-of-a-prompt