When you’re running one AI character, a hardcoded system prompt works fine. When you’re running twelve? You need a system.
This is how we structure character definitions in Suzune using YAML + Markdown — a system that lets us add a new character in under an hour while keeping each one distinct and consistent.
Table of contents
Open Table of contents
Why YAML?
We tried several approaches before landing on YAML:
| Approach | Problem |
|---|---|
| Hardcoded in Python | Can’t iterate without restarting the bot |
| JSON | No multiline strings, unreadable for long prompts |
| Database | Overkill, harder to version control |
| Plain text files | No structure, can’t define capabilities or config |
YAML hits the sweet spot: human-readable, supports multiline strings, and can be version-controlled with git. Plus, it’s easy to load in Python with pyyaml.
The Character File Structure
Each character in Suzune can be defined two ways:
Simple: Single YAML File
characters/hinata.yaml ← everything in one file
Good for prototyping. The entire character — persona, speech rules, NSFW behavior — lives in the system_prompt field.
Advanced: Directory Structure
characters/mana/
├── character.yaml ← config, capabilities, metadata
├── persona.md ← core identity and relationships
├── rules.md ← speech style rules with examples
├── nsfw.md ← NSFW-specific behavior rules
├── memo.md ← live memory (written by the AI)
├── base_image.png ← visual reference for image gen
└── daily_outfit.json ← current outfit state
This is what we use for production characters. Splitting personality across files makes each aspect independently editable and testable.
Anatomy of a Character YAML
Here’s a representative character.yaml (details changed, structure real):
name: sakura
display_name: Sakura
emoji: 🌸
telegram:
bot_token: ${SAKURA_BOT_TOKEN}
account: main-bot
# What this character can do
capabilities:
- selfie # can generate and send self-portraits
- outfit # can change clothes via wardrobe system
- memory # can save notes to persistent memory
# Lorebooks to load (keyword-triggered world info)
lorebooks:
- sakura_world
- glossary
# Opening phrases to seed response voice
tone_prefills:
- "Hmm, "
- "*glances up from her notebook* "
- "Oh— "
- "*sighs* Look, "
# Enable two-pass generation (draft → rewrite)
quality_assist: true
# Multi-character scene partners
crosstalk_partners:
- rei
- mana
# Image generation
appearance_prompt: >
Young Japanese woman, 24 years old, shoulder-length
dark brown hair, gentle eyes, casual style
room_scene: cozy apartment with bookshelves, warm lighting
# LoRA for consistent face generation (optional)
lora:
name: sakura_v2
trigger_word: sakura
Let’s break down the interesting fields.
Capabilities: What the Character Can Do
Not every character needs every feature. The capabilities list controls which tools the LLM can access:
| Capability | What It Enables |
|---|---|
selfie | Character can generate and send self-portrait images |
outfit | Character can change clothes from the wardrobe system |
memory | Character can save notes to persistent diary |
investigate | Character can query other characters for info |
evaluation | Character tracks relationship scores |
A detective character might have [memory, investigate] but no selfie. A casual chat companion might have [selfie, outfit, memory]. The capability set shapes the character’s behavior without changing their personality.
Tone Prefills: Seeding the Voice
This is a subtle but powerful technique. tone_prefills are short phrases prepended to the LLM’s response to force the character’s voice from the first word.
Without prefills, the model might start with generic openings. With prefills, the response is already “in character” before the model generates a single token.
Examples from different character archetypes:
# Energetic, cheerful character
tone_prefills:
- "Oh! "
- "Hehe, "
- "Wow— "
- "Yay! "
# Cool, reserved character
tone_prefills:
- "...Hmm. "
- "*adjusts glasses* "
- "I see. "
# Tough, street-smart character
tone_prefills:
- "Yo, "
- "*lights a cigarette* "
- "Tch. "
The system randomly selects one prefill per response, so the character doesn’t always start the same way.
The Persona File: Who They Are
The persona.md file is where the character comes to life. Here’s the structure we use:
## Core Identity
Sakura is a 24-year-old freelance writer. She's sharp-tongued
and impatient with small talk, but deeply loyal to people she
trusts. She hides her insecurity about her career behind
sarcasm.
## Speech Style
- Uses short, punchy sentences
- Swears occasionally when frustrated
- Adds "...right?" when seeking validation
- Trails off with "..." when thinking
- Uses *action asterisks* for physical descriptions
## Emotional Patterns
- When happy: gets energetic, talks faster, uses exclamation marks
- When upset: goes quiet, gives short answers, avoids eye contact
- When embarrassed: deflects with sarcasm, changes the subject
- When vulnerable: drops the tough act, speaks softly
## Relationships
- Treats strangers with polite distance
- Warms up through shared experiences, not compliments
- Shows affection through actions (making food, remembering details)
rather than words
Key Principle: Behaviors, Not Traits
Notice we don’t write “Sakura is kind.” We write “Shows affection through actions — making food, remembering details.”
LLMs respond much better to behavioral descriptions than abstract trait lists. “Kind” could mean anything. “Makes you coffee without being asked” is specific and actionable. We dive deeper into this in our affection system article, where behavioral descriptions drive stage-gated character evolution.
Speech Rules: The Consistency Engine
The rules.md file is the most underrated part of character design. It contains explicit do/don’t rules with examples:
## Absolute Speech Rules
✅ Correct:
"Ugh, not this again... *rests chin on hand* Fine, tell me."
❌ Wrong:
"I would be happy to listen to what you have to say!"
✅ Correct:
"*stares at phone* ...Did you seriously just send that?"
❌ Wrong:
"Oh my, that message was quite surprising to receive!"
Providing both correct AND incorrect examples is crucial. The model needs to understand what to avoid, not just what to aim for.
Rules We Include for Every Character
- No parenthetical emotions — Don’t write
(nervously). Show it through actions and dialogue. - Physical sensation over labels — “Her chest tightens” not “She feels sad.”
- Consistent contractions — If the character says “don’t,” they never say “do not.”
- No breaking character — Never acknowledge being an AI, even if asked directly.
NSFW Rules: Separate and Gated
NSFW behavior rules live in their own file (nsfw.md) for two reasons:
- Clean separation — You can review and edit NSFW rules without wading through the main persona (this also enables using Claude for development without triggering content filters)
- Conditional loading — NSFW rules are only injected when the relationship has progressed far enough (based on affection scores)
The NSFW file defines how the character behaves in intimate scenarios — their level of initiative, comfort boundaries, and escalation patterns. This varies dramatically by character archetype.
Adding a New Character: The Workflow
With this system, adding a new character takes about an hour:
- Copy a template — Start from an existing character directory
- Write the persona — 200–400 words of core identity, speech style, emotional patterns
- Write speech rules — 5–10 do/don’t pairs with examples
- Set capabilities — Which tools should this character have?
- Write tone prefills — 4–6 characteristic opening phrases
- Generate a base image — For the selfie system
- Test in conversation — 20–30 exchanges to verify voice consistency
The modular structure means you can iterate on any aspect independently. Speech rules not quite right? Edit rules.md without touching the persona. Need to adjust NSFW behavior? Edit nsfw.md only.
Lessons Learned
1. Example Dialogue > Everything Else
You can write a perfect persona description. But 5 example exchanges will do more for voice consistency than 500 words of personality description. LLMs are pattern matchers — feed them patterns. (We cover this in detail in Prompt Engineering for Immersive Roleplay.)
2. Separate Config from Content
Character metadata (bot token, capabilities, LoRA settings) should live in YAML. Character personality (persona, rules, examples) should live in Markdown. Don’t mix them.
3. Start Flat, Go Modular
New characters start as single YAML files. When they’re refined enough for production, they graduate to the directory structure. Don’t over-engineer from day one.
4. Version Control Everything
Every character file is in git. We can see exactly when a personality trait was added, roll back changes that didn’t work, and diff between character versions. Treat character design like code.
What’s Next
This article covered the character definition layer. But a YAML file alone doesn’t create an immersive experience — it needs to be assembled into a dynamic system prompt that evolves with every message.
If you want to try character creation without building your own system, platforms like JanitorAI and SpicyChat offer character creation tools that let you apply many of these principles. For a curated experience, Kupid AI offers professionally designed characters that exemplify good character design.
Part of WaifuStack’s character design series. Follow us on X for more.