Prompting

The Parts of a Prompt: Adding One Piece at a Time

0 of 18 complete

0%

Contents

Back|PromptingThe Parts of a Prompt: Adding One Piece at a Time
1/18
43 min left
Prerequisites
One Call, End to End: The Chapter in a Single Requestrequired
Related Topics
Chat Templates: The Text a Conversation BecomesHow Models GenerateWhat a Language Model Actually Outputs: Odds for Every Next TokenHow Models GenerateThe Context Window: What Happens When a Prompt Does Not FitHow Models GenerateQuantization: The Same Model in Fewer BitsHow Models GenerateOne Call, End to End: The Chapter in a Single RequestHow Models Generate
1 of 18

A Pile of Letters and Four Boxes

Imagine you give a new helper a pile of customer letters and four boxes, and you say only: "Sort these." They will sort them, but into what? They might make piles by length, by date, by how angry the writer sounds. If you tell them the four boxes are billing, delivery, returns and account, they get much closer. If you also tell them to write only the box name on each letter, you can count the result. And if you explain where the edges are, for example that a refund of money goes in billing, the hard letters stop landing in the wrong box.

A prompt to a language model works the same way. It is not one block of text. It has parts, and each part does one job. This lesson takes a real task, sorting customer messages into four categories, and builds the prompt up one part at a time. After each step it measures two things on 40 messages: how often the model picked the right category, and how often its answer could be used by a program as it stood.

An illustration of a woman at a desk sorting cut paper cards into a box with coloured dividers, a laptop beside her. Under the heading the prompt decides which box, and how the answer looks. Beneath: billing, delivery, returns, account. With the message alone, 0 of 40 answers were usable; with all five parts, 40 of 40.

The result is a ladder you can reuse for any prompt: which part to add when the answers come back wrong, and which part to add when they come back right but in a shape your code cannot read.

Five Parts, in Plain Words

A hand-drawn list headed the five parts, in plain words, titled what goes into a prompt. Instruction: what to do, and which answers are allowed. Format rule: the exact shape the answer must have. Context: facts the model needs, such as what each category means. Examples: finished cases, question and answer. Input: the text to work on, here the customer's message. Beneath: this lesson adds them one at a time, and scores each step.

Here are the five parts this lesson uses. Different books name them differently; the names matter less than the jobs.

Instruction. What you want done, including which answers are allowed. "Put this message into one of these categories: billing, delivery, returns, account."

Format rule. The exact shape of the answer. "Answer with the category word only, in lower case, and nothing else." A format rule is meant to change how the answer is written down, not which category the model picks. As you will see, on this run it did a little of both.

Context. Facts the model needs and cannot guess. Here, one line per category saying what belongs in it, such as "billing: charges, payments, invoices, prices and refunds of money."

Examples. Finished cases: a message and its correct category, written out in full. They show the task instead of describing it.

Input. The text the model works on, here the customer's message, followed by "Category:" so the model knows where its answer goes.

Two more words matter for the scores. An answer is right when the first word of the reply, after any leading "Category:", is the correct category. An answer is usable (the lab prints it as clean) when the whole reply, after making it lower case and dropping spaces at either end, is exactly the category word, so a program can compare it directly. In the lab's code and report, gold means the correct category I wrote down for each message.

The Task and the Five Prompts

An editorial frame labelled the text the model receives, top to bottom, headed prompt E, the fullest one, cut into its parts, titled one prompt, five parts. Five boxes: instruction, put this customer message into one of billing, delivery, returns, account; format rule, answer with the category word only, in lower case; context, one line per category saying what belongs in it; two examples, a message about a charge, then billing, one about a password, then account; input, Message: the customer's text, then Category:. Beneath: prompt A is only the last box, about 44 tokens; prompt E is all five, about 169.

I wrote 40 short customer messages for this test, 10 for each category, such as "I was charged twice for the same order this month" (billing) and "My account is locked after too many attempts" (account). I tried to give each one a single clear category, and I wrote the correct label for each before running anything.

Then I built five prompts. Each one keeps everything in the one before and adds one part:

  • A: the message alone, then "Category:".
  • B: an instruction naming the four categories, then the message.
  • C: B plus the format rule.
  • D: C plus the context, one line per category.
  • E: D plus two worked examples.

Every prompt went to qwen2.5:3b through Ollama's chat endpoint, at temperature 0 (so the model always takes its most likely token, as in lesson 2 of the previous chapter), with room for up to 40 written tokens. Five prompts times 40 messages is 200 calls. I wrote the context lines and the examples before the first run, not after looking at the mistakes, so they were not tuned to fix the errors you will see.

Prompt A: The Message Alone

A table headed prompt A, the message alone, then Category:, titled it made up its own categories. Labels: 38 different first lines for 40 messages. Examples: Billing or Payment Issues, Account Management, Tax/Finance. Length: 23 of 40 replies ran on to the 40-token limit. Usable: 0 of 40, none was one of our four words alone. Beneath: nothing in the prompt said which answers were allowed.

With only the message and the word "Category:", the model had no idea which categories existed, so it invented its own. The 40 replies had 38 different first lines: "Billing or Payment Issues", "Account Management", "Tax/Finance", "Financial Services/Transactions", and many that began "This message falls under the category of" and kept going. 23 of the 40 replies ran all the way to the 40-token limit.

None of the 40 was usable: not one reply was a single one of our four words. By the "right" score, 10 of 40 counted, because a made-up label such as "Category: Billing or Payment Issues" happens to start, after "Category:", with one of our words. Some of those made-up labels were sensible, such as "Account Management", but nobody could build a program on answers that change their wording from message to message.

This is not the model being bad. The prompt did not say what the answers could be, and there was no way for the model to know. The helper sorting letters would have done the same.

Prompt B: Name the Allowed Answers

Two panels headed prompt B, the instruction names the four categories, titled right, but not usable. Left, names the right category: 37 of 40, the first word, after any Category:. Right, usable as it stands: 19 of 40, the reply is the word alone. Beneath: every one of the 18 right-but-messy replies began with Category: (18 of 18).

Prompt B adds one sentence: "Put this customer message into one of these categories: billing, delivery, returns, account." That single instruction moved the right answers from 10 of 40 to 37 of 40. The model now knew the allowed answers and almost always picked a sensible one.

But only 19 of the 40 replies were usable. The other 18 right answers were wrapped: every one of them began with "Category:", as in "Category: billing". A person reads that as billing immediately. A program that makes the reply lower case and checks whether it equals "billing" says no.

A two-column page headed real replies to prompt B, the instruction alone, titled same answer, different shape. Left, usable: "Billing", "billing", "billing"; 19 of 40 like this. Right, right, but wrapped: "Category: billing", "Category: billing", "Category: delivery"; 18 of 40 like this, code that compares words would call them wrong.

This gap between right and usable is the main idea of this lesson. When you judge a prompt only by reading a few replies, B looks nearly perfect. When your code uses the replies, half of them fail. You need to measure both.

B also had 3 answers that were wrong. "I need a refund of the shipping charge you added by mistake" came back as returns. "I cannot sign in with Google any more" came back as billing, followed by a note admitting that it did not really relate to billing. And "Do you ship to Iceland?" got a long reply saying the message did not fit any category, quoting it back wrongly as "Do you [sic] to Iceland?". The model wrote "[sic]", a mark editors use to point out a mistake, in place of the word "ship".

Prompt C: Say Exactly What the Answer Looks Like

A bar chart headed 40 messages, each prompt adding one part to the one before, titled right, and usable, prompt by prompt. For prompts A to E, two bars each: names the right category 10, 37, 36, 40, 40; usable as it stands 0, 19, 36, 40, 40. Beneath: A message alone, B + instruction, C + format rule, D + context, E + two examples.

Prompt C adds the format rule: "Answer with the category word only, in lower case, and nothing else." Usable answers jumped from 19 to 36 of 40. Every right answer was now also usable: the two bars for C in the chart are the same height. The "Category:" wrapper disappeared, and so did the long explanations.

Right answers went from 37 to 36, one fewer, but that small net change hides three moves. The Iceland question, a long reply under B, became "delivery", which is right. And two account messages that B had answered correctly, the forgotten password and the two-factor phone number, became "billing", which is wrong. So the format rule mostly fixed the shape of the answers, but it also moved some of them. Adding any text to a prompt changes what the model reads, and so it can change answers you did not mean to touch. That is why you score every version, not only the one you think you improved.

A sketched bar chart headed the format rule added: mistakes, by the category that was right, titled prompt C's four wrong answers. Account, 3 wrong, a long bar. Billing, 1 wrong, a short bar. Delivery, 0 wrong, and returns, 0 wrong, with no bar. Beneath: 3 account messages came back as billing; 2 of them had been right under prompt B.

Three of the four mistakes were account messages answered with "billing": the Google sign-in, the forgotten password, and the phone number for two-factor login. Two of those were new under C; the Google sign-in was already wrong under B. "Billing" is the first category in the instruction's list, but one run cannot tell us why the model chose it: the list was in the same order under B, where two of these three were right. The fourth mistake was the shipping-charge refund, answered as returns, as it was under B.

Prompt D: Draw the Lines Between Categories

Prompt D adds the context: four lines, one per category, saying what belongs in it. Account got "signing in, passwords, profile details, privacy and emails from us". Billing got "charges, payments, invoices, prices and refunds of money".

With those lines, all 40 answers were right and all 40 were usable. The three account messages (the Google sign-in, the forgotten password and the two-factor phone number) moved to account, because the context now said that signing in, passwords and profile details go there. The shipping-charge refund moved to billing, because the context said refunds of money go there.

Be careful with that last one, because it is a good lesson about context. "A refund of the shipping charge" could reasonably be billing (it is money) or returns (people often say refund when they send something back). I labelled it billing when I wrote the test, and I wrote a context line that agrees with that label. A different company could fairly put it in returns. Context does not make the model smarter. It tells the model where your boundaries are, which is why it fixed exactly the messages that could belong to either of two categories.

A sketch of five coloured boxes stacked top to bottom, joined by arrows, headed sketched, the five prompts, each adding one part, titled building up the prompt. A message alone: usable 0 of 40. B + instruction: usable 19 of 40. C + format rule: usable 36 of 40. D + context: usable 40 of 40. E + two examples: usable 40 of 40. Beneath: each step keeps everything above it and adds one part.

Prompt E: Examples, and a Result That Says Little

Prompt E adds two worked examples: "I was billed for an item that was out of stock", then "Category: billing"; and "My password reset link has expired", then "Category: account". The score stayed at 40 of 40 right and 40 of 40 usable.

It would be wrong to conclude from this that examples do not help. Prompt D was already perfect on these 40 messages, so there was nothing left for the examples to improve. A test where the score cannot go up cannot show whether something helps. This is called a ceiling: the score is stuck at its top, not because nothing works, but because there is no room above.

Examples are widely used, especially when a format is hard to describe in words, or when the categories are subtle. To see what they do, you need a harder task, where the prompt without examples still makes mistakes. A later lesson in this chapter does that, and also tests whether the order and choice of examples change the answers.

What this run does show is that examples are not free: 31 tokens here, the second largest part after the context's 61, so they should be added only when they help.

What Each Part Cost

Five isometric blocks headed median prompt tokens, including the message, titled every part costs tokens. A, 44; B, 62; C, 77; D, 138; E, 169; each block taller than the one before. Beneath: height is prompt tokens; the context and the examples are the big ones.

Every part adds tokens, and every token counts towards each call. Unless the start of the prompt is reused (lesson 6 of the previous chapter), the model reads all of it again each time, and lesson 5 of that chapter showed that the wait before the first word grows with the prompt's length. Here the prompts are short, so the time is small, but the pattern matters when the context grows to pages.

A two-column page headed what each added part cost, and what it bought, titled tokens in, answers out. Part added, and cost and gain: instruction, 18 tokens, usable 0 to 19; format rule, 15 tokens, usable 19 to 36; context, 61 tokens, usable 36 to 40; two examples, 31 tokens, usable 40 to 40. Beneath: medians of 40 prompts each; the examples bought nothing here, D was already 40 of 40.

The median (the middle value of the 40) prompt grew from 44 tokens (A) to 62 (B), 77 (C), 138 (D) and 169 (E). These are the counts Ollama reports, so they include the chat template's own markers and its default system message, which the previous chapter's lesson 8 measured; that is why even prompt A, a message of about ten words, comes to 44. The instruction cost 18 tokens and took usable answers from 0 to 19. The format rule cost 15 and took them from 19 to 36. The context cost 61 and took them from 36 to 40. The examples cost 31 and, on this set, bought nothing.

The cheapest parts did the most. On this task, naming the answers and fixing the format were short sentences with a large effect. Context and examples are longer, and they are worth it when the task has real edges to explain, as the account messages did here.

Try It Yourself

This script sends three of the messages through three of the prompts, so you can watch each part change the answer.

A real screenshot of VS Code with parts_demo.py open, lines 1 to 35 visible. It defines INSTRUCTION, FORMAT and CONTEXT as strings, a PROMPTS dictionary where each prompt adds a part to the one before, three messages about signing in with Google, a refund of a shipping charge and shipping to Iceland, and a function ask that sends a text to Ollama's chat endpoint with temperature 0 and up to 40 tokens. Beneath: copy it from the box on the slide.

Before you run this lab. It uses qwen2.5:3b, running in Ollama on your own computer. If you have not set that up yet, the lab setup guide shows how to install Ollama, download the model and check that everything works, on macOS, Windows or Linux. You can use a different model instead: the guide shows the one line to change, and your numbers will differ from the ones in this lesson.

"""The same question, asked with more parts of a prompt each time.

Run it with Ollama running and qwen2.5:3b pulled (see the lab setup guide):
    python parts_demo.py
"""
import json
import urllib.request

INSTRUCTION = "Put this customer message into one of these categories: billing, delivery, returns, account."
FORMAT = "Answer with the category word only, in lower case, and nothing else."
CONTEXT = ("billing: charges, payments, invoices, prices and refunds of money.\n"
           "delivery: shipping, couriers, tracking and parcels that have not arrived or arrived damaged.\n"
           "returns: sending an item back, exchanges and the return process.\n"
           "account: signing in, passwords, profile details, privacy and emails from us.")

PROMPTS = {                                   # each one adds a part to the one before
    "instruction": [INSTRUCTION],
    "+ format": [INSTRUCTION, FORMAT],
    "+ context": [INSTRUCTION, FORMAT, CONTEXT],
}
MESSAGES = ["I cannot sign in with Google any more.",
            "I need a refund of the shipping charge you added by mistake.",
            "Do you ship to Iceland?"]


def ask(text):
    body = {"model": "qwen2.5:3b", "stream": False, "messages": [{"role": "user", "content": text}],
            "options": {"temperature": 0, "num_predict": 40}}
    req = urllib.request.Request("http://localhost:11434/api/chat", data=json.dumps(body).encode(),
                                 headers={"Content-Type": "application/json"})
    return json.loads(urllib.request.urlopen(req).read())["message"]["content"]


for msg in MESSAGES:
    print(f"\n{msg}")
    for name, parts in PROMPTS.items():
        reply = ask("\n\n".join(parts + [f"Message: {msg}\nCategory:"]))
        print(f"  {name:<12} {reply.strip().splitlines()[0][:60]!r}")

The Lab Report

A real terminal recording of python parts.py report. qwen2.5:3b on Apple M4, 24 GB; 40 hand-written messages, 10 per category; temperature 0. A table of prompt, right, clean, prompt tokens and median tokens written: A message alone, 10 of 40, 0 of 40, 44, 40.0; B + instruction, 37, 19, 62, 4.0; C + format rule, 36, 36, 77, 2.0; D + context, 40, 40, 138, 2.0; E + two examples, 40, 40, 169, 2.0. Then the wrong replies for A, B and C: A's include This issue falls under the category of Billing and Payment Disputes and Category: Tax/Finance; B's, Category: returns for a billing message, the Iceland refusal, and billing for an account message; C's, returns for a billing message and billing for three account messages.

The lab is scripts/labs/prompting/parts.py. It holds the 40 messages and their labels, builds the five prompts, sends all 200 calls and stores every reply in parts.json. The report shows the counts and the first few wrong replies for each prompt, so you can read what went wrong and not only how often.

The median tokens written tell the same story from another side. Most of prompt A's replies (23 of 40) ran to the 40-token limit, so its median is 40; B's were about 4 tokens ("Category: billing"); from C on, 2 tokens, one word and the end-of-turn marker.

One detail of the scoring deserves a mention, because I got it wrong the first time. My first version of the lab counted a reply as right only if its very first word was the category, so "Category: billing" scored as wrong. That made B look far worse than it was (19 right instead of 37) and made the format rule look like it fixed understanding, when it only fixed shape. I changed the check to skip a leading "Category:" and rescored the stored replies without calling the model again. A scoring rule is part of the experiment, and it can be wrong in the same way a prompt can.

A sequence diagram with three columns: the lab, Ollama and the check. Step one, the lab sends Ollama the prompt, parts plus message. Step two, Ollama returns the reply text. Step three, the lab sends the check the reply and the right category. Step four, the check returns right? usable?. Beneath: 200 calls, 5 prompts times 40 messages, temperature 0.

Score Replies in Your Browser

This box has no model. It holds eight real replies from prompt B and scores each one two ways: right, and usable as it stands. Change a reply, or add your own, and see which score moves.

It prints "names the right category: 6 of 8" and "usable as it stands: 3 of 8": the same gap as the full lab, in miniature. Try changing "Category: billing" to "billing." with a full stop and watch it count as right but not usable. Then think about what your own code would do with that full stop.

The Code, Part by Part

The parts as strings. INSTRUCTION, FORMAT and CONTEXT are plain Python strings. Keeping each part in its own variable makes it easy to add or remove one and measure the difference, which is exactly what this lesson did.

The prompts as lists. PROMPTS maps a name to a list of parts. Each list is the one before plus one more part. The script joins the parts with blank lines and puts the message last, followed by "Category:".

The call. ask sends the whole prompt as one user message to Ollama's chat endpoint, /api/chat, with temperature 0 so the answer does not change from run to run, and num_predict 40 so a long reply is cut instead of running on.

The print. It prints only the first line of each reply, up to 60 characters, with !r so you can see the quotes and spot a "Category:" wrapper or a stray full stop.

Which Part Is Missing?

A flowchart headed when the answers are not what you need, titled which part is missing?. Look at 20 replies leads to: its own labels? Yes: add an instruction that names them. No leads to: right label, extra words? Yes: add a format rule. No leads to: wrong label on edge cases? Yes: add context that draws the lines. No: try examples, a later lesson. Beneath: read the replies first; they tell you which part to add.

When a prompt's answers are not what you need, the replies usually tell you which part is missing, if you read them.

The model uses its own labels. It does not know the allowed answers. Add an instruction that names them.

The label is right, but wrapped in other words. It knows the answer but not the shape. Add a format rule that describes the exact shape.

The label is wrong on the cases near an edge. It does not know where your lines are. Add context that says what belongs where, especially for the cases your own team would argue about.

Still wrong, with no pattern you can describe. Then show it, with examples, which a later lesson in this chapter measures.

A table headed what each part did on these 40 messages, titled one job per part. Instruction: names the allowed answers, right 10 to 37. Format rule: makes the answer usable by code, 19 to 36. Context: draws the lines between categories, 36 to 40 right. Examples: no gain here, because D was already 40 of 40. Beneath: measured once, on one model, on messages written for the test.

When to Use Each Part, and When Not To

Use an instruction always. Tell the model what you want; here, without it, the model invented its own answers. Name the allowed answers when there is a fixed set.

Use a format rule whenever code reads the answer. If a person reads it, the format matters less. If a program compares, parses or stores it, the format is the whole difference between working and failing. A later lesson in this chapter goes further, with Ollama's option to force a JSON shape (JSON is a standard text format for structured data that programs read easily).

Use context when the task has edges. If your categories overlap, or your company defines them in its own way, say so. Do not paste a whole manual as context when four lines do the job: every token counts on every call, and a later lesson in this chapter measures what a long document does to the instructions around it.

Use examples when words are not enough. They are the second most expensive part here, and on an easy task they add nothing. Add them when the prompt without them still makes mistakes you cannot describe in a sentence.

Do not add everything by habit. Every part you add is text you must keep correct. A stale example or an out-of-date context line can pull answers in the wrong direction, just as a good one pulls them in the right one.

What This Lab Can and Cannot Tell You

A two-column page headed read before you quote a number from this lesson, titled what was measured, and what was not. Measured: 40 messages, one model; temperature 0, one run; labels I wrote myself. Not measured: real, messy customer text; other models; a fresh set of messages.

This lab measured 40 short messages that I wrote, with labels I chose, on one small model, once, at temperature 0. Real customer messages are longer, messier and more often ambiguous. A different model might need the instruction less, or the context more. And because I wrote both the messages and the context lines, a fresh set of messages written by someone else is the fair test of whether the final prompt really works, which a later lesson in this chapter shows how to build.

What carries over is the shape: an instruction fixes what the model aims at, a format rule fixes how the answer is written, context fixes the edges, and you only see the difference between right and usable if you measure both.

Two brand cards headed the tools, with their logos, titled what the test ran on. Ollama: qwen2.5:3b, Apple M4, 24 GB. Python: 200 chat calls, 5 prompts times 40 messages.

Three editorial boxes inside a frame labelled twelve lessons, three groups, headed prompting as engineering, the chapter ahead, titled where this lesson sits. Writing the prompt: parts (this lesson), clear instructions, system and user messages, examples, formats, quoting input. How prompts behave: thinking step by step, small rewordings, instructions around long documents. Prompts as code: templates, testing a prompt on cases, one prompt end to end. Beneath: every lesson measures its claim on a model you can run.

What to Do on Monday

A hand-drawn list headed for your own prompt, titled four things to do. 1, name them: list the answers you allow. 2, shape it: say exactly what the answer looks like. 3, draw lines: define the edge cases in a line each. 4, score twice: right, and usable as it stands. Beneath: then read the replies, not only the score.

Take one prompt from your own work that sorts, labels or extracts something. Split it into its parts, in separate variables, the way the script above does. Write 20 test inputs with the answers you expect. Then score the replies two ways: does it pick the right answer, and could your code use the reply as it stands? If the two numbers differ, you need a format rule. If the first number is low on the cases near an edge, you need context. And read the wrong replies before changing anything; they will usually tell you which part is missing.

A closing card headed to keep, titled parts, added one at a time. In large type: 0, 19, 36, 40, 40. Beneath: usable answers of 40, from the message alone to all five parts. Then: score right, and usable.

Knowledge Check

Knowledge Check

4 questions - Score 80% to pass

Q1

With the instruction but no format rule (prompt B), 37 of 40 answers named the right category but only 19 were usable. Why?

Q2

Adding the format rule (prompt C) moved usable answers from 19 to 36. What did it do to right answers?

Q3

Two worked examples (prompt E) added nothing: 40 of 40 before and after. What is the right conclusion?

Q4

Your prompt returns the right label for most inputs but the wrong one on cases your team would argue about. Which part should you add first?

This is a real run in VS Code's terminal.

A real screenshot of VS Code's terminal after running python parts_demo.py. I cannot sign in with Google any more: instruction billing, + format billing, + context account. I need a refund of the shipping charge you added by mistake: instruction Category: returns, + format returns, + context billing. Do you ship to Iceland?: instruction, The message "Do you [sic] to Iceland?" does not fit into any; + format delivery; + context delivery.

The three messages were chosen because each one changes along the way. The Google sign-in is billing until the context says sign-in belongs to account. The shipping refund is returns, first wrapped in "Category:" and then bare, until the context says refunds of money are billing. The Iceland question is a long reply saying it fits no category until the format rule forces a one-word answer, and then it is delivery. On my laptop, the first line of each reply matched the lab's stored reply for the same message and prompt. On your computer, a different Ollama version or chip can tip a choice between two nearly equal tokens, so a small difference is possible.