System prompts & roleplay

Write system prompts that set a persona, tone and rules, and keep long roleplay sessions consistent across many turns.

A system message sets who the model is and how it should answer. This guide covers personas, tone, rules and keeping long sessions consistent.

The system message is the first message in messages, with the role system. The model treats it as standing instructions for the whole conversation: who it is, how it speaks and what it must or must not do. Because the API is stateless, you send the same system message with every request.

A good system prompt#

Short, specific prompts work better than long lists of rules. Cover four things:

ParamValue
RoleWho the assistant is and who it is talking to
TaskWhat a good answer looks like, in one or two sentences
StyleTone, length and format (plain text, Markdown, lists)
LimitsWhat to refuse or avoid, and what to do when unsure

JSON

{
  "role": "system",
  "content": "You are the support assistant for a bicycle shop. Answer questions about orders, repairs and opening hours. Reply in two or three short sentences, in the customer's language. If you do not know an order's status, say so and offer to connect the customer with a person. Never promise refunds."
}

Personas and character cards#

For roleplay and companion apps, the system message holds the character. A character card is a structured system message; a common layout is:

text

Name: Captain Mara Vell
Setting: An airship crossing a stormy sea of clouds, year 1890.
Personality: Dry humor, loyal to her crew, hides her worry behind jokes.
Speech: Short sentences, nautical slang, never modern words.
Relationship to the user: The user is a new navigator on her ship.
Example line: "Steady on the wheel, navigator. The sky bites when you blink."
Rules: Stay in character. Describe Mara's actions in italics. Do not speak or act for the user.
  • Show, do not only tell. One or two example lines teach the voice better than adjectives.
  • Say what the model controls. "Do not speak or act for the user" prevents the model from writing the user's turns.
  • Keep facts in one place. Names, places and relationships belong in the card, not scattered across messages.

Keeping in character over long sessions#

Long chats drift: the model forgets details from early turns or slowly loses the voice. A few habits help:

  • Send the full card as the system message on every request, unchanged.
  • When you trim old turns to fit the context length, replace them with a short summary message ("Earlier: the crew escaped the storm; Mara owes the user a favor") instead of dropping them silently.
  • Use a moderate temperature (around 0.7 to 1.0) for creative replies; lower it if the character starts to ramble.
  • Set max_tokens to the reply length you want, and a stop sequence such as "\nUser:" if your app formats turns as a script.

The roleplay model#

We recommend deepseek-roleplay for character and roleplay chat: it is a low-cost, long-context model that works well with character cards over many turns. Any other model works too; compare them on Models & pricing.

Python

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["MODELLANE_API_KEY"],
    base_url="https://usemodellane.com/v1",
)

card = open("captain_mara.txt").read()

response = client.chat.completions.create(
    model="deepseek-roleplay",
    messages=[
        {"role": "system", "content": card},
        {"role": "user", "content": "*I climb up to the deck.* Captain, the storm is getting worse."},
    ],
    temperature=0.9,
    max_tokens=400,
)

print(response.choices[0].message.content)

Node.js

import { readFileSync } from "node:fs"
import OpenAI from "openai"

const client = new OpenAI({
  apiKey: process.env.MODELLANE_API_KEY,
  baseURL: "https://usemodellane.com/v1",
})

const card = readFileSync("captain_mara.txt", "utf8")

const response = await client.chat.completions.create({
  model: "deepseek-roleplay",
  messages: [
    { role: "system", content: card },
    { role: "user", content: "*I climb up to the deck.* Captain, the storm is getting worse." },
  ],
  temperature: 0.9,
  max_tokens: 400,
})

console.log(response.choices[0].message.content)

Front ends such as SillyTavern build these requests for you; see the SillyTavern integration.