What are system, user, and assistant roles in AI chat?

Chat APIs use three message roles: system sets the AI's behaviour and constraints, user contains the human's messages, and assistant contains the AI's previous replies. Together they form the conversation history.

AILLM

Overview

Modern LLM APIs model conversations as an ordered list of messages, each tagged with a role. This structure lets the model understand who said what, follow ongoing instructions, and maintain multi-turn context. Getting the roles right is fundamental to building reliable chat applications.

Key Points

  • •system: sets the AI's persona, capabilities, tone, and hard constraints. Processed before any user turn.
  • •user: the human's input - questions, commands, data to process, or conversation history.
  • •assistant: the AI's previous replies. Include past turns to give the model memory of what it already said.
  • •Only the system role is optional - some providers treat the first user message as the implicit system context.
  • •You can inject data directly into the user or system role - it does not have to come from a real human.
  • •OpenAI also supports a tool role for returning function call results back to the model.

Examples

Basic multi-turn conversation structure

typescript
const messages = [
  // 1. System role: sets behaviour for the whole session
  {
    role: 'system' as const,
    content: `You are a helpful TypeScript tutor.
Explain concepts clearly with short code examples.
Keep answers under 150 words unless the user asks for more detail.`,
  },

  // 2. First user turn
  { role: 'user' as const, content: 'What is a generic type?' },

  // 3. Assistant reply (from a previous API call - stored in your app)
  {
    role: 'assistant' as const,
    content: 'A generic type is a placeholder that gets filled in at use time...',
  },

  // 4. Follow-up question in the same session
  { role: 'user' as const, content: 'Can you show me a real example with arrays?' },
];

const response = await client.chat.completions.create({
  model: 'gpt-4o',
  messages,
});

Injecting data into the system prompt

typescript
// The system role is a great place to inject dynamic context
async function answerWithUserContext(userId: string, question: string) {
  const user = await db.users.findById(userId);
  const subscription = await db.subscriptions.findByUserId(userId);

  return client.chat.completions.create({
    model: 'gpt-4o',
    messages: [
      {
        role: 'system',
        content: `You are a support agent for Acme SaaS.

Current user context:
- Name: ${user.name}
- Plan: ${subscription.plan}  (Pro features: ${subscription.plan === 'pro' ? 'enabled' : 'disabled'})
- Account created: ${user.createdAt.toDateString()}

Answer questions about their account. Do not guess about features they do not have access to.`,
      },
      { role: 'user', content: question },
    ],
  });
}

Maintaining conversation history in a chat app

typescript
type Message = { role: 'system' | 'user' | 'assistant'; content: string };

class ChatSession {
  private history: Message[] = [];

  constructor(systemPrompt: string) {
    this.history.push({ role: 'system', content: systemPrompt });
  }

  async send(userMessage: string): Promise<string> {
    this.history.push({ role: 'user', content: userMessage });

    const response = await client.chat.completions.create({
      model: 'gpt-4o',
      messages: this.history,
    });

    const reply = response.choices[0].message.content ?? '';
    this.history.push({ role: 'assistant', content: reply });
    return reply;
  }
}

const chat = new ChatSession('You are a helpful cooking assistant.');
await chat.send('What can I make with eggs and cheese?');
await chat.send('How long does that take?'); // model remembers the previous answer

Pro Tip

Put all hard constraints (things the model must never do) in the system prompt, not in the user message. A user message saying "be concise" is a polite request; a system prompt saying "always respond in under 3 sentences" is a hard instruction the model follows more reliably.