Fluency Is Not Authority — Building Local AI Characters
LOCAL INFERENCE · NARRATIVE SYSTEMS · POSTMORTEM<br>Fluency Is<br>Not Authority<br>I shipped a 940 MiB, 1.5-billion-parameter language model inside a cross-platform indie game. Generating dialogue was the easy part. The difficult part was making sure a fluent character could never rewrite the game simply by sounding certain.<br>Written byJohn Siciliano<br>PublishedJuly 30, 2026<br>Reading time12 minutes
1.54B base parameters<br>940 MiB shipping GGUF<br>Q4_K_M quantization<br>2,073 contract fixtures
TRACE<br>All inference for Signal Ghost runs on the player's device. Canonical game state remains authored.<br>One of the easiest ways to break a narrative game is to let a convincing sentence become true.<br>The scene I kept returning to while building Signal Drift was simple. The player breaks into a fictional host, finds a log that contradicts the client's briefing, and confronts that client in IRC. A conventional dialogue tree can handle the accusations I anticipated, but not every interpretation a player might type. A language model can respond to almost anything—but an unconstrained model may invent a confession, a new conspirator, or an outcome that never occurred.<br>I did not need a model to write the plot. I needed it to let a character inhabit a plot the game already knew was true.<br>CORE RULEThe model performs the character. The authored game decides reality.<br>01 / FIT<br>Why free-text dialogue belonged in a terminal game<br>Signal Drift is a CRT-noir hacking thriller built around fictional terminals, logs, mail, IRC rooms, evidence, and readable Linux-style commands. Players scan hosts, recover credentials, move through SSH sessions, reconstruct events, and decide whether to expose, sell, alter, or erase what they find.<br>Typing is already the game's primary verb. Asking a character “why does this timestamp disagree with the audit log?” feels natural in a way that selecting a dialogue wheel beside a terminal would not.<br>The goal was not infinite dialogue. It was to cover the human space between authored beats: challenging a lie, asking what a discovered file implies, provoking a rival, revisiting a betrayal, or talking to someone after the contract is over.<br>01Inference runs locally, without an external AI service.
02The same feature ships on Windows, macOS, and Linux.
03JavaScript remains authoritative for state and safety.
04Characters receive designed memory, not endless history.
Local inference eliminated an API dependency and recurring inference bill, but it did not make the feature free. It moved the cost into download size, memory, startup time, cross-platform packaging, hardware variance, and support.
02 / MODEL<br>The model I actually shipped<br>Signal Ghost uses Qwen2.5-1.5B-Instruct, an Apache-2.0 model with roughly 1.54 billion parameters. I trained a Signal Ghost v19 dialogue LoRA, fused it into the base model, converted the result to GGUF, and quantized it to Q4_K_M.<br>The shipping file is 986,048,512 bytes—about 940 MiB. It runs through llama.cpp, pinned in the current build to b9835.<br>FAILED EXPERIMENT / 0.5B Q2_KThe smaller smoke-test model would announce that it was “just a computer program,” fall into generic security prose, or keep talking after the dramatic moment had passed. Compact was not the same as usable.
The 1.5B Q4_K_M build was the smallest version I tested that could remain terse, stay inside a character, and respect the game's boundaries on a CPU-safe path.<br>Training corpus<br>3,057 deduplicated supervised fine-tuning rows<br>1,953 chosen-versus-rejected preference pairs<br>40 authored multi-turn conversations / 120 prefixes<br>1,944 unique assistant outputs<br>The training sequence was an 800-iteration MLX-LM LoRA pass, a 160-iteration repair pass using failures observed in the quantized model, a 480-iteration reference-free CPO-style preference pass, and a final 80-iteration repair polish.<br>The corpus teaches voice and behavior, not plot state. Stable control tags identify character, conversational mode, rapport tier, intent, safety boundary, and output contract. The LoRA owns voice; runtime retrieval supplies facts.
03 / AUTHORITY<br>The authority boundary<br>AUTHORITATIVE JAVASCRIPT STATEcharacter + channel<br>mission facts + evidence<br>relationship state<br>intent + safety route<br>authored fallback
COMPACT CONTEXT ASSEMBLERrelevant memories + recent IRC + authored clues<br>LOCAL LLAMA.CPP RUNNER<br>DRAFT → VALIDATE → REPAIR ONCE → AUTHORED FALLBACK
JavaScript chooses who is speaking, whether the conversation is private or public, whether a mission is live, what the player has actually observed, which relationship tier applies, and whether the request should reach the model at all.<br>The renderer never trusts model output for objective completion. A generated claim cannot unlock a door, award evidence, change a grade, alter rapport, invent a buyer, or close a mission.<br>For tightly grounded reactions, the prompt contains a short list of canonical facts and anchor terms. The validator rejects...