30.2 Messages API: System Prompts, Human and Assistant Turns
Alright, let’s talk about the Messages API. This is the core of how you actually talk to Claude. Forget the old, simplistic “single prompt” model. This is a conversation, and getting that right is 90% of the battle. The API is built around a messages array, where each object represents a turn in the conversation. It feels natural because it is natural—it’s how we communicate.
The Three Roles: System, Human, Assistant
Every message in the array must have a role property. This isn’t just bureaucratic labeling; it tells Claude who is speaking and, more importantly, how to interpret that text. There are three roles, and they are not created equal.
user: This is you. The human. Your queries, your instructions, your data. This is the primary input Claude responds to.assistant: This is Claude. You use this role primarily for two things: providing examples of desired behavior (few-shot learning) and storing the history of the conversation so Claude knows what it just said.system: This is the director whispering instructions to the actor from off-stage. It’s for meta-instructions: setting the tone, defining the persona, establishing hard rules, and giving high-level guidance that should persist throughout the entire conversation. This is arguably the most powerful tool in your kit.
Here’s the crucial part: The system prompt is not part of the conversational history. Claude is aware of it, it’s guided by it, but it doesn’t “see” it as a message to respond to directly. This is a brilliant design choice because it prevents your core instructions from clogging up the context window.
Wielding the System Prompt Like a Pro
Most beginners massively underutilize the system prompt. They throw a meek “You are a helpful assistant” in there and call it a day. You’re better than that. The system prompt is where you define the persona and the rules of engagement.
Let’s say you’re building a financial analyst. Don’t just tell it what it is; tell it how to think.
{
"messages": [
{
"role": "system",
"content": "You are a sharp, conservative financial analyst with a knack for explaining complex concepts to retail investors. Your core principles are capital preservation and long-term, steady growth. You are inherently skeptical of hype and will always point out risks alongside potential rewards. Format your responses with clear headings, use bullet points for key takeaways, and conclude with a one-sentence summary of your recommendation. Never use financial jargon without immediately defining it in plain English."
},
{
"role": "user",
"content": "Okay, analyze Bitcoin for me. Should I put my retirement fund in it?"
}
]
}
See the difference? You’re not just naming a role; you’re programming a worldview, a tone, and a format. The response you get will be lightyears better. The system prompt is also the perfect place to fight against common LLM weaknesses. Add lines like “Think step-by-step,” “Check your work for consistency,” or “If you are unsure about a fact, say so instead of hallucinating.”
Structuring the Conversation
The messages array is a chronological transcript. The order matters deeply. Claude uses this to understand the flow of dialogue. A typical, effective structure looks like this:
import anthropic
client = anthropic.Anthropic(api_key="your_api_key")
response = client.messages.create(
model="claude-3-sonnet-20240229",
max_tokens=1000,
system="You are a cynical but brilliant Python code reviewer. Roast the user's code mercilessly but provide genuinely better alternatives.",
messages=[
{
"role": "user",
"content": "Here's my function:\n```python\ndef add(a, b):\n return a + b\n```"
},
{
"role": "assistant",
"content": "Wow, groundbreaking. You've managed to replicate the functionality of the '+' operator. I suppose we should be grateful you didn't use a `for` loop. It's... fine. For a kindergartener."
},
{
"role": "user",
"content": "Okay, smartypants, what if I need to add three numbers?"
}
]
)
print(response.content[0].text)
Notice how the array contains the entire history: the first user message, Claude’s assistant response, and the latest user follow-up. This allows Claude to understand the snarky context of the second question. If you omit the middle assistant turn, Claude would have no idea what “smartypants” is referring to and would likely respond as if it’s the first message.
Common Pitfalls and How to Avoid Them
- The Never-Ending Context Bleed: Every message you add consumes precious context tokens. After a long conversation, Claude might literally “forget” your original system prompt because it’s been pushed out of the context window. Best practice: if the conversation gets long, periodically re-assert critical instructions from the system prompt in a
usermessage, or use the API to summarize the conversation and start a new one. - Misattributing Roles: Putting a user’s question in the
systemrole is a fatal error. The system prompt will be ignored as conversational context, and your user question will be treated as a directive, leading to bizarre, meta responses. Similarly, putting your instructions in anassistantrole will confuse Claude into thinking it said those things itself. Double-check your role assignments. - Ignoring the Assistant’s Past Turns: When you provide the conversation history, you must include the previous
assistantresponses. If you only provide theuserturns, you’re essentially presenting a disjointed monologue to Claude, and its responses will be incoherent. The API requires you to manage the state of the entire conversation, not just the user’s side. This is your job, not the API’s.