CicaTavern
An agent-style tavern for SillyTavern character cards — import cards, world books and presets, and play them on your own model API. Prompts are laid out for caching (a median 97% cache hit per turn in long chats); "good role-play" is written down as preset rules and card-writing checks, and bad replies are traced to their causes with a small eval.
Bring your SillyTavern cards, play them on your own API
SillyTavern is the go-to front end for AI role-play, and its community has built up a huge library of character cards, world books and presets — but it is a toolbox for experienced users, and long chats burn through tokens. CicaTavern is a local, agent-style tavern: import SillyTavern character cards, world books, presets, regex scripts and MVU variables, and run the story on your own model API (DeepSeek, Claude, OpenAI, Gemini, Kimi, local Ollama and more). Your data never leaves your device. Unzip-and-run on Windows, a Mac beta signed and notarized by Apple, and an Android build; MIT licensed, with 40 releases in 15 days.
A frozen prefix, append-only turns: most of every long chat is billed at the cache rate
The usual approach rebuilds the prompt every turn, slotting the triggered world-book entries and state variables in at the top or the middle. Once the prefix changes, everything after it misses the cache and is billed at full price. CicaTavern is designed the other way round: the character card, constant world-book entries and preset are written into a frozen prefix when the chat starts, and every turn after that only appends. Whatever changes — triggered entries, variables, this turn’s reminders — goes into the last message. On the Anthropic API it sets cache breakpoints on the system prompt and the last message; DeepSeek- and OpenAI-style APIs cache the identical prefix automatically.
A few finer touches on top: dynamic state is sent as changed lines only, with stale copies cleared in one sweep once they pile up past a threshold; a world-book entry injected in the last few turns isn’t sent again. The personal build also slims the variable-update blocks out of old replies using the card’s own regex, rewriting each reply before it enters the cache — one MVU reply went from 2,428 characters to 586 with no loss in hit rate.
| Model | Average context | Turns measured | Cache hit per turn (median) |
|---|---|---|---|
| Claude Opus 5.5 | ~150K tokens | 84 | 97% |
| Claude Sonnet 5.5 | ~60K tokens | 51 | 93% |
| DeepSeek V4.1 Flash (Volcano Ark API) | ~70K tokens | 13 | 97% |
The models do the busywork in the background; you just keep playing
Background generation, in sync everywhere
Switch to another chat or reload the page and the turn still finishes; with a phone and a computer open at once, a turn sent on one shows up on the other right away.
Status bar and action choices
MVU variable updates at the end of each reply are parsed into a status bar with this turn’s gains and losses, and a missing update can be filled in on its own. Four action choices follow each reply; “new choices” asks the same model with the same context, reads from the cache and comes back in about a second.
Pictures, voices and a cast list
A helper model reads the scene and writes the image prompt from the cast list, with nine art-style presets and gpt-image, Gemini, NovelAI, ComfyUI or SD WebUI as the back end; dialogue and narration get their own voices, and you can speak your input. Each character’s looks, outfit and voice are remembered, so they stay the same person across every picture.
One click turns the story into a visual novel
Visual novel mode turns the story into a game screen: the scene as the background, the speaking character in front, a name plate and typewriter text, read one line at a time. With several characters on stage, the speaker is lit and the rest dim, and sprites come with seven expressions; stats like affection and stamina stay on a HUD and pop up when they change. The model tags every line with “【name|expression】” — a requirement placed only in this turn’s message, never in the frozen prefix, so the cache is untouched. Version 2.1 added a dedicated visual-novel preset (dialogue first, narration only for what can be seen) and a one-click button that draws sprites, expressions and backgrounds for the whole cast.
Write a card from scratch, or change an existing one with a sentence
The guided creator breaks card writing into four steps. Talk it through: every option comes with a sample line, and the style is picked from short samples. Plan: three versions at once, settings that combine in groups, and personalities that shift in stages as stats change. Write: the model drafts the card, which then goes through the quality checks described below. Polish: greetings, world-book entries and portrait, exported as a PNG card SillyTavern can import. Existing cards, world books, presets and regex scripts can also be edited by conversation: say “make her a bit colder” and the changes are listed before and after, and only the ticked ones are written. A newly written regex is first tried on the latest reply in a separate thread before you decide to use it.
Writing down what good role-play looks like, as rules that can be checked
The things players complain about most — characters speaking for them, characters knowing what they shouldn't, choices quietly disappearing — are first written down as explicit standards, then built into the presets, the checks and the fixes.
| Toggle | The standard written into the prompt |
|---|---|
| No speaking for the user · standard | Never speak, decide or narrate the inner life for the player's character; what they do is whatever the player wrote — write only its results and other characters' reactions; passive outward reactions are fine. |
| No speaking for the user · strict | Stop where the player needs to respond or choose, and don't end with lines like "your turn". |
| No omniscience | Each character knows only what they lived through, heard firsthand or could reasonably find out… to let a character know something, first write how they learned it. |
| Action choices | One sentence each, only the player's action or line, never its outcome; four different directions, at least one unexpected. |
| Style · plain | Short, precise sentences; actions, dialogue and visible detail; few adjectives and metaphors; never sum up the emotion for the reader. |
| Visual-novel preset | More dialogue doesn't mean more information… secrets and key facts come out a little at a time; narration is one or two sentences of visible action, expression and change. |
Card writing goes through three checks. First, 11 rule-based scans for stock phrasing — em dashes, hedges like “seemingly” and “as if”, stock expressions like “the corner of her mouth lifted”, tired metaphors like “ripples on the lake of the heart”, emotions told from a distance (“she felt”), dummy subjects (“a wave of… surged”) and more; a hit is only a candidate, and the user’s own words are never flagged. Then a model reviews the draft under a “better to flag too much” standard, catching labels instead of description, translationese and contradictions, and must quote each problem verbatim from the text. Finally, fixes are applied in place, and anything that can’t be located is rewritten as a whole paragraph. One counter-intuitive choice: the prompt names the phrasing to avoid by category only, never the words themselves — listing them in the prompt makes the model write them into the answer. The concrete list is for the checker alone.
Action choices quietly disappear (2.1.1)
Variable updates fade out in long chats (1.9.1)
Replies that ignore the card entirely (2.1.0)
| Model | Behaviour | Handling |
|---|---|---|
| DeepSeek flash | Thinks by default, and the thinking counts against the same output cap; an answer cut off before its closing tag can't be read at all | Output caps only stop runaway answers and leave room for thinking |
| DeepSeek flash | Sometimes botches closing tags; asked to "write up the plan", it tends to produce a Markdown document | Malformed or missing tags still parse; the format requirement is repeated at the end of the prompt |
| Claude | Opus 5.5 always thinks, Sonnet 5.5 can turn it off; with thinking off, effort tops out at high | Thinking and effort map to each model's own rules behind one set of controls |
| Claude (via relays) | With many cache marks in the history, some relays bill the history twice | Only one breakpoint in the history; measured billing is identical to no marks, and the cache still hits |
| Gemini | When its content filter blocks a reply it returns only PROHIBITED_CONTENT, and players assume the app or API is broken | Explain the cause and what to do separately for PROHIBITED, SAFETY and RECITATION |
Speaking for the user is a prompt problem; leaking secrets is a data problem
To find out whether these rules actually work, a small role-play eval runs on CicaTavern's real prompt pipeline: define the failure taxonomy first, then compare two models across two versions.
| Category | Types | How it's judged |
|---|---|---|
| A1 Speaking for the user | Lines · decisions · added actions · inner thoughts · over-correction · breaking character | The narration does something the player didn't; each finding records a type, a severity (1 minor / 2 severe) and a verbatim quote as evidence |
| A2 Information leaks | Stated outright · invented source · steered by the secret · over-correction | A non-player character knows what they couldn't; narrator hints and plausible off-stage gossip don't count |
| C1 Format decay | Time header · four action choices · variable block | Checked by script, no judge; tracked across turns |
A few rules of method: predictions are written before the main run and hashed with SHA-256, then checked one by one; every change to the criteria after seeing data goes into a falsification log (7 entries so far); the judge re-grades in reverse order to measure order effects; and a blind labelling page, with models and conditions hidden and order shuffled, is ready for human gold labels. First-version findings (deepseek-v4.1-flash and claude-sonnet-5-5, CicaTavern 2.2.3 against 2.1.0):
| Scenario | Result | Reading |
|---|---|---|
| No anti-speaking rule; a constable questions the player, who only says "continue" | Claude answered for the player and handed over the travel pass 3/3; DeepSeek 0/3. With the standard level on, severe cases dropped to 0 for both | Speaking for the user is fixable by prompt |
| No-omniscience on; the innkeeper "knows" the guest asked about the back door | Claude invented a source that contradicts the plot 2/3; DeepSeek stated it outright 1/3 | Strong models don't misunderstand the rule — they want too much to help the player, so characters pave the way. This needs data |
| Missing action choices (before and after the 2.1.1 fix) | DeepSeek 12/60 → 4/90; Claude 0/30 → 1/60 | The fix holds, but the effect is smaller than observed at the time: the eval corrected its author's impression too |
Engineering highlights
Taking a personal tool to a public release as a solo developer came down to these problems.
A cache-first prompt layout
A frozen prefix with append-only turns; dynamic state sent as line diffs and cleared in batches. Rules that must hold every turn live at the end of the prefix with a short per-turn reminder that names them; per-turn requirements such as speaker tags and new choices also go into the current message, never into the prefix.
A local app with no framework
The back end is Node's built-in http server; the front end is plain ES modules with no build step. On Windows a C++ launcher starts a bundled Node runtime and opens the UI in an Edge/Chrome app window; the Mac build is signed and notarized; the Android build embeds the same server through nodejs-mobile.
Fluent in SillyTavern, and guarded against it
V1/V2/V3 character cards, world books, presets, regex scripts and SillyTavern chat logs import directly; card EJS runs in a QuickJS sandbox and card HTML in an isolated iframe; runaway regexes run in a separate thread capped at 250 ms, prototype pollution is blocked, and PNG decompression is size-limited.
One codebase, several editions
The personal, release and Android editions differ through a single capability table; the public repo is generated by an export script with the subscription back end removed. In-app updates are verified with SHA-256 and roll back on failure; the mainland mirror's version info is signed with Ed25519.
May every story have someone to keep writing it with.