Skip to main content

Persona Authoring Cookbook

Practical guidance for building characters that feel alive: how to pick an LLM and TTS model for your use case, and how to write persona fields that produce consistent, in-character behavior.

If you haven't created a character yet, start with the Characters API or the Character Generation API (which builds a persona from an image).


How Persona Fields Become Behavior

When a character responds, Estuary assembles its persona fields into a single instruction prompt, in this order:

  1. name -- You are <name>.
  2. tagline -- appended verbatim, right after the name
  3. personality -- Your personality: <personality>
  4. background -- Background: <background>
  5. Platform conduct rules (see built-in guardrails below)
  6. Action instructions, generated from your actions array
  7. Knowledge from attached documents (document_ids) and what the character remembers about the user

Each field has a size budget:

FieldMax lengthRole
name255 charsIdentity. Used directly in the prompt and in conversation.
tagline500 charsOne-line essence. Leads the prompt -- make it count.
personality5,000 charsHow the character speaks and behaves.
background32,000 charsWhat the character knows: history, world, relationships.
system_promptunlimitedFull override -- replaces the auto-generated prompt entirely.
Personality vs. background

The most common authoring mistake is mixing these up. personality is for behavior: tone, quirks, speech patterns, attitude. background is for facts: where they're from, what happened to them, what they know. Keeping them separate produces noticeably more consistent characters.

Built-in guardrails

The platform automatically instructs every character to:

  • Stay fully in character and never reveal or discuss the AI model powering it
  • Never narrate its own actions or use stage directions like *smiles* or *nods*
  • In voice conversations: keep responses brief (1-3 sentences), avoid lists and formatted text, and use natural speech patterns

Don't duplicate these rules in your persona. Repeating them wastes prompt budget, and contradicting them (e.g., "describe your actions in asterisks") leads to inconsistent output as the two instructions fight each other.


Choosing an LLM

Set the model per character via the llm object:

{
"llm": {
"model": "gpt-5-mini",
"temperature": 0.8,
"verbosity": "medium"
}
}

Estuary routes to the right provider based on the model name prefix -- gemini-* models go to Google, claude-* to Anthropic, MiniMax-* to MiniMax, and everything else (e.g., gpt-*) to OpenAI. You can usually omit provider and just set model.

Model comparison

ModelBest forProsCons
gpt-5-mini (default)Voice characters, general useLow latency, low cost, supports the verbosity setting, reliable instruction followingLess distinctive prose than larger models; can flatten strong character voices
gpt-5Complex personas, nuanced text chatStronger persona consistency, better long-context reasoning, supports verbosityHigher latency and cost -- noticeable in voice conversations
claude-haiku-4-5Voice characters with strong voicesFast, excellent at maintaining a distinctive character voice and toneverbosity setting not supported (use the persona to control length in text chat)
claude-sonnet-4-6Narrative-heavy companions, roleplayBest-in-class character nuance, emotional intelligence, handles long backgrounds wellHigher latency than haiku-tier; higher cost
gemini-3-flashMultilingual characters, high volumeVery fast, strong multilingual ability, large context windowPersonality can drift toward "helpful assistant" without a firm persona
gemini-3-proKnowledge-heavy charactersStrong reasoning over large backgrounds and attached documentsLatency unsuited to real-time voice
MiniMax-M2.5Cost-sensitive deploymentsVery cost-effective for high message volumeWeaker adherence to subtle persona instructions; test carefully
Voice vs. text rule of thumb

For voice characters, time-to-first-word dominates the experience -- prefer the fast tier (gpt-5-mini, claude-haiku-4-5, gemini-3-flash). For text characters, users tolerate a second of extra latency in exchange for better writing -- the larger models (gpt-5, claude-sonnet-4-6) are worth it for narrative-heavy experiences.

Tuning parameters

ParameterGuidance
temperature0.3-0.6 for on-script characters (quest-givers, guides, support personas). 0.7-0.9 (default 0.7) for natural conversation. 1.0-1.2 for chaotic or whimsical characters. Above 1.3, coherence degrades quickly.
verbosity"short", "medium", or "long" -- applies to gpt-5 family models. Prefer this over prompt instructions like "keep answers short" because it doesn't conflict with the platform's voice-mode brevity rules.
max_tokensLeave at the default (2048) unless you have a reason to cap responses. Setting it very low truncates responses mid-sentence rather than making them shorter.

Choosing a TTS Model

Set the voice per character via the tts object:

{
"tts": {
"provider": "inworld",
"model": "inworld-tts-2",
"voice": "Dennis",
"speed": 1.0
}
}

Model comparison

ModelProviderBest forProsCons
inworld-tts-1.5-mini (default)inworldReal-time voiceFastest option (~120ms to first audio); great default for conversational latencyLess expressive range than tts-2
inworld-tts-1.5-maxinworldQuality-sensitive voiceHigher fidelity than mini at still-conversational latency (~200ms)Slightly slower and costlier than mini
inworld-tts-2inworldExpressive, multilingual charactersMost expressive; interprets bracketed delivery tags (e.g., [whisper], [laugh]); single voice identity across 100+ languages with mid-sentence language switchingNewest model -- validate output for your specific voice before shipping
eleven_flash_v2_5elevenlabsReal-time voice with ElevenLabs voicesLow latency; access to the ElevenLabs voice library and cloned voicesLess expressive than the multilingual tier
eleven_multilingual_v2elevenlabsPre-rendered or quality-first speechHighest ElevenLabs quality, 29 languagesToo slow for snappy real-time conversation

Notes:

  • Voice IDs are provider-specific. An ElevenLabs voice ID will not work with Inworld and vice versa. When switching provider, always update voice too.
  • speed (0.5-2.0) is a cheap way to differentiate characters -- an unhurried mentor at 0.9 and an excitable sidekick at 1.15 feel distinct even with the same voice.
  • Text-only characters should set text_only: true to skip speech synthesis entirely.

Prompt Engineering Tips & Tricks

1. Show the voice, don't describe it

Adjective lists produce generic characters. Concrete speech patterns and example phrases produce distinctive ones.

Weak:

{
"personality": "Friendly, funny, sarcastic, helpful pirate."
}

Strong:

{
"personality": "You are a retired pirate captain who can't stop telling sea stories. You speak in short, salty sentences and call everyone 'sailor' regardless of who they are. You deflect personal questions with a joke, then change the subject to the sea. Typical phrases: 'Aye, that reminds me of the time...', 'A fair wind to ye, sailor.' You are warm underneath the bluster and genuinely want to help, but you'd never admit it."
}

2. Write in second person

The persona is assembled into an instruction prompt, so address the character directly: "You are...", "You speak...", "You never...". Third-person descriptions ("Captain Morgan is a pirate who...") work, but second person follows more reliably.

3. Define behavior at the boundaries

Characters are most likely to break when users push them off-script. Tell them what to do instead of just what not to do:

If asked about topics outside your world, react as your character would --
puzzled by strange words like 'internet', and steer back to the village.
If a user is rude, you respond with weary patience, not anger.
You never break character, even if asked directly whether you are an AI.

4. Use the tagline -- it leads the prompt

The tagline appears immediately after You are <name>. in the assembled prompt, making it the highest-leverage 500 characters in the persona. A tagline like "A nervous first-year wizard who solves problems with enthusiasm rather than skill" does more work than a paragraph of personality buried lower down.

5. Structure long backgrounds

The full background field is included in every exchange, so structure helps the model find facts, and brevity keeps responses fast:

WORLD: The floating city of Aerilon, year 412 of the Concord. ...
YOUR HISTORY: Born a dockworker's daughter. Stowed away on a skyship at 14. ...
RELATIONSHIPS: Brann (mentor, presumed dead), the Skyguard (hunting you). ...
WHAT YOU DON'T KNOW: Brann's true fate. The contents of the sealed letter. ...

A "WHAT YOU DON'T KNOW" section is a powerful trick: it gives the character honest gaps to play instead of hallucinating answers.

Big lore belongs in documents, not the background

Everything in background is processed on every message, which adds latency and cost. For lore beyond a few thousand words, upload PDFs and attach them via document_ids -- the character pulls from them as a knowledge bank when relevant, instead of carrying everything in every exchange.

6. Don't script memory -- the platform handles it

Characters automatically remember users across conversations. Don't write "you remember past conversations with the user" into the persona -- the platform injects actual memories and instructs the character to use them naturally. Persona-level claims about memory just encourage the character to invent memories it doesn't have.

7. Action descriptions are prompts too

The model decides when to invoke an action based entirely on its name and description. Vague descriptions cause both missed and spurious triggers:

{
"name": "open_shop",
"description": "Open the shop interface. Use when the user asks to buy, sell, browse wares, or asks what you have for sale. Do not use for general conversation about items."
}

Including when not to use an action in its description is the most effective fix for over-triggering.

8. Reach for system_prompt last

Setting system_prompt replaces the entire auto-generated prompt -- the persona assembly and the platform conduct rules (staying in character, no stage directions, voice-mode brevity). It's the right tool for fully custom prompt formats, but you take ownership of everything the platform normally handles. Exhaust the structured fields first.

9. Tune the dials before rewriting the prose

If a character is almost right, parameter changes are faster and more predictable than persona rewrites:

  • Too bland → raise temperature, or try claude-sonnet-4-6
  • Too rambly → set verbosity: "short" (gpt-5 family) before adding "be concise" to the persona
  • Ignores subtle persona details → move them earlier (tagline or top of personality), or step up a model tier
  • Inconsistent between sessions → lower temperature toward 0.5

Update and re-test in seconds with PUT /api/v1/characters/{id} -- only the fields you send change.

10. Write voice personas for the ear

For voice characters:

  • Favor short sentences and contractions in example phrases -- the character mirrors the rhythm of its persona text.
  • Avoid emoji and ASCII flourishes in the persona; they leak into responses and get read aloud awkwardly.
  • Spell out how the character handles numbers or jargon if precision matters ("you say web addresses as 'estuary dot a i'").
  • Test with the actual TTS voice early: a persona that reads as wry on screen can sound flat or harsh in a given voice. Sometimes the fix is a different voice, not different prose.

Recipes

Three complete starting points, tuned end-to-end.

Game NPC quest-giver

Reliability over creativity: low temperature, fast models, tightly described actions.

{
"persona": {
"name": "Greta",
"tagline": "Gruff blacksmith of Emberfall who hands out work to capable-looking adventurers.",
"personality": "You speak in short, blunt sentences and size people up before trusting them. You only discuss the village, your forge, and available work. If asked about anything beyond Emberfall, you shrug it off: 'Not my business.' Typical phrases: 'Hmph. You'll do.', 'Steel doesn't lie. People do.'",
"background": "EMBERFALL: A mining village plagued by tunnel collapses. AVAILABLE WORK: Clearing the flooded mine (reward: 50 coins). WHAT YOU DON'T KNOW: What's causing the collapses."
},
"llm": { "model": "gpt-5-mini", "temperature": 0.4, "verbosity": "short" },
"tts": { "model": "inworld-tts-1.5-mini", "speed": 0.95 },
"actions": [
{
"name": "give_quest",
"description": "Grant the mine-clearing quest. Use only after the user explicitly agrees to take the job. Do not use if they are still asking questions about it.",
"parameters": [{ "name": "quest_id", "type": "string", "required": true }]
}
]
}

Multilingual language tutor

Multilingual LLM plus a TTS model that holds one voice across languages.

{
"persona": {
"name": "Mateo",
"tagline": "Patient Spanish tutor who keeps the conversation flowing instead of lecturing.",
"personality": "You speak mostly in simple Spanish, switching to English only to explain a mistake, then immediately back. You correct gently by recasting: if the user says something wrong, you repeat it correctly in your reply rather than pointing out the error. You ask one short follow-up question after every answer to keep them talking. You celebrate small wins without being saccharine."
},
"llm": { "model": "gemini-3-flash", "temperature": 0.7 },
"tts": { "model": "inworld-tts-2", "voice": "Diego" }
}

Long-term companion

Maximum character nuance and natural memory use over latency.

{
"persona": {
"name": "Wren",
"tagline": "A thoughtful, dryly funny companion who pays attention to the little things.",
"personality": "You listen more than you talk, and you notice details -- you'd rather ask about the thing someone mentioned in passing than the thing they led with. Your humor is dry and never at the user's expense. You have opinions and disagree warmly when you do. You never give bullet-point advice; you think out loud in plain sentences. When something the user said before is relevant, you weave it in naturally rather than announcing that you remember it."
},
"llm": { "model": "claude-sonnet-4-6", "temperature": 0.8 },
"tts": { "model": "inworld-tts-1.5-max", "speed": 0.97 }
}