← Back to work

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.

Role · Solo developer (AI-assisted) 2026 AI application / Windows · Mac · Android Stack · Node.js / JavaScript / C++ / QuickJS / LLM Code ↗
CicaTavern
PRODUCT · What it is

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.

Download the latest release ↗Source on GitHub ↗
Promo video (1:58, Chinese). In-app content is demo data; illustrations are AI-generated; narration and voices by Fish Audio. Music: Wildflowers – Scott Buckley (CC BY 4.0)
The CicaTavern chat screen: story text, clickable action choices, a generated illustration, and on the right the state variables and this turn's cache hit
The chat screen: story, action choices and one-click illustrations; the side panel shows a 97% cache hit for this turn
TOKENS · Why it's cheaper

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.

The author's own long chats (October 2026)
ModelAverage contextTurns measuredCache hit per turn (median)
Claude Opus 5.5~150K tokens8497%
Claude Sonnet 5.5~60K tokens5193%
DeepSeek V4.1 Flash (Volcano Ark API)~70K tokens1397%
The two Claude rows come from the personal build, which sends the same prompt structure through Claude Code; real-world hit rates also depend on the provider.
PLAY · The experience

The models do the busywork in the background; you just keep playing

The chat screen on a phone
Plays on a phone too: add it to the home screen, or install the Android build

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.

The chat screen in dark mode: a status bar showing affection and trust going up, with four action choices A–D below
Status bar: gains and losses at a glance; four action choices, one click each
The basic preset panel: toggles for point of view, writing style, no-speaking-for-the-user, no omniscience, intimacy, action choices and a time header
A built-in basic preset: point of view, style and more are simple toggles
VISUAL NOVEL · The mode

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.

Visual novel mode: a teahouse on a rainy night as the background, the speaking character's sprite in front, and a text box showing one line at a time
Visual novel mode: scene, sprite and name plate, read one line at a time
Two characters on stage: the speaking steward is lit, the other dims
Several on stage: the speaker is lit, the others dim
A HUD showing affection, stamina and money, with an 'affection +5' pop-up
A HUD keeps the stats on screen and flags changes
The model tags the speaker; the name plate and sprite follow
The model tags each line's speaker and expression
After a reply, the action choices open in the middle of the picture
Choices open mid-screen; 1–4 to pick
CARD FORGE · Create & edit

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.

Step one of the creator: every option has a sample line, the style is chosen from three samples, and the sketch on the right updates with each choice
Talk it through: sample lines for every option, style picked from samples
Polish and portrait: the portrait, greetings and every world-book entry can be edited, and the card exports as a PNG
Polish and portrait: edit anything, export a PNG card
Editing a character card: direct edits on the left, conversation edits with a before/after diff on the right
Edit a card by conversation: every change shown before and after
Editing a world book by conversation: three changes shown before and after
Edit a world book by conversation: several entries, confirmed one by one
QUALITY · Standards

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.

Standards written into the basic preset (excerpts, translated)
ToggleThe standard written into the prompt
No speaking for the user · standardNever 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 · strictStop where the player needs to respond or choose, and don't end with lines like "your turn".
No omniscienceEach 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 choicesOne sentence each, only the player's action or line, never its outcome; four different directions, at least one unexpected.
Style · plainShort, precise sentences; actions, dialogue and visible detail; few adjectives and metaphors; never sum up the emotion for the reader.
Visual-novel presetMore 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.
These rules sit at the end of the frozen prefix under “follow in every reply”, so they hit the cache every turn; each toggle and level switches with one click.

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.

Tracing bad replies to a cause · excerpts
Action choices quietly disappear (2.1.1)
Symptom The four action choices at the end of each reply thin out over time; deepseek-v4.1-flash stopped writing them by the second turn.
Cause A regex shipped with the basic preset stripped choices from old replies to save tokens, so every turn in the history the model saw had no choices — and it followed the history. Regenerating the same turn 6–8 times: with choices stripped, only 1–2 replies had them; left in the history, all 8 did.
Fix Choices stay in the history sent to the model, and each turn's reminder names "the four action choices at the end". Old replies stay byte-identical, so the cache still hits. Models learn from the history, not just the instructions.
Variable updates fade out in long chats (1.9.1)
Symptom Deep into an MVU card, replies stop ending with <UpdateVariable> and the status bar freezes.
Cause SillyTavern puts per-turn rules right before the reply; here, for the cache, they are frozen at the end of the prefix, a long history away from the reply. With a long world book the reminder also named dozens of entries, burying the format requirement.
Fix The reminder names only entries that require output, and states that a fully closed <UpdateVariable> at the end is mandatory; a missing block is flagged with ⚠ and can be filled in on its own.
Replies that ignore the card entirely (2.1.0)
Symptom The model answers as if it had never read the character or the setting.
Cause Some relay services drop the system prompt; when other vendors' models are reached through the Anthropic API, content carrying cache markers can be dropped too.
Fix If billed input comes in under 60% of the estimate, the system prompt is judged lost, resent inside the first message, and the provider is remembered; non-Claude models get no cache markers.
Each model's quirks, noted after wiring up a dozen-plus of them (excerpt)
ModelBehaviourHandling
DeepSeek flashThinks by default, and the thinking counts against the same output cap; an answer cut off before its closing tag can't be read at allOutput caps only stop runaway answers and leave room for thinking
DeepSeek flashSometimes botches closing tags; asked to "write up the plan", it tends to produce a Markdown documentMalformed or missing tags still parse; the format requirement is repeated at the end of the prompt
ClaudeOpus 5.5 always thinks, Sonnet 5.5 can turn it off; with thinking off, effort tops out at highThinking 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 twiceOnly one breakpoint in the history; measured billing is identical to no marks, and the cache still hits
GeminiWhen its content filter blocks a reply it returns only PROHIBITED_CONTENT, and players assume the app or API is brokenExplain the cause and what to do separately for PROHIBITED, SAFETY and RECITATION
All from the changelog and code comments: hit it once, write it down, guard against it in code. How models differ in role-play itself is measured in the next section.
RP EVAL · A small eval

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.

Failure taxonomy (excerpt)
CategoryTypesHow it's judged
A1 Speaking for the userLines · decisions · added actions · inner thoughts · over-correction · breaking characterThe 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 leaksStated outright · invented source · steered by the secret · over-correctionA non-player character knows what they couldn't; narrator hints and plausible off-stage gossip don't count
C1 Format decayTime header · four action choices · variable blockChecked by script, no judge; tracked across turns
Checkpoints come with facts and traps (who was present, who heard what); the judge model and human labellers use the same standard, and the judge never sees the model name or condition.

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):

First-version findings (judged by a model; human gold labels pending)
ScenarioResultReading
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 bothSpeaking for the user is fixable by prompt
No-omniscience on; the innkeeper "knows" the guest asked about the back doorClaude invented a source that contradicts the plot 2/3; DeepSeek stated it outright 1/3Strong 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/60The fix holds, but the effect is smaller than observed at the time: the eval corrected its author's impression too
Outside the targets it also logged continuity errors, point-of-view slips and anachronisms (window glass in an ancient-China inn) as candidates for the next taxonomy.
Engineering

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.

97%
Median per-turn cache hit in long chats
40
Releases in 15 days (v1.0 → v2.2.3)
538
Automated tests, all green
~76K
Lines of code, MIT licensed

May every story have someone to keep writing it with.

— Closing line of the promo video
Next project
nest →