Prompt format and tool grammar #
This is the contract between the app and the weights. train/world.py renders
it for training and web/world.ts for the browser; node test/parity.mjs
compares 300 random states byte for byte, checks that the word lists and tool
sets are identical, and checks that every event line the app writes matches
exactly one of the validator's patterns (and that offers are recognised as
offers).
Chat prompt #
## tokenling chat
pet: Mochi · kid · day 3 · curious, foodie
owner: Sam
now: 21:40 night, rainy
needs: tummy hungry, fun happy, energy sleepy, clean
remembers: Sam likes frogs; Sam has a cat named Bo
today: ate a cookie; played catch (4 stars); had a bath
---
Sam: what time is it?
Mochi: [clock]{21:40 night} it's so late!! [emote:sleepy] [want:snack]
pet:isname · stage · day N · trait, trait.now:isHH:MM part-of-day, weather, plus, NAME is asleepand/or, NAME is sick. Part of day: morning 05–11, afternoon 12–16, evening 17–20, night 21–04.needs:turns the 0–100 stats into words (below).remembers:is the newest three memories; omitted when empty.today:is the newest six entries after filteringOWNER said: …from chat headers; omitted when empty. Diary headers retain those quotes.- After
---, one line per message. The prompt always ends atName:.
Stat words (thresholds are floor(value * n / 101)):
| Stat | Words, low to high |
|---|---|
| tummy | starving, hungry, peckish, full, stuffed |
| fun | sad, bored, okay, happy, giddy |
| energy | exhausted, sleepy, okay, awake, hyper |
| clean | stinky, messy, clean, sparkly (printed alone, without a label) |
Diary prompt #
## tokenling diary
pet: Mochi · kid · day 3 · curious, foodie
owner: Sam
weather: rainy
remembers: …
today: …
---
dear diary,
The header has weather: instead of now:/needs:. The model writes the
entry and ends with <|endoftext|>; optional [doodle:NAME] stickers go on
the last line.
Event lines #
Lines starting with * are written by the app, never the model. They are the
only way the pet learns what the owner did or what happened to its body.
* Sam offered Mochi a strawberry * * Sam offered Mochi a bath *
* Sam offered Mochi medicine * * Sam asked Mochi to play catch *
* Sam played catch with Mochi (4 stars) * * Sam patted Mochi *
* Sam turned off the lights * * Mochi woke up *
* Mochi pooped * * Sam cleaned up the poop *
* Sam came back after 3 hours * * Mochi grew into a teen! *
world.py:event_patterns() is the validator for training data; world.ts:events
builds them. Foods take "a" or "an" (a(food)).
Messages and pats may wake a sleeping pet by a body roll; a successful roll
writes * Mochi woke up * before the message or pat. Lights on rolls after
its event line. Lights off and cleaning do not roll. Offers never wake first:
a sleeping pet's [yes] is followed by woke up, then the accepted care.
The app never inserts an event between an offer and its answer.
Offers #
The first four lines above are offers (world.py:offer_patterns()). The
pet's very next line must contain exactly one [yes] or [no]; owner lines
may come between. The app feeds, bathes, gives medicine or starts the catch
game only on [yes], and treats a line without an answer as no. [yes] and
[no] are invalid anywhere else. After [yes] to catch, the game runs and the
app writes the played catch … (N stars) line. Pre-v2 data used direct
* Sam fed Mochi … *, gave … a bath and gave … medicine lines; those no
longer exist.
Sleeping lines #
While asleep, each pet line contains exactly one [dream:X], optionally
with a mumble of at most four words. No other tags are allowed, except [no]
on the answering line of an offer it sleeps through. A sleeping pet can
instead accept with short words and [yes] only (no dream), followed
immediately by the app's woke up line. [dream] is invalid awake, and
[sleep] is invalid already asleep. A nap starts after the [sleep] line;
a subsequent instant wake must precede an owner message or pat.
DREAMS contains the six foods, eight gifts, and ball, moon, sun, cloud,
rain, frog, cat, ghost, music and sparkle. Dream content comes from the brain.
Silent lines and idle turns #
A pet line made only of tags (Mochi: [walk:left],
Mochi: [walk:right] [emote:happy], Mochi: [want:snack]) is a silent
line: the pet acting on its own. The app asks for a pet line not only after
something happens ("react" turns) but also every 8–20 seconds while the page
is visible ("idle" turns, the pet continuing after its own line or an event).
Training data has at most two silent lines in a row, and the app keeps at most
the newest two consecutive silent lines in the chat.
Tools #
Tools are inline tags in the pet's own line. Tag syntax is
[name] or [name:argument]. The argument is at most 80 characters and cannot
contain ] or a newline.
| Tag | Values | App effect |
|---|---|---|
[emote:X] |
happy, love, sad, sleepy, wave, dance, spin, blush, grumpy, wow, giggle, cry | LCD animation and floating hearts, notes or a bang |
[want:X] |
snack, play, bath, sleep, pats, medicine | thought bubble above the pet for 25 s |
[remember:FACT] |
a fact naming the owner, without gendered pronouns | appended to S.memories (max 60, newest three in the header) |
[gift:X] |
flower, pebble, star, leaf, shell, heart, drawing, feather | placed on the shelf, toast, a note in the day log |
[diary:NOTE] |
free text | note: NOTE added to the day log |
[sleep] |
none | the pet falls asleep |
[dream:X] |
DREAMS above |
sleeping thought bubble for 25 s and a dream chip; at most two dreams in the day log |
[walk:X] |
left, right | the pet walks 14–28 LCD pixels that way (one per line) |
[yes], [no] |
none | answers an offer (see above) |
Lookups #
| Tag | Result written by the app |
|---|---|
[clock] |
HH:MM part-of-day |
[look] |
shelf: a, b, c · toy: T · window: weather plus · poop xN if any |
[recall:KEYWORD] (one or two words) |
up to two memories sharing a word with the keyword (newest first, joined with ; ), or nothing |
[yesterday] |
the first sentence of the last finished diary page, or no diary yet |
The exchange looks like [clock]{21:40 night}. During generation the model
stops after the closing ] (or the single token ][, which brain.ts trims),
the app appends {…}, and the model continues the same line. In training data
the {result} is part of the context but carries no loss, so the model learns
to call the tool, not to fake the answer.
recall matching (world.words) lowercases, takes [a-z0-9'] words of three
or more characters, drops a short stop list and a trailing plural s, and
matches on any shared word.
Stopping and trimming #
- Chat generation stops at the first newline or a completed lookup tag and allows at most 64 new tokens per hop (40 for idle turns). Sampling is temperature 0.85, top-k 40, top-p 0.92.
- Diary generation allows 220 tokens and also stops on a stray
\n##or a run of blank lines. fit()inbrain.tsdrops the oldest chat lines until header + lines + prefix + the new-token budget fit in 1024 tokens (512 for idle turns, whose cost is mostly prompt processing).
Tokenization pitfalls #
Name:andName:tokenize differently. Always end the prompt withName:; the model emits" word"tokens itself.- GPT-2 has a merged
][token, so[look][gift:x]closes a lookup mid-token. The stopper regex allows a trailing[, and the app removes it. - Do not use a repetition penalty (see
gotchas.md).