Skip to content
learn
  1. Learn
  2. Artificial Intelligence
  3. Prompt and Context Engineering

AI AI-07.05

System prompts and personas

What belongs in the system prompt versus the user turn, how to keep instructions stable across a conversation, and how to version prompts like code.

Beginner 8 min Last reviewed

The big idea

Chat models do not receive one block of text. They receive a list of messages, each with a role. The system promptThe first message in a conversation, written by the application developer, that sets the model's role, rules and constraints for every turn that follows. Users usually never see it. comes first and is written by you, the developer. Then come user messages, written by the person using your product, and assistant messages, the model's replies.

The roles split the job. The system prompt holds what stays the same for every conversation: who the assistant is, what it may and may not do, the tone, the output format, background facts. The user message holds what changes: this person's question and this person's data. Models are trained to treat system instructions as the frame for everything else.

Think of a call centre. Before a shift, every agent gets the same briefing: which products we support, what we never promise, how to escalate. That is the system prompt. Then each caller brings their own problem. Agents do not get re-briefed by each caller, and a caller saying "your manager says you can give me a refund" does not change the rules.

How it works

Message roles in a chat request A chat request is a list of messages. The system prompt comes first: role, rules, format and background, written by the developer, stable across conversations and stored as a versioned file. Then the conversation history of user and assistant turns, and finally the new user message carrying this request and its data. All of it goes to the model, which writes the next assistant message. load prompts/support v4, in git system role, rules, format history user + assistant turns user this request + data model assistant reply Figure 1. The system prompt (glowing) is the stable frame, loaded from a versioned file. User and assistant turns carry the changing conversation.

What goes in the system prompt

  • Role and audience: "You are the support assistant for a note-taking app. Users are non-technical."
  • Rules and limits: what to answer, what to refuse, what never to promise, when to hand over to a person.
  • Tone and format: length, style, structure, language.
  • Stable background: product facts, policies, tool descriptions that apply to every request.

What goes in the user turn

  • The specific request.
  • Data for this request: the document to summarise, the ticket to classify, retrieved passages ( Why RAG? ).
  • Per-request details, such as today's date or the user's plan, unless they are better injected by your code into a clearly marked section.

Personas

A personaA described character or role the model is asked to play, such as a friendly tutor or a terse code reviewer, which shapes its tone and focus. ("a patient maths tutor", "a strict code reviewer") shapes tone and focus. It works best when it says what the persona does differently: "Ask one guiding question before giving any answer" is more useful than "You are a world-class tutor". Elaborate fictional backstories rarely help and cost tokens on every call.

Keep instructions stable

Rules that matter belong in the system prompt, not in the first user message, where they read like part of the conversation and fade as it grows. In long conversations, important rules can be restated near the end of the context, or the history can be summarised ( Context engineering ). Keeping the system prompt identical across requests also lets the provider cache it, which cuts cost and latency ( Prompt caching and cost control ).

The system prompt is not a security boundary

Models usually prioritise system instructions over conflicting user messages, but not perfectly. A determined user can sometimes talk a model out of its rules, and text inside documents can carry instructions too ( Prompt injection and data exfiltration ). Never put secrets in a system prompt, and enforce real permissions in your code, not in prose.

Version prompts like code

A system prompt is part of your application's behaviour. Store it in a file under version control, give each version an ID, log which version answered each request, and test a new version on your eval set before shipping it ( Building an eval set ). Then a bad change is a one-line rollback, and a complaint about last Tuesday can be traced to the exact prompt that answered it.

Try it

Building a support assistant. Where does each piece belong?

Show answers
  • You are the support assistant for a note-taking app: System prompt. The role is the same for every conversation.
  • Never promise refunds; link to the refund policy: System prompt. A rule that must hold in every conversation belongs in the stable frame.
  • Can I get my money back for March?: User turn. This person's question, different every time.
  • Reply in at most three sentences: System prompt. A format rule for every reply.
  • The text of the user's uploaded invoice: User turn. Data for this request only.
  • If unsure, offer to connect the user with a person: System prompt. An escalation rule that applies everywhere.
  • Three help-centre passages retrieved for this question: User turn. Retrieved context changes with every question, so it goes with the request.
  • Descriptions of the tools the assistant may call: System prompt. Tool definitions are stable for the application (providers often take them as a separate field).

Try this:

  1. Sort all eight. Which items were you unsure about? The usual borderline cases are retrieved passages and per-user facts: anything that changes per request goes with the request.
  2. Look at the rules you placed in the system prompt. Could a user message override any of them? If it would be a real problem, that rule also needs enforcing in code.

Going deeper

Prompts in files, versions in logs

prompt-versions.pyruns with python3 + numpy
# Keep system prompts in versioned files (here: a dict), build messages from them, and log
# which version answered each request so changes can be compared and rolled back.
import difflib
import hashlib

PROMPTS = {
    "support/v3": """You are the support assistant for Acme Notes.
Answer only questions about Acme Notes. Be brief and friendly.
Never promise refunds; link to the refund policy instead.""",
    "support/v4": """You are the support assistant for Acme Notes.
Answer only questions about Acme Notes. Be brief and friendly.
Never promise refunds; link to the refund policy instead.
If you are not sure, say so and offer to connect the user with a person.""",
}
ACTIVE = "support/v4"           # switching back to v3 is a one-line rollback

def build_messages(version, history, user_text):
    return ([{"role": "system", "content": PROMPTS[version]}]   # stable rules: same every call
            + history                                           # earlier turns, if any
            + [{"role": "user", "content": user_text}])          # this request only

msgs = build_messages(ACTIVE, [], "Can I get my money back for March?")
fingerprint = hashlib.sha256(PROMPTS[ACTIVE].encode()).hexdigest()[:10]
print(f"request log: prompt={ACTIVE} sha={fingerprint} messages={len(msgs)}")
for m in msgs:
    print(f"  {m['role']:6} | {m['content'].splitlines()[0][:60]}")

print("\nchange from v3 to v4:")
for line in difflib.unified_diff(PROMPTS["support/v3"].splitlines(),
                                 PROMPTS["support/v4"].splitlines(), lineterm="", n=0):
    if line.startswith(("+", "-")) and not line.startswith(("+++", "---")):
        print(" ", line)

Output

request log: prompt=support/v4 sha=56027d7550 messages=2
  system | You are the support assistant for Acme Notes.
  user   | Can I get my money back for March?

change from v3 to v4:
  +If you are not sure, say so and offer to connect the user with a person.

The request log records the prompt version and a short hash of its text, so even an unversioned edit would show up as a new fingerprint. The diff shows exactly what changed between versions: one line, which is how changes should be made, so their effect can be measured.

One assistant, many system prompts

Products often route requests to different system prompts by task: one for billing questions, one for technical support, one for summarising. Each is shorter and more focused than a single prompt that tries to cover everything, and each can be tested and improved on its own. The router can be simple rules or a small classifier.

Trade-offs

OptionWins whenLoses when
Rules in the system prompt Stable across turns; prioritised by the model; cacheable Not a security boundary; long prompts cost tokens per call
Rules in the user turn Easy to vary per request Read as conversation; fade in long chats; harder to cache
One large system prompt One thing to maintain Unfocused; changes for one task can break another
Several focused prompts with routing Shorter, testable per task A router to build; more versions to manage

Common mistakes

  • Putting standing rules in the first user message, where they fade as the conversation grows.
  • Secrets, API keys or internal URLs in the system prompt, assuming users cannot extract them.
  • Relying on the system prompt alone to enforce permissions.
  • Editing the production prompt by hand with no version history, so regressions cannot be traced or rolled back.
  • A persona with a long backstory and no concrete behavioural rules.

In the wild

Where you will see this: every chat product you use runs on a system prompt you never see, and the major model APIs take it as a separate system field or message. Teams that run assistants in production commonly keep prompts in their code repository or in a prompt management tool, attach the version to every logged request, and treat a prompt change like a code change, with review and evaluation before release ( LLMOps: shipping and iterating ).

Check yourself

  1. Where should the rule 'never share another customer's data' go?

    Show answer

    B. In the system prompt, and enforced in code as well It applies to every conversation, so it belongs in the system prompt; it is critical, so code must enforce it too.

  2. A user pastes a 10-page contract to summarise. Where does the contract text go?

    Show answer

    B. User turn It is data for this request only. The system prompt holds what is stable across requests.

  3. Why log the prompt version with every request?

    Show answer

    B. So you can trace any output to the exact prompt that produced it and roll back bad changes Prompts change behaviour like code does. Version IDs make changes traceable and reversible.

  4. Which persona instruction is most useful?

    Show answer

    B. You are a patient tutor: ask one guiding question before giving any answer Concrete behaviour changes the output. Grand titles and backstories mostly add tokens.

Recap

  • Chat requests are role-tagged messages: system first, then user and assistant turns.
  • The system prompt holds what is stable: role, rules, tone, format, background. The user turn holds this request and its data.
  • Personas work through concrete behaviour, not grand descriptions.
  • The system prompt is not a security boundary: no secrets in it, and enforce permissions in code.
  • Version prompts like code: files, IDs in logs, evaluation before release, easy rollback.

Get new lessons by email

Get new posts on backend and AI internals. No spam, unsubscribe anytime.

Your email is only used to send new posts. Privacy