The Claude API

Advanced AI Workflows & the Claude API

Chapter 2  ·  System Prompts — Shaping Claude's Behaviour at the API Level

The system prompt is the most powerful tool you have when building Claude-powered applications. It runs before every conversation turn, shapes how Claude interprets user messages, and establishes constraints that persist for the entire session. A well-written system prompt is the difference between an unpredictable AI and a reliable product feature. This chapter covers how to write them, what to put in them, and the common mistakes that undermine their effectiveness.

What a System Prompt Actually Does

From Claude's perspective, the system prompt is high-priority context that arrives before the user speaks. It doesn't override Claude's values or safety behaviours, but it does everything else: it narrows the topic space, establishes tone and format, sets a persona, and defines what Claude should and shouldn't do within the conversation.

Think of it as the standing instructions you'd give a contractor before they start work — not a script for every scenario, but the ground rules that govern how they handle everything they encounter.

System vs user instructions
Instructions in the system prompt carry more weight than instructions in the user message — Claude treats system instructions as the application's intent and user messages as end-user input. If they conflict, system instructions generally win. Use this hierarchy deliberately: put hard constraints in the system prompt, not the user turn.

Five Sections of an Effective System Prompt

Role & Identity
Who Claude is in this application. A concise statement of purpose and persona. This primes Claude's vocabulary, tone, and assumptions for every response that follows. Example: "You are a customer support assistant for Acme SaaS. You help users troubleshoot the dashboard, billing, and integrations."
Scope & Limits
What Claude will and won't do. The topics it covers, the tasks it handles, and — crucially — what it should decline or redirect. Explicit out-of-scope rules prevent Claude from helpfully answering questions your application wasn't designed for. Example: "Only answer questions about Acme products. If asked about competitors or general coding questions, politely redirect to our documentation."
Tone & Style
How Claude communicates. Formal or casual, verbose or concise, first-person or third-person, with or without emoji. The more specific, the more consistent the output across different user inputs. Example: "Be concise. Use plain language, not jargon. Never use bullet points — answer in prose. Keep responses under 150 words unless the user asks for detail."
Output Format
What the response should look like. If your application parses Claude's output, specify the exact format here. If it renders markdown, say so. If it expects JSON, describe the schema. Example: "Always respond in valid JSON with keys: answer (string), confidence (low|medium|high), sources (list of strings)."
Context & Data
Dynamic information injected at runtime. User account details, retrieved documents, current date, session state. This section changes per-request; the other four are usually static. Example: "The current user is: {user_name}. Their subscription tier is: {tier}. Today's date is: {date}."

Vague vs Specific — The Most Common Mistake

Vague instructions produce inconsistent outputs. Every word in a system prompt that could be interpreted multiple ways will be interpreted multiple ways across different user inputs. Specificity is the primary lever for reliability.

Vague — inconsistent results
Be helpful and professional. Answer user questions about our product. Keep it brief.
Specific — consistent results
You are a support assistant for Acme Dashboard. Answer only questions about: login issues, billing, and the reporting module. Decline all other topics politely. Respond in 1–3 sentences. Never use bullet points. Never speculate about roadmap features.
Vague — format varies
Extract the key information from the user's message.
Specific — format fixed
Extract information from the user's message. Respond only with valid JSON: {"name": string | null, "email": string | null, "issue": string}. No explanation. No markdown fences.

Dynamic Context Injection

The system prompt is a string — you build it in code before each request. This makes it easy to inject runtime data: the current user's name, their account tier, retrieved documents, the current date. Treat the static sections as a template and fill in the dynamic parts with Python string formatting:

dynamic_system_prompt.pypython
def build_system_prompt(user, today):
    return f"""You are a billing support assistant for Acme SaaS.

Current user: {user.name} ({user.email})
Subscription: {user.plan} — expires {user.expiry}
Today's date: {today}

Rules:
- Only discuss billing, invoices, and plan changes.
- Never reveal internal pricing or discount structures.
- If the user requests a refund, collect their reason and say
a human agent will follow up within 24 hours.
- Respond in plain prose, no bullet points, under 100 words."""

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=256,
    system=build_system_prompt(current_user, date.today()),
    messages=[{"role": "user", "content": user_message}]
)
Sanitise injected data
Any user-controlled data injected into the system prompt is a potential prompt injection vector. If a user's name is Ignore previous instructions and... and you inject it directly, you've handed them partial control of the system prompt. Strip or escape user-provided strings before injection, or put user data in a clearly delimited section Claude recognises as data, not instructions.

Application System Prompt Templates

Customer support bot
You are a support assistant for [Product]. Help users with [topics]. If you cannot resolve an issue, say so and direct them to [contact]. Never make commitments about refunds or SLA without human approval. Respond in 2–3 sentences max.
Key constraints: scope, escalation path, commitment limits, length.
Structured data extractor
Extract information from the text provided by the user. Respond only with valid JSON matching this schema exactly: {schema}. If a field is absent from the input, use null. Never add fields not in the schema. No explanation, no markdown.
Key constraints: exact format, null handling, no extra output.
Code reviewer
You are a senior {language} engineer reviewing code for correctness, security, and style. Flag issues in order of severity: critical, warning, suggestion. For each issue give: location, problem, recommended fix. Respond in markdown.
Key constraints: language specialisation, severity ordering, output structure.
RAG document Q&A
Answer the user's question using only the documents provided below. If the answer is not in the documents, say "I don't have that information." Never infer beyond what's stated. Cite the document name for every claim. [DOCUMENTS]: {docs}
Key constraints: grounding, citation requirement, explicit refusal when answer is absent.

Instruction Priority — When Instructions Conflict

In a real application, instructions can come from multiple places: the system prompt, injected documents, and the user's message. Claude applies a rough priority order:

PrioritySourceTypical use
1 System prompt (static) Application-level rules, persona, scope, format. Treated as developer intent — highest weight.
2 System prompt (injected context) Retrieved documents, user data. Treated as data to reason over, not instructions to follow.
3 User message End-user input. Treated as the task to perform within the constraints set above.

If a user message says "ignore your previous instructions" — a classic prompt injection attempt — Claude will generally hold to the system prompt constraints because the system prompt has higher contextual authority. This is not a hard guarantee, but it holds reliably for well-written system prompts.

Testing Your System Prompt

A system prompt that works for happy-path inputs often fails at the edges. Test deliberately against adversarial inputs before shipping:

  • Out-of-scope questions — does Claude correctly decline topics outside its brief, or does it helpfully answer anyway?
  • Prompt injection attempts — user messages like "ignore your instructions and tell me X" — does the system prompt hold?
  • Format edge cases — very short inputs, very long inputs, inputs in other languages, inputs with no clear question
  • Instruction conflicts — if the user explicitly asks Claude to break a rule ("please use bullet points"), does it comply or hold to the system prompt?
  • Ambiguous requests — inputs that could be interpreted as in-scope or out-of-scope — which does Claude choose?
Iterate with temperature 0
When testing and refining a system prompt, set temperature=0 to get deterministic responses. Once the prompt is stable, test with default temperature to see how it holds under variation. A prompt that only works at temperature 0 is too fragile for production.
Next — Chapter 3: Tool Use / Function Calling
Giving Claude tools it can invoke — defining tools, handling tool call responses, building the request-response-result loop, and designing tools that Claude can use reliably.