AI that stays on script
Your authored content is the foundation. The AI enhances it, not replaces it. Semantic retrieval, state machines, structured generation, and layered guardrails keep the AI under your creative control.
The Problem
Game developers' #1 fear with AI: "What if the AI says something wrong, off-brand, or breaks the game?" Unconstrained generative AI produces slop. The industry has converged on this realization: Meaning Machine's "bullying the AI," Ubisoft's "controlled improv." The answer isn't to avoid AI. It's to wrangle it.
Four Patterns for Creative Control
Pattern 1: Semantic KV Store
Author hundreds of dialogue lines with metadata, including occupation, weather, time of day, and reputation. The engine embeds them and at runtime finds the right one via vector similarity search with facet filters. Not generative slop. Your writers' words, surfaced at the right moment.
{
"key_text": "guard patrols the market square on a calm day",
"answer": "Move along, citizen. Market's open, but I've got my eye on you.",
"facets": {
"npc_occupation": "guard",
"player_reputation": "neutral",
"weather": "clear"
}
}Pattern 2: State-Driven Dialogue
Gate dialogue behind emotional states. Characters progress through authored story beats: calm → nervous → defensive → cornered. Build branching narratives without branching code.
{
"character_id": "victor_crane",
"emotion": "calm",
"key_text": "asking about art dealings",
"answer": "I've been collecting for decades. Everything in my gallery is authentic.",
"triggers": []
}{
"character_id": "victor_crane",
"emotion": "nervous",
"key_text": "pressing about provenance of specific paintings",
"answer": "Look, I don't appreciate the interrogation. My records are impeccable.",
"triggers": [
{ "set_emotion": "defensive" }
]
}Pattern 3: Structured Generation
When the AI does generate, constrain what it can say. Force the output into a fixed set of choices, a JSON schema, a regex pattern, or a formal grammar. The model physically cannot produce tokens outside the constraint, so your game code never receives unexpected values.
let player_action = "drew a weapon";
let npc_name = "the guard";
// The model can ONLY output one of these choices
let reaction = engine.llm().generate_choice(
&format!("The player {player_action}. How does {npc_name} react?"),
&["draw_weapon", "call_backup", "flee", "negotiate"]
).await?;
// Returns: "call_backup"Combine with the other patterns: retrieve authored content via the KV store, and when no authored line matches well enough, fall back to structured generation that is guaranteed to produce a value your game logic can handle.
Pattern 4: Guardrails Cascade
Layered safety, each tier faster than the next:
- Keyword blocklists, ~0.1 ms
- ML classifiers, ~5 ms
- LLM judges, ~200 ms
- Content rewriters, ~300 ms
Start with a preset (ChildSafe, GameSafe). Then customize everything: your blocklists, your classifiers, your judge prompts.
The key distinction: don't just block, rewrite. Transform problematic content into something safe while preserving intent.
Player types: "a skeleton warrior dripping with
blood and gore"
Rewrite: "a skeleton warrior with ancient battle
scars and dark armor."
How It All Fits Together
The foundation is authored content. The AI augments context via semantic search. State machines control narrative progression. When generation is needed, it's constrained through structured output: JSON schemas, choice lists, and regex patterns ensure the model only produces values your game code expects. Guardrails enforce content boundaries at every layer, and fallback to authored lines kicks in when confidence is low.
Built-in answer caching ensures that recurring prompts (NPC barks, tooltip descriptions, repeated player interactions) return instant cached results without hitting the model again. Prefix caching reuses shared context across requests so the system prompt and world state are only processed once per conversation.
Creative control isn't a feature. It's the architecture.