What a Prompt Is, and Isn't: How the Model Reads Instructions; the Four Elements of Role / Task / Format / Constraints
First get clear on how the model actually reads what you write, then use four elements — role, task, format, constraints — to turn a fuzzy requirement into a work ticket the model can execute reliably.
Today's Goals
- Explain in your own words why a prompt isn't a command but context you give the model
- Break a fuzzy requirement into role, task, format, and constraints, and write each one out
- State the division of labor between the system prompt and the user message, and explain why rules belong in the system prompt
This course runs five days, and all five circle one thing: taking the fuzzy requirement in your head and writing it down as a work ticket the model can read, act on, and reuse tomorrow. Today we lay the foundation — how the model actually reads that paragraph you wrote, and which four parts make up a ticket worth handing over. When you have read the walkthrough and finished the lab, come back to the top of this page and check off the three goals one by one.
Plain-Language Walkthrough
A prompt is not a command, it is context
Picture this. A new colleague joins your team — talented, but with zero context on your project, and you can only communicate through a written work ticket. They read the ticket and get to work; they never come back to ask a clarifying question. You write "add the validation to the TODO endpoints" on the ticket. What happens? They guess. They guess which fields you meant by validation, they guess what a failure should return, they guess whether tests are part of the job. When the guess lands you call them sharp; when it misses you call them unreliable. But the problem was never in the colleague — it was in the ticket. You treated the defaults living in your own head as things they already knew.
A large language model reads a prompt almost exactly the way that new colleague reads the ticket, with one difference that makes it more extreme: it does not even have the option of walking over to ask. What it does with your text is, at bottom, answer the question "given this text, what is the most reasonable thing to come next." It is completing a piece of text, not executing a command. That distinction sounds like word games, and it decides everything you write from here on. A command says do this, and the executor either does it or refuses. Context says here is the situation, and the completer infers the most likely continuation from the situation. The more specific you are, the narrower the set of reasonable continuations becomes and the more stable the output gets. The vaguer you are, the more the model has to fill your blanks with the general case it absorbed from an enormous pile of text — and the general case rarely matches your particular project.
So what is prompt engineering actually engineering? Not some secret incantation. It is the systematic work of supplying the context the model is missing: it does not know who you are, what you want, what shape you want it in, or what it must not touch. Those four blind spots line up exactly with the four elements we cover today. Get the mental model straight first: you are not issuing orders, you are writing a sufficiently detailed ticket for a capable colleague who knows nothing about the situation.
Why a person understands you and the model gets it wrong
Start with an example that will feel familiar. You say to a colleague who already knows the codebase, "add input validation and matching unit tests to the TODO API," and they can start immediately, because a pile of shared defaults sits behind that sentence: whether the project runs Express or FastAPI, which cases validation usually covers, which status code the team returns on failure, which test framework is in use, where test files live. Not one of those facts appears in the sentence, and the colleague who already knows the codebase has all of them.
The model does not. So it fills every blank with the most common practice in its training data. It may reach for a validation library you have never used. It may return 422 when your team has standardized on 400. It may use Jest when the project runs Vitest. It may helpfully normalize your route paths along the way. None of those choices is wrong on its own, and every one of them differs from what you wanted. That is the whole reason a person understands you and the model gets it wrong: the sentence does not carry enough information, and the model will never pause to ask — it will only substitute a default.
Ambiguity is the other face of the same problem. In "add the validation," does add mean extend the existing code or rewrite it? Does "matching unit tests" mean one test per validation rule, or one test covering all of them? A human resolves those forks automatically from context; the model picks one at random — and two runs of the same sentence may pick differently, which is the real source of the instability people complain about. So before you write a single line of prompt, do one thing: read your requirement out loud, and at every noun and verb ask yourself whether the new colleague could read that word a second way. If they could, pin it down. The move takes no skill at all, and it removes more than half of your output variance.
Elements one and two: role and task
Splitting the missing context into four buckets is all the four elements are: role, task, format, constraints. They are not a standard. They are a practical checklist — finish a prompt, run it past the four boxes, and see which one is empty.
Role answers "from whose vantage point are we looking at this." Many people write the role as a job title: "you are a senior backend engineer." That sentence carries almost no information, because a senior backend engineer might care about performance, or architecture, or readability, and the model has no idea which one you meant. An effective role gives a vantage point and a focus: "you are a backend engineer doing code review, you have seen plenty of endpoints fall over in production because nobody validated the input, and your habit is to list every field that can arrive and then ask, one field at a time, what happens when it is empty, oversized, or the wrong type." After reading that, the model knows three things: it should hunt for holes rather than rewrite features, it should look at edge cases before the happy path, and it should work by listing fields and interrogating boundaries. In ticket terms: the first line is not "you are an engineer," it is "today you are looking at this code on behalf of QA" — one sentence and their stance is fixed.
Task answers "what does the finished thing look like, and how do we know it is finished." The most common hole here is a missing definition of done. "Add input validation" has no end, so the model decides for itself where to stop. "Cover four cases — missing required field, wrong type, title over 200 characters, invalid date — with at least one test each, all of them runnable and green" has an end. A fixed sentence pattern helps: on what, do what, done when what. Leave out any of the three and the model has to invent it.
The task box has one more easily missed point: you have to actually hand over the material that "on what" refers to. If you want validation added to a piece of code, paste the code. If you want the team's conventions followed, paste the conventions. The model has no access to your repository; it cannot see anything you did not paste. That is obvious in a chat window, and easy to forget the moment you start assembling requests in code.
Elements three and four: format and constraints
Format answers "what shape does the output take." There is exactly one test for whether a format is written well: can a machine check it. "Clear and readable," "structured," and "concise" cannot be checked, because you cannot write code that decides whether a paragraph is clear. "Output three sections in order: a list of at most five items, then the revised code in one code block, then the test file in a second code block, with no pleasantries between sections" can be checked — you can count sections, items, and code blocks. The tighter the format, the more directly a program can consume the output. D3 upgrades this box straight into a JSON schema so a program can pull fields out without parsing prose, and when D4 builds a test set, the whole scoring logic depends on the format being checkable. For today, build one habit: no adjectives in the format box.
Constraints answer "what not to do, and why." Note the second half. Models follow bare negative instructions unreliably: "do not add new libraries" may or may not survive. But "do not add new libraries, because dependencies in this project need approval and a new one would stall this change" both sticks and generalizes — when the model later considers pulling in a date-parsing library, it recognizes that as a new dependency too. The reason is not politeness; it is the basis on which the model judges the cases you forgot to list. Constraints also draw the boundary of the change. Models love to refactor while they are in there, and without a line drawn they will expand the diff past the point where you can review it. One sentence — "mention anything unrelated to this task in the first list only, do not change it" — buys back a lot of review time.
Put the four together and the first version of the ticket for our running example looks roughly like this. It is plain-text prompt content, not code:
You are a backend engineer doing code review. You have seen plenty of endpoints
fall over because nobody validated the input, and your habit is to list every
field that can arrive, then ask what happens when it is empty, oversized, or
the wrong type.
Do two things to the TODO API code below:
1. Add request-body validation to the create and update endpoints, covering
missing required fields, wrong types, titles over 200 characters, and
invalid date formats. Every failure returns 400 with a readable message.
2. Write at least one unit test per validation case, plus one happy-path test,
using the framework already present in the code.
Done when: all tests run and pass, and each test name says which bad input it
covers without the reader opening the code.
Output three sections in order: a list of at most 5 validation gaps; the
revised endpoint code in one code block; the test file in a second code block.
No pleasantries between sections. Do not output files you did not change.
Do not add a validation library, because dependencies need approval. Do not
change route paths, methods, or the success response shape, because a
front end already calls them. Mention unrelated refactors in the list only.
(paste the code here)Compare that against the original one-liner, "add input validation and matching unit tests to the TODO API." It is ten times longer, and every added sentence answers one place the model would otherwise have guessed. That is prompt engineering in its plainest form: not cleverer, just more complete.
System prompt and user message: what is stable and what changes
The ticket above is one continuous block of text, but when you actually call a model through an API it gets split across two slots: the system prompt and the user message. The system prompt is the role description the colleague received on their first day, and it barely changes. The user message is today's ticket, and it is different every time. The model reads both as context; what differs is stability and weight. The system prompt stays in force for the whole conversation and models generally adhere to it more closely. The user message is one turn among many, and it gets diluted as the conversation grows.
That gives you a practical rule: anything the next task will also need goes in the system prompt; anything true only this once goes in the user message. Split the ticket above along that line. The role, the format rules, "no new dependencies," and "mention unrelated refactors, do not change them" hold for any code-review task on this team, so they go in the system prompt. "Which four validation cases," "do not change paths or responses because a front end calls them," and the code itself hold only this once, so they go in the user message. Notice that the constraints got cut in half: the four elements are a writing-time taxonomy, the system prompt and user message are a deployment-time taxonomy, and they do not map one to one.
In code the split looks nearly identical across providers:
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic() // reads ANTHROPIC_API_KEY automatically
// System prompt: the role description. Anything the next task also needs lives here.
const SYSTEM = [
'You are a backend engineer doing code review. You list every field that can arrive, then ask what happens at each boundary.',
'Output three sections in order: validation gaps (at most 5), the revised code (one code block), the test file (a second code block).',
'Do not add new dependencies, because dependencies need approval. Mention refactors unrelated to this task in one line; do not make them.',
].join('\n')
// User message: today's ticket, true only this once.
const task = [
'Add request-body validation to the create and update endpoints: missing required field, wrong type, title over 200 characters, invalid date.',
'Do not change route paths, methods, or the success response, because a front end already calls them.',
'The code follows:',
code,
].join('\n')
const res = await client.messages.create({
model: 'claude-sonnet-5',
max_tokens: 4096,
system: SYSTEM,
messages: [{ role: 'user', content: task }],
})
console.log(res.content[0].type === 'text' ? res.content[0].text : '')from anthropic import Anthropic
client = Anthropic() # reads ANTHROPIC_API_KEY automatically
# System prompt: the role description. Anything the next task also needs lives here.
SYSTEM = "\n".join([
"You are a backend engineer doing code review. You list every field that can arrive, then ask what happens at each boundary.",
"Output three sections in order: validation gaps (at most 5), the revised code (one code block), the test file (a second code block).",
"Do not add new dependencies, because dependencies need approval. Mention refactors unrelated to this task in one line; do not make them.",
])
# User message: today's ticket, true only this once.
task = "\n".join([
"Add request-body validation to the create and update endpoints: missing required field, wrong type, title over 200 characters, invalid date.",
"Do not change route paths, methods, or the success response, because a front end already calls them.",
"The code follows:",
code,
])
res = client.messages.create(
model="claude-sonnet-5",
max_tokens=4096,
system=SYSTEM,
messages=[{"role": "user", "content": task}],
)
print(res.content[0].text)Why must the rules live in the system prompt? Three engineering reasons. First, dilution: in a long conversation the user messages pile up, and a rule written in the first user turn is buried in the middle by the tenth, with adherence dropping visibly. The system prompt does not take part in that dilution. Second, control: in a real product the system prompt is assembled by your backend and users never touch it, which is the only way to guarantee that every user is bound by the same rules. Put the rules in the user message and they sit mixed in with user input. Third, cost: most providers' prompt caching matches on a prefix, and the system prompt is the most stable prefix you have. Concentrating the unchanging parts there lets thousands of later calls reuse the cache, and the bill drops noticeably. When D5 covers migrating across models, you will see that how the system prompt is organized also decides how much you have to rewrite when you swap providers.
Writing the running example as a first-draft prompt
Finally, chain today's material into a procedure you can follow. You will walk through all of it in the lab.
Step one: write the raw requirement down verbatim, with no polish — "add input validation and matching unit tests to a TODO API in Express or FastAPI." Step two: go word by word hunting for defaults and ambiguity. What does validation cover, what does a failure return, how fine-grained are the unit tests, which framework, where do the files go. Every time you find one, add a line to one of the four boxes. Step three: audit the four boxes. Does the role carry a vantage point and a focus? Does the task carry a definition of done? Can the format be checked mechanically? Do the constraints carry reasons? Step four: label every sentence either stable or changing, and send the stable ones to the system prompt and the changing ones to the user message. Step five: run it once, compare the output against your definition of done, and wherever it falls short go back to step two and fill that specific gap.
There is no technique anywhere in those five steps. It is all just moving the defaults from your head onto the page. And yet you will find the output steadies considerably afterward — not because the model got smarter, but because you stopped leaving things for it to guess. Techniques start tomorrow: giving it examples, making it think before answering, making it check its own work. But those techniques are built on top of today's ticket, and if the ticket itself is unclear, a technique only amplifies the confusion.
Source Reading
Hands-On Lab
Today's lab is document-shaped: no dependencies to install, no API key needed. Open labs/prompt-engineering-5days/day-01-four-part-template, read the filled-in example in solution/ first, then go back to starter/ and fill it in yourself:
- Read
solution/four-part-template.mdend to end, paying attention to the "why it is written this way" line after each element, so you see which missing piece of information every sentence supplies. - In part one of
starter/four-part-template.md, fill in role, task, format, and constraints for the running example, then self-check against acceptance criteria 1 through 3. - Fill in part two: split the four elements line by line into the system prompt and user message columns, label each line stable or changing, and confirm every stable line sits in the system prompt column.
- Fill in part three: pick a question you actually asked an AI recently and got a poor answer to, rewrite it with the same template, run it before and after, and write down which checkable point improved.
Interview Questions
Today's three questions are in the question bank below, covering three directions: what a prompt fundamentally is, how the four elements divide the work, and why rules belong in the system prompt. Read the analysis before the answer points — the analysis teaches you the derivation, and the points are only where that derivation lands. All three are marked as applying to both the domestic Chinese and the global market.
Checklist and Tomorrow
- Explain in your own words why a prompt isn't a command but context you give the model
- Break a fuzzy requirement into role, task, format, and constraints, and write each one out
- State the division of labor between the system prompt and the user message, and explain why rules belong in the system prompt
- All 5 acceptance criteria of the four-element template pass
- You can answer at least 2 of the 3 interview questions without looking at the points
Tomorrow (D2) we add techniques on top of this ticket: showing the model a couple of finished jobs (few-shot examples), making it think before it answers (chain of thought), and splitting a big task across several small tickets that check their own work (step-by-step and self-checks). Those techniques are everywhere online, but we will also cover something almost nobody covers — what each technique costs, and the circumstances in which none of them can save you. Learn the boundary first and the technique second, and you will not pile every technique on at once the moment something goes wrong.
Interview questions
What exactly is being engineered in prompt engineering, and how does it differ from writing a requirements doc or a design spec?提示词工程到底在工程什么?它和写需求文档、写技术方案有什么本质区别?
Common in ChinaCommon overseasBasic#prompt-basics#mental-modelHow to reason about it · think before answering
- The screen here is whether the candidate knows the model completes text rather than executes commands. 'Clever wording that makes the model obey' signals chat-app experience only.
- Start from the reader: a spec is read by people who share project context; a prompt is read by a completer with zero context that never asks a clarifying question, so every implicit default must be spelled out.
- Then justify the word engineering: reproducibility, testability, versioning. A prompt should run against a test set, live in the repo, and diff cleanly between versions.
- Conclusion: prompt engineering is making implicit context explicit and managing that text like code; phrasing tricks are a small part.
- Likely follow-up: how is that different from a brief for an outsourced team? The team pushes back with questions; the model does not, so a prompt must carry its own completion criteria.
分析过程 · 先想清楚再作答
- 这题在筛「有没有理解模型是在补全而不是执行」。答成「用巧妙的措辞让模型听话」会被判为只会用聊天产品;答出「系统性补齐模型缺少的上下文」才算入门。
- 拆法:先问自己「读者是谁」。需求文档的读者是有项目背景的人,可以依赖共享默认;提示词的读者是一个没有任何项目背景、也不会停下来提问的补全器,所有默认信息都得显式写出。
- 再落到「工程」二字:可复现、可测试、可版本化。提示词写完要能跑测试集、要进仓库、要能对比两版差异——这才是它区别于「写一段话」的地方。
- 结论:提示词工程是把隐性上下文显式化、并把这段文本当代码一样管理的工程活动;措辞技巧只是其中很小的一部分。
- 可预期的追问:那和写给外包团队的需求说明有什么区别?答案是外包会反问,模型不会,所以提示词对完整性的要求更高,且要在没有反馈回路的前提下自带完成标准。
Key points
- The model completes text rather than executing commands; a prompt is context, and specificity narrows the plausible continuations
- What gets engineered is the missing information: perspective, completion criteria, output shape, boundaries with reasons
- Unlike a spec, the reader shares no background and never asks back, so completeness and explicit done-criteria matter more
- Engineering implies testable, versioned, comparable artifacts, not one-off clever phrasing
答题要点
- 模型在补全一段文本而不是执行命令,提示词是给它的上下文,写得越具体可能的下文越窄、输出越稳
- 工程的对象是「模型缺的信息」:视角、完成标准、输出形状、边界与理由,也就是四要素
- 区别于需求文档:读者没有共享背景、不会反问,所以完整性要求更高、必须自带完成标准
- 「工程」意味着可测试、可版本化、可对比,而不是一次性的巧妙措辞
What problem does each of the four prompt elements — role, task, format, constraints — solve? If you could keep only three, which would you drop and why?角色、任务、格式、约束四要素各解决什么问题?如果只能保留三个,你会砍掉哪个,为什么?
Common in ChinaCommon overseasIntermediate#prompt-basics#four-elementsHow to reason about it · think before answering
- The first half is a warm-up; the second half tests whether you can map each element to a specific way the model would otherwise guess, and rank the cost of each wrong guess.
- Map them: role fixes perspective and focus, task fixes the finish line, format decides whether downstream code can consume the output, constraints bound the change surface.
- To pick the one to drop, ask whether its absence makes results unstable or unusable. Missing role skews focus but stays usable; missing done-criteria means the model never knows when to stop; missing format breaks parsers; missing constraints lets edits sprawl.
- Conclusion: in most engineering settings role is the most droppable, because a specific task plus a strict format already imply the perspective — provided the task states what to care about.
- Expect the follow-up 'then why does everyone write a role?' Because it is cheap and compresses many implicit preferences into one line, which pays off in chat-style use where the task cannot be fully specified.
分析过程 · 先想清楚再作答
- 前半句是送分,后半句才有区分度:它在考你是否知道每个要素对应模型的哪一种「猜」,以及哪种猜错的代价最小。
- 拆法:把每个要素映射到一个「模型会猜错的地方」——角色对应视角与关注点,任务对应终点在哪,格式对应输出能否被程序消费,约束对应改动范围与不可碰的边界。
- 判断哪个可砍:看缺了之后是「结果不稳定」还是「结果不可用」。缺角色多半是关注点偏了但仍可用;缺任务的完成标准会让模型不知何时停;缺格式会让下游解析失败;缺约束会让改动面失控。
- 结论:多数工程场景下角色最可砍,因为任务与格式写得足够具体时视角已经被隐含;但要说明前提是任务里已经写清了关注点。
- 追问几乎必然是「那为什么大家还都写角色」——答案是它便宜且能一句话压缩大量隐性偏好,在任务没法写得很细的对话场景里性价比最高。
Key points
- Role sets perspective; task sets the goal and done-criteria; format makes output mechanically checkable; constraints bound scope with reasons
- Each element removes one kind of guess the model would otherwise make
- Role is the most droppable once task and format are specific enough to imply the perspective
- Done-criteria and checkable format are the least negotiable because downstream code depends on them
答题要点
- 角色定视角与关注点;任务定做什么与完成标准;格式定输出形状是否可机械核对;约束定不可碰的边界与理由
- 每个要素对应模型的一种「猜」,缺哪个就多一种不稳定
- 可砍的是角色:任务与格式足够具体时视角已隐含,但前提是任务里写清了关注点
- 任务的完成标准与格式的可核对性最不能省,因为它们直接决定输出能不能被程序消费
Why do rules belong in the system prompt rather than the user message? Cover adherence, control, and cost, and name what the system prompt cannot guarantee.为什么规则要放系统提示而不是用户消息?请从遵从度、管控和成本三个角度说明,并指出系统提示做不到什么。
Common in ChinaCommon overseasIntermediate#system-prompt#prompt-basicsHow to reason about it · think before answering
- The tell is whether you cover all three angles and name a limitation. 'System prompts carry more weight' alone reads as memorized.
- Adherence: user turns get diluted as the conversation grows, the system prompt stays in force. Control: the backend assembles the system prompt and users cannot touch it, so rules apply uniformly. Cost: prompt caching matches on prefixes, and the system prompt is the most stable prefix.
- The limitation is the differentiator: higher adherence is not a guarantee, prompt injection can still steer the model, so security boundaries need code-level enforcement outside the model.
- Conclusion: rules go in the system prompt for stability, consistency and cost, but it is a strong suggestion, not a hard constraint.
- Follow-ups: can the system prompt go last? Possible but unwise — models weight early instructions more and it breaks the cache prefix. What should stay out? Per-request task details, which would bust the cache and hurt reuse.
分析过程 · 先想清楚再作答
- 题眼在「三个角度」和「做不到什么」。只答「系统提示权重高」是背概念,面试官要看的是你有没有在生产里拼过系统提示。
- 拆法:遵从度看多轮稀释——用户消息会被后续对话淹没,系统提示全程生效;管控看谁能改——系统提示由后端统一拼装、用户碰不到,规则放这里才能对所有用户一致;成本看缓存——提示缓存按前缀命中,系统提示是最稳定的前缀。
- 「做不到什么」是区分度所在:系统提示遵从度高不等于绝对,提示注入可以让模型跑偏,所以安全边界不能只靠系统提示,要在模型外用代码兜底。
- 结论:规则进系统提示是为了稳定、一致、省钱;但它是「强建议」不是「硬约束」,硬约束必须在代码层实现。
- 追问方向:系统提示可以放在对话末尾吗?可以但不推荐——多数模型对靠前指令更敏感,且会破坏缓存前缀;另一个追问是「哪些内容不该进系统提示」,答案是每次都变的任务细节,放进去会让缓存失效且难以复用。
Key points
- Adherence: user turns get diluted over a long conversation, the system prompt stays in force
- Control: the backend assembles it and users cannot edit it, so rules apply to everyone
- Cost: prompt caching matches prefixes, so stable content in the system prompt maximizes cache hits
- Limit: it is not a security boundary; prompt injection can bypass it, so enforce hard rules in code
答题要点
- 遵从度:用户消息会被多轮对话稀释,系统提示全程生效
- 管控:系统提示由后端统一拼装,用户碰不到,规则才能对所有人一致
- 成本:提示缓存按前缀命中,系统提示是最稳定的前缀,不变的内容集中在这里最省钱
- 做不到的:它不是安全边界,提示注入可以绕过,硬约束必须在代码层兜底
Comments
Sign in to join the discussion
No comments yet — be the first.