Topic 3.9
Playbook: Prompt Patterns Library
In one line
Five prompt patterns you will use in almost every project, each built by a small function and each checked by code: classify, extract, summarise, rewrite and answer from context.
Think of it like this
A toolbox with a hammer, a screwdriver and a tape measure. You do not invent a new tool for each job; you pick the right one, and you know how to check the result (the picture is straight, the screw is tight). These prompt patterns are your first five tools, each with its check.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Pattern
- A prompt shape you can reuse for a common job.
- Label list
- The fixed set of answers a classifier may return.
- Refusal sentence
- An exact sentence the model must use when it cannot answer, such as I don't know.
- Grounded answer
- An answer that uses only the context you supplied.
- Check
- A small function that returns pass or fail for a model reply.
Step by step
01Step 1: one builder per pattern
classify_prompt, extract_prompt, summarise_prompt, rewrite_prompt, answer_prompt. Each begins with a TASK: line, includes only the parts it needs and wraps the data in tags. Small, boring, easy to test.
02Step 2: one check per pattern
A label is in the label list. The extract reply parses as JSON with exactly the fields. The summary has at most 8 words. The rewrite has no exclamation marks and keeps a sorry. The grounded answer contains "5 days", and the missing-context answer is exactly the refusal sentence.
03Step 3: run them all against a fake model
The fake model reads the first line (TASK: classify) and returns a fixed reply. For the answer pattern it checks the context. The program prints pass or fail for each of six cases and a total.
04Step 4: plug in the real model
The real call sits behind one function with the same shape as the fake: prompt in, text out. The "real SDK" block shows the call with the OpenAI-compatible SDK and a Groq base URL, and with plain HTTP in Java. The checks do not change.
def call_model(prompt):
... # send the prompt to your provider, return the reply text
reply = call_model(classify_prompt("I was charged twice", LABELS))
print(reply in LABELS)05Step 5: grow the library
When a new job appears, add a builder, a check and a regression case. Keep the library in one module, version the prompts (Topic 3.8) and log which pattern each call used.
Try it yourself
- 1
Break one check on purpose
Change the summarise limit from 8 to 5 words (in the check and the builder) and run. Only one line should fail. Which one, and what would a real project do next: shorten the prompt, retry, or relax the limit?
- 2
Add a sixth pattern
Add a "translate" pattern: a builder, a branch in the fake model, and a check. Run in all three languages and compare.
- 3
Swap in the real model
Copy the Python real-SDK block, set
GROQ_API_KEY, and callcall_modelon the classify prompt. Does it return a bare label? Which pattern from this phase would you add if it does not?
Code & diagrams
The fake model returns fixed replies per task. The builders and checks are the production-shaped part.
Expected output
classify: pass
extract: pass
summarise: pass
rewrite: pass
answer, in context: pass
answer, not in context: pass
6 of 6 recipes passNot run here: it needs a network call and your own GROQ_API_KEY from the environment. Python and JavaScript use the OpenAI-compatible SDK with Groq's base URL; Java uses plain HTTP and Jackson. Groq deprecates max_tokens in favour of max_completion_tokens. Check the model name in Groq's model list, because hosted models are retired from time to time.
import os
from openai import OpenAI # pip install openai
client = OpenAI(api_key=os.environ["GROQ_API_KEY"], base_url="https://api.groq.com/openai/v1")
def call_model(prompt):
response = client.chat.completions.create(
model="openai/gpt-oss-20b", # a reasoning model: its hidden thinking uses the output limit too
messages=[{"role": "user", "content": prompt}],
temperature=0,
reasoning_effort="low",
max_completion_tokens=400,
)
return response.choices[0].message.content
# Same shape as fake_model: prompt in, text out. Swap it in and run the same RECIPES checks.
print(call_model("TASK: classify\nLabels: billing, shipping, other\nReply with the label only.\n<text>\nI was charged twice\n</text>"))
Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Remove the refusal sentence
In answer_prompt, delete the sentence "If the answer is not there, say: I don't know." and in the fake model make the missing-context case return "Refunds usually take a week." (what a model without the rule might invent).
Myth vs fact
Myth
One universal prompt can do all five jobs.
Fact
Separate patterns are easier to test, cheaper (shorter prompts) and easier to fix when one job regresses.
Myth
If the check passes on a fake model, the real one will pass.
Fact
The fake proves your builders and checks work. Run the same checks on the real model, often, with real data.
Myth
Checks make prompts unnecessary to improve.
Fact
Checks only tell you pass or fail. You still improve the prompt (and add examples) when they fail.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Treat each pattern as a typed function: the input types, the output type and the failure behaviour. This is the shape Phase 4 turns into
call -> parse -> validate -> repair -> fallback. - ▸
Keep checks cheap and deterministic (word counts, label lists, JSON parse). Reserve judge models for qualities rules cannot measure (Phase 9).
- ▸
Record pattern name, prompt version, model and check result with every call. A drop in one pattern's pass rate then points straight to the cause (Phase 11).
Remember this
- 1
Recipe: classify. When to use it: route tickets, tag content, decide a category. What it guards against: chatty replies and labels you did not list. How it works: list the allowed labels, say "reply with the label only", wrap the text in tags, then check that the reply is in the label list. Adapt it by adding an
otherorunknownlabel, and by adding examples (Topic 3.3) for borderline cases. - 2
Recipe: extract. When to use it: pull fields (name, order number, date) from messy text. What it guards against: made-up values for fields that are not in the text. How: name the fields, ask for JSON only, say "use null for a missing value", and check that the reply parses and has exactly the fields. Phase 4 adds schemas, validation and repair.
- 3
Recipe: summarise. When to use it: shorten long text for a small space. What it guards against: summaries that are too long or add facts. How: state the maximum words as a number, put the text in tags, and check the word count. Adapt it with the reader ("for a support agent") and a rule such as "use only facts from the text".
- 4
Recipe: rewrite. When to use it: change tone or clarity while keeping the meaning (polite, plain, shorter). What it guards against: dropped facts and added promises. How: name the tone, say "keep every fact", and check simple rules in code (no exclamation marks, key facts still present). Always have a person review rewrites that speak for your company.
- 5
Recipe: answer from context. When to use it: questions about your own documents or policies. What it guards against: invented answers. How: put the context in tags, say "answer only from the context" and give an exact refusal sentence for when the answer is missing ("I don't know."), then check both cases: the answer is present, and the refusal appears when the context lacks it. Phase 8 grows this into full retrieval-augmented generation.
- 6
How the recipes fit together. Each recipe is a function that builds a prompt (Topics 3.2 and 3.7), keeps data in tags (3.4), asks for a checkable output (3.1) and has a test (3.8). The runnable example keeps them in one list of
(name, prompt, check)and runs every check against a fake model. To use a real model, replace the fake with one function that sends the prompt and returns the text, and run the same checks.
Explain it without notes
For each of the five patterns, name the failure it guards against and the check that catches it.
How do you make a model say "I don't know" reliably in an answer-from-context prompt?
Why keep prompt builders and checks as small separate functions?
Practice
Write classify_check(reply, labels) that returns True if the reply, ignoring spaces and capital letters, is one of the labels.
Write summary_check(reply, max_words) that returns True if the reply has at most max_words words and does not end with a question mark.
Trade-offs
- ↔
Many small patterns are testable and cheap but need a library to organise them; one big prompt is quick to start and hard to fix.
- ↔
Strict checks reduce bad output but reject some good replies; decide how a failed check is handled (retry, repair, fallback).
- ↔
Rewrite and summarise checks catch only what rules can see; a person or a judge model must cover quality and tone.
Done when you can
Before you ship: every prompt comes from a builder function with named parts, not pasted strings.
Before you ship: untrusted text is escaped and wrapped in tags, and no secret is in any prompt.
Before you ship: every pattern has a code check and a regression case, including the "answer is missing" case.
Before you ship: prompt id, version and model are logged with every call.
Before you ship: a failed check has a defined outcome (retry, repair, fallback), not a crash.