The role, constraint and format blocks that make a system prompt hold
A system prompt is the only instruction the model cannot be talked out of mid-conversation. These are the blocks that belong in one and the order they work best in.
Written in this order because each one narrows the space left by the one above it.
| Block | What it does | Failure it prevents |
|---|---|---|
| Role | Who the model is and who it is talking to | Generic, unplaceable tone |
| Capability | What it may do, and with which tools | Inventing actions it cannot take |
| Constraint | Hard limits — length, scope, forbidden topics | Scope creep on long threads |
| Format | The exact output shape expected | Prose where you needed JSON |
| Refusal | What to do when the request falls outside scope | Confident answers to the unanswerable |
| Instead of | Write | Why |
|---|---|---|
| "Be concise" | "Answer in at most 3 sentences" | A number is checkable; an adjective is not |
| "Do not hallucinate" | "If the context does not contain the answer, say so" | Names the observable behaviour, not the internal state |
| "Use good formatting" | "Return a markdown table with columns X, Y" | Removes every degree of freedom you did not want |
| "Think step by step" | "Work through the steps in <thinking> tags first" | Gives the reasoning somewhere to live |
| "Be helpful" | Delete the line | Costs tokens and changes nothing |
Anything that must survive the whole conversation goes in the system prompt — role, constraints, output format. Anything about this one request goes in the user message. Instructions placed in a user turn get diluted as the thread grows.
As long as it needs to be and no longer. Every token is re-sent on every turn, so it is the most expensive text in your application. Delete any line you cannot name a failure for.