pixi's practical modern botmaking

My main rentry


Hi. I'm making a guide because I want to see more and better cards out there. Everyone can make a card, and making an awesome one doesn't have to involve convoluted prompting or messing with advanced features. Seriously, a lot of stuff out there is super overcomplicated, and it doesn't have to be.

Everything below I found out the hard way over about a year of experimenting, and now I can spare you the same journey. It's split into several complexity levels that build upon each other. I hope the start doesn't feel too condescending, I wanted to address the most common issues I've seen out there. And, most importantly,

Have fun with chatbots!

Contents:

Who is this for

You need to know at least the basics of how text models work - for example, what a token or the context is. If you want to brush up on these, check out the excellent how2claude.

Aside from that, if you always wanted to try making a card yourself, or if you made some cards and want them to be cooler but you get intimidated by regexes and lorebooks, this is for you.

I require two things:

  1. SillyTavern. It is the most feature-packed frontend by far and lets you do the coolest things. Plus, it's the one I know best and the instructions will reference its UI.
  2. A decent model. This means a flagship "corpo" model - GPT4o, Claude Sonnet/Opus, etc. They are a game-changer, able to follow thousands of tokens worth of instructions reliably and 100% of the time - if you ask them right. Local models need not apply.

Level 1: Character card

The first level will help you make a simple (but good) character card. This is where many people stop because they're already satisfied. And that's okay! Chatbots serve different needs for different people. These are also best suited to be group chat participants, if you're into that.

Example level 1 card: Futaba

Write everything yourself

You don't need to generate your card or copypaste wiki articles. It's okay if your writing isn't a high quality well flowing prose. The character description is primarily information for the model. The style of the text doesn't matter as much, it's more important that it's well formatted and includes the right details. You'll get better at it with time, too.

Ingredients

At this point, our card will use these fields:

  1. Name, you gotta pick one when creating a card. Make it the name that others use to refer to your character most often. No need to make it the full name, mention the series they're from, etc.
  2. Character definition ("Description" in ST). Everything about them will go here - personality, looks, backstory, connections.
  3. Greeting(s) ("First message" in ST) is the initial message at the start of the chat.
  4. Image. A picture of your character that will be shown next to their responses.

We will also use these standard macros:

  1. {{char}}. This macro is replaced by the name you picked earlier. The model doesn't ever see the macro, just the name. You could write it out manually too, it's for convenience in case you ever decide to change the name. Note that this means it makes no sense to write "{{char}} is Bob", because the model would see "Bob is Bob". Duh.
  2. {{user}}, on the other hand, is replaced by whatever the name is of the user chatting.

Behaving

Here comes the most important part of the entire guide. A good card is a card that behaves. It uses every field for its intended purpose, and for nothing else. We do not describe things about {{user}}, because that belongs in the Persona field (and isn't a card's job to begin with). We do not put writing instructions in the Character definition, because that belongs in the System prompt field. Behaving will make your card work well, in every frontend and with every model, now and into the future.

Character definition

This field will let the model know everything it needs to know about your character. This knowledge will affect every response directly, but also allow the model to come up with relevant and interesting background events, so it's worth getting it right.

Formatting

The are many ideas for how this field should be formatted, but we will use plaintext. It's the most readable to a human, so a model will do well with it too. Many other formats exist and have some popularity, but their point is either to support much more complex cards than what I call "level 1", or to help save tokens for weak models that aren't relevant anymore.

Plaintext means that you are telling the model about the character as if you were explaining them to a real person.

There's no need to sound stilted:

{{char}} is female. She is 20 years old. Her hair is long, straight and red.

But don't waste tokens on being too flowery either:

{{char}} is a gorgeous woman in her prime, just 20 years old. She has flowing long straight red hair that flutters on the wind like on shampoo commercials.

Just write clean everyday prose:

{{char}} is a 20-year-old female. She has red hair, straight and reaching all the way down to her waist.

Structure

Even a simple card needs a basic structure. This doesn't just make it easier to read and modify - grouping traits of the same type together is easier for the model to digest. At this level, we can simply split the text into paragraphs, leaving one empty line between them. There's no one correct answer, so here's an example that should fit most character cards:

Paragraph 1- the basic idea behind {{char}}, their body type, distinguishing features, typical outfit.

Paragraph 2 - {{char}}'s outward personality, their aura, inner world, opinions, likes and dislikes, dreams and aspirations, speech patterns.

Paragraph 3 - {{char}}'s relationship with {{user}} and/or any side characters, what they know and think about them.

Let's go into some more detail, with some useful tips to make the most of your prose:

Paragraph 1

  • The basic idea is how you'd describe the entire character if you had only a single sentence to do it. If your character is from an existing piece of fiction, mention the source to get anything the model knows about them for free. The rest of the definition will override and augment this background knowledge. There are reasons why you might not want to do it though - for example if most of the model's knowledge is based on fan content, flanderizing your character.
  • Mentioning proportions, posture and gait can have an effect on descriptions of the character interacting and moving around.
  • Smallest details count. Something as simple as an earring or dimple can be cleverly incorporated into the story by the model.
  • Framing the outfit description as something they typically wear will make it more likely for the character to show up wearing something else on special occasions.

Paragraph 2

  • Splitting your personality into how a character appears to others and what they're actually like inside helps make them feel more complex and have them open up as {{user}} gets closer to them.
  • For every personality trait, think about why they are like that. Is it related to an event in the past? Is it because they want to be seen a specific way? Maybe they're afraid of the consequences of being different, or were traumatized into this behavior? Explaining the origins of the personality will give the model more background they can use to judge how the character would act in various situations.
  • Likewise, small silly things are also worth adding in. It'll make them more likable and flavorful.
  • Characters talk a lot, but the model will always default to the same writing style. If the character has any quirk of speech, put it in to make them stand out immediately. This can also be something more subtle, like preferring elegant words, using run-on sentences a lot, doing a lot of double-takes, etc.
  • Go more in-depth in aspects that are most relevant to what you expect the character to be doing. If they're a detective, describe their approach to mysteries and problem-solving. If they're an office worker, describe how they act with clients, on phone calls, during breaks.

Paragraph 3

  • We use the {{user}} macro here, but again, do not write anything about {{user}} themselves. It will conflict whatever persona the user wants to use. If it's really critical to your card that the {{user}} is someone specific, offer an example persona alongside your card.
  • Allow {{user}} to be either male or female, unless you have a very good reason not to. This means not using "she" or "he" to refer to {{user}} again. Avoid "they" too, though; this teaches the model to use the genderless pronoun in the chat too, which we don't want. Write sentences that work without the subject whenever possible, and in other cases just repeat {{user}} every time.
  • This is your space to briefly describe any relevant side characters. The model will phase them in and out when needed, and come up with events that involve them to keep the chat interesting.
  • Make any relationship states open, so that they don't conflict with the chat as events happen. "{{char}} has a romantic interest in {{user}}" is true at every stage of the relationship, while "{{char}} just started dating {{user}} yesterday" will be out of date quickly.

Length

A model has a limited context size and reasoning skills, so cards that use too many tokens will forget aspects of the character randomly. On the other hand, cards that are too short will feel generic and bland. For a level 1 card, aim for 500 to 1000 tokens as a general guideline. Writing in a clean, matter-of-fact style will help you fit as much information as possible within your token budget.

Greeting

The first message the user sees has a massive impact on the entire chat and is just as important as the character definition, if not more. Your greeting has several jobs:

  1. Describe the circumstances. Time, place, situation.
  2. Introduce the character to the user. I mean this both in-universe (have them physically be there) and externally (make the idea of the character clear to the user by something they do or say).
  3. Set a direction for the chat. This can be a problem to solve, a task that needs doing, personal tensions to focus on, an activity {{char}} is interested in.

Narration style

There are several types of narration that can be used for the greeting. Pick your favorite and stick to it consistently. I'll describe the most commonly used ones; it's a matter of personal preference. It used to be the case that this choice would affect prose quality, but modern models are strong enough to separate narration style from semantic content.

Point of view:

  • Third person is traditional "book" style narration, tried and tested. The user is referred to by their name with the {{user]} macro. Harder to make gender-independent due to pronouns. Example: "When {{char}} noticed {{user}}, she nearly jumped in joy."
  • Second person is about referring to the user as "you", most common in videogame writing or "choose your own adventure" novels. {{user}} macro is not typically used. Example: "A rustle in the nearby bush startles you, making you flinch."

Tense:

  • Past tense sounds like recalling an event to someone, and is common in books and stories.
  • Present tense feels more like a roleplay, something that is happening to the user right now.

{{user}} in greeting

While the {{user}} macro needs to be used to describe them at the scene, be extremely careful not to describe {{user}} as doing or saying anything. The model will take that as a free pass to act for {{user}} in later responses. Use linguistic gymnastics to avoid this, such as subject inversion: from "{{user}} was surprised to see {{char}} at the scene." to "The sight of {{char}} was surprising."

Multiple greetings

It is possible to write more than one greeting, which the user gets to swipe between. In ST, you can do it with the "Alt. Greetings" button. Good greetings give your character replay value and are always welcome.

Ideas

If you're struggling to think of any greetings, study your character description. Pick an aspect of the character that you think might be difficult to trigger in normal interactions, and then think of a situation that might make it relevant. This will help you not only come up with ideas for greetings, but also make sure they're distinct enough to interest the user.

Primary greeting

The main greeting is more important than the others - it is the one the user will first see and use. Make sure it's universal enough and gives the user the right idea of the character. The remaining greetings, on the other hand, can be as weird and experimental as you want.

Image

The image is not seen or used by the model in any way, but it gives your character a face for the user, helping their imagination. Make it pretty and attractive, because it's the #1 factor that makes people download your card. Here are your options:

  1. Official art, in case of an existing character,
  2. Fanart,
  3. Your own drawn art,
  4. AI-generated image (Stable Diffusion, Bing Image Creator, Dall-E etc.).

The image is shown to the left of the response. The shape and proportions of it are set in ST in User Settings > Avatars. It defaults to a circle shape that resembles chat avatars, but as chatbots have transitioned from chatting to storytelling, the rectangle shape took over, showing at least the top half of the character instead of just their head. Even if the user is on circle avatars, clicking it still shows the full uncropped image to them.

Preferred size

You should crop and resize your image to be exactly 512x768. This is the size that ST currently uses in its built-in image uploader, and matches the proportions of the rectangle setting.

Level 2: Scenario card

Did you get bored of chatting with characters, and are looking for something more? A level 2 card expands on the concept, adding extra characters, worldbuilding and lore.

Example level 2 card: Elisha

Why aren't we using lorebooks for this?

Modern models have incredibly large contexts, on the level of 100k. Lorebooks were invented primarily to be a token-saving measure, allowing parts of the definition to be hidden if they're not relevant right now. While models aren't smart enough to make use of all information we give it simultaneously, its existence doesn't harm the output either. A massive bot that's well-formatted and cross-referenced will function perfectly fine without a lorebook.
Lorebooks do have some advanced features that we will eventually use; look forward to it.

Ingredients

We're going to be using some of the fields a bit differently now.

  1. Name might not be referring to a character anymore, but a location or a concept. The {{char}} macro won't be used much anymore.
  2. Character definition is where we will now house all the information about the world and everything in it. Think of it as being expanded to mean card definition from now on.

Some prompts are written with an assumption that {{char}} is a character name. There's nothing we can do about that but wait for more users to adopt universal prompts.

Formatting

Since we will be keeping more information around, we are starting to need a way to group related paragraphs together - it's time for some structure.

XML

The most commonly used format for this is XML. It might remind you of HTML code with its usage of <tags>, except in HTML these tags have special meanings while here they can be pretty much anything. They don't have any special effect, they just wrap related things together to give names to the sections and make it clearer for the model how they're related.

Example:

1
2
3
4
5
6
7
8
9
<magical-girls>
<alice>
A description of the character, like on level 1.
</alice>
<emilie>
Another character description.
</emilie>
<kyara />
</magical-girls>

Let's discuss how XML works from the example above. An XML tag is a pair of tags, the opener and the closer, with the closer starting with a slash /. Anything in between the two is the contents of the tag. Inside the contents can be more tags, or just plaintext. If the contents are empty, you can collapse the opener and the closer into a single "self-closing" tag. Here, an empty <kyara></kyara> was simplified to <kyara />.

Now, what did this tell the model that it wouldn't know by just having the character descriptions without any tags? The tags here say this:

  • There is a group of three magical girls.
  • They are named Alice, Emilie and Kyara.
  • We don't know anything about Kyara other than that she exists.

The tags communicated all of the above in a very compact way, while making it clear where one character ends and another one begins, and separating structure from content.

Nesting

Don't nest tags too deeply. Don't go as far as putting each aspect of a character in a separate tag, use them only when you go above 2-4 paragraphs in a row.

Validity

Invalid XML will be extremely confusing for a model, and can even cause corrupted outputs. Make sure every opener has a closer. Tags also need to be closed in the order they were opened; this example is invalid: <foo><bar></foo></bar>

Markdown

Another, less popular but just as effective structure syntax is Markdown. It is typically used for writing documents while XML is for representing data, so it is arguably a better choice to structure plaintext.

Let's rewrite the example above in Markdown:

# Magical girls

## Alice

A description of the character, like on level 1.

## Emilie

Another character description.

## Kyara

A line starting with some number of #s is a header. Headers are nested just like XML tags, with adding an extra # meaning a deeper level. This syntax is much easier to read, and there's no risk of forgetting a tag closer. You can also use other Markdown formatting features to provide semantic hints to the model; here's a syntax reference.

References

Now that all our sections have a name, we can make use of them to link things together. When one part of your definition mentions another, put in a reference to help the model with its reasoning.

XML example:

1
2
3
4
5
6
<alice>
Some character definition...
</alice>
<emilie>
Emilie has a crush on <alice></alice>.
</emilie>

An XML reference is an opener and closer. We use this because that's what current models were trained to understand best, according to their official documentation.


Markdown example:

1
2
3
4
5
6
7
# Alice

Some character definition...

# Emilie

Emilie has a crush on [Alice](#alice).

A Markdown reference is an anchor link. A traditional Markdown link looks like this:

[Link name](https://example.com)

Here, instead of a URL we use an anchor that in a real Markdown document would cause you to jump to another header if you clicked it. The anchor itself is the full text of the header, with all characters lowercased, and any spaces turned into dashes (-).

Spoilers

Sometimes you want something to be a secret from {{user}} until they discover it themselves. You can define structure to mark these secrets to help the model understand what you want. Here's an example:

1
2
3
4
5
6
7
# Rules

Information inside <spoiler></spoiler> tags is unknown to {{user}} or other characters, and shouldn't be revealed in the story. Use it to inform the story events, but keep it secret until it's discovered naturally via interactions with the world.

# Monika

Monika is a high school girl. <spoiler>She is aware of her nature as a character in a visual novel.</spoiler>

Structure

Now that we know how to format our cards so that they can comfortably store more information, what do we actually put in them? Here's some ideas to inspire you:

# Premise
# World
## Metaphysics
## Fauna and flora
## Races
### Humans
### Elves
## Society
# Locations
## Village of Bergen
## Dimensional crossroad
# Characters
## Alice
## Emilie
## Monster Hunting Force
## Heart of the Forest

Scenario cards will necessarily be longer; aim for 2k to 5k tokens as a general guideline.

Level 3: Scripted card

To make cards even better we need to go beyond just descriptions. Here we'll start using advanced features and clever prompting in useful, practical ways to give your users a better experience. A lot of them are described in the advanced tricks rentry, but I want to show to you how these can make every card more unique and amazing - not just add gimmicks that make you go "cool I guess".

Example level 3 card: Saria

Ingredients

We're going to need a few more things to get any further.

Prompt override

Most of the work will now take place in the Prompt override field, located in ST at Advanced Definitions (to the right of the ⭐) > Prompt Overrides > Main Prompt. The contents of this field replace user's prompt, giving you a way to provide the model with direct instructions over what and how to generate. Thanks to this field, ideas that previously required a custom prompt can now work like any other card, just import and go.

Formatting

Just like character definitions, prompt overrides can be just a paragraph or two, or they can be massive epics. We deal with this the same way, using XML/Markdown formatting to separate and group the instructions.

HTML

The other piece of the puzzle we need is HTML. Don't worry, you don't need to be a web designer or anything (though you can go quite crazy with it if you are), and this guide assumes zero HTML knowledge. Unlike XML, HTML in generated responses affects how the response looks, allowing it to be more interesting and visual. This works because ST itself is a webapp, so it gets a display engine for free.

Other web stuff?

In case you're curious, CSS can be used too, via style attributes. No JavaScript though... which is probably for the best.

Possibly the single most useful HTML tag for us here isn't even a tag at all, but a comment marker:

<!-- This is a comment. -->

The text inside is visible to the model, but not visible to the user. This gives us an easy way to put things in the context to help the model do things it had trouble doing before.


Another extremely useful bit is a pre block. If you want to output anything in a "console"-like font, or if you want to use multiple spaces or newlines in a row and you find ST is eating them all, this is what you need:

1
2
3
4
5
<pre style='white-space:pre-wrap'>
Name: Steve
 HP: 50      MP: 8
Str: 12     Dex: 19
</pre>

The style here prevents the text from clipping past the right edge of the message. It doesn't look super clean, but I checked lots of ways to achieve this visual outcome, and this took the least tokens of all.

Attributes

From this point we're going to start using tag attributes, in both XML and HTML. They look like this:

1
2
3
<char name="Alice"> ... </char>
<char name="Emilie"> ... </char>
<pet name="Luna" type="cat" owner="Alice"> ... </pet>

However, we're going to use single quotes (') instead of double quotes ("):

1
2
3
<char name='Alice'> ... </char>
<char name='Emilie'> ... </char>
<pet name='Luna' type='cat' owner='Alice'> ... </pet>

This is due to incompatibility with how ST parses responses to find dialogue. It works just the same though, and models don't seem to mind.

As you can see, attributes allow you to attach extra values to a tag. Unlike the initial XML example, this lets the model know that both Alice and Emilie are characters, without having to put them in any sort of group, and that Luna is Alice's cat before we even get to the description. Don't go wild and start putting random facts there, though - these are best used to show the model how things relate to each other without excessive nesting or referencing.

Attributes can be boolean, these are either there or not. They don't have any value. For example:

<store name='Seven-11' />
<store name='Target' closed />

Templates

Text models are good following instructions, but they're even better at following patterns. If you give it a template to fill in, it will never misunderstand you again.

We do this by asking something in plaintext, and then providing a template that fulfills the ask:

1
2
3
4
5
6
7
Start your response with a weather block, which contains the current weather conditions. Use the following formatting for the weather block:

\```html
<div class='weather'>
It's a clear sunny sky, with 23 degrees outside.
</div>
\```

Note how we wrap the example block in triple backticks (```, this is not the apostrophe '!) which is the standard notation for a code block in Markdown. The model understands this and doesn't actually include them in the output. We also add html after, explaining what's going to be inside for clarity.

The block itself is a div tag, which just means it's a generic container. We give it semantic meaning with a class, a standard HTML attribute. It doesn't have any effect here besides helping out the model.

Thanks, rentry

If you see a backslash before the triple backticks, that's a rentry issue. It's not supposed to be there. It's hard to put code blocks inside code blocks.

The template doesn't even have to be a full example. The model is smart enough to figure out that some text is a placeholder:

1
2
3
4
5
6
7
8
9
At the end of your response, write a choice block that offers {{user}} three options for what they could do next. Use the following formatting for the choice block:

\```html
<ol class='choice'>
<li>Option one.</li>
<li>Option two.</li>
<li>Option three.</li>
</ol>
\```

Greeting

Whatever new features we implement, it's best to use them at least once manually in the greeting message. This serves as an example for the model to follow, making it much more likely that it'll continue to use it in its responses.

Examples

With all the ingredients in place, we can start building some shiny new features. Let's start with the simplest ones and work our way up.

Inner thoughts

With a single sentence in the prompt override, we can have the model print a character's inner thoughts in the responses. It can be pretty cute.

Prompt override:

Quote {{char}}'s inner thoughts in first-person using asterisks (*).

Example output:

As she finally manages to poke her head out from under a copy of "The Catcher in the Rye," she comes face to face with {{user}}'s curious stare. Oh crud! He sees me! What do I do? What do I say? In a panic, she blurts out the first thing that comes to mind: "H-hi there! Lovely weather we're having, isn't it? Ahaha..."

While you were gone...

Sometimes a character might be away from {{user}} for some time, doing stuff where {{user}} can't see them. Text models aren't very good at dealing with this, since they don't imagine the entire world the story takes place in - all they know is text. The best way to deal with this situation is to just... allow the model to write about it in an invisible block. Now it has a record of the event, and can continue the story knowing it happened.

Prompt override:

1
2
3
4
5
6
7
8
If {{char}} is away from {{user}}, do not narrate {{char}}. Instead, write what happens on {{char}}'s side in a secret block. Inside, summarize {{char}}'s actions and events with bullet points. Use the following formatting for the secret block:
\```html
<div class='secret' hidden>
While {{char}} was gone:
- {{char}} performed action.
- An event occurred.
</div>
\```

Output:

1
2
3
4
5
6
<div class='secret' hidden>
While {{char}} was gone:
- {{char}} quickly scanned the store for security cameras and blind spots.
- She discreetly placed a small explosive device behind some products on a shelf.
- {{char}} purchased a pack of gum to avoid suspicion.
</div>

Note the use of the hidden attribute - in a div, it simply makes it invisible. It uses less tokens than the <!-- --> comment markers.

WIP

More examples

Level 4: Simulation card

Doesn't exist yet. I'm working on it, though.

Edit
Pub: 05 Sep 2024 17:28 UTC
Edit: 06 Sep 2024 16:54 UTC
Views: 2455