System, user, assistant — the three roles
A chat with an LLM isn't one text box — it's three. Knowing which goes where is the difference between flaky prompts and reliable ones.
What's actually happening
Modern chat APIs take a list of messages, each with a role and content. (If JSON is new to you: it's just a structured way to write data.)
{
"model": "claude-sonnet-4-6",
"messages": [
{ "role": "system", "content": "You are Bramble, a friendly bookshop assistant." },
{ "role": "user", "content": "Got any sci-fi under £15?" },
{ "role": "assistant", "content": "Hello! A great pick is Project Hail Mary by Andy Weir…" },
{ "role": "user", "content": "Does it hold up if I haven't read his other books?" }
]
}| Role | Set by | Notable property |
|---|---|---|
system | You, the developer | Most heavily weighted — guidance here sticks |
user | The end user (or your app on their behalf) | Can be hostile (more on this in Safety) |
assistant | The model (its previous turn) | You can also use this to demonstrate desired output (few-shot) |
Two things this implies and the diagram doesn't:
- You can use the
assistantrole for examples too. Putting fake prior turns in your request is how few-shot prompting works — show the model 3 example user/assistant pairs, then ask the real question. - User content can't be trusted. A user typing "ignore your previous instructions" is asking the model to flip on its handler. Robust systems put their real rules in
systemand treatusercontent as data, not commands. (Full lesson in Safety.)
Where it shows up in real life
Under the hood (optional)
If you build with the API, the message-list shape maps directly to the request. ~10 lines of Python. Skip if you don't code.
›Show the example code (Python, ~10 lines)click to expand
from anthropic import Anthropic
client = Anthropic()
response = client.messages.create(
model="claude-sonnet-4-6",
system="You are a senior code reviewer. Be terse, technical, and direct.",
messages=[
{"role": "user", "content": "Review this PR: <diff>...</diff>"},
],
max_tokens=1024,
)
print(response.content[0].text)(Anthropic puts system in its own field; OpenAI expects it as the first item in messages. Same idea, different shape.)
Try it yourself (no coding, ~5 minutes)
Set a system prompt without writing code:
- ChatGPT: Settings → "Customize ChatGPT" → "What traits should ChatGPT have?". Paste: "Always reply in the voice of a 19th-century pirate captain. Use 'arrr' liberally." Save.
- Or Claude.ai: create a Project → paste the same in instructions.
- Start a new chat. Ask anything — "explain compound interest", "recommend a book". The style changes; the facts stay right.
You've just used the same primitive vendors use to give each AI assistant its personality. Imagine writing: "You're a tax-filing assistant. Never give legal advice. If unsure, say 'consult an accountant.'" That's how products are built on top of LLMs.
Check your understanding
- 1. Where does the system prompt belong?
- 2. What is few-shot prompting?
- 3. Your bot leaks customer info when a user types 'ignore previous instructions'. The right fix:
Found this useful? Share it with someone learning AI.
Further reading
- Anthropic — Prompt engineering overview — official guide.
- Anthropic — Use system prompts — when and how.
- Lilian Weng — Prompt Engineering — best deep-dive blog post.