docs(workshops): LLM voice pipeline workshop — D-138, D-123 amended, D-124 superseded
3-round workshop (7 participants + Qatux + SI) deciding content generation architecture for NPC observable behaviors and dialogue. Key decisions: - D-138: LLM re-voicing pipeline (Gemma 2B Q4, llama-cpp-rs, bundled) - Behaviors + dialogue both re-voiced; tells always passthrough - Tells as read-only context inputs shaping surrounding content tone - Cache-as-determinism, separate thread pools, layered hardware detection - Two-spike validation: plumbing first, then integration - D-123 amended (authoring tool + runtime enhancement) - D-124 superseded (door walked through) - Q-012 and Q-057 resolved Artifacts: Krenn injectors v2, NI-1-5, culture template, 6 dialogue constraints, tell-tone injectors, spike payloads, 12-risk register. 9 tickets created (#638-#647). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -12,7 +12,7 @@ Cross-domain decisions live in one file with cross-reference notes in related fi
|
||||
|------|--------|-----------|
|
||||
| [architecture.md](architecture.md) | Technical foundation | D-008, D-009, D-010, D-012, D-020, D-026, D-030, D-031, D-041, D-042, D-054, D-055, D-066, D-068, D-073, D-085, D-088, D-094, D-096, D-097, D-099, D-100, D-101, D-102, D-103, D-106, D-108, D-109, D-113, D-133, D-134, D-135, D-136, D-137 |
|
||||
| [perception.md](perception.md) | Player observation | D-011, D-015, D-016, D-017, D-018, D-019, D-033, D-035, D-043, D-044, D-045, D-046, D-047, D-048, D-049, D-052, D-056, D-057, D-058, D-059, D-060, D-061, D-067, D-069, D-070, D-071, D-072, D-076, D-077, D-078, D-086 |
|
||||
| [content.md](content.md) | NPC, dialogue, templates | D-023, D-024, D-025, D-028, D-029, D-032, D-034, D-035, D-036, D-037, D-050, D-062, D-063, D-064, D-074, D-075, D-084, D-090, D-092, D-093, D-095, D-098, D-104, D-105, D-107, D-121, D-122, D-123, D-124, D-125, D-126, D-127, D-128, D-129, D-130, D-131, D-132 |
|
||||
| [content.md](content.md) | NPC, dialogue, templates | D-023, D-024, D-025, D-028, D-029, D-032, D-034, D-035, D-036, D-037, D-050, D-062, D-063, D-064, D-074, D-075, D-084, D-090, D-092, D-093, D-095, D-098, D-104, D-105, D-107, D-121, D-122, D-123, D-124, D-125, D-126, D-127, D-128, D-129, D-130, D-131, D-132, D-138 |
|
||||
| [scope.md](scope.md) | Game concept, prototype | D-001, D-003, D-005, D-006, D-007, D-013, D-014, D-027, D-038, D-039, D-051, D-053, D-065, D-087, D-089, D-091, D-114, D-115, D-116, D-117, D-118, D-119, D-120 |
|
||||
| [process.md](process.md) | Team, workflow | D-004, D-021, D-022, D-040 |
|
||||
| [questions.md](questions.md) | Open questions (index) | Q-001 through Q-054 |
|
||||
|
||||
+40
-10
@@ -314,22 +314,29 @@ How narrative, NPCs, and world content are created: content tiers, NPC generatio
|
||||
- **Supersedes:** Named NPC assignments in [D-034](#d-034-the-friend--production-level-npc-pattern) (Kael/Sera as hand-authored characters — see amendment on D-034)
|
||||
- **Cross-reference:** [D-123](#d-123-generative-ai-for-npc-content-templating-via-culture-vectors) (AI templating), [D-129](#d-129-npc-personality-traits--behavior-first-relationships-codified-for-systems) (NPC personality model)
|
||||
|
||||
### D-123: Generative AI for NPC content templating via culture vectors
|
||||
### D-123: Generative AI for NPC content — build-time authoring tool and runtime voice pipeline
|
||||
- **Date:** 2026-03-05
|
||||
- **Decision:** NPC content (dialogue pools, voice, vocabulary) is generated using generative AI with culture vectors, tone, and accent prompts as constraints. Culture vectors are the primary prompt constraint — they prevent the AI pipeline from defaulting to genre conventions. The AI pipeline is an authoring tool for content assembly, not a runtime system. Limited vocabulary acceptable at first; AI templating scales content as the generator matures.
|
||||
- **Rationale:** The copy pool for all-generated NPCs at scale is enormous. Generative AI with culture-vector constraints is the only viable path to populating it without hand-authoring every line. Culture profiles (Miri prerequisite) become the primary authoring deliverable feeding the pipeline.
|
||||
- **Source:** Where's the Fun? Workshop, Round 4 Interview, Decision 8
|
||||
- **Date (amended):** 2026-03-07
|
||||
- **Decision:** The AI pipeline operates in two distinct modes with different safety profiles:
|
||||
- **Build-time mode (authoring tool):** Content generated at build time for baked hub zones. Subject to mandatory human review before shipping. AI as an accelerated authoring tool producing content humans review and approve.
|
||||
- **Runtime mode (background enhancement):** Content generated during gameplay for non-baked zones, via a background inference queue, when "AI-Enhanced Dialogue" is enabled. Not human-reviewed per line. Safety provided by three layers: (1) base-text-as-fallback — always present and complete; (2) build-time-validated injectors — only pre-validated prompts used, never ad-hoc; (3) runtime contamination filter — lightweight check before content is served.
|
||||
- **Non-negotiable constraints (both modes):** Culture vectors are the primary prompt constraint. The AI does not default to genre conventions. Authorial control governs what the LLM may and may not produce through injector clauses, negative constraints, and pipeline routing rules. The AI pipeline applies voice to authored semantic content; it does not generate narrative decisions, base text, tell behaviors, secret-tier dialogue (D-028 Layer 3), or anchor lines (D-092). These categories are always authored and always served as-authored.
|
||||
- **Rationale:** Full pipeline (behaviors + dialogue) is the correct scope. A system that voices observed behavior but not spoken dialogue creates register whiplash at the highest-investment moment of player engagement. Build-time mode preserves the human-review safety model. Runtime mode enables scaling to the generated world with base-text fallback as the permanent safety net.
|
||||
- **Source:** Where's the Fun? Workshop (original); LLM Voice Pipeline Workshop (amendment)
|
||||
- **Raised by:** Team Leader (Jeroen)
|
||||
- **Dissent:** None
|
||||
- **Dissent:** None on amendment
|
||||
- **Amended by:** [D-138](#d-138-llm-re-voicing-pipeline-for-npc-voice) (LLM Voice Pipeline Workshop, 2026-03-07)
|
||||
- **Cross-reference:** [D-121](#d-121-voice-is-culture-driven--job-as-modifier) (culture-primary voice), [D-128](#d-128-culture-implicit-in-starting-location--krenn-system-equals-krenn-culture) (culture profile as generator input)
|
||||
|
||||
### D-124: In-game ollama for live NPC dialogue — deferred, door open
|
||||
### D-124: In-game ollama for live NPC dialogue — ~~deferred~~ SUPERSEDED by D-138
|
||||
- **Date:** 2026-03-05
|
||||
- **Decision:** Running a dressed-down version of ollama in-game for live NPC dialogue is possible and interesting, but deferred. The door is explicitly left open — this is not a rejected alternative, it is a future investigation item. For v0.2, NPC dialogue uses template-assembled content (D-123). Live in-game AI dialogue is post-proof-of-life.
|
||||
- **Rationale:** Live AI dialogue requires solving NPC quality floor, performance, and determinism questions that are out of scope for the generator proof-of-life. Deferred until the base generator is proven solid.
|
||||
- **Source:** Where's the Fun? Workshop, Round 4 Interview, Decision 9
|
||||
- **Date (superseded):** 2026-03-07
|
||||
- **Decision:** ~~Running a dressed-down version of ollama in-game for live NPC dialogue is possible and interesting, but deferred.~~ **Superseded by [D-138](#d-138-llm-re-voicing-pipeline-for-npc-voice).** The in-game AI system uses `llama-cpp-rs` (not ollama) with GGUF Q4_K_M quantization, bundled with the game, running background inference via an isolated thread pool. The key constraint from D-124 remains binding through D-123 (amended): this system does not drive live narrative decisions. It applies voice to authored semantic content.
|
||||
- **Rationale:** The LLM Voice Pipeline Workshop (2026-03-07) walked through the door D-124 left open. The quality, performance, and determinism questions D-124 cited as blockers are addressed by cache-as-determinism, base-text fallback, and layered hardware detection.
|
||||
- **Source:** Where's the Fun? Workshop (original); LLM Voice Pipeline Workshop (supersession)
|
||||
- **Raised by:** Team Leader (Jeroen)
|
||||
- **Dissent:** None
|
||||
- **Superseded by:** [D-138](#d-138-llm-re-voicing-pipeline-for-npc-voice)
|
||||
|
||||
### D-125: World is quietly responsive — gradient of caring by social proximity
|
||||
- **Date:** 2026-03-05
|
||||
@@ -400,6 +407,29 @@ How narrative, NPCs, and world content are created: content tiers, NPC generatio
|
||||
- **Dissent:** None
|
||||
- **Cross-reference:** [D-129](#d-129-npc-personality--traits--behavior-first-relationships-codified-for-systems) (relationships as consequence substrate)
|
||||
|
||||
### D-138: LLM Re-voicing Pipeline for NPC Voice
|
||||
- **Date:** 2026-03-07
|
||||
- **Decision:** NPC observable behaviors and dialogue are processed through an LLM re-voicing pipeline that translates culture-neutral semantic base text into character-voiced output. The pipeline is a background runtime enhancement, not a live generation system. Tell behaviors are base-text passthrough — always. Active tell state influences the re-voicing prompt for surrounding content (tells are read-only inputs to the LLM, never LLM outputs). The game is complete and functional without the pipeline; it is an enhancement that elevates voice quality for players with sufficient hardware.
|
||||
- **Architecture:**
|
||||
- **Model:** Gemma 2 2B (Q4_K_M, ~1.5GB), bundled with game. Phi-3 (MIT) as fallback. No Chinese-origin models.
|
||||
- **Runtime:** `llama-cpp-rs` with GGUF format. Separate inference thread pool at below-normal priority.
|
||||
- **Content tiers:** Baked (hub zones, build-time, human-reviewed) → Pre-voiced (background queue, priority-ordered) → Base text fallback (always present).
|
||||
- **Tell treatment:** Passthrough always. Tell state flows into re-voicing prompts as universal tone injectors. Cultural flavor is conditional and additive — humans are humans first; micro-expressions and body language must remain universally recognizable. Per-culture tell-tone tables are optional enrichment, not a launch requirement.
|
||||
- **Determinism:** Cache-as-determinism. LLM generates once per seed; result cached. Cache lookup is deterministic.
|
||||
- **Caching:** 6 variants per line (neutral + 5 TellCategory states). Key: `(npc_stable_id, line_id, tell_state, culture_id)`.
|
||||
- **Hardware:** "AI-Enhanced Dialogue" toggle. Layered detection: RAM check → TPT benchmark → recommendation. No hard minimum spec floor. Player can always override.
|
||||
- **Distribution:** Model bundled in game install (~1.5GB).
|
||||
- **Protected categories (never re-voiced):** Tell behaviors, secret-tier dialogue (D-028 Layer 3), anchor lines (D-092), relationship-specific lines naming third parties.
|
||||
- **Validation:** Two-spike strategy. Spike 1: Rust `sr-voice` CLI + manual prompt testing (Jeroen/Mellanie/Paula). Spike 2: full pipeline integration.
|
||||
- **Rationale:** D-122 (all NPCs generated) and D-128 (culture implicit in starting location) require NPC voice to scale across zones and cultures without O(R×Z×C) hand-authoring. The re-voicing model is the only architecture that scales while preserving content quality. Base-text fallback ensures the game is complete without the pipeline.
|
||||
- **Source:** LLM Voice Pipeline Workshop (2026-03-07)
|
||||
- **Raised by:** Team Leader (Jeroen), with Gestalt, Tyre, Paula, Mellanie, Ozzie, Miri, Troblum
|
||||
- **Dissent:** Miri flagged concern about cultural philosophy at 2B model size — addressed via hybrid injector format (instruction + example pairs) and spike validation.
|
||||
- **Amends:** [D-123](#d-123-generative-ai-for-npc-content--build-time-authoring-tool-and-runtime-voice-pipeline) (scope extended from authoring tool to authoring + runtime)
|
||||
- **Supersedes:** [D-124](#d-124-in-game-ollama-for-live-npc-dialogue--deferred-superseded-by-d-138) (in-game AI no longer deferred)
|
||||
- **Resolves:** Q-057 (composable behavior generation), Q-012 (generation expansion method)
|
||||
- **Cross-reference:** [D-010](architecture.md#d-010) (information boundaries), [D-121](#d-121-voice-is-culture-driven--job-as-modifier) (culture-primary voice), [D-122](#d-122-all-npcs-generated--named-npcs-deferred) (all NPCs generated), [D-128](#d-128-culture-implicit-in-starting-location--krenn-system-equals-krenn-culture) (culture as generator input), [D-029](#d-029-population-entanglement-ratio--305020) (NPC tier model), [D-092](perception.md#d-092) (anchor lines)
|
||||
|
||||
---
|
||||
|
||||
*37 decisions. Last updated: 2026-03-05 (D-121–D-132 added; D-023, D-024, D-028, D-029, D-032, D-034, D-036 amended; D-032 superseded — Where's the Fun? Workshop)*
|
||||
*38 decisions. Last updated: 2026-03-07 (D-138 added; D-123 amended; D-124 superseded — LLM Voice Pipeline Workshop)*
|
||||
|
||||
@@ -10,10 +10,11 @@ Narrative, NPCs, dialogue, templates, setting, worldbuilding, and storyteller me
|
||||
- **Assigned to:** Gestalt, Nigel
|
||||
|
||||
### Q-012: Generation expansion method for dialogue
|
||||
- **Status:** Open
|
||||
- **Status:** Resolved
|
||||
- **Question:** How does the 4x generation expansion pass work? LLM-based, template-based, or rule-based? Affects how base lines are authored — LLM needs style-strong anchors; rules need substitution patterns.
|
||||
- **Assigned to:** Gestalt, Mellanie
|
||||
- **Source:** Content Gap Analysis Workshop (Mellanie R2)
|
||||
- **Resolution:** LLM-based re-voicing via bundled Gemma 2B Q4. Culture-neutral semantic base text is the LLM seed; culture injectors + trait modifiers + tell-context tone shape the output. Resolved by [D-138](content.md#d-138-llm-re-voicing-pipeline-for-npc-voice) (LLM Voice Pipeline Workshop, 2026-03-07).
|
||||
|
||||
### Q-013: Line previewer temporal progression
|
||||
- **Status:** Open
|
||||
|
||||
@@ -0,0 +1,285 @@
|
||||
# LLM Voice Pipeline Workshop — Round 1: Systems Design Inventory
|
||||
|
||||
**Author:** Gestalt
|
||||
**Round:** 1 (Inventory — divergent)
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## What I Read
|
||||
|
||||
- `docs/architecture/proposed-llm-voice.md` — full proposal
|
||||
- `server/src/bin/generator_spike.rs` — full spike including gen_want, gen_tells, gen_behaviors, apply_relationship_behaviors
|
||||
- `server/src/npc/blueprint.rs` — NpcBlueprint, CulturalMarkers, ZoneSpec, CultureProfile
|
||||
- `server/src/npc/generate.rs` — gen_want, gen_tells, gen_skills, full 10-axis pipeline
|
||||
- `server/src/npc/mod.rs` — Want, WantKind, TellSystem, Tell, TellTrigger
|
||||
- `content/global/rural-zone-spec.ron` — current hand-authored quality bar
|
||||
- `content/global/culture-krenn.ron` — culture profile with void-oaths and speech register
|
||||
- `decisions/content.md` — D-121, D-122, D-123, D-128, D-024, D-029
|
||||
- `decisions/architecture.md` — D-010 (information boundaries)
|
||||
- `decisions/scope.md` — D-114, D-115, D-117, D-119
|
||||
- `decisions/questions-content.md` — Q-012, Q-015 (Q-057 not yet in decisions files — it must be the question this workshop is formally introducing)
|
||||
|
||||
---
|
||||
|
||||
## The Setup: What the Spike Proved and What It Exposed
|
||||
|
||||
Let me be concrete about what the generator actually does to behaviors before I evaluate any option.
|
||||
|
||||
The spike has two separate behavior pipelines:
|
||||
|
||||
**Pipeline 1 — Observable behaviors (blueprint.rs / generator_spike.rs):**
|
||||
Role-specific behaviors drawn from `RoleSpec.typical_behaviors`. These are the strings the player *sees* as ambient NPC activity. Current quality bar from rural-zone.ron:
|
||||
|
||||
> "stops to talk with a passing farmer, eyes still scanning the perimeter"
|
||||
> "wipes grease on the thigh of her coveralls between jobs"
|
||||
> "holds eye contact through a long pause, waiting for the price to land"
|
||||
|
||||
These are already composited observations — character + situation + cultural texture in a single image.
|
||||
|
||||
**Pipeline 2 — TellSystem (generate.rs / npc/mod.rs):**
|
||||
`gen_tells()` derives tells from PersonalityTraits + Secret severity. Each Tell has:
|
||||
- `trigger: TellTrigger` — `Always` or `StressAboveThreshold` (simulation state, never player-visible)
|
||||
- `behavior: String` — the observable string the player reads
|
||||
|
||||
Current tell strings:
|
||||
```
|
||||
Major secret + stress: "becomes evasive and avoids eye contact"
|
||||
Cautious + stress: "checks surroundings repeatedly"
|
||||
Deceptive + stress: "affects exaggerated calm"
|
||||
Honest + always: "makes direct eye contact"
|
||||
Social + always: "greets passersby unprompted"
|
||||
Curious + always: "lingers near unusual activity"
|
||||
```
|
||||
|
||||
These two pipelines serve fundamentally different functions. **This distinction is the most important thing in this document.**
|
||||
|
||||
---
|
||||
|
||||
## The Three Options Through a Systems Design Lens
|
||||
|
||||
### Option 1: Hand-authored pools (current)
|
||||
|
||||
**Mechanical evaluation:** Doesn't scale and already isn't scaling. The copy team's decision to rename `rural-zone-spec.ron` → `krenn-rural-zone.ron` is a canary. They made the combinatorial explosion explicit: each file is already culture-specific, not a reusable template.
|
||||
|
||||
The numbers from the brief (4 roles × ~50 behaviors × N zones × M cultures) understate the problem. With the 10-axis model (D-024), behaviors also need to vary across:
|
||||
- Secret severity (tells differ when secret is exposed vs. hidden)
|
||||
- Want intensity (high-intensity Want leaks differently than low)
|
||||
- Relationship state (behaviors toward specific people require relational context)
|
||||
|
||||
Hand-authored pools at full fidelity is O(R × Z × C × Want × Secret × Relationship). That's not thousands of lines — it's hundreds of thousands.
|
||||
|
||||
**What it preserves well:**
|
||||
- Total mechanical control. Every behavior string is a deliberate authorial decision.
|
||||
- Tells remain precisely authored micro-behaviors with no ambiguity.
|
||||
- Zero risk of lore contamination.
|
||||
|
||||
**Verdict:** Correct for Tier 1 authored content. Impossible at the generator scale D-122 requires.
|
||||
|
||||
---
|
||||
|
||||
### Option 2: Composable primitives (Q-057)
|
||||
|
||||
**The proposal:** Decompose behaviors into role actions + culture modifiers + context tags. Assemble at runtime.
|
||||
|
||||
**Mechanical evaluation:** This is a grammar engine, not a voice system. Let me break down why that matters.
|
||||
|
||||
The rural-zone.ron behaviors work because they're *already composed* — they ARE the composition, rendered as a unified observation:
|
||||
|
||||
> "stops to talk with a passing farmer, eyes still scanning the perimeter"
|
||||
|
||||
The compositional structure inside this is roughly: `[social_action][krenn_directness][vigilance_subtext]`. But you can't decompose it without destroying the observation. The thing that makes this line work is that the vigilance is *incidental* — the character is doing something social while their body does something watchful. That tension is the content. A grammar that assembles "SOCIAL_ACTION + VIGILANCE_TAG" produces:
|
||||
|
||||
> "talks to a farmer. Watches the perimeter."
|
||||
|
||||
That's not a worse version of the same thing. It's a fundamentally different kind of content — behavior report vs. observed character.
|
||||
|
||||
**The relationship behavior problem is worse.** Consider: "talks past Rask without making eye contact." This is:
|
||||
1. A social action with a specific named target
|
||||
2. A relational tell (avoidance encoded as action)
|
||||
3. A character-reads-character moment for the player
|
||||
|
||||
Composable primitives would need to represent this as `SOCIAL_BYPASS(target=Rask) + TELL(avoidance)`. But then the assembly problem: how do you compose "talking past someone" + "eye contact avoidance" into natural language without an LLM? You can't. You're back to either hand-authoring the assembly rules for every combination, or you need an LLM anyway to render the composed structure as prose.
|
||||
|
||||
**Composable primitives is useful as an authoring scaffold, not a runtime engine.** If we use it to structure how authors *think about* behaviors (role action + cultural modifier), that has value. As the player-facing output mechanism, it produces mechanical-feeling text.
|
||||
|
||||
**What it preserves well:**
|
||||
- Structural correctness — assembled behaviors are always valid
|
||||
- Scales through combination rather than enumeration
|
||||
- Explicitly tags mechanical content (good for downstream filtering)
|
||||
|
||||
**Verdict:** The right answer for the authoring schema; the wrong answer for the rendering layer.
|
||||
|
||||
---
|
||||
|
||||
### Option 3: LLM re-voicing
|
||||
|
||||
**The proposal:** Write culture-neutral semantic base lines. Use a 2B-class model to translate them into character voice using injector clauses (personality, culture, mood).
|
||||
|
||||
**Mechanical evaluation:** This is the most systems-compatible option for the rendering layer — IF we handle the tell separation correctly.
|
||||
|
||||
The i18n analogy is the right frame. Base text is `en-semantic`. Re-voiced text is `en-KRENN-BOLD`. The information is stable; the expression varies.
|
||||
|
||||
The proposal correctly identifies that base text serves triple duty: LLM seed, graceful fallback, and LLM-off experience. From a systems standpoint, this is elegant — one authored artifact doing three mechanical jobs simultaneously.
|
||||
|
||||
**Where it works cleanly:**
|
||||
- Ambient observable behaviors (pipeline 1): "tends crops in the field" → re-voiced to "works the irrigation channels before the morning rotation." The information class is *texture*, not *signal*. LLM variance here is fine.
|
||||
- Dialogue (what NPCs say): Culture and personality naturally belong in re-voicing.
|
||||
- Relationship framing (non-critical): "nods to a colleague" can be re-voiced without mechanical consequence.
|
||||
|
||||
**Where it introduces risk:**
|
||||
- **Tells (pipeline 2)**: This is the danger zone. I'll address this separately below.
|
||||
- **Lore contamination**: The Krenn vocabulary is load-bearing. "Void take it" is not flavor — it's a cultural signal that this NPC belongs to the Settled Reach, not to a generic sci-fi game. A 2B model that hasn't been heavily fine-tuned may default to genre conventions ("damn it", "blast", "stars and garters"). The culture profile has explicit vocabulary (void-oaths, void-adjacent exclamations) that must survive re-voicing intact.
|
||||
|
||||
**Verdict:** Right for the rendering layer. Requires architectural guardrails for mechanical content. The model size ceiling is the critical risk — addressed below.
|
||||
|
||||
---
|
||||
|
||||
## The Core Mechanical Problem: Tells Are Not Flavor Text
|
||||
|
||||
This is where I need to be direct because it's the most important systems design question in this workshop.
|
||||
|
||||
**Tells are mechanical signals.** The player reads them to infer NPC hidden state. They are the physical expression of the information asymmetry mechanic (D-007, D-010 principle 2). The player is not reading for entertainment — they are doing pattern recognition.
|
||||
|
||||
Current tell grammar:
|
||||
| Trigger | Trait | String | What it signals |
|
||||
|---------|-------|--------|-----------------|
|
||||
| Always | Honest | "makes direct eye contact" | Baseline positive tell |
|
||||
| StressAboveThreshold | Major secret | "becomes evasive and avoids eye contact" | Something is wrong |
|
||||
| StressAboveThreshold | Cautious | "checks surroundings repeatedly" | Anxiety/vigilance |
|
||||
| StressAboveThreshold | Deceptive | "affects exaggerated calm" | Suppression behavior |
|
||||
| Always | Curious | "lingers near unusual activity" | Interest tell |
|
||||
| Always | Bold | "maintains confident posture" | Baseline character texture |
|
||||
|
||||
The player who learns this grammar can read an NPC's stress level and trait structure from observation alone. That's the point. That's D-007 pillar 1 expressed as mechanics.
|
||||
|
||||
**Now apply LLM re-voicing to "becomes evasive and avoids eye contact":**
|
||||
|
||||
Good re-voicing (semantic preserved): "seems guarded today — won't quite hold your gaze"
|
||||
Acceptable re-voicing: "looks past you when speaking, answers in short clips"
|
||||
Bad re-voicing: "moves through the space with unusual quickness" — WRONG PHENOMENON
|
||||
Bad re-voicing: "seems nervous about the patrol schedule" — ADDED FALSE INFORMATION (crosses D-010 information boundary)
|
||||
Bad re-voicing: "seems different somehow" — LOST SIGNAL (vague, unreadable)
|
||||
|
||||
The bad variants aren't stylistically worse — they're *mechanically broken*. They either corrupt the signal or introduce false state information. A 2B model running local inference with injector clauses cannot be trusted to reliably distinguish "rephrase" from "replace the phenomenon" when processing 10-word behavior strings at scale.
|
||||
|
||||
**The asymmetric information question from the brief:**
|
||||
|
||||
"If the same tell reads differently to different players due to phrasing variation, is that a feature or a bug?"
|
||||
|
||||
**It's a bug, not a feature.** Here's why:
|
||||
|
||||
The game's core promise (D-005, D-007) is that asymmetric information is a *skill* — players who observe carefully develop a mental model that gives them better reads on NPC state. Variance in tell phrasing undermines tell literacy. If "becomes evasive" appears as five different phrasings across five NPCs, the player can't learn the pattern.
|
||||
|
||||
The "emergent asymmetric information" framing would only apply if *different players seeing different phrasings* produced different reads — which would be interesting — but the tell-reading skill is about *the same player* developing pattern recognition across encounters. Phrasing variance across NPCs makes that pattern harder to learn, not more interesting.
|
||||
|
||||
Emergent asymmetric information comes from the INFORMATION STRUCTURE (what the player knows vs. what the NPC knows), not from phrasing variance. LLM variance in flavor text is genuinely emergent. LLM variance in mechanical signals is noise.
|
||||
|
||||
---
|
||||
|
||||
## The Tell Protection Architecture
|
||||
|
||||
Here's what I'm proposing we debate in Round 2:
|
||||
|
||||
**Two-track re-voicing based on content type:**
|
||||
|
||||
| Content type | Re-voicing mode | Rationale |
|
||||
|---|---|---|
|
||||
| Ambient observable behaviors | Free re-voicing | Texture. No mechanical signal. Variance is flavor. |
|
||||
| Relationship framing (non-tell) | Constrained re-voicing | Preserve who/what. Vary the expression. |
|
||||
| Relationship tells | Tracked re-voicing | Phenomenon must be preserved. Only vocabulary/register varies. |
|
||||
| TellSystem behaviors | Locked semantic core | The phenomenon is the mechanical content. Culture-voice the expression, never replace the phenomenon. |
|
||||
| Dialogue (general) | Free re-voicing | Character voice. Culture and mood. |
|
||||
| Dialogue (state-revealing) | Tracked re-voicing | Cannot introduce false state. Must preserve the information class. |
|
||||
|
||||
**"Locked semantic core" implementation sketch:**
|
||||
|
||||
Each tell in `gen_tells()` carries a `semantic_core` alongside the behavior string:
|
||||
|
||||
```
|
||||
Tell {
|
||||
trigger: TellTrigger::StressAboveThreshold,
|
||||
behavior: "becomes evasive and avoids eye contact", // base text / fallback
|
||||
semantic_core: Some("avoidance_behavior"), // re-voicing constraint
|
||||
}
|
||||
```
|
||||
|
||||
The re-voicing prompt for a tell becomes:
|
||||
> "Rephrase in [culture] register. PRESERVE: [semantic_core]. Do not add information. Do not change the observable phenomenon."
|
||||
|
||||
This is a constrained task — much easier for a 2B model than free generation. The model is doing localization, not creation. The semantic_core tag is the guardrail.
|
||||
|
||||
**Who adds semantic_core tags?** This is an authoring question for Mellanie to answer. My recommendation: they're authored alongside the behavior strings in `gen_tells()` at implementation time. It's a small schema addition. The cost is low; the mechanical protection is high.
|
||||
|
||||
---
|
||||
|
||||
## What Breaks If We Choose the Wrong Option
|
||||
|
||||
### Wrong choice: Hand-authored pools at scale
|
||||
**Domain break:** The generator produces shallow characters. D-122 requires all NPCs generated; D-128 requires culture implicit in location. Without scalable content, Krenn-flavor NPCs are indistinguishable from generic sci-fi archetypes. The want/state/tell pipeline is fully functional, but the behavioral surface those tells appear in is thin and repetitive. The player's tell literacy grows but they have nothing interesting to read.
|
||||
|
||||
### Wrong choice: Composable primitives as runtime output
|
||||
**Domain break:** The system produces mechanically correct but experientially hollow characters. The "assembled" quality is noticeable — behaviors read like database queries, not observed humans. Worse: relationship behaviors composed from primitives lose the observational unity that makes them readable. "Colleague + avoidance behavior" reads differently than "walks past Rask without making eye contact." The second one is investigative data. The first one is a tag list rendered as prose.
|
||||
|
||||
The deeper break: composable primitives require a composition render step that either uses an LLM (in which case, just use LLM re-voicing with structure) or produces mechanical output (which breaks the experiential quality). You end up needing both systems and gaining the complexity of each.
|
||||
|
||||
### Wrong choice: LLM re-voicing without tell protection
|
||||
**Domain break:** Tell literacy becomes unteachable. Players who invest in learning the observational grammar find that the patterns don't hold — the same Tell trigger appears with different phenomenological signatures across NPCs. The entire mechanic underpinning D-007 pillar 1 degrades from "skill you develop" to "noise you sometimes parse correctly."
|
||||
|
||||
Specific failure mode: a Deceptive NPC under stress should read as "suppression" (affects exaggerated calm). If LLM re-voicing outputs "seems guarded and formal" half the time and "stays very still and quiet" the other half — both are valid re-voicings, neither is wrong in isolation — but the player can't learn to recognize "suppression" as a pattern. The information is technically in the text, but the grammar is unstable.
|
||||
|
||||
Lore contamination failure: the LLM introduces "thanks be to the Maker" (generic religious flavor) instead of "void take it" (Krenn void-oath). Now we have an NPC that sounds like they're from a fantasy game. Culture-vector injectors mitigate this but don't eliminate it at 2B model size.
|
||||
|
||||
---
|
||||
|
||||
## My Position
|
||||
|
||||
**The right architecture is LLM re-voicing with tell protection.**
|
||||
|
||||
More specifically, the hybrid that Q-057/Q-012 have been circling around:
|
||||
|
||||
1. **Composable primitives as the authoring scaffold** — structure how authors specify behaviors (role action, cultural modifier, relationship context). This is the schema, not the rendering layer.
|
||||
|
||||
2. **LLM re-voicing as the rendering layer** — translate semantic base text into culture-voiced output. Free re-voicing for ambient behaviors. Tracked re-voicing for relationship content. Locked semantic core for tells.
|
||||
|
||||
3. **Tells are excluded from free re-voicing entirely** — they are authored once (or generated with gen_tells + semantic_core tags) and culture-voiced with explicit semantic preservation constraints.
|
||||
|
||||
4. **The NPC tier model maps cleanly to re-voicing intensity:**
|
||||
- D-029's 20% ambient → free re-voicing, no tell constraints needed (few systemic hooks)
|
||||
- D-029's 50% background → constrained re-voicing, relationship behaviors tracked
|
||||
- D-029's 30% semi-unique → tracked re-voicing across all content, semantic_core on all tells
|
||||
- Tier 1 authored (deferred per D-114) → hand-authored, no LLM
|
||||
|
||||
This architecture respects D-010 (information boundaries — the LLM cannot introduce false game state), D-121 (culture-primary voice — the injector system does this correctly), D-122 (all NPCs generated at scale — only LLM re-voicing makes this viable), and the five pillars (D-007 — asymmetric information as master mechanic requires tell reliability).
|
||||
|
||||
The base text / re-voiced text / graceful fallback model is elegant systems design. Write once, serve three purposes. I support it.
|
||||
|
||||
---
|
||||
|
||||
## What I Need Answered Before I Can Commit
|
||||
|
||||
**The player's tell literacy model: cross-NPC grammar or fresh-each-time?**
|
||||
|
||||
If players are expected to develop a **cross-NPC grammar** — "I've seen that eye-contact pattern three times, it means deception tell under stress" — then tell strings must be semantically stable not just per-NPC but across all NPCs. The re-voicing of tells must produce consistent phenomenological families. "Evasive" across Krenn culture should look like a recognizable family of avoidance behaviors, not random variance.
|
||||
|
||||
If tells are **fresh-each-time** (each observation is context-specific, players interpret them in isolation rather than pattern-matching), then more variance is acceptable, because the player isn't building a grammar — they're reading individual characters.
|
||||
|
||||
The 10-axis model (D-024) and the Q-052 analysis of storyteller hint delivery both suggest cross-NPC grammar is the intent. The D-039 wow moments include "The Character's Eye" — the player's character flagging something the player didn't notice, which implies the player is expected to develop pattern recognition that the game can get ahead of.
|
||||
|
||||
If the answer is cross-NPC grammar (which I believe it is), then tell re-voicing must produce **semantic families**, not free variation. "Avoidance" behaviors must all read as avoidance. "Suppression" behaviors must all read as suppression. The LLM prompt for tell re-voicing must specify the semantic family explicitly, and output validation must check phenomenological consistency against the family.
|
||||
|
||||
This also determines how we define success criteria for the spike: not just "does the output sound good?" but "can a naive player develop reliable tell-reading from these outputs?"
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Question from brief | Gestalt position |
|
||||
|---|---|
|
||||
| Re-voice observable behaviors, dialogue, or both? | Both, with different modes per content type |
|
||||
| Does re-voicing preserve the tell? | Only with explicit architectural protection (tracked re-voicing + semantic_core) |
|
||||
| How does determinism work? | Generate once per seed and cache. Tells should be deterministic per NPC (same culture + same trait = same semantic family). |
|
||||
| Which NPC tiers get which pipeline? | Ambient: free re-voice. Background: constrained. Semi-unique: tracked. Tier 1 (deferred): hand-authored. |
|
||||
| Is tell variance a feature or a bug? | Bug. Tell literacy requires semantic stability across encounters. |
|
||||
| What breaks with wrong option? | Hand-authored: shallow content at scale. Composable: hollow output, broken relationship behaviors. LLM without protection: tell grammar unteachable. |
|
||||
| One question before commit | Is the player's tell literacy model cross-NPC grammar or fresh-each-time? |
|
||||
@@ -0,0 +1,267 @@
|
||||
# LLM Voice Pipeline Workshop — Round 2: Systems Design Evaluation
|
||||
|
||||
**Author:** Gestalt
|
||||
**Round:** 2 — Convergent Evaluation
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## Resolving Q-R1-01: The Tell Literacy Model
|
||||
|
||||
**Question:** Is the player's tell literacy model cross-NPC grammar (players learn "avoidance = hiding something" across all NPCs) or fresh-each-time (each NPC's tells are unique)?
|
||||
|
||||
**Answer: Cross-NPC grammar — specifically, phenomenon-class grammar.**
|
||||
|
||||
Here's the argument from the evidence.
|
||||
|
||||
### Evidence in the codebase and design decisions
|
||||
|
||||
`gen_tells()` in `generate.rs` generates tells from trait+trigger combinations. The behavior strings are constants — same trait, same trigger, always the same string:
|
||||
|
||||
```
|
||||
Deceptive + StressAboveThreshold → "affects exaggerated calm" (always)
|
||||
Cautious + StressAboveThreshold → "checks surroundings repeatedly" (always)
|
||||
Major secret + stress → "becomes evasive and avoids eye contact" (always)
|
||||
```
|
||||
|
||||
The generator produces at most ~12 distinct tell behavior strings in the entire game. This is not an accident — it's a grammar. The design intent is that these strings represent recognizable classes of observable behavior that the player can learn to associate with internal states.
|
||||
|
||||
Q-052 makes the learning model explicit: "Hours 1-5: full hints. Hours 15+: player reads the world by behavioral tells alone. Not harder combat — a quieter, more trusting world." The game has a teacher that backs off as the player develops tell literacy. That only works if there IS a tell literacy to develop — a learnable grammar, not random case-by-case observation.
|
||||
|
||||
D-039 wow moment #2 ("The Character's Eye") is the tell literacy game stated directly: "My character is smarter than me." The character's internal monologue flags something the player missed. This moment only works if (a) there was a tell in the observable space and (b) the player hadn't yet learned to see it. The game is explicitly modeling a skill gap the player closes over time.
|
||||
|
||||
**The grammar works at the phenomenon class level, not the phrasing level.**
|
||||
|
||||
This is the crucial nuance. The player doesn't learn "when I see the exact string 'affects exaggerated calm' that means Deceptive+stress." They learn "when I see suppression behavior — exaggerated stillness, forced normalcy — that NPC is hiding something consciously." The phenomenon class (suppression, avoidance, surveillance, fidgeting) is the unit of pattern recognition.
|
||||
|
||||
### Implications for Proposal B
|
||||
|
||||
This is directly relevant to whether Proposal B's constrained re-voicing is safe.
|
||||
|
||||
**Constrained re-voicing is safe IF the constraint preserves phenomenon class membership.**
|
||||
|
||||
The failure mode is phenomenon class migration:
|
||||
- "affects exaggerated calm" → "seems composed and unhurried" — still suppression class, SAFE
|
||||
- "affects exaggerated calm" → "looks away when you approach" — avoidance class instead of suppression, BROKEN
|
||||
- "affects exaggerated calm" → "moves with unusual speed" — hurried class, completely broken signal
|
||||
|
||||
The `semantic_core` constraint must be written at the phenomenon-class level, not just as an abstract label. This matters for implementation:
|
||||
|
||||
| Too abstract (unreliable) | Precise (reliable for 2B model) |
|
||||
|---|---|
|
||||
| `"PRESERVE: suppression_behavior"` | `"PRESERVE: forced calm. The NPC appears deliberately composed and unhurried. Must not show avoidance, fidgeting, or hurry."` |
|
||||
| `"PRESERVE: avoidance_behavior"` | `"PRESERVE: eye contact avoidance. The NPC avoids holding your gaze. Must not show aggression or forced calm."` |
|
||||
| `"PRESERVE: surveillance_behavior"` | `"PRESERVE: environmental scanning. The NPC checks their surroundings and aware of exits. Must not show avoidance or stillness."` |
|
||||
|
||||
The abstract label is a human-readable tag. The precise constraint is what actually guides a 2B model reliably. If we implement Proposal B, `semantic_core` should store the precise constraint language, not just the category name.
|
||||
|
||||
For 5-15 word tells, constrained re-voicing is actually EASIER for the 2B model than free re-voicing of ambient behaviors. The input is short, the output should be short, the constraint is explicit. This is the regime where small instruction-following models perform most reliably.
|
||||
|
||||
**Conclusion on Q-R1-01:** Cross-NPC grammar at the phenomenon-class level. Proposal B's constrained re-voicing is safe with precise `semantic_core` language. Proposal A (passthrough) is also safe — it's the conservative floor, not the optimum.
|
||||
|
||||
---
|
||||
|
||||
## Resolving Q-R1-03: The Tell Data Model Separation
|
||||
|
||||
**Question:** Is separating `tell_behaviors` from `observable_behaviors` in the data model implementable and correct?
|
||||
|
||||
**Answer: Yes, and the pipeline architecture makes it natural. But the implementation requires understanding where tells actually live.**
|
||||
|
||||
### Where tells currently live
|
||||
|
||||
The codebase has a structural split I need to be clear about, because it affects how "implementable" this is:
|
||||
|
||||
**In the blueprint pipeline** (`npc/blueprint.rs`, `generator_spike.rs`):
|
||||
```rust
|
||||
pub struct NpcBlueprint {
|
||||
pub observable_behaviors: Vec<String>, // from RoleSpec.typical_behaviors
|
||||
// No tell_behaviors field — tells are not currently in the blueprint
|
||||
}
|
||||
```
|
||||
|
||||
**In the ECS pipeline** (`npc/generate.rs`, `npc/mod.rs`):
|
||||
```rust
|
||||
// TellSystem is a separate ECS component — generated from traits + secret at spawn time
|
||||
fn gen_tells(traits: &PersonalityTraits, secret: &Secret) -> TellSystem { ... }
|
||||
```
|
||||
|
||||
Tells are not authored — they're generated by `gen_tells()` from trait + secret combinations. They don't appear in RON files. They're computed at entity spawn time.
|
||||
|
||||
For the re-voicing pipeline to work cleanly, tells need to be accessible before they hit the ObserverSnapshot. The correct implementation:
|
||||
|
||||
### Proposed data model change
|
||||
|
||||
**Step 1: Add `semantic_core` to `Tell`** (in `npc/mod.rs`):
|
||||
```rust
|
||||
pub struct Tell {
|
||||
pub trigger: TellTrigger,
|
||||
pub behavior: String, // base text / passthrough / re-voiced
|
||||
pub semantic_core: String, // re-voicing constraint (precise phenomenon description)
|
||||
}
|
||||
```
|
||||
|
||||
The `semantic_core` for each tell is authored alongside the behavior strings in `gen_tells()`. There are ~12 distinct tell types — this is a one-time authoring task of 12 constraint sentences.
|
||||
|
||||
**Step 2: Add `tell_behaviors` to `NpcBlueprint`** (in `npc/blueprint.rs`):
|
||||
```rust
|
||||
pub struct NpcBlueprint {
|
||||
pub observable_behaviors: Vec<String>, // ambient — route to free re-voicing
|
||||
pub tell_behaviors: Vec<TellBehavior>, // mechanical — route to constrained/passthrough
|
||||
}
|
||||
|
||||
pub struct TellBehavior {
|
||||
pub behavior: String, // base text
|
||||
pub semantic_core: String, // re-voicing constraint
|
||||
pub trigger_type: String, // "always" or "stress" (for documentation, not gameplay use)
|
||||
}
|
||||
```
|
||||
|
||||
**Step 3: Produce tells at blueprint time** via a standalone function that mirrors `gen_tells()` without ECS:
|
||||
```rust
|
||||
// New function in generator pipeline (not requiring World)
|
||||
fn gen_tell_behaviors_for_blueprint(
|
||||
traits: &[PersonalityTrait],
|
||||
secret_severity: SecretSeverity,
|
||||
) -> Vec<TellBehavior> { ... }
|
||||
```
|
||||
|
||||
This mirrors the existing pattern in `generator_spike.rs`, which already reimplements ECS-level logic as standalone functions for blueprint generation.
|
||||
|
||||
### Why this separation is correct
|
||||
|
||||
**1. The pipeline routing is by field, not content inference.**
|
||||
|
||||
Paula and Mellanie both flagged this requirement. The re-voicing pipeline must route based on the structural location of the string (which field it came from), not by analyzing whether the string looks like a mechanical signal. Content analysis is fragile; field routing is deterministic.
|
||||
|
||||
With `observable_behaviors` and `tell_behaviors` as separate fields, the routing rule is trivial:
|
||||
```
|
||||
NpcBlueprint.observable_behaviors → free re-voicing queue
|
||||
NpcBlueprint.tell_behaviors → locked/constrained queue
|
||||
```
|
||||
|
||||
No content parsing. No heuristics. The data model enforces the distinction.
|
||||
|
||||
**2. It respects D-010 (information boundaries) structurally.**
|
||||
|
||||
D-010 principle 2: "every piece of game state is tagged with who knows it." Tell behaviors are a specific class of observable state — they're what the player can observe about the NPC's internal state. Tagging them as a distinct field makes the information class explicit in the data model, not implicit in editorial convention.
|
||||
|
||||
**3. The ECS `TellSystem` is unaffected.**
|
||||
|
||||
The existing `TellSystem` ECS component remains as the runtime authoritative source. The blueprint's `tell_behaviors` is the pre-generation staging ground. At entity spawn time, the ECS pipeline can:
|
||||
- Load tell behaviors from the pre-voiced cache (if available)
|
||||
- Fall back to the `gen_tells()` generated base text (if not)
|
||||
- The `TellSystem` struct may optionally store both the base text and the voiced text for graceful fallback
|
||||
|
||||
This is the same pattern as the broader base-text/voiced-text architecture. Tells get the same fallback model as everything else.
|
||||
|
||||
**4. The authoring burden is minimal.**
|
||||
|
||||
`gen_tells()` currently has ~12 tell types, each with a one-line behavior string. Adding `semantic_core` is 12 additional sentences written once by Gestalt or Tyre at implementation time. The copy team doesn't author tells (they're generated algorithmically) — so this doesn't create copy team overhead.
|
||||
|
||||
**Conclusion on Q-R1-03:** Implementable and correct. The separation exists already at the ECS level (`TellSystem` as a distinct component). Adding it to the blueprint and routing by field (not content) is the right architectural expression of that separation. The `semantic_core` field on `Tell` is the mechanism that makes Proposal B work.
|
||||
|
||||
---
|
||||
|
||||
## Proposal Evaluation
|
||||
|
||||
### Proposal A: Conservative — Behaviors Only, Tells Locked
|
||||
|
||||
**Recommendation: Acceptable, not preferred.**
|
||||
|
||||
**Does it work mechanically?** Yes. Base text passthrough for tells is absolutely safe. The phenomenon-class grammar works in base text form — the current tell strings ("becomes evasive and avoids eye contact") are clear enough that cross-NPC pattern recognition is possible.
|
||||
|
||||
**What it misses:** Cultural texture on tells. A Krenn person avoiding eye contact should look different from whatever a high-register culture's evasion looks like. D-121 (voice is culture-driven) applies to tells too — a Krenn NPC's deception tell should read like a Krenn person hiding something, not like a generic sci-fi NPC hiding something. Passthrough surrenders this.
|
||||
|
||||
Ozzie's "contrast is a feature" argument deserves consideration. Base text standing out against voiced ambient behaviors might make tells MORE immediately recognizable — they read differently precisely because they weren't culture-voiced. This is a legitimate UX argument, not just a consolation prize for the conservative choice. I don't know if it's empirically correct; it's a testable hypothesis.
|
||||
|
||||
**Can I live with Proposal A?** Yes.
|
||||
|
||||
**Minimum change to make it acceptable:** None needed — it's already acceptable. If we choose A, I'd request we commit to revisiting tell culture-voicing as a v0.3 task once we've validated the base system. The phenomenon-class grammar holds in base text; we're just leaving cultural texture on the table.
|
||||
|
||||
---
|
||||
|
||||
### Proposal B: Two-Track — Behaviors + Constrained Tell Re-voicing
|
||||
|
||||
**Recommendation: Preferred.**
|
||||
|
||||
**Does it work mechanically?** Yes, with the `semantic_core` precision requirement from Q-R1-01 above. The constrained re-voicing task is well-suited to a 2B model: short input, explicit constraint, short output. This is easier than free re-voicing of longer ambient behaviors.
|
||||
|
||||
**The spike test for this proposal must answer:** Does Gemma 2B reliably stay within the phenomenon class when given precise constraint language? This is a concrete, measurable success criterion: author 12 tell re-voicing prompts (one per tell type), run 20 completions each, score by phenomenon-class preservation. If ≥18/20 stay in the correct class for each tell type, the approach is viable. If not, fall back to passthrough (Proposal A) for tells.
|
||||
|
||||
**Interaction with the data model (Q-R1-03):** Proposal B requires `tell_behaviors` as a first-class field AND `semantic_core` on each tell. The data model change described in Q-R1-03 is a prerequisite for B, not optional.
|
||||
|
||||
**The one complexity:** The `semantic_core` language must be authored carefully. 12 sentences, but they're precision-critical. I'd recommend reviewing them against actual 2B model outputs during the spike before committing them as the canonical constraint text.
|
||||
|
||||
**Can I live with Proposal B?** Yes — it's my recommendation.
|
||||
|
||||
---
|
||||
|
||||
### Proposal C: Full Pipeline — Behaviors + Dialogue, Tells Locked
|
||||
|
||||
**Recommendation: Conditionally acceptable. Depends on Tyre and Troblum's Round 2 assessment.**
|
||||
|
||||
**Does it work mechanically?** Probably, but with meaningful scope risk. Dialogue has structural protection (access tier, trust tier tags — Paula's correct observation) that ambient behaviors don't. The dialogue system's existing tag model is actually better-suited to constrained re-voicing than ambient behaviors are.
|
||||
|
||||
**But:** The scope increase is real. Two content types, two prompt templates, two validation passes, two quality bars. The spike becomes more complex. If dialogue quality at 2B is insufficient, we're forced into a larger model that may violate the hardware budget (C-6 established Q4 as hard requirement on 8GB shared RAM).
|
||||
|
||||
**From a systems standpoint:** The information-safety question for dialogue is different from tells. Dialogue can leak game state in ways that tells don't — an NPC who "shouldn't know" something could be prompted to say it via a poorly constrained re-voicing prompt. The access tier and trust tier tags mitigate this structurally, but the LLM can still hallucinate knowledge beyond the tag constraints. This is a lore contamination risk class that doesn't exist for observable behaviors.
|
||||
|
||||
**My preference on sequencing:** A or B first (validate the simpler problem), C after the spike proves the pipeline. Proposal C is the right end state. Getting there via A→C or B→C is safer than attempting C in the first spike.
|
||||
|
||||
**Can I live with Proposal C?** Yes, if Tyre confirms 2B quality is sufficient for dialogue AND Troblum confirms the RAM budget holds for the additional prompt context.
|
||||
|
||||
**Minimum change to make C acceptable:** Define a failure mode and fallback for dialogue quality. If the 2B model doesn't meet bar for dialogue, the fallback shouldn't be "abandon the whole pipeline" — it should be "scope to behaviors only (falling back to Proposal A or B)." This needs to be built into the spike design.
|
||||
|
||||
---
|
||||
|
||||
## Resolution Matrix
|
||||
|
||||
| Question | Answer |
|
||||
|----------|---------|
|
||||
| Which proposal do you recommend? | **B** (Two-Track) |
|
||||
| Blockers in Proposal B? | `tell_behaviors` field + `semantic_core` on `Tell` required before spike design. Scope is well-defined and achievable. |
|
||||
| Can you live with Proposal A? | Yes |
|
||||
| Can you live with Proposal C? | Yes, conditionally (see above) |
|
||||
| Minimum change to Proposal A? | None needed for acceptability. Commit to tell culture-voicing as future work. |
|
||||
| Minimum change to Proposal C? | Define explicit fallback to behaviors-only if dialogue quality fails the spike. |
|
||||
|
||||
---
|
||||
|
||||
## Additional Systems Notes for Round 2
|
||||
|
||||
### On the T-3 composable primitives question
|
||||
|
||||
Round 1 notes document T-3 as "artifact or reject?" for composable primitives. My Round 2 position: **not needed in the architecture.**
|
||||
|
||||
The two-track re-voicing system (base text + injector clauses) subsumes what composable primitives were trying to achieve. Composable primitives were an attempt to make the authoring generative without LLM help — assemble behaviors from components at runtime. The LLM re-voicing approach does the same job more cleanly: authors write complete, evocative base lines (which they already have from the Sprint 25 spike), and the LLM applies cultural voice. The composition step IS the LLM.
|
||||
|
||||
The authoring benefit of composable primitives (structured thinking about role + culture + context) is real but can be captured in the injector clause design without building a composition engine. Injector clause authoring guides authors toward the same structured thinking without requiring a formal grammar system.
|
||||
|
||||
Composable primitives: closed as a rendering layer, not needed as schema.
|
||||
|
||||
### On D-123 amendment language
|
||||
|
||||
All three proposals amend D-123 ("authoring tool, not runtime system"). The correct framing:
|
||||
|
||||
> D-123 is amended: "NPC content (dialogue pools, voice, vocabulary) is generated using generative AI with culture vectors as primary constraints. The AI pipeline functions as both an authoring tool for batch content generation AND a background runtime system for on-demand pre-voicing. The runtime component generates content in the background before the player arrives, caches the result, and uses base text as graceful fallback. Culture profiles remain the primary authoring deliverable."
|
||||
|
||||
This preserves the spirit of D-123 (culture vectors as primary constraint, culture profiles as the deliverable) while being honest that the runtime system is in-game. D-124 is superseded.
|
||||
|
||||
### On the spike success criteria
|
||||
|
||||
For my domain (systems design), the spike must answer:
|
||||
1. Does behavior re-voicing preserve cultural identity? (Run Krenn-voiced output past Miri's criteria)
|
||||
2. For Proposal B: does constrained tell re-voicing preserve phenomenon class? (12 tell types × 20 completions, score by class preservation rate — target ≥90%)
|
||||
3. Does the tell literacy grammar hold after re-voicing? (Can a naive reader identify "avoidance" vs. "suppression" vs. "surveillance" from the re-voiced tells?)
|
||||
|
||||
Criterion 3 is the real test. The spike should include a blind evaluation: show 10 re-voiced tell strings to someone who hasn't read the original base text, ask them to categorize the behavior. If they can reliably assign to the correct phenomenon class, the grammar survived re-voicing.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Q-R1-01 resolved:** Cross-NPC grammar at the phenomenon-class level. Proposal B is safe with precise `semantic_core` language that specifies the phenomenon concretely ("the NPC avoids holding eye contact") not just abstractly ("avoidance_behavior").
|
||||
|
||||
**Q-R1-03 resolved:** Correct and implementable. Tells are already separated from ambient behaviors at the ECS level. Adding `tell_behaviors: Vec<TellBehavior>` to `NpcBlueprint` and `semantic_core: String` to `Tell` is the natural expression of that existing separation. Field routing (not content analysis) is the correct implementation model.
|
||||
|
||||
**Proposal recommendation: B**, with the understanding that A is acceptable and C is the right long-term destination.
|
||||
@@ -0,0 +1,334 @@
|
||||
# LLM Voice Pipeline Workshop — Round 3: D-record, Tell-as-Context Design, Spike 1 Criteria
|
||||
|
||||
**Author:** Gestalt
|
||||
**Round:** 3 — Decision and Commitment
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## 1. D-138 Draft: LLM Re-voicing Pipeline for NPC Voice
|
||||
|
||||
**Claimed ID:** D-138
|
||||
**Domain:** content
|
||||
**Status:** DRAFT — requires SI to formally record in decisions/content.md
|
||||
|
||||
---
|
||||
|
||||
### D-138: LLM re-voicing pipeline for NPC voice
|
||||
|
||||
- **Date:** 2026-03-07
|
||||
- **Decision:** NPC observable behaviors and dialogue are processed through an LLM re-voicing pipeline that translates culture-neutral semantic base text into character-voiced output. The pipeline is a background runtime enhancement, not a live generation system. Tell behaviors are base-text passthrough — always. Active tell state influences the re-voicing prompt for surrounding content without the tell text itself being re-voiced. The game is complete and functional without the pipeline; it is an enhancement that elevates voice quality for players with sufficient hardware.
|
||||
|
||||
**Architecture:**
|
||||
|
||||
| Layer | What | How |
|
||||
|---|---|---|
|
||||
| Semantic base text | Culture-neutral behaviors and dialogue | Authored in RON files; serves as LLM seed, graceful fallback, and LLM-off experience simultaneously |
|
||||
| Tell behaviors | Mechanical signals (avoidance, suppression, surveillance, etc.) | Base-text passthrough — NEVER sent to LLM. Always served as authored. |
|
||||
| Tell context injectors | Active tell state influence on surrounding content | Per-TellCategory tone instructions that shape how behaviors/dialogue are re-voiced; tells inform without being re-voiced |
|
||||
| Culture injectors | Culture-specific voice (register, oath vocabulary, negatives) | 150–250 tokens per culture; sourced from CultureProfile.speech; negative injectors in shared prefix |
|
||||
| Trait + mood modifiers | Personality and current emotional state | ~10 tokens each; layered atop culture injector |
|
||||
| Re-voiced output | Cached, player-facing voiced content | Generated per (NPC × tell_state × culture); cached at generation time; served at runtime by cache lookup |
|
||||
|
||||
**Content tiers:**
|
||||
|
||||
1. **Baked** — Hub zones (Sova Transit District and other major locations) ship with pre-voiced content generated at build time, human-reviewed before shipping. This is the quality reference and the player's first-hours experience.
|
||||
2. **Pre-voiced** — Background queue generates voiced content for adjacent zones before the player arrives. Priority: plot-critical NPCs first, then semi-unique, then ambient. Queue processes in a separate thread pool at below-normal priority.
|
||||
3. **Base text fallback** — If pre-voicing hasn't completed, base text is served. Designed to be neutral, not broken. Pre-voicing catches up in the background; voiced content is ready on the player's next visit.
|
||||
|
||||
**Tell-state variant caching:** For each behavior and dialogue line, the pipeline pre-voices one version per TellCategory state (Neutral + Nervous + Angry + Friendly + Guarded + RoutineDeviation = 6 variants). At runtime, the game reads the NPC's current active tell state and serves the matching pre-voiced variant. No runtime inference is triggered by tell-state changes — it is a cache lookup.
|
||||
|
||||
**Data model changes:**
|
||||
|
||||
```rust
|
||||
// NpcBlueprint — tell_behaviors as first-class field, routing by field not content
|
||||
pub struct NpcBlueprint {
|
||||
pub observable_behaviors: Vec<String>, // → free re-voicing queue
|
||||
pub tell_behaviors: Vec<TellBehavior>, // → base-text passthrough always
|
||||
// ...
|
||||
}
|
||||
|
||||
pub struct TellBehavior {
|
||||
pub category: TellCategory, // Nervous | Angry | Friendly | Guarded | RoutineDeviation
|
||||
pub base_text: String, // base text — also the final shipped text
|
||||
}
|
||||
|
||||
// Individual lines — anchor line protection (Paula, N-2)
|
||||
pub struct VoicedLine {
|
||||
pub base_text: String,
|
||||
pub anchor_line: bool, // true = passthrough regardless of field; protects Tier 1/2 notable NPC lines
|
||||
}
|
||||
```
|
||||
|
||||
**Model provenance:** Gemma 2B (Google) primary, quantized Q4_K_M (~1.5GB). Phi-3 (Microsoft) as fallback if Gemma 2B fails quality bar in the spike. No Chinese-origin models (Qwen/Alibaba excluded). Reconsider only if both candidates fail benchmarks.
|
||||
|
||||
**Inference runtime:** `llama-cpp-rs` with GGUF Q4_K_M quantization. Separate thread pool from world generation to prevent memory bandwidth contention.
|
||||
|
||||
**Hardware detection (layered, no hard floor):**
|
||||
1. RAM check — can the model load alongside the game?
|
||||
2. Time-per-token benchmark on first enable — background inference latency estimate
|
||||
3. Recommendation to disable if below threshold; player can always override
|
||||
4. "AI-Enhanced Dialogue" toggle always present — OFF delivers base text everywhere
|
||||
|
||||
**Distribution:** Model bundled in the game install (~1.5GB added). No optional download step for the base model.
|
||||
|
||||
**Two-spike delivery plan:**
|
||||
- Spike 1: Rust llama-cpp-rs wrapper (plumbing only) + manual prompt experiments (Jeroen, Mellanie, Paula). Validates model choice and prompt architecture. No game integration.
|
||||
- Spike 2: Full integration — pre-voicing queue, cache-as-determinism, thread pool isolation, baked content generation, hardware detection, fallback behavior.
|
||||
|
||||
- **Rationale:** D-122 (all NPCs generated) and D-128 (culture implicit in starting location) require NPC voice to scale across zones and cultures without O(R×Z×C) hand-authoring. The re-voicing model — translate culture-neutral semantic base text into character voice — is the only architecture that scales while preserving content quality. The base-text fallback ensures the game is complete without the pipeline. Tell-as-passthrough with context influence preserves the information asymmetry mechanic (D-010) while giving tells cultural texture through their influence on surrounding content. Tells are READ-ONLY inputs; the LLM never owns tell text.
|
||||
- **Raised by:** LLM Voice Pipeline Workshop (2026-03-07), full team. Jeroen's decisions are the binding inputs.
|
||||
- **Dissent:** Miri flagged concern about cultural philosophy at 2B model size — addressed via hybrid injector format (instruction + example pairs) and spike validation.
|
||||
- **Amends:** [D-123](content.md#d-123-generative-ai-for-npc-content-templating-via-culture-vectors) — see amendment text below.
|
||||
- **Supersedes:** [D-124](content.md#d-124-in-game-ollama-for-live-npc-dialogue--deferred-door-open) (in-game AI deferred — the door is now open and entered).
|
||||
- **Cross-reference:** [D-010](architecture.md#d-010-multiplayer-ready-architectural-baseline) (information boundaries — tells are READ-ONLY inputs; LLM cannot produce game state), [D-121](content.md#d-121-voice-is-culture-driven--job-as-modifier) (culture-primary voice), [D-122](content.md#d-122-all-npcs-generated--no-named-hand-authored-characters) (all NPCs generated), [D-128](content.md#d-128-culture-implicit-in-starting-location--krenn-system-equals-krenn-culture) (culture implicit in location), [D-029](content.md#d-029-population-entanglement-ratio--305020) (NPC tier model — tier mapping for re-voicing priority)
|
||||
|
||||
**D-123 Amendment text:**
|
||||
> *D-123 is amended as follows: The AI pipeline operates in two modes. Baked mode: content is generated at build time and reviewed by humans before shipping — this preserves D-123's authorial control constraint. Runtime mode: content is generated in the background during gameplay without per-line human review, when "AI-Enhanced Dialogue" is enabled. All other D-123 constraints remain binding in both modes: culture vectors are the primary prompt constraint, the AI does not default to genre conventions, and authorial control governs what the LLM may and may not produce. The AI pipeline does not drive live narrative decisions — it applies voice to authored semantic content. Culture profiles (Miri) remain the primary authoring deliverable.*
|
||||
|
||||
---
|
||||
|
||||
## 2. Tell-as-Context Design
|
||||
|
||||
### The Core Insight
|
||||
|
||||
Tells are READ-ONLY inputs to the LLM. The tell text is never sent to the LLM for re-voicing — it is always served as authored base text. But when an NPC's tell state is active, that state flows into the re-voicing prompt for the NPC's observable behaviors and dialogue as a **tone injector**.
|
||||
|
||||
The distinction matters mechanically: the tell communicates NPC internal state to the observant player. If the LLM re-voices the tell, the phenomenon might shift and the mechanical signal corrupts. If the tell influences surrounding content, the player perceives a coherent character — their dialogue and movement feel consistent with their internal state — without the game explicitly labeling that state.
|
||||
|
||||
**The effect we're producing:** An NPC under Guarded tell state should feel guarded. Their base-text tell ("becomes evasive and avoids eye contact") is unchanged. But their re-voiced dialogue ("All in one piece. What do you need?") comes out differently than when they're in neutral state — more clipped, more words chosen, a slight sense of something unsaid. The player who has learned the tell grammar sees the tell AND hears it echoed in the surrounding content. The player who hasn't learned the grammar yet just notices the NPC feels slightly off — which is the right experience.
|
||||
|
||||
### The Five Tell Context Injectors
|
||||
|
||||
One injector per TellCategory. These are tone instructions — they describe HOW to phrase the content, not WHAT to add. They must not name the tell state. They must not introduce new information. They modulate expression.
|
||||
|
||||
| TellCategory | Tone Injector |
|
||||
|---|---|
|
||||
| `Neutral` | *(no injector — free re-voicing with culture + trait only)* |
|
||||
| `Nervous` | "This NPC's words come slightly faster than usual, briefer. They don't elaborate. A phrase drops off before it's finished. Do not say they seem nervous or afraid." |
|
||||
| `Angry` | "This NPC's words are measured and deliberate — not shouting, containing. A word hits harder than the context requires. Do not say they seem angry." |
|
||||
| `Friendly` | "This NPC offers slightly more than asked. A word of genuine warmth lands casually. They don't perform friendliness — it just shows. Do not add compliments or over-warmth." |
|
||||
| `Guarded` | "This NPC chooses each word with a half-second more care than normal. They answer what was asked, no more. There is nothing wrong here. Do not say they seem guarded or evasive." |
|
||||
| `RoutineDeviation` | "This NPC is elsewhere in their mind. They are present but preoccupied — answers are on track but land a beat late, like they're half attending. Do not explain why or name what they're thinking about." |
|
||||
|
||||
**Critical constraints on all tone injectors:**
|
||||
- Do not name the internal state ("nervous", "angry", "hiding", "guarded", "distracted")
|
||||
- Do not add information not in the base text
|
||||
- Do not change the content — only the texture of expression
|
||||
- The resulting output must pass the deniability test: could the player explain this phrasing without knowing the tell was active?
|
||||
|
||||
### Prompt Assembly with Tell Context
|
||||
|
||||
The re-voicing prompt for a behavior or dialogue line in a given tell state assembles as:
|
||||
|
||||
```
|
||||
[SYSTEM/PREFIX — universal negative injectors]
|
||||
You are re-voicing NPC dialogue for a game set in the Settled Reach, a gritty working-class
|
||||
science fiction setting. Never reference: religion, military titles, fantasy elements,
|
||||
Earth geography, banter/wit unearned by context, or anachronistic technology. Never invent
|
||||
new facts, locations, or relationships. Output only the re-voiced line.
|
||||
|
||||
[CULTURE INJECTOR — per CultureProfile, ~150-250 tokens]
|
||||
Krenn culture: direct, working-class, minimal pleasantries. Vocabulary markers:
|
||||
void-oaths ("void take it", "blood and void"), clipped greetings ("hey", "all good?"),
|
||||
no contractions avoided — they use contractions naturally. Register is not formal.
|
||||
Example of Krenn register: [brief paired example demonstrating Krenn voice]
|
||||
|
||||
[TRAIT MODIFIER — per NPC's PersonalityTraits, ~10 tokens each]
|
||||
This character is Bold: confident, speaks their mind directly.
|
||||
|
||||
[TELL CONTEXT INJECTOR — per active TellCategory, ~30-40 tokens]
|
||||
This NPC chooses each word with a half-second more care than normal. They answer what
|
||||
was asked, no more. There is nothing wrong here. Do not say they seem guarded or evasive.
|
||||
|
||||
[TASK — base text]
|
||||
Re-voice in this character's voice: "All in one piece. What do you need?"
|
||||
```
|
||||
|
||||
Total prompt for behavior with tell context: ~200-350 tokens (well within the 150-token culture + 150-token tell/other budget).
|
||||
|
||||
### Caching Architecture
|
||||
|
||||
The 6-variant per line model (Neutral + 5 TellCategory states) enables runtime determinism:
|
||||
|
||||
```
|
||||
Cache key: (npc_stable_id, line_id, tell_state, culture_id)
|
||||
Cache value: voiced_text: String
|
||||
|
||||
// At pre-voicing time (generation or background queue):
|
||||
for each NPC in zone:
|
||||
for each behavior/dialogue line:
|
||||
for each TellCategory in [Neutral, Nervous, Angry, Friendly, Guarded, RoutineDeviation]:
|
||||
voiced = llm.revoice(base_text, culture_injector, trait_modifier, tell_injector)
|
||||
cache.insert((npc_id, line_id, tell_state, culture_id), voiced)
|
||||
|
||||
// At runtime (zero inference):
|
||||
fn get_voiced_line(npc_id, line_id, current_tell_state, culture_id) -> String {
|
||||
cache.get((npc_id, line_id, current_tell_state, culture_id))
|
||||
.unwrap_or_else(|| base_text(line_id)) // graceful fallback
|
||||
}
|
||||
```
|
||||
|
||||
**Cost:** 6× inference per line at generation time. At runtime: pure cache lookups, zero inference triggered by tell-state changes.
|
||||
|
||||
**Why this is the right model:** D-010 principle 4 (deterministic simulation with input events). The voiced content is determined at generation time by (seed + culture + NPC traits). Tell state is a runtime variable that selects from pre-computed variants. This keeps the pre-voicing pipeline in the background where it belongs and the gameplay loop fast and deterministic.
|
||||
|
||||
**Fallback order:**
|
||||
1. Pre-voiced variant for current tell state → serve it
|
||||
2. Pre-voiced neutral variant → serve it (content matches, tone is neutral — acceptable degradation)
|
||||
3. Base text → always present, always correct
|
||||
|
||||
This means a player will almost never see raw base text once the pre-voicing pipeline has completed for a zone. The neutral variant is a sufficient fallback that sounds intentional.
|
||||
|
||||
### What the Player Experiences
|
||||
|
||||
The player who has learned the tell grammar:
|
||||
1. Sees the base-text tell ("becomes evasive and avoids eye contact") — mechanical signal, unchanged
|
||||
2. Hears the Guarded-influenced dialogue — coherent with the tell, amplifying the read
|
||||
3. Pattern: "this NPC's words are as guarded as their eyes"
|
||||
|
||||
The player who hasn't yet learned the tell grammar:
|
||||
1. Sees the base-text tell — may not yet know what it means
|
||||
2. Hears the Guarded-influenced dialogue — senses something slightly off
|
||||
3. The monologue system (Q-052) may flag it: "The Character's Eye" moment
|
||||
4. Next time they encounter this pattern on a different NPC, they recognize it
|
||||
|
||||
Both experiences are correct. The tell-context architecture serves both simultaneously.
|
||||
|
||||
### What the LLM Must Never Do with Tells
|
||||
|
||||
These are absolute constraints, tested in Spike 1:
|
||||
|
||||
1. **Never name the state**: "seems nervous" / "appears guarded" / "is hiding something" — explicit tell labeling destroys the signal's mechanical value
|
||||
2. **Never add knowledge**: "carefully, as if worried about the patrol" — the LLM cannot introduce narrative content not in the base text or injectors
|
||||
3. **Never replace phenomenon with inference**: "says nothing" instead of "answers briefly" — the base-text content must survive re-voicing
|
||||
4. **Never overplay**: exaggerating tone injectors into theatrical performance destroys the deniability that makes tells work
|
||||
|
||||
---
|
||||
|
||||
## 3. Spike 1 Success Criteria: "Does This Even Play?"
|
||||
|
||||
### What Spike 1 Is
|
||||
|
||||
Spike 1 is: build the Rust inference wrapper, load Gemma 2B and Phi-3, then Jeroen + Mellanie + Paula manually craft prompts and run them by hand. No game integration. The goal is to answer "does this even play?" before committing to Spike 2 integration.
|
||||
|
||||
From my domain (systems design), "does this even play?" means five specific things:
|
||||
|
||||
---
|
||||
|
||||
### Criterion 1: Information Preservation
|
||||
|
||||
**What it tests:** Does re-voiced content preserve the mechanical information the player needs?
|
||||
|
||||
Observable behaviors and dialogue carry game-relevant information: what an NPC is doing, what they know, what they want. Re-voicing must modulate expression without removing or distorting content.
|
||||
|
||||
**Protocol:**
|
||||
- Select 10 behaviors and 10 dialogue lines covering a range of mechanical content (actions, facts, offers, refusals)
|
||||
- Re-voice each through Gemma 2B, culture + trait injectors only (Neutral state)
|
||||
- Give a reader unfamiliar with the base texts ONLY the re-voiced versions
|
||||
- Ask: "what is this NPC doing / saying?" for each
|
||||
- Compare reader's summary to what the base text communicates
|
||||
|
||||
**Success bar:** 9/10 for both behaviors and dialogue — the reader's summary matches the mechanical content of the base text. The one failure is examined for pattern (is it a prompt issue, a model issue, a base-text issue?).
|
||||
|
||||
---
|
||||
|
||||
### Criterion 2: Tell-Context Tone Without Naming
|
||||
|
||||
**What it tests:** Does the tell context injector modulate tone without the LLM naming or inferring the tell state?
|
||||
|
||||
This is the core mechanical test for D-138's tell-as-context design.
|
||||
|
||||
**Protocol:**
|
||||
- Take 5 behaviors and 5 dialogue lines, re-voice each in Neutral and Guarded states
|
||||
- Blind review: show reviewer ONLY the re-voiced Guarded outputs (no Neutral comparison, no context about tells)
|
||||
- Ask two questions:
|
||||
- "Does this NPC feel like they're being careful about something?" (yes/no)
|
||||
- "Does this text explicitly say or imply what they're being careful about?" (yes/no)
|
||||
- Also scan all 10 outputs for forbidden phrases: "nervous", "guarded", "hiding", "evasive", "worried", or any inference about NPC internal state
|
||||
|
||||
**Success bar:**
|
||||
- ≥4/5 behaviors and ≥4/5 dialogue lines: reviewers sense the undertone
|
||||
- 0/10 outputs: explicit state naming or inference. This is a HARD requirement — any explicit naming fails the test regardless of tone success rate
|
||||
- Repeat for Nervous and RoutineDeviation (the two most distinct tone profiles)
|
||||
|
||||
---
|
||||
|
||||
### Criterion 3: Cultural Grammar Survival
|
||||
|
||||
**What it tests:** Does culture remain legible after re-voicing? Culture is a tell — hearing Krenn speech should tell the player something about where this NPC is from.
|
||||
|
||||
**Protocol:**
|
||||
- Re-voice 10 Krenn base texts (mix of behaviors and dialogue)
|
||||
- Check for oath vocabulary: do void-oaths appear in outputs where they're appropriate?
|
||||
- Check register: does the output read working-class, direct, minimal pleasantries?
|
||||
- Compare 5 Krenn re-voiced outputs against 5 "generic sci-fi NPC" sentences that could come from any game
|
||||
- Blind reviewer: can they identify which 5 are Krenn-flavored vs. generic?
|
||||
|
||||
**Success bar:**
|
||||
- Void-oath vocabulary appears in ≥3/5 appropriate outputs (where an exclamation is called for)
|
||||
- Blind reviewer correctly identifies Krenn-vs-generic at ≥8/10 (they shouldn't be guessing)
|
||||
- Zero outputs that sound like fantasy, military, Earth-based, or comedic-banter registers
|
||||
|
||||
---
|
||||
|
||||
### Criterion 4: No False Information
|
||||
|
||||
**What it tests:** Does the LLM stay within the information the base text and injectors provide?
|
||||
|
||||
This is a D-010 constraint (information boundaries). The LLM cannot introduce facts the NPC doesn't know, locations that don't exist, relationships that aren't authored.
|
||||
|
||||
**Protocol:**
|
||||
- Review ALL outputs from Criteria 1-3 for false information
|
||||
- Flag anything the LLM added that isn't in: (a) base text, (b) culture injector, (c) trait modifier, (d) tell context injector
|
||||
|
||||
**Success bar:** 0 false information introductions. This is a hard requirement. Any false information in any output is a spike finding that must be addressed before Spike 2 integration, regardless of how good the output otherwise is.
|
||||
|
||||
---
|
||||
|
||||
### Criterion 5: The "Does This Feel Like a Place?" Test
|
||||
|
||||
**What it tests:** Does the re-voiced output produce the experience of encountering a real inhabitant of the Settled Reach, not a generated NPC?
|
||||
|
||||
This is the qualitative gut check that can't be fully quantified. It's the question the whole workshop has been building toward.
|
||||
|
||||
**Protocol:**
|
||||
- After running all manual prompts, Jeroen, Mellanie, and Paula each answer independently:
|
||||
1. "Pick one output from today that felt like a real person. Read it aloud. Does it sound like the Settled Reach?"
|
||||
2. "Pick one output that fell flat. What made it fall flat? Model failure, prompt failure, or base-text failure?"
|
||||
3. "If you read this NPC's dialogue in a playtest and didn't know it was LLM-generated, would you think it was authored?"
|
||||
|
||||
**Success bar:** This is not a pass/fail criterion — it's a diagnostic that informs Spike 2. But:
|
||||
- If all three reviewers struggle to find an output that "felt like a real person," the spike has failed qualitatively regardless of quantitative scores
|
||||
- The "fell flat" analysis is as important as the passes — it tells us where to invest prompt engineering effort
|
||||
|
||||
---
|
||||
|
||||
### Summary of Spike 1 Go/No-Go
|
||||
|
||||
| Criterion | Hard requirement? | Target |
|
||||
|---|---|---|
|
||||
| Information preservation (behaviors) | No | ≥9/10 |
|
||||
| Information preservation (dialogue) | No | ≥9/10 |
|
||||
| Tell-context tone (undertone sensed) | No | ≥8/10 |
|
||||
| Tell-context: zero explicit naming | **YES** | 0/20 |
|
||||
| Cultural grammar (Krenn legible) | No | ≥8/10 correct identifications |
|
||||
| No false information | **YES** | 0 instances |
|
||||
| Qualitative "real person" | No | At least 1 convincing output per reviewer |
|
||||
|
||||
**Go decision rule:** Both hard requirements met + at least 4/5 soft criteria pass = proceed to Spike 2 with the winning model. Hard requirement failure = fix the prompt architecture before Spike 2, regardless of soft criteria scores.
|
||||
|
||||
**Model selection:** Run all criteria against both Gemma 2B and Phi-3. The winning model is the one that passes both hard requirements and scores higher across soft criteria. If both fail hard requirements, that's a prompt architecture problem — fix the prompts and re-run before concluding the models are insufficient.
|
||||
|
||||
---
|
||||
|
||||
## Appendix: Tell Category → Tone Injector Authoring Notes
|
||||
|
||||
For Mellanie and Paula's reference during Spike 1 prompt crafting:
|
||||
|
||||
The tone injectors above are v1 drafts. The spike should test them and refine. Key authoring principles:
|
||||
1. **Describe the EXPRESSION, not the internal state**: "words come slightly faster" not "seems nervous"
|
||||
2. **Anchor the instruction to observable behavior**: what the reader hears/sees, not what the NPC feels
|
||||
3. **Include a negative constraint**: every injector ends with "Do not say they [explicit state label]"
|
||||
4. **Keep it short**: 30-40 tokens. The model doesn't need an essay. Precision > length.
|
||||
5. **Test the deniability**: after seeing an output, could you explain the phrasing without knowing the injector was active? If yes, the injector is working correctly.
|
||||
@@ -0,0 +1,144 @@
|
||||
# Round 1: Content Authoring Inventory
|
||||
**Author:** Mellanie
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## Reading notes before I start
|
||||
|
||||
I went back to the source: `rural-zone-spec.ron`, `industrial-zone-spec.ron`, `culture-krenn.ron`, the generator spike, and the relevant D-records. The question this workshop is actually asking is not "do we use an LLM?" — D-123 already decided generative AI is in the pipeline. The question is: **what model, what scope, and what does the copy team own versus the system?**
|
||||
|
||||
---
|
||||
|
||||
## 1. Which option produces the best authoring workflow?
|
||||
|
||||
**Option 3 (LLM re-voicing) — with specific constraints.**
|
||||
|
||||
Here's why Option 2 (composable primitives) is the wrong tool for the copy team: it shifts authoring from writing character voice to writing a grammar engine. "Role actions + culture modifiers + context tags" is a data schema problem, not a copywriting problem. The copy team writes sentences that breathe. Composition engines produce sentences that compile. Players notice the difference.
|
||||
|
||||
Here's why Option 1 (hand-authored) is already failing: the RON work from #630 — fifty lines per role — is good. It's exactly the quality we want. But we renamed those files from `rural-zone-spec.ron` to `krenn-rural-zone.ron` to make explicit what we already knew: every zone file is really culture × zone content, authored from scratch. Adding a second culture means authoring from scratch again. The math doesn't work.
|
||||
|
||||
Option 3 works because **the copy team's current output is already the right input.** The behavior lines in `rural-zone-spec.ron` — "tends rows of low-growing crops with a long-handled hoe," "patches a cracked irrigation pipe with strips of bonding tape" — these are semantic lines. Specific, observable, functional. They don't need to be culture-neutral to be LLM seeds; they need to be specific enough that the LLM has something real to revoice. They already are.
|
||||
|
||||
What Option 3 adds for the copy team: **a thin injector authoring layer, once per culture.** Five to ten culture injector clauses for Krenn. We write them once; they voice every Krenn NPC. That is the leverage point.
|
||||
|
||||
---
|
||||
|
||||
## 2. What does the authoring workflow look like?
|
||||
|
||||
Three layers, each with a clear owner:
|
||||
|
||||
### Layer 1: Base text (copy team, per-role, per-zone)
|
||||
This is already being written. The `typical_behaviors` arrays in zone RON files. No format change needed at this layer. The copy team continues writing specific, observable, present-tense action lines. Rules:
|
||||
- No culture-specific vocabulary (exclamations, Krenn idioms) — those belong to the voiced tier
|
||||
- Specific enough to be evocative as fallback; not so culture-loaded that the LLM is fighting the base text
|
||||
- The existing lines in `industrial-zone-spec.ron` and `rural-zone-spec.ron` are already at the right register
|
||||
|
||||
### Layer 2: Culture injectors (copy team, per-culture, authored once)
|
||||
This is the new work. Currently `culture-krenn.ron` has a `speech` section: register, filler_words, greetings, farewells, exclamations. These were designed as generator inputs, not LLM injector instructions. They're useful source material, but "direct, minimal pleasantries, gets to the point" is a description of a voice, not an instruction to an LLM.
|
||||
|
||||
**The copy team should author 5-10 explicit injector clauses per culture** — sentences written directly as LLM persona instructions. Not derived automatically from the existing culture RON fields; the existing fields weren't designed for this. Written from scratch by the copy team, once per culture, living in a new `voice_injectors` field in the culture RON.
|
||||
|
||||
Example injectors for Krenn (draft):
|
||||
- "Your speech is direct. No pleasantries. Get to the point because everyone's short on time."
|
||||
- "You are community-oriented and pragmatic. You trust people who show up and do the work."
|
||||
- "You are suspicious of distant authority and institutional rank. Competence is what earns respect."
|
||||
- "You might use words like 'void take it', 'stars', or 'cold vacuum' when surprised or frustrated."
|
||||
- "You use first names. Family names belong to forms and arrest records."
|
||||
|
||||
These are what the LLM receives as persona context. The copy team writes them; the pipeline uses them verbatim.
|
||||
|
||||
### Layer 3: Personality injectors (copy team, per-trait, authored once)
|
||||
The proposal mentions 10-20 personality trait injector clauses. These should be authored by the copy team, not auto-derived from trait names. "Bold" in the Settled Reach is not generic confidence — it's Krenn-bold, which reads as directness and willingness to say an uncomfortable thing in front of people. The trait injectors need to be written with the world in mind.
|
||||
|
||||
Ten traits = ten injector clauses. One-time cost, high leverage.
|
||||
|
||||
---
|
||||
|
||||
## 3. Does the base text need to change for LLM re-voicing?
|
||||
|
||||
**Do not strip culture vocabulary from base text.** The fallback experience depends on it.
|
||||
|
||||
Players who run with AI-Enhanced Dialogue OFF see base text. If we strip it to minimal semantics — "tends crops," "checks manifest" — the fallback reads as placeholder text. The current RON lines ("tends rows of low-growing crops with a long-handled hoe") are functional prose. They do real work as fallback.
|
||||
|
||||
What needs to change is **authorial awareness**, not the format:
|
||||
- Base text should avoid culture-specific vocabulary, which should live only in the voiced tier
|
||||
- Base text should avoid first-person register (it's observable behavior, third-person present)
|
||||
- Tells embedded in behaviors need to be structurally separable — see section 5 below
|
||||
|
||||
The one RON format addition I'd propose: **an optional `voice_injectors` field on the culture RON** (not the zone RON) for the explicit LLM persona clauses. Everything else stays.
|
||||
|
||||
---
|
||||
|
||||
## 4. How do we quality-control LLM output?
|
||||
|
||||
Three failure modes, three responses:
|
||||
|
||||
**Failure mode A — Lore contamination.** The LLM introduces references, technologies, or cultural facts that don't exist in the Settled Reach. ("The Imperial Fleet," "FTL drives," real-world idioms.)
|
||||
|
||||
Response: I'll write a blocklist of excluded vocabulary and genre conventions — a short document the validation pass uses. Baked content (hub zones) gets human spot-check of all LLM output before ship. This is manageable because baked zones are finite. Build-time validation catches hard violations; human review catches drift.
|
||||
|
||||
**Failure mode B — Voice drift.** The LLM drifts from Krenn register toward generic sci-fi. All NPCs start sounding the same.
|
||||
|
||||
Response: Per-culture ground-truth examples. I'll write 20-30 "this is what good Krenn-voiced output looks like" examples per culture, used as LLM few-shot examples and as QA reference. Runtime content gets sampled at 5% and logged for periodic review. Not every line, but enough to detect systemic drift.
|
||||
|
||||
**Failure mode C — Injected exclamation in wrong context.** Personality injectors applied mechanically produce jarring results: "Void take it, the manifest checks out." The cultural exclamation was injected without situational awareness.
|
||||
|
||||
Response: The composition engine (proposal section 5) needs a context gate on culture exclamation injectors — they should only fire in high-affect situations, not neutral task behaviors. This is a systems concern but the copy team can flag which base-text lines are neutral-register and which are emotionally charged, helping the injector assembly logic.
|
||||
|
||||
**On the tell system specifically:** tell-adjacent lines require stricter QA than general behaviors. See section 5.
|
||||
|
||||
---
|
||||
|
||||
## 5. How much of the #630 work survives?
|
||||
|
||||
By option:
|
||||
|
||||
| Option | Survival rate | What changes |
|
||||
|--------|--------------|--------------|
|
||||
| Option 1 (hand-authored) | 100% | Nothing. The work is the product. |
|
||||
| Option 2 (composable) | 20-30% | Lines become raw material for extracting primitives. Significant rewrite in a different authoring grammar. |
|
||||
| Option 3 (LLM re-voicing) | ~90% | Lines become base text. Minor cleanup for register consistency. The culture-specific vocabulary moves to injectors. |
|
||||
|
||||
The existing `rural-zone-spec.ron` and `industrial-zone-spec.ron` lines are already good LLM seeds. "Slumps into a break room chair and stares at nothing for a full minute before reaching for a drink" — that's specific, evocative, and has real situational texture. The LLM can revoice the register; it can't manufacture that specificity. The copy team's investment in specificity survives.
|
||||
|
||||
The only category that needs authoring review: behaviors that contain Krenn cultural vocabulary should be flagged and either cleaned to neutral base text or moved to an explicit `voiced_behaviors` array for baked zones where copy team authors the voiced variant directly.
|
||||
|
||||
---
|
||||
|
||||
## 6. What breaks if we choose the wrong option?
|
||||
|
||||
**Choose Option 1:** The copy team becomes the hard scaling wall. By the time we have three cultures and five zone types, we need 750+ authored behavior lines before any dialogue. Every new zone type, every cultural variant, every sprint with new NPCs requires fresh hand-authored sentences. The content team becomes a bottleneck that grows with the world. Q-012 stays open forever.
|
||||
|
||||
**Choose Option 2:** The composition engine produces grammatically correct but voice-flat output. "Bold dock worker at Krenn industrial zone performs checking manifest with direct confidence." Players notice that NPCs sound assembled. The tell system suffers most — tells that need to read as natural behavior start reading as labeled states. The copy team's skill set (voice, rhythm, specificity) doesn't map to grammar-authoring. We'd be asking them to work in a medium they don't think in.
|
||||
|
||||
**Choose Option 3 with bad injectors:** All NPCs converge to a middle-ground voice. The injectors become wallpaper — the LLM acknowledges them and ignores them at small model sizes. This is the most specific risk at 2B-class models: if the model can't hold culture register AND personality AND situational context simultaneously, it defaults to something legible but generic. The spike should test injector faithfulness specifically, not just fluency.
|
||||
|
||||
**Choose Option 3 without protecting tells:** Tell-bearing behaviors get revoiced like any other line. A tell that was authored as "checks exits habitually" might become "seems to always know where the exits are" for one generation and "glances toward the doors every few minutes" for another. Both are informative, but they're not the same signal. Players on different seeds encounter different phrasings of the same tell. Is that a feature (phrasing variance = feel of a living world) or a bug (the tell mechanic is information delivery, not poetry)? This needs a decision before we commit.
|
||||
|
||||
---
|
||||
|
||||
## 7. One question before I can commit
|
||||
|
||||
**Can tells be structurally separated from regular behaviors in the RON schema?**
|
||||
|
||||
Specifically: is there a `tell_behaviors` field (or equivalent) that the LLM pipeline treats differently from `typical_behaviors`? Or are tell-carrying lines mixed into the same array?
|
||||
|
||||
If tells are mixed in, the pipeline has no way to distinguish "this line is flavor" from "this line is information." The LLM will revoice both, and information fidelity becomes a probabilistic bet, not an authoring guarantee.
|
||||
|
||||
If they're separable, the copy team can author tell lines to be LLM-resistant — short, specific, verb-first ("checks exits habitually"), with clear behavioral focus — and flag them for pass-through or constrained revoicing. That's an authoring problem I can solve.
|
||||
|
||||
If they're not separable, the architecture needs to answer: is observable-behavior revoicing even safe for tells? Or does the LLM only revoice dialogue, and tells always use base text?
|
||||
|
||||
This is a Gestalt + Tyre question. Their answer determines whether I can sign off on Option 3 for the full behavior pipeline, or only for dialogue.
|
||||
|
||||
---
|
||||
|
||||
## Summary position
|
||||
|
||||
**Recommend Option 3 (LLM re-voicing).** The copy team's existing work is already structured correctly for this pipeline. The per-culture injector authoring is low-volume, high-leverage, and within the copy team's skill set. Base text format needs no structural change — only authorial discipline about register.
|
||||
|
||||
The one unresolved blocker: tell-line structural separation. If that's solvable at the schema level, I'm in.
|
||||
|
||||
The thing that would break Option 3 fatally: if 2B-class models can't reliably preserve culture register when personality and mood injectors are also active. That's the spike's core test. I'll need to write test payloads — give me the model candidates and I'll produce the semantic lines and injector combos to run.
|
||||
@@ -0,0 +1,157 @@
|
||||
# Round 2: Content Authoring Evaluation
|
||||
**Author:** Mellanie
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## D-123 tension: is the amendment language acceptable?
|
||||
|
||||
**Short answer:** Yes for A and B. Needs revision for C.
|
||||
|
||||
The amendment "authoring tool AND background runtime enhancement" is accurate and acceptable for Proposals A and B. What D-123 was protecting against was live, autonomous, player-prompt-driven generation — an LLM improvising narrative outside authorial control. Background pre-voicing doesn't do that. The LLM receives authored base text, authored injectors, and authored constraints. It applies register. That's closer to a pipeline tool that happens to run on the player's machine than to a runtime AI system in the dangerous sense.
|
||||
|
||||
D-123's core principles survive the amendment:
|
||||
- Culture vectors as primary prompt constraint — **preserved**
|
||||
- AI doesn't default to genre conventions — **preserved** (that's what injectors + negative lists are for)
|
||||
- Authorial control over what the LLM can and can't do — **preserved**
|
||||
|
||||
The only thing that changes: "not a runtime system" → "background runtime enhancement when AI-Enhanced Dialogue is ON."
|
||||
|
||||
**Proposal C's language is a problem.** "D-123 is fully superseded" implies throwing out the whole decision. The runtime restriction is the only bit that needs to change. The rest of D-123 — culture vectors primary, no genre-convention defaults, authorial constraints binding — needs to stay in force for Proposal C just as much as for A and B. If "fully superseded" means we're free to ignore culture vectors and let the LLM rephrase however it wants for dialogue, that's a regression, not an improvement.
|
||||
|
||||
**Proposed amendment language for all three proposals:**
|
||||
|
||||
> D-123 is amended as follows: "The AI pipeline is an authoring tool for content assembly AND a background runtime enhancement when AI-Enhanced Dialogue is enabled. All other constraints remain binding: culture vectors are the primary prompt constraint, the AI does not default to genre conventions, and authorial control governs what the LLM may and may not produce. The AI pipeline does not drive live narrative decisions — it applies voice to authored semantic content."
|
||||
|
||||
This covers all three proposals. No proposal fully supersedes D-123; they all amend it.
|
||||
|
||||
One specific correction to flag: `proposed-llm-voice.md` Section 4 gives as an example Krenn Culture injector: "Your speech is formal and avoids contractions." This is wrong. Krenn is direct and working-class, not formal. It uses contractions constantly ("shift's calling", "gotta move", "can't get there from here"). If this example injector shipped as-is, every Krenn NPC would sound like a mid-level bureaucrat. The injector drafts in this document (below) correct this.
|
||||
|
||||
---
|
||||
|
||||
## Resolution matrix
|
||||
|
||||
| Question | Answer |
|
||||
|----------|--------|
|
||||
| Which proposal do I recommend? | **B** |
|
||||
| Blockers in Proposal B? | One: semantic core labels require careful per-tell authoring — doable but copy team needs a definition of the full tell taxonomy first (from Gestalt/Tyre) |
|
||||
| Can I live with Proposal A? | Yes — clean, safe, and the architecture supports adding B later |
|
||||
| Can I live with Proposal C? | Yes, with amended D-123 language and hard requirement for human review of all baked dialogue output |
|
||||
| Minimum change to A to make it acceptable | Nothing — A is already acceptable |
|
||||
| Minimum change to C to make it acceptable | (1) Amend D-123 language as above, (2) require human review sign-off on baked dialogue before ship, (3) treat 2B dialogue quality as a spike gate — if it fails, scope back to A/B |
|
||||
|
||||
---
|
||||
|
||||
## Authoring load by proposal
|
||||
|
||||
### Proposal A: culture injectors + base text only
|
||||
|
||||
**New copy work:**
|
||||
- Culture injector clauses: 5-10 per culture (~8 for Krenn — see drafts below)
|
||||
- Trait modifier clauses: 1 per trait, 10 traits (10 sentences total)
|
||||
- Negative injectors / lore contamination blocklist: ~20-30 excluded terms and genre phrases (one-time, I own this)
|
||||
- Tell behavior flagging: just identifying which existing behaviors are tells, no new writing required — the passthrough system handles the rest
|
||||
|
||||
**Ongoing work:**
|
||||
- Per-culture injectors when new cultures are added (same one-time cost per culture)
|
||||
- Blocklist maintenance as new lore contamination patterns are identified
|
||||
|
||||
**Volume estimate:** ~2-3 days of focused copy work to stand up Krenn completely. Each additional culture: ~1 day.
|
||||
|
||||
**Assessment:** This is the right authoring load for the copy team. Low volume, permanent leverage.
|
||||
|
||||
---
|
||||
|
||||
### Proposal B: + semantic core labels for tells
|
||||
|
||||
**Additional new copy work beyond A:**
|
||||
- `semantic_core` labels for each tell type: e.g., `"avoidance_behavior"`, `"nervous_fidget"`, `"concealment_tell"`, `"hostile_suppression"`, `"knowledge_gap_tell"`
|
||||
- These aren't just labels — they're constraints that must precisely name the phenomenon the tell must preserve
|
||||
- I can draft these, but I need the full tell taxonomy first: how many tell categories, what are the behavioral expressions per category? The `tell_state.rs` shows 5 categories (Nervous, Angry, and others). I need the full enumeration from Gestalt/Tyre.
|
||||
- Estimated: 15-25 semantic core labels, plus documentation of what each means for the LLM constraint
|
||||
|
||||
**Assessment:** Moderate additional work, high value. The semantic core label is a copy team artifact — it requires understanding both the narrative intent (what the tell is communicating to the player) and the LLM instruction (what must survive revoicing). This is exactly the kind of precision work the copy team should own, not generate automatically. The tell taxonomy spec from Gestalt blocks me here.
|
||||
|
||||
---
|
||||
|
||||
### Proposal C: + dialogue injector context
|
||||
|
||||
**Additional new copy work beyond A:**
|
||||
- Dialogue context fields in the prompt (relationship, access tier, trust tier) already exist as tags in the D-028/D-035 taxonomy — copy team doesn't author new tags, just validates the existing tags are being passed correctly
|
||||
- But: baked dialogue for hub NPCs requires **human review** before ship — this is the real load
|
||||
- How many dialogue lines per hub NPC? If Sova Transit District has ~20 ambient NPCs × 10 dialogue lines each, that's 200 voiced lines to review at bake time
|
||||
- At realistic review speed (read, judge, flag or approve), 200 lines takes a day
|
||||
- This is recurring cost for each new baked zone, not one-time
|
||||
- Two validation passes (behavior + dialogue) instead of one
|
||||
|
||||
**Assessment:** The additional authoring load isn't in writing — the tags exist. It's in **reviewing LLM dialogue output** at bake time, which is labor-intensive if dialogue quality at 2B is inconsistent. If the model is reliable, review is fast. If it drifts, review becomes a bottleneck that grows with every new baked zone.
|
||||
|
||||
---
|
||||
|
||||
## My recommendation: Proposal B
|
||||
|
||||
**Why B over A:** Culture-voiced tells are worth having. A Krenn NPC who's nervous about a secret should express that nervousness in a Krenn-flavored way — not a generic sci-fi way. Proposal B enables this. The semantic_core constraint is the right mechanism: it tells the LLM what phenomenon to preserve, not how to express it. That's good architecture.
|
||||
|
||||
**Why B over C:** Dialogue at 2B is the high-risk bet. Behaviors are short-form (5-15 words), the prompt is simple, failure is obvious and recoverable. Dialogue is longer, the prompt is more complex, and a subtle failure — dialogue that's fluent but slightly off-register — is harder to catch. Behaviors first; if the model proves itself, add dialogue.
|
||||
|
||||
**The spike should include a B-gate:** After validating behavior re-voicing (Proposal A tests), run a constrained re-voicing test with semantic_core on 5 tell behaviors. If the phenomenon survives in all 5 cases, we've validated B. If not, we ship A and add B when we have a stronger model.
|
||||
|
||||
**If the spike fails for B's constrained tells:** Fall back to A. The architecture supports it — `tell_behaviors` is a passthrough field regardless, and the semantic_core is an optional constraint layer on top.
|
||||
|
||||
---
|
||||
|
||||
## Krenn injector clauses — corrected drafts
|
||||
|
||||
The example in `proposed-llm-voice.md` ("Your speech is formal and avoids contractions") describes the opposite of Krenn culture. These are the corrected injectors.
|
||||
|
||||
**Note on format:** These are written as direct LLM persona instructions — second person, imperative register. They should appear verbatim in the injector prompt, not as description-of-description.
|
||||
|
||||
---
|
||||
|
||||
**Krenn Culture — Voice Injectors (v1, for spike validation)**
|
||||
|
||||
1. "Be direct. Don't waste words. Everyone you talk to is short on time, including you."
|
||||
|
||||
2. "You're working-class and pragmatic. You grew up in a community where you either show up and do the work, or you don't — and everyone notices which one you are."
|
||||
|
||||
3. "You don't trust distant authority. Management that hasn't worked a shift, institutions that talk big and deliver slow, credentials without competence — you've seen all of it, and it doesn't impress you."
|
||||
|
||||
4. "When something surprises or frustrates you, expressions like 'void take it', 'stars', 'cold vacuum', or 'blood and void' come naturally. They're not dramatic — they're just how people here talk."
|
||||
|
||||
5. "You use first names. Family names belong on contracts, registrations, and arrest records. Not in conversation."
|
||||
|
||||
6. "Loyalty runs narrow and deep. Your crew, your shift, your street. Not abstractions."
|
||||
|
||||
7. "You greet people briefly: 'hey', 'morning', 'shift treating you alright?' No ceremony."
|
||||
|
||||
8. "You're not rude — you're honest. If something's wrong, you say so. If something's fine, you say that too. You don't pad."
|
||||
|
||||
---
|
||||
|
||||
**Usage notes for the injector assembly system:**
|
||||
|
||||
- All 8 clauses should be included for every Krenn NPC regardless of role or trait. Culture is the baseline register.
|
||||
- Trait injectors layer on top: a Krenn-Bold NPC gets clause 8 amplified; a Krenn-Cautious NPC gets clause 8 dampened slightly.
|
||||
- Mood injectors override where relevant: Krenn-Angry should suppress the directness of clause 8 toward bluntness; Krenn-Nervous should suppress clause 3's confidence.
|
||||
- Do NOT use clause 4 (void-oaths) in neutral-register behaviors. Gate it to high-affect contexts. This is the composition engine's responsibility, but flag it explicitly so the system doesn't inject "void take it" into "checks a manifest."
|
||||
|
||||
---
|
||||
|
||||
## Tell taxonomy blocker
|
||||
|
||||
I need the following from Gestalt + Tyre before I can write semantic core labels for Proposal B:
|
||||
|
||||
1. Full enumeration of tell categories (I see 5 in `tell_state.rs` but only partially — what are all five?)
|
||||
2. Whether tell categories map 1:1 to semantic core labels or whether one category can have multiple labels (e.g., "Nervous" might express as `nervous_fidget`, `avoidance_behavior`, or `concealment_tell` depending on context — are these separate semantic cores or one?)
|
||||
3. Confirmation that `tell_behaviors: Vec<String>` is the accepted schema field name — I'll use this in the semantic core label documentation
|
||||
|
||||
Once I have the tell taxonomy, I can draft all semantic core labels within a day. They're not long — they're precise.
|
||||
|
||||
---
|
||||
|
||||
## One thing that should not be left open
|
||||
|
||||
The proposed-llm-voice.md lists Gemma 2B and Phi-3-mini as spike candidates. The updated proposals specify Gemma 2 2B (Q4_K_M) as the resolved candidate (C-6). I want to confirm: **is the spike still testing both models, or just Gemma 2 2B?**
|
||||
|
||||
From a copy team perspective, the spike test payloads I'll write will work with either model — I'll produce semantic lines + injector combos, not model-specific prompts. But if we're testing both, I want to write payloads that stress-test injector faithfulness specifically, because that's where 2B models tend to drift. Tell me what you need and I'll have test payloads ready.
|
||||
@@ -0,0 +1,372 @@
|
||||
# Round 3: Content Authoring — Injectors, Spike Payloads, Workflow Spec
|
||||
**Author:** Mellanie
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## Reading Jeroen's decisions
|
||||
|
||||
The scope is Proposal C: behaviors AND dialogue, full pipeline. Tells are passthrough, but they inform context for surrounding content. Two spikes. Gemma primary, Phi fallback. Bundled model. This is the right call — especially the tell-as-context model, which is cleaner than constrained re-voicing. The tell itself stays literal and legible; the NPC's surrounding voice reflects their state. Players read the contrast correctly.
|
||||
|
||||
One implication for my work: the dialogue spike payload question (N-5 from Round 2, "does sufficient base dialogue exist for a Proposal C spike?") — I'm answering it below by writing the spike payloads myself. We can test with authored samples before the full dialogue pool is complete.
|
||||
|
||||
---
|
||||
|
||||
## 1. Corrected Krenn Injector Clauses (finalized)
|
||||
|
||||
These supersede the v1 draft from `mellanie-round2.md`. Changes from v1:
|
||||
- Added two example pairs (Miri's hybrid format recommendation: small models are pattern matchers before instruction-followers)
|
||||
- Tightened clause 6 for phrasing clarity
|
||||
- Separated system-layer negative injectors (now in Section 4) from culture-specific injectors
|
||||
|
||||
**Format:** Direct LLM persona instructions — second person, imperative register. Appear verbatim in the injector prompt. Total approximate token count with examples: ~220-240 tokens.
|
||||
|
||||
---
|
||||
|
||||
**Krenn Culture — Voice Injectors v2 (finalized for Spike 1)**
|
||||
|
||||
```
|
||||
1. Be direct. No pleasantries. Everyone you talk to is short on time, and so are you.
|
||||
|
||||
2. You're working-class and pragmatic. Competence is what earns respect here, not rank or credentials.
|
||||
You grew up in a community where you either show up and do the work or you don't, and everyone
|
||||
notices which one you are.
|
||||
|
||||
3. You're suspicious of distant authority — management that hasn't worked a shift, institutions that
|
||||
talk big and deliver slow. You've seen it. It doesn't impress you.
|
||||
|
||||
4. When something surprises or frustrates you, expressions like "void take it", "stars",
|
||||
"cold vacuum", or "blood and void" come naturally. They're not dramatic — they're just
|
||||
how people here talk.
|
||||
|
||||
5. You use first names. Family names belong on contracts and arrest records, not in conversation.
|
||||
|
||||
6. Loyalty runs narrow and deep. Your crew, your shift, your street. Not abstractions.
|
||||
|
||||
7. You greet people briefly: "hey", "morning", "shift treating you alright?" No ceremony.
|
||||
|
||||
8. You're not rude — you're honest. If something's wrong, you say so. If it's fine,
|
||||
you say that too. You don't pad.
|
||||
```
|
||||
|
||||
**Example pairs (pattern anchors for small model):**
|
||||
|
||||
```
|
||||
Example 1:
|
||||
BASE: "declines to answer a question about the overnight run"
|
||||
VOICED: "Look, that's not mine to say."
|
||||
|
||||
Example 2:
|
||||
BASE: "acknowledges a colleague's greeting while continuing to work"
|
||||
VOICED: "Hey. Yeah. Catch you at shift end."
|
||||
```
|
||||
|
||||
**Assembly notes for pipeline:**
|
||||
- All 8 clauses apply to every Krenn NPC regardless of role or trait. Culture is the baseline.
|
||||
- Clauses 4 (void-oaths) must be gated to high-affect context by the composition engine — do not inject into neutral-register task behaviors.
|
||||
- Trait injectors layer on top. A Krenn-Cautious NPC gets clause 8 dampened ("you don't say everything you think"); a Krenn-Bold NPC gets clause 8 amplified.
|
||||
- Mood injectors override where relevant: Angry suppresses clause 7 (greetings become terse to absent); Nervous suppresses clause 8 (bluntness becomes deflection).
|
||||
- Tell context (see Section 5 of this document) layers on top of the above when a TellCategory is active.
|
||||
|
||||
---
|
||||
|
||||
## 2. Spike 1 Prompt Samples
|
||||
|
||||
These are the test payloads for Jeroen, Mellanie, and Paula to feed through the Rust wrapper manually. Designed to stress-test different injector combinations across behaviors and dialogue. Each payload includes: base text, character context, active injectors, and what we're specifically watching for.
|
||||
|
||||
---
|
||||
|
||||
### Behavior Samples
|
||||
|
||||
**B-1: Ambient neutral — farmer, low stakes**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn farmer, traits [Bold, Honest], mood neutral
|
||||
INJECTORS: Krenn culture v2 (all 8 clauses + examples), Bold trait modifier, neutral mood
|
||||
BASE TEXT: "checks the section's light cycle timer before deciding whether to water"
|
||||
```
|
||||
|
||||
*Watch for:* Krenn register emerging on a mundane agricultural task. The line is specific and should stay specific — the LLM should voice the register, not dilute the detail. If it comes back as "checks the irrigation system thoughtfully," something's wrong.
|
||||
|
||||
---
|
||||
|
||||
**B-2: Ambient social — dock worker, off-shift**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn dock worker, traits [Social, Curious], mood tired
|
||||
INJECTORS: Krenn culture v2, Social trait modifier, tired mood
|
||||
BASE TEXT: "slumps into a break room chair and stares at nothing for a full minute before reaching for a drink"
|
||||
```
|
||||
|
||||
*Watch for:* The base text already has strong texture. The ideal revoice is minimal interference — the Krenn voice should emerge without the model rewriting the specificity out of the line. If the output loses the "full minute" or "stares at nothing," the model is overwriting rather than voicing.
|
||||
|
||||
---
|
||||
|
||||
**B-3: High-affect situation — dock worker, discovering a problem**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn dock worker, traits [Honest, Cautious], mood anxious
|
||||
INJECTORS: Krenn culture v2, Honest trait modifier, Cautious trait modifier, anxious mood
|
||||
BASE TEXT: "discovers a discrepancy in a manifest that shouldn't be there"
|
||||
```
|
||||
|
||||
*Watch for:* Does void-oath vocabulary appear where appropriate (anxious discovery)? Does the Honest trait make them visibly reluctant to move past it rather than flag it quietly? The anxious mood should not produce melodrama — Krenn anxiety is tight and working-class, not expressive.
|
||||
|
||||
---
|
||||
|
||||
**B-4: Relationship-driven (positive) — foreman observing a subordinate**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn foreman, traits [Honest, Social], mood positive
|
||||
INJECTORS: Krenn culture v2, Honest + Social trait modifiers, positive mood
|
||||
RELATIONSHIP CONTEXT: "this NPC watches a newer hire figure something out on their own and respects that"
|
||||
BASE TEXT: "watches a new hire figure something out on their own and says nothing"
|
||||
```
|
||||
|
||||
*Watch for:* Krenn approval is quiet and doesn't announce itself — "says nothing" is the approval. If the model adds a nod, a grunt of satisfaction, or any verbal acknowledgment, it's over-emoting. The Krenn way is to let competence be seen without commentary.
|
||||
|
||||
---
|
||||
|
||||
**B-5: Relationship-driven (negative) — technician, non-acknowledgment**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn technician, traits [Bold], mood suppressed
|
||||
INJECTORS: Krenn culture v2, Bold trait modifier, suppressed mood
|
||||
RELATIONSHIP CONTEXT: "this NPC has an unresolved conflict with the NPC they're passing"
|
||||
BASE TEXT: "passes a colleague in the corridor without acknowledging them"
|
||||
```
|
||||
|
||||
*Watch for:* Does the non-acknowledgment read as a deliberate choice rather than distraction? Krenn conflict registers as pointed silence, not absence. If the model makes it ambiguous ("walks past without noticing"), the relational information is lost.
|
||||
|
||||
---
|
||||
|
||||
**B-6: Tell-context behavior — dock worker, Nervous tell active**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn dock worker, traits [Cautious, Honest], mood anxious
|
||||
INJECTORS: Krenn culture v2, Cautious trait modifier, anxious mood
|
||||
TELL CONTEXT: "this NPC is under stress and concealing something. Their attention is divided. They appear normally busy, but their focus is not fully on the task."
|
||||
BASE TEXT: "waits for a loading bay to clear before moving to the next task"
|
||||
```
|
||||
|
||||
*Watch for:* Does the tell context color the voiced behavior without surfacing the tell explicitly? The output should feel like a person who is preoccupied — slightly mechanical, not fully present — without stating that. The tell itself ("avoids eye contact with the dock supervisor") is a separate line, not this one.
|
||||
|
||||
---
|
||||
|
||||
**B-7: Social greeting, high-affect — mechanic receiving unexpected news**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn mechanic, traits [Bold, Curious], mood shocked
|
||||
INJECTORS: Krenn culture v2, Bold + Curious trait modifiers, shocked mood
|
||||
BASE TEXT: "stops what she's doing and looks up when she hears the news"
|
||||
```
|
||||
|
||||
*Watch for:* Shocked Krenn should produce a brief physical stop, not an emotional monologue. Does a void-oath appear? Does it stay short? The Curious trait should make the NPC want to know more — does that register as a follow-up question impulse?
|
||||
|
||||
---
|
||||
|
||||
### Dialogue Samples
|
||||
|
||||
**D-1: Low access tier — stranger interaction, foreman deflecting**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn foreman, traits [Bold, Honest], mood neutral
|
||||
INJECTORS: Krenn culture v2, Bold + Honest trait modifiers, neutral mood
|
||||
ACCESS TIER: low (stranger, no established relationship)
|
||||
TRUST LEVEL: none
|
||||
BASE TEXT: "I can't help with that."
|
||||
```
|
||||
|
||||
*Watch for:* A simple refusal in Krenn voice should be short and final, not apologetic, not elaborated. Does it add unnecessary softening ("I'm sorry, but...")? Does it add unnecessary hostility? The ideal output is something like "Can't help you there." or "Wrong person." — direct, not unkind, not extended.
|
||||
|
||||
---
|
||||
|
||||
**D-2: Medium access tier — mechanic redirecting to another NPC**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn mechanic, traits [Honest, Social], mood neutral
|
||||
INJECTORS: Krenn culture v2, Honest + Social trait modifiers, neutral mood
|
||||
ACCESS TIER: medium (familiar face, some rapport)
|
||||
TRUST LEVEL: acquaintance
|
||||
RELATIONSHIP: colleague (positive)
|
||||
BASE TEXT: "You'd want to ask Voss about that, not me."
|
||||
```
|
||||
|
||||
*Watch for:* First-name usage ("Voss") should feel natural — not introduced by the model, already in the base text, but should it be adjusted to feel more like a recommendation than a dismissal? Also: does the medium access tier change the tone? At low tier, the equivalent might be "Not my area." The same information delivered with slightly more investment.
|
||||
|
||||
---
|
||||
|
||||
**D-3: High access tier — technician disclosing a problem**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn technician, traits [Honest, Curious], mood concerned
|
||||
INJECTORS: Krenn culture v2, Honest + Curious trait modifiers, concerned mood
|
||||
ACCESS TIER: high (trusted, established relationship)
|
||||
TRUST LEVEL: trusted
|
||||
BASE TEXT: "Something's been off with the overnight manifest since last week. I logged it. Nobody's followed up."
|
||||
```
|
||||
|
||||
*Watch for:* This is the highest-stakes test. The information must survive intact — "since last week," "I logged it," "nobody's followed up" — these are specific and gameplay-relevant. The Honest trait should make the NPC clearly willing to say this; the Curious trait should hint at "and I want to know why." Does Krenn concern register as a practical complaint rather than dramatic worry?
|
||||
|
||||
---
|
||||
|
||||
**D-4: Dialogue with Nervous tell context — dock worker deflecting**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn dock worker, traits [Bold], mood stressed
|
||||
INJECTORS: Krenn culture v2, Bold trait modifier, stressed mood
|
||||
ACCESS TIER: medium (familiar face)
|
||||
TRUST LEVEL: acquaintance
|
||||
TELL CONTEXT: "this NPC is suppressing stress and deflecting. Their responses are shorter than usual and more clipped even for them."
|
||||
BASE TEXT: "Everything's fine. The shift's running fine."
|
||||
```
|
||||
|
||||
*Watch for:* "Everything's fine" said by a Bold Krenn NPC who is actually stressed should ring hollow in a specific way. Krenn-Bold overstating normalcy should read as over-assertion, not calm confidence. The tell context ("shorter than usual, more clipped") should push the output toward something like "Fine. Shift's fine." — the repetition is the tell.
|
||||
|
||||
---
|
||||
|
||||
**D-5: High-affect dialogue — foreman, angry, denying involvement**
|
||||
|
||||
```
|
||||
CHARACTER: Krenn foreman, traits [Honest, Bold], mood angry
|
||||
INJECTORS: Krenn culture v2, Honest + Bold trait modifiers, angry mood
|
||||
ACCESS TIER: medium
|
||||
TRUST LEVEL: acquaintance
|
||||
BASE TEXT: "I don't know who approved that, but it wasn't me and it wasn't my shift."
|
||||
```
|
||||
|
||||
*Watch for:* Krenn anger is specific and accusatory, not generalized. Does the output stay pointed? Does a void-oath appear? Does Bold make the NPC say this with more force than necessary (good) rather than pulling back (bad)? The line should have edge — not drama.
|
||||
|
||||
---
|
||||
|
||||
## 3. Authoring Workflow Spec
|
||||
|
||||
This documents the full copy team workflow under the final architecture: full pipeline (behaviors + dialogue), tells as passthrough with context influence.
|
||||
|
||||
---
|
||||
|
||||
### What the copy team authors
|
||||
|
||||
**Base text (ongoing, per zone/role/dialogue pool)**
|
||||
- `typical_behaviors` arrays in zone RON files — specific, observable, present-tense, no culture-specific vocabulary
|
||||
- Dialogue line pools in D-028 tagged format — base text as semantic layer
|
||||
- Register requirement: specific enough to serve as functional fallback; neutral enough that the LLM has room to add culture voice without fighting the base text
|
||||
- Volume: already being authored at ~50 lines/role (zone RONs), dialogue pools per D-028 architecture
|
||||
|
||||
**Culture injectors (once per culture, copy team owns)**
|
||||
- `voice_injectors` field in culture RON (new field — Tyre to add to schema)
|
||||
- 8-10 explicit LLM persona instruction sentences in second-person imperative register
|
||||
- 2 brief example pairs demonstrating correct culture voice
|
||||
- Copy team writes; copy team reviews spike output against these as ground truth
|
||||
- New culture cost: ~1 day of focused copy work
|
||||
- Current status: Krenn v2 above is ready for Spike 1
|
||||
|
||||
**Trait modifier clauses (once total, copy team owns)**
|
||||
- 1 injector clause per personality trait, 10 traits total
|
||||
- Written in world-specific terms, not generic personality descriptions
|
||||
- "Bold" means: `"You say the uncomfortable thing in front of people. You don't wait to be asked."` — not generic "confident"
|
||||
- "Cautious" means: `"You watch before you move. You finish thinking before you speak."` — not generic "careful"
|
||||
- I'll draft all 10 and share before Spike 1
|
||||
|
||||
**Negative injectors (system prompt layer, copy team writes, Tyre integrates)**
|
||||
- These go in the shared system/prefix prompt — not in the culture injector — to preserve the culture token budget
|
||||
- NI-1: No references to religion, gods, or prayer (the Settled Reach has none)
|
||||
- NI-2: No military rank honorifics (Commander, Admiral, Captain as rank — these read as Earth-military, not Settled Reach institutional)
|
||||
- NI-3: No incorrect technology terms (no warp, no hyperspace, no artificial gravity as a casual reference — use "plate gravity" or describe effects without naming the system)
|
||||
- NI-4: No contemporary Earth idioms or wit patterns (no sarcastic one-liners, no modern internet-derived irony)
|
||||
- NI-5: No Earth cultural references (Earth place names, Earth history, Earth religion)
|
||||
- NI-6: No other-franchise vocabulary (no Force, no Void of other settings, no recognizable lifted sci-fi terminology)
|
||||
- Approximate token cost: ~130-150 tokens in system prompt
|
||||
|
||||
**Anchor line flags (copy team, per notable NPC)**
|
||||
- Following Paula's N-2 proposal: `anchor_line: bool` flag in dialogue pool data model
|
||||
- Copy team flags lines that must not be re-voiced under any circumstances
|
||||
- Volume: only Tier 1 and Tier 2 notable NPCs; not ambient Tier 3
|
||||
- When flagged: line passes through to player exactly as authored, same as tells
|
||||
|
||||
---
|
||||
|
||||
### What the copy team does NOT author
|
||||
|
||||
**Tell behavior constraints (Gestalt + Tyre)**
|
||||
- The 5 TellCategory enums (`Nervous`, `Angry`, `Friendly`, `Guarded`, `RoutineDeviation`) map to 5 voice context clauses
|
||||
- These context clauses are injected when the relevant TellCategory is active — informing how surrounding behaviors and dialogue are voiced
|
||||
- The tell behaviors themselves pass through unchanged; the context clauses are not output, they're input constraints
|
||||
- I can write these 5 context clauses (it's copy work) — but the semantic definitions of what each category means must come from Gestalt before I draft. Flagging as a dependency.
|
||||
|
||||
**Tell base texts (automated)**
|
||||
- Tell behaviors are algorithmically generated from `DerivedTellState` per Tyre's Round 2 clarification
|
||||
- Fixed library: 5 categories × N cultures = ~20-40 voiced tell strings total, baked at build time per culture
|
||||
- Copy team does not author these; copy team reviews them once per culture as part of the culture QA process
|
||||
|
||||
---
|
||||
|
||||
### Review process: baked vs pre-voiced
|
||||
|
||||
**Baked content (hub zones, first hours of gameplay)**
|
||||
|
||||
This is the quality reference — what the player's first experience of the voiced system looks like. Human review is mandatory before ship.
|
||||
|
||||
Process:
|
||||
1. Tyre or Troblum runs the inference pipeline on all hub zone NPCs (Sova Transit District)
|
||||
2. Output is written to a review file per NPC, behavior/dialogue line by line
|
||||
3. I review each output against three criteria: (a) culture register correct, (b) no lore contamination, (c) specific base text content preserved
|
||||
4. Lines that pass: approved. Lines that fail: either rewritten by hand (treat as authored) or base text escalated (override with a better base text)
|
||||
5. I sign off on the baked output before it's committed to the build
|
||||
|
||||
Volume estimate: Sova Transit District at ~20 NPCs × 8 behaviors + 10 dialogue lines each = 360 voiced lines to review. Realistically 3-4 hours of review if output quality is good.
|
||||
|
||||
**Pre-voiced runtime content**
|
||||
|
||||
No human review of individual lines before player encounters them. This is the risk-managed tier.
|
||||
|
||||
Quality controls:
|
||||
- 5% of all runtime-voiced output is sampled to a log file
|
||||
- I review sampled logs on a per-sprint basis (fast when output quality is stable; longer when drift is detected)
|
||||
- Automated keyword scan against NI-1 through NI-6 blocklist runs on all output — any hit generates a flag for review
|
||||
- If the keyword scan hit rate rises above 2%, it's a signal that model or injector drift has occurred and a prompt audit is needed
|
||||
|
||||
**Injector maintenance**
|
||||
- Culture injectors are versioned. When I update an injector clause, all pre-voiced content generated with the previous version is invalidated (cache invalidation follows injector version hash)
|
||||
- This is Tyre's architecture decision, but I need to know the mechanism — if I can't iterate injectors without full cache invalidation, I have to be more conservative about updates
|
||||
|
||||
---
|
||||
|
||||
## 4. D-123 Amendment Review
|
||||
|
||||
Paula is drafting the amendment text. My proposed language from Round 2 has broad support and is reproduced here for Paula's reference. Jeroen's decision explicitly requires the build-time/runtime mode distinction, which Paula's N-1 framing correctly identified and which my language below incorporates.
|
||||
|
||||
**Proposed amendment text (for Paula's review and refinement):**
|
||||
|
||||
> D-123 is amended as follows: The AI pipeline operates in two modes: (1) build-time authoring tool — content generated before ship, reviewed by humans, committed to the build as reviewed; and (2) background runtime enhancement — content generated during gameplay as the player moves through the world, without per-line human review, governed by automated validation and periodic sampling. Both modes are in scope for the Settled Reach.
|
||||
>
|
||||
> All original D-123 constraints remain binding in both modes: culture vectors are the primary prompt constraint; the AI does not default to genre conventions; authorial control governs what the LLM may and may not produce. The AI pipeline does not generate narrative decisions — it applies voice to authored semantic content. D-124 is superseded.
|
||||
|
||||
**Copy-team-specific flag for Paula:** The amendment should explicitly note that anchor lines (D-092) and tell behaviors are excluded from LLM re-voicing in both modes. These are not covered by D-123 as written but must be covered by the amended text to prevent ambiguity.
|
||||
|
||||
---
|
||||
|
||||
## 5. Tell Context Clauses (draft, pending Gestalt sign-off on category semantics)
|
||||
|
||||
These are the 5 injector clauses that inform the LLM about an NPC's active tell state. They are injected into the behavior/dialogue prompt when a TellCategory is active. The tell itself never goes to the LLM; these context clauses do.
|
||||
|
||||
Draft — written for Krenn culture register but should be culture-neutral as system context:
|
||||
|
||||
| TellCategory | Context clause injected into prompt |
|
||||
|---|---|
|
||||
| `Nervous` | "This NPC is under stress and concealing something. Their attention is divided. They appear normally busy but their focus is not fully on what's in front of them. Responses may be shorter or more clipped than usual." |
|
||||
| `Angry` | "This NPC is suppressing anger. The surface is controlled, but there's an edge under it. Their patience is shorter than normal. They complete tasks but don't invite conversation." |
|
||||
| `Friendly` | "This NPC is in an open, positive state. They're more likely than usual to volunteer a word, hold a moment of eye contact longer, or acknowledge a familiar face." |
|
||||
| `Guarded` | "This NPC is watchful and giving nothing away. They answer questions with the minimum required. They are not hostile — they are contained." |
|
||||
| `RoutineDeviation` | "This NPC is not where they normally are or doing what they normally do. Something has changed. Their behavior may be slightly off-pattern in ways that could be read as distraction or purpose." |
|
||||
|
||||
**Note for Gestalt:** If the category semantics are substantially different from what's above, let me know and I'll revise. These are drafted from `tell_state.rs` partial visibility.
|
||||
|
||||
---
|
||||
|
||||
## One thing to settle before Spike 1
|
||||
|
||||
**Tell context clause culture-neutrality:** The 5 context clauses above are written to be culture-neutral — they describe the NPC's internal state without Krenn register. They're constraints, not output. This is intentional: culture injectors voice the output; tell context clauses describe the state. If the clauses are written in Krenn voice, the LLM may voice the constraint itself rather than apply it.
|
||||
|
||||
Gestalt — confirm: context clauses are system prompt input, the player never sees them, and they should be maximally descriptive rather than voiced? If so, the drafts above are correct. If tell context clauses need to be per-culture (e.g., "this Krenn NPC is under stress..."), the authoring burden increases significantly and I'd want to know now.
|
||||
@@ -0,0 +1,202 @@
|
||||
# Round 1: World Consistency Inventory — Miri
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Worldbuilding / Setting Consistency / IP Originality
|
||||
**Round:** 1 (Inventory)
|
||||
|
||||
---
|
||||
|
||||
## Opening position
|
||||
|
||||
Let me check this against what we've established before I endorse anything.
|
||||
|
||||
I've read the full proposal, the generator spike, both zone specs, the Krenn culture profile, and the relevant D-records (D-121, D-122, D-123, D-128). My position: **Option 3 (LLM re-voicing) is correct in direction, but the proposal as written underestimates the injection complexity required to preserve cultural distinctiveness at 2B model size.** The base-text-as-fallback architecture is worldbuilding-sound. The injector clause system, as currently described, is not deep enough to produce Krenn voices rather than generic SF working-class voices.
|
||||
|
||||
This is fixable. It is not a reason to reject Option 3. But it needs to be flagged clearly before the spike is designed.
|
||||
|
||||
---
|
||||
|
||||
## 1. Which option best preserves world consistency and cultural distinctiveness?
|
||||
|
||||
### Option 1 — Hand-authored pools (current)
|
||||
|
||||
**Setting note:** This is the highest-fidelity option for Krenn specifically, but it encodes a trap. We've invested enough authoring to make Krenn feel like a place. Adding a second culture — say, a station culture with different history and different relationship to the void — requires rebuilding the entire content layer from scratch. Option 1 preserves what we have; it cannot scale to what the Reach requires.
|
||||
|
||||
The cultural distinctiveness of Krenn in the current zone files is not accidental. Lines like "wipes grease on the thigh of her coveralls between jobs" and "explains a repair in clipped shorthand without looking up" are specific and earned. That specificity comes from Mellanie understanding Krenn culture well enough to author from the inside. You cannot template that away. What you can do is provide the LLM with enough cultural context that it produces output that doesn't contradict it.
|
||||
|
||||
**Verdict:** Best quality, worst scalability. Viable only for Krenn, only for v0.2.
|
||||
|
||||
### Option 2 — Composable primitives
|
||||
|
||||
**Setting note:** The cultural markers system already exists in the NpcBlueprint — `speech_register`, `filler_words`, `greeting`. These are well-defined discrete items. The problem is that composable primitives can only assemble *vocabulary*; they cannot assemble *worldview*.
|
||||
|
||||
"Void take it" is in the culture RON as an exclamation. A composable system can insert it correctly when an NPC exclaims. But it cannot decide that a Krenn character, when stressed, says "cold vacuum" rather than "void take it" — that requires understanding the emotional register each phrase carries. Krenn speech is working-class and compressed, not working-class and verbose. Composition engines tend toward additive assembly; Krenn culture requires compression and omission.
|
||||
|
||||
More seriously: composable primitives cannot prevent the cultural void-oath from being inserted in a context where it reads wrong. The behavior "hauls produce to the market stall before the morning exchange opens" doesn't naturaly carry an exclamation — but a template that tries to add cultural flavor might generate something like "hauls produce, grumbling 'void take it' at the weight." That's not wrong vocabulary. It's wrong register.
|
||||
|
||||
**Verdict:** Sufficient for vocabulary, insufficient for worldview. Produces Krenn-vocabulary characters that don't feel Krenn.
|
||||
|
||||
### Option 3 — LLM re-voicing (hybrid recommended)
|
||||
|
||||
**Setting note:** The architecture of this option is sound worldbuilding. The semantic base text as the gameplay layer and the voiced text as the enhancement layer maps cleanly to how the setting works diegetically — the world is always legible; the insert just adds resolution. A player who plays without AI enhancement experiences a functional Krenn world; one with it enabled hears the grain.
|
||||
|
||||
The injector clause structure — culture as baseline, personality as flavor, mood as override — matches the cultural hierarchy we've established (D-121: voice is culture-driven, job as modifier). This is not a coincidence; it's a correct abstraction of what the existing culture RON encodes.
|
||||
|
||||
**My concern is specifically about 2B-class model capability.** See Section 2.
|
||||
|
||||
**Verdict:** Correct direction. Quality ceiling depends on injector depth and model instruction-following capability.
|
||||
|
||||
---
|
||||
|
||||
## 2. Can injector clauses preserve culture-specific vocabulary at 2B model size?
|
||||
|
||||
This is where I need to be cautious.
|
||||
|
||||
The proposal describes the Krenn cultural injector as: *"Your speech is formal and avoids contractions."*
|
||||
|
||||
**That is the wrong injector for Krenn.** Krenn speech is not formal. It is direct-informal. Formal-without-contractions describes a completely different culture. This example injector reads like a placeholder written for a generic "culture adds formality" slot. If this is the actual injector that ships, we will produce NPCs who sound like junior civil servants, not people who live in a pressurized box 180 years from Earth.
|
||||
|
||||
The actual Krenn cultural injector needs to encode:
|
||||
|
||||
1. **Register:** Direct but not hostile. Short because time is genuinely scarce, not because they're unfriendly.
|
||||
2. **Oath vocabulary:** Void-adjacent exclamations only. "Void take it," "cold vacuum," "blood and void." Not divine oaths. Not secular Earth oaths ("damn it," "hell," "crap"). Space is the threat that kills you, not a metaphysical abstraction.
|
||||
3. **Community anchors:** Crew, shift, and street as emotional reference points. Not family in the traditional sense. Not institution. The people you'd bleed for are the people on your shift.
|
||||
4. **Competence signaling:** Respect is earned through doing the work. Characters signal this through precision of observation and action, not through status talk.
|
||||
5. **Negative space:** What NOT to say. No quips. No banter-for-banter's-sake. No Firefly register. No "sir"/"ma'am" deference culture. No references to political institutions by name.
|
||||
|
||||
This is approximately 200-300 words of instruction. At 2B model size, the effective instruction-following window for stylistic constraints is uncertain. Small models are known to:
|
||||
|
||||
- Anchor to the most-represented working-class register in training data (which is contemporary American/British English)
|
||||
- Treat unfamiliar vocabulary ("void take it") as errors and smooth them to standard alternatives
|
||||
- Flatten cultural subtlety under pressure from the base text's neutral English
|
||||
|
||||
**My assessment:** A 2B model can probably preserve oath *vocabulary* if the injector explicitly lists the terms and instructs their use. It cannot reliably preserve the *philosophy* behind the vocabulary. The difference between a character who says "void take it" because they were told to and a character who says it because space genuinely terrifies them — that lives in tone and context, not in word selection.
|
||||
|
||||
**Practical floor:** Injector clauses can guarantee correct oath vocabulary and correct register description. They cannot guarantee that the model uses them with correct Krenn weight. The spike must explicitly test oath preservation and register accuracy, not just fluency.
|
||||
|
||||
---
|
||||
|
||||
## 3. How do we prevent the LLM from introducing lore-breaking content?
|
||||
|
||||
Setting note — I need to enumerate what can actually go wrong here, because "lore contamination" is too vague to design against.
|
||||
|
||||
### Type A: Franchise bleed
|
||||
|
||||
At 2B, the model's working-class SF character register draws heavily from training data: Firefly, The Expanse, Babylon 5, Mass Effect ambient NPCs. These feel like the Settled Reach superficially (space, working class, pragmatic) but are not it. Indicators:
|
||||
|
||||
- Firefly register: "Shiny," quippy banter, frontier-town affect
|
||||
- The Expanse register: Belt creole vocabulary, anti-inner-planets resentment framing
|
||||
- Mass Effect register: Military protocol, "Commander/Spectre" deference vocabulary
|
||||
|
||||
**Guard:** Negative injectors. The cultural injector should include explicit NOT-lists: "Do not use military rank terms. Do not use contractions as markers of informality. Do not produce quips or banter." This is unusual prompting but necessary at small model sizes.
|
||||
|
||||
### Type B: Anachronistic technology
|
||||
|
||||
The model knows what generic SF NPCs talk about. Wormholes are in the Settled Reach vocabulary — good. Holoscreens, jump drives, FTL ships, blasters — not in the Settled Reach. "Insert" is the correct term for neural implants; the model may substitute "neural link," "implant," "chip," "interface." The span gate is the correct term; the model may produce "wormhole portal," "jump gate," "stargate."
|
||||
|
||||
**Guard:** Terminology whitelist in injectors. This is a short list: insert, span gate, horizon gate, void, the Reach. Instruction: "Only use the following terms for technology and infrastructure: [list]." This must be verified explicitly in the spike.
|
||||
|
||||
### Type C: Setting-neutral social structures
|
||||
|
||||
The model may produce NPCs who reference senators, admirals, corporations, megacities — structures that exist in generic SF but not in the Settled Reach's specific institutional topology. For Krenn, the relevant institutions are the Commission (distant authority, suspect), the shift structure (immediate authority, respected if competent), and the local community (primary loyalty).
|
||||
|
||||
**Guard:** Injector should specify institutional vocabulary. "When referencing authority, use: Commission, shift lead, port authority. Do not use: government, military, senate, council, corporation."
|
||||
|
||||
### Type D: Social register bleed
|
||||
|
||||
Working-class characters in English-language training data sound like contemporary Earth working class. Krenn working class has 180 years of post-Earth cultural evolution in an enclosed artificial environment. The biggest surface tell is: **contemporary Earth profanity and social reference**. A Krenn character should not reference sports, religion, nationalism, or other Earth-rooted social fabric. The model will produce these because they are statistically dominant in training data for working-class dialogue.
|
||||
|
||||
**Guard:** Explicit exclusion in injectors. "Do not reference religion, sports, nationality, or Earth-origin social structures."
|
||||
|
||||
### Type E: Want/Tell contamination
|
||||
|
||||
This is the most dangerous type. The Want/tell system generates deliberately ambiguous behavioral signals — the player is supposed to read them, not have them explained. If the LLM re-voices a Want tell, it might either: (a) neutralize the ambiguity into a flat description, or (b) over-explain it into an obvious broadcast.
|
||||
|
||||
Base text: *"checks the vault door twice before walking away"*
|
||||
Bad re-voice A (neutralized): *"walks past the vault door"* — tell removed entirely
|
||||
Bad re-voice B (over-explained): *"lingers nervously near the vault door, clearly worried about something inside"* — tell made too explicit
|
||||
|
||||
**Guard:** Want tells must be in the protected-content category. They are not candidates for re-voicing. The base text for a Want tell IS the player-facing text. This needs to be a hard architectural boundary.
|
||||
|
||||
---
|
||||
|
||||
## 4. How does re-voicing interact with the cultural markers system?
|
||||
|
||||
The current `CulturalMarkers` struct carries:
|
||||
- `speech_register` (a string)
|
||||
- `filler_words` (a vec of strings)
|
||||
- `greeting` (a string)
|
||||
|
||||
These are already discrete, enumerable, culture-authored items. They are the *output* of the generator, not the LLM injector input. This creates a possible alignment problem.
|
||||
|
||||
If the LLM injector says "use filler word 'look'" but the NPC's generated `CulturalMarkers.filler_words` contains `["right", "yeah"]` — which governs? The struct was built from the culture RON with randomness applied. The injector is built from the culture RON directly.
|
||||
|
||||
More importantly: the cultural markers system is already doing what Option 3 proposes, for vocabulary. It is assigning culture-specific vocabulary to individual NPCs. The LLM injector would be a second layer doing the same thing at the prose level.
|
||||
|
||||
**My recommendation:** The cultural markers struct should be the **source of truth** for the LLM injector's per-NPC vocabulary. When constructing the injector prompt, pull `filler_words`, `greeting`, and `speech_register` from the NPC's generated blueprint, not from the culture RON directly. This ensures the voiced output is consistent with what the blueprint already specifies, and avoids the dual-source problem.
|
||||
|
||||
This also means the LLM injector for vocabulary is zero-additional-authoring — it reads from the already-generated NpcBlueprint.
|
||||
|
||||
---
|
||||
|
||||
## 5. What breaks if we choose the wrong option?
|
||||
|
||||
### If we choose Option 1 (hand-authored only)
|
||||
|
||||
**Setting cost:** Krenn is permanently the only culture with full coverage. Every other culture the team needs — and the Reach requires multiple cultures for the investigation mechanics to work — starts from nothing. The IP originality problem is managed by authoring, but only for Krenn. Everywhere else defaults to generic SF.
|
||||
|
||||
More importantly: **D-128 says culture is implicit in location**. As we add locations, we add culture requirements. Option 1 makes every new location a content crisis.
|
||||
|
||||
### If we choose Option 2 (composable primitives, no LLM)
|
||||
|
||||
**Setting cost:** Characters produce the right vocabulary in the wrong contexts. A composable system that assembles "void take it" as a cultural marker will insert it wherever the culture modifier fires, regardless of whether the character is mildly inconvenienced or confronting existential danger. Krenn exclamations are calibrated by severity — "stars" is mild, "blood and void" is serious. Template assembly has no severity model.
|
||||
|
||||
More practically: composable primitives produce dialogue that reads as *assembled*. The player will notice the seams. The immersive sim depends on NPCs feeling like inhabitants. Assembled dialogue breaks that.
|
||||
|
||||
### If we choose Option 3 poorly (LLM with shallow injectors)
|
||||
|
||||
**Setting cost:** Franchise bleed at scale. Every NPC sounds vaguely like a Space Western/Military SF character. The Settled Reach stops feeling like its own place and starts feeling like a mashup of recognizable genre influences. This is the IP originality failure mode — not copyright infringement, but creative dissolution. If a reader could point at any random NPC and say "that's The Expanse," we've failed.
|
||||
|
||||
The secondary failure: **void-oaths become decorative**. If the model uses "void take it" and "damn it" interchangeably based on training data frequency, the oath stops carrying worldbuilding weight. The player stops reading it as a setting signal.
|
||||
|
||||
### If we choose Option 3 correctly (LLM with deep injectors, protected tells)
|
||||
|
||||
**Residual risk:** The seam between base-text and voiced-text may be perceptible when the player first encounters a slow-generated NPC. From a worldbuilding perspective, this is survivable — the base text is designed to be legible, not broken. But the transition needs to be invisible. If a player sees the base text and the voiced text in close succession (e.g., first visit vs. return visit after pre-voicing completes), the delta in quality might draw attention to the system rather than the world.
|
||||
|
||||
---
|
||||
|
||||
## 6. One question I need answered before I can commit
|
||||
|
||||
**What is the effective token budget for cultural injector clauses in the final prompt construction?**
|
||||
|
||||
This is the binding constraint for everything I've described. If the total prompt is structured as:
|
||||
|
||||
`[Task instruction] + [Base text] + [Mood injector] + [Personality injectors] + [Cultural injector] + [Format instruction]`
|
||||
|
||||
...then the cultural injector is competing for space with everything else. At 2B model size, very long prompts produce worse instruction-following, not better. The cultural injector I described above — register, oath vocabulary, community anchors, competence signaling, exclusions — requires approximately 200-300 words to encode Krenn accurately. If the budget is 50-80 tokens, we can specify register and list the oaths but nothing else. If it's 200+ tokens, we can encode the cultural philosophy.
|
||||
|
||||
**The quality ceiling of culture preservation in this system is directly determined by prompt budget.** I cannot assess whether Option 3 can preserve Krenn cultural distinctiveness until Tyre tells me how many tokens the cultural injector can consume without degrading output quality at 2B model size.
|
||||
|
||||
If the answer is "under 100 tokens," we need to revisit the injector architecture and consider culture-specific few-shot examples rather than instruction-only injectors. Few-shot examples may produce better Krenn register than instructions about Krenn register — but they consume more tokens and require authoring examples for each culture.
|
||||
|
||||
---
|
||||
|
||||
## Summary position
|
||||
|
||||
| Criterion | Option 1 | Option 2 | Option 3 (hybrid) |
|
||||
|-----------|----------|----------|-------------------|
|
||||
| Krenn cultural distinctiveness | Excellent | Adequate | Good, if injectors are deep |
|
||||
| Multi-culture scalability | Poor | Moderate | Excellent |
|
||||
| Void-oath preservation | Guaranteed | Vocabulary only | Depends on model + budget |
|
||||
| Lore contamination risk | None | Low | Moderate (franchise bleed) |
|
||||
| Want/tell protection | Guaranteed | Guaranteed | Requires explicit protection |
|
||||
| IP originality | Guaranteed | Guaranteed | Requires negative injectors |
|
||||
|
||||
**Recommended path:** Option 3 (LLM re-voicing) with:
|
||||
1. Want tells and relationship-specific behaviors as explicitly protected, non-re-voiced content
|
||||
2. Cultural injectors sourced from NpcBlueprint.cultural_markers (not re-derived from culture RON)
|
||||
3. Negative injectors (NOT-lists) as a first-class component of cultural injection
|
||||
4. Spike must explicitly test oath preservation and franchise-bleed resistance, not just fluency
|
||||
5. The current placeholder Krenn injector ("formal, avoids contractions") must be replaced before any quality assessment is valid
|
||||
|
||||
The base-text-as-fallback architecture is correct and worldbuilding-sound. The progressive enhancement model maps cleanly to how the setting works. My only blocker is knowing the prompt token budget before I can assess whether deep injectors are feasible at 2B.
|
||||
@@ -0,0 +1,302 @@
|
||||
# Round 2: World Consistency Evaluation — Miri
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Worldbuilding / Setting Consistency / IP Originality
|
||||
**Round:** 2 (Convergent Evaluation)
|
||||
|
||||
---
|
||||
|
||||
## Resolution Matrix
|
||||
|
||||
| Question | Answer |
|
||||
|----------|--------|
|
||||
| Which proposal do you recommend? | **A**, with one named condition |
|
||||
| Are there blockers in your recommended proposal? | Yes — one: the 150-token injector budget needs a hybrid instruction+example structure, not instruction-only. Details below. |
|
||||
| Can you live with Proposal B? | Yes, conditional on spike proving >98% semantic core preservation before deployment |
|
||||
| Can you live with Proposal C? | No for v0.2. Architecturally sound but wrong sequencing. Reasons below. |
|
||||
| Minimum change to make B acceptable | Define the spike success threshold for tell semantic preservation explicitly (≥98%) and treat failure as automatic fallback to Proposal A passthrough |
|
||||
| Minimum change to make C acceptable | Defer dialogue re-voicing to a follow-up sprint; treat C as A + "extend to dialogue after validation" |
|
||||
|
||||
---
|
||||
|
||||
## Q-R1-04: Is 150 Tokens Sufficient for Cultural Injector Clauses?
|
||||
|
||||
This is the question I raised in Round 1. Now that I have a concrete budget number (150 tokens ≈ 100-120 words of English), I can give a concrete answer.
|
||||
|
||||
### What 150 tokens can encode
|
||||
|
||||
An aggressive compression of the Krenn cultural injector:
|
||||
|
||||
---
|
||||
*Krenn System culture. Direct-informal register — short because time is scarce, not unfriendly. Competence earns respect; showing up matters more than rank. Community references: crew, shift, street. Exclamations ONLY from: "void take it" / "stars" / "blood and void" / "void's sake" / "cold vacuum." Greetings: hey, morning, shift treating you alright. Farewells: shift's calling, gotta move. NO religious oaths, NO quips, NO sir/ma'am deference. Filler words: look, right, yeah, so.*
|
||||
---
|
||||
|
||||
Word count: ~85 words. Token count: ~100-115 tokens. **This fits within the 150-token budget.**
|
||||
|
||||
What this encodes at 150 tokens:
|
||||
- Speech register descriptor ✓
|
||||
- Oath vocabulary (explicit list, mandatory constraint) ✓
|
||||
- Greeting/farewell pool ✓
|
||||
- Filler word pool ✓
|
||||
- Minimal NOT-list (3 items) ✓
|
||||
- Community anchor vocabulary ✓
|
||||
|
||||
### What 150 tokens cannot encode
|
||||
|
||||
What is missing at 150 tokens:
|
||||
|
||||
1. **The reason behind the register.** "Direct-informal" describes the surface; it doesn't explain that Krenn directness is *compressed purposefulness* — short because the work is real and time is genuinely scarce — not shortness-as-personality. A 2B model interpreting "direct-informal" without this context will produce casual American working-class dialogue. Krenn is not casual American. It's earned competence under material constraint.
|
||||
|
||||
2. **Void-oath philosophy.** The culture RON describes Krenn oaths as "void-adjacent — space is real here, and hostile. They don't swear by gods or governments. They swear by what kills you." This is the *reason* the oath vocabulary is what it is. A model that has the vocabulary list but not this context will use "void take it" as a rule, not as an instinct — and the register difference is visible in how and when the oath appears.
|
||||
|
||||
3. **Deep NOT-list.** 150 tokens gives room for 3-4 exclusions. The full exclusion set needed to prevent franchise bleed is 8-10 items. See Section 4 for the full universal negative injector set.
|
||||
|
||||
4. **Social calibration.** Krenn culture is community-oriented but not warm-in-the-American-sense. Outsiders are "tolerated but watched." Loyalty "runs narrow and deep — to your crew, your shift, your street." This social topology shapes how NPCs interact with the player in ways that a register descriptor cannot convey.
|
||||
|
||||
### 150 tokens vs. 300 tokens: what changes
|
||||
|
||||
At 300 tokens, you can add:
|
||||
|
||||
- A worldview sentence: "Settled Reach workers live in sealed environments — space is the hostile reality outside the hull, not a romantic backdrop. Void-oaths reflect proximity to vacuum, not metaphor."
|
||||
- Behavioral guidance: "Krenn workers show competence through visible action, not through claiming status. They answer questions with the minimum needed and add context only when it affects the work."
|
||||
- Extended NOT-list: Full set of 8-10 exclusion categories rather than 3.
|
||||
- Social calibration: "Outsiders are politely watched, not warmly welcomed. Trust is earned through reliable work, not through friendliness."
|
||||
|
||||
The difference between 150 and 300 tokens is the difference between **following rules** and **embodying a voice**. At 150 tokens, the model follows a vocabulary list. At 300 tokens, it has enough philosophical context to make sensible judgment calls in edge cases the list doesn't cover.
|
||||
|
||||
### Instruction-only vs. few-shot examples at this budget
|
||||
|
||||
**Instruction-only at 150 tokens:** Viable for vocabulary preservation. Risky for register. The model applies rules without understanding the cultural context behind them.
|
||||
|
||||
**Few-shot only at 150 tokens:** Not viable. A single example pair costs ~50-70 tokens. With 150 tokens, you can fit 2 example pairs and nothing else. A 2B model inferring cultural rules from 2 examples alone will generalize poorly.
|
||||
|
||||
**Hybrid (instructions + examples) at 200-250 tokens:** This is the recommendation. Specifically:
|
||||
|
||||
- ~80-90 tokens: minimal instruction set (register, oath vocabulary list, 3-4 NOT-items)
|
||||
- ~120-140 tokens: 2 brief example pairs showing Krenn voice in practice
|
||||
|
||||
Example pair format:
|
||||
```
|
||||
BASE: "checks the gate without looking at you"
|
||||
KRENN: "runs the check, eyes on the panel — gives you a nod when it clears"
|
||||
---
|
||||
BASE: "works on the conduit"
|
||||
KRENN: "traces the run with a handheld light, finds the splice, fixes it without ceremony"
|
||||
```
|
||||
|
||||
Each pair: ~35-40 tokens. Two pairs: ~70-80 tokens. Adding these to the 100-token instruction core produces ~170-180 total — still under 250 tokens.
|
||||
|
||||
**Why examples outperform instructions at 2B model size:**
|
||||
|
||||
Small models are pattern matchers before they are instruction-followers. An example that demonstrates Krenn register (spare phrasing, visible competence, no ceremony) is more reliably reproduced than an instruction describing the same properties in the abstract. The instruction tells the model *what* to do; the example shows it *what the output looks like*.
|
||||
|
||||
### Verdict on Q-R1-04
|
||||
|
||||
**150 tokens is sufficient for vocabulary preservation only.** It is insufficient for register philosophy and provides a minimal NOT-list. The spike should test 150-token instruction-only against 200-token hybrid (instructions + 2 examples) and measure:
|
||||
|
||||
1. Oath vocabulary correct usage rate (target: >95%)
|
||||
2. Register accuracy (blind review: "does this sound like the Settled Reach or generic SF?")
|
||||
3. Franchise bleed rate (target: <2% of outputs contain recognizably non-Settled-Reach vocabulary or tone)
|
||||
|
||||
If the 150-token instruction-only version meets those thresholds, it's acceptable. My prediction: it meets criterion 1 but struggles with criteria 2 and 3. The hybrid version at 200 tokens is the recommendation.
|
||||
|
||||
---
|
||||
|
||||
## Evaluating the Three Proposals Against World Consistency
|
||||
|
||||
### Proposal A: Conservative — Behaviors Only, Tells Locked
|
||||
|
||||
**World consistency assessment: Strongest of the three.**
|
||||
|
||||
Proposal A's tell passthrough is the correct worldbuilding decision. Tells are not flavor — they are the observable surface of the information asymmetry mechanic (D-007 pillar 1). The base-text tell strings are authored to be precise. Passthrough preserves that precision absolutely.
|
||||
|
||||
The behaviors-only scope is also correct sequencing. Observable behaviors are short-form (5-15 words), the failure mode is bounded (a poorly re-voiced behavior is aesthetic damage, not mechanical damage), and the quality bar is clear (the current zone RON strings are the reference).
|
||||
|
||||
**Specific world consistency concerns for Proposal A:**
|
||||
|
||||
The 150-token injector budget (all three proposals share this for ambient behaviors) is adequate for vocabulary but requires the hybrid instruction+example structure I described above. If the injector is instruction-only at 150 tokens, the output will be Krenn-vocabulary but not necessarily Krenn-register.
|
||||
|
||||
The NOT-list in the injector needs the universal negative injectors (see Section 4). These are not culture-specific — they prevent franchise bleed for any culture, including cultures we haven't authored yet.
|
||||
|
||||
**Lore contamination surface: Small.** 5-15 word behaviors. Wrong vocabulary is visible and correctable. The specific cultural tell that a behavior uses wrong oath vocabulary is immediately audible.
|
||||
|
||||
**My blocker for Proposal A:** The injector architecture must use the hybrid instruction+example format at ~200 tokens, not instruction-only at 150 tokens. If Troblum can confirm 200 tokens is within throughput tolerance for the ambient behavior use case (shorter strings, higher volume), this is resolved.
|
||||
|
||||
**Verdict: Recommended.** Cleanest risk profile. Tell safety is absolute. Pipeline is testable. If the injector hybrid is confirmed viable, no remaining blockers.
|
||||
|
||||
---
|
||||
|
||||
### Proposal B: Two-Track — Behaviors + Tells with Semantic Core
|
||||
|
||||
**World consistency assessment: Appealing in theory, risk in practice.**
|
||||
|
||||
I want to give Gestalt's semantic_core proposal credit — it's architecturally elegant and the theory is correct. A tell that reads "avoidance_behavior" in Krenn dialect should express differently than one in a different culture. The player who has spent 20 hours in the Reach should learn to read Krenn avoidance as distinct from other cultures' avoidance. That cultural specificity is worldbuilding-good.
|
||||
|
||||
The risk is asymmetric failure. Proposal A fails visibly and audibly (wrong vocabulary in a behavior line). Proposal B fails invisibly and mechanically — a tell that *sounds fine* but does not preserve the phenomenon it was authored to signal. That is a corrupted gameplay-information path that may not be detected in testing because it reads as acceptable prose.
|
||||
|
||||
**The specific risk surface I'm watching:**
|
||||
|
||||
The current tell grammar contains behaviors like:
|
||||
- "affects exaggerated calm" — this is a precise observation: the NPC is performing composure, not naturally composed
|
||||
- "becomes evasive and avoids eye contact" — two behaviors combined into one tell, which is what makes it readable
|
||||
- "checks surroundings repeatedly" — frequency ("repeatedly") is load-bearing; "checks surroundings" is a different tell
|
||||
|
||||
Can a 2B model, given semantic_core = "suppression_behavior", preserve the "exaggerated" quality that distinguishes performed calm from natural calm? Can it preserve the "repeatedly" that makes the second tell a tell rather than a normal behavior? My concern is that the model preserves the category (avoidance, suppression, vigilance) but loses the specific qualifier that makes each tell *readable as a tell* rather than readable as neutral behavior.
|
||||
|
||||
**Setting note:** The void-oath vocabulary issue is *more* dangerous in tells than in ambient behaviors. An ambient behavior that uses wrong vocabulary is a minor lore break. A tell that uses wrong vocabulary may read as a different tell entirely — wrong signal, wrong player inference. If "becomes evasive and avoids eye contact" is re-voiced with a Krenn register that produces "keeps to themselves, moves through the space quiet" — that is NOT a strong avoidance tell. It could be an introversion tell, a neutral behavior, or nothing. The vocabulary change produced a semantic shift.
|
||||
|
||||
**Condition for acceptability:** The spike must test Proposal B's constrained re-voicing on tell strings explicitly and measure whether the phenomenon survives at >98% accuracy. "Phenomenon survives" means: a blind reviewer, shown the base text tell and the re-voiced tell, identifies them as expressing the same observable pattern. Below 98%, fall back to Proposal A passthrough for tells.
|
||||
|
||||
**Lore contamination surface: Medium.** Same as A for ambient behaviors. Additional surface in tell re-voicing where cultural register change could corrupt signal.
|
||||
|
||||
**Verdict: Acceptable with spike threshold condition. Not recommended over A for v0.2.**
|
||||
|
||||
---
|
||||
|
||||
### Proposal C: Full Pipeline — Behaviors + Dialogue, Tells Locked
|
||||
|
||||
**World consistency assessment: Architecturally correct, wrong sequencing.**
|
||||
|
||||
Dialogue is where cultural voice matters most to the player. What NPCs *say* is where Krenn identity is most legible — their speech register, their void-oaths used naturally in conversation, their working-class pragmatism in how they respond to the player. I agree with Paula that dialogue is the highest-value target for re-voicing.
|
||||
|
||||
The problem is dialogue is also the highest-risk target for lore contamination at 2B model size.
|
||||
|
||||
**Why dialogue is harder for small models:**
|
||||
|
||||
Dialogue is longer (15-40 words), more contextually demanding (relationship state, conversation topic, access tier, trust tier), and more culturally legible — a player listens to an NPC speak for several sentences and forms a detailed cultural read. A single behavioral mis-register is a blip. A dialogue mis-register persists across the conversation.
|
||||
|
||||
The 400-500 token total prompt for dialogue means the cultural injector (~100-200 tokens) competes with dialogue context (~80 tokens) for the model's effective attention window. At 2B model size, longer prompts can *dilute* adherence to specific constraints — the model pays more attention to the most recent context and less to constraints stated earlier in the prompt. This means the cultural injector may get less weight in a 400-token dialogue prompt than in a 200-token behavior prompt.
|
||||
|
||||
**The franchise bleed failure mode at dialogue scale:**
|
||||
|
||||
For behaviors: "wrong vocabulary once" is the failure mode — detectable, bounded.
|
||||
|
||||
For dialogue: "sounds like the wrong franchise for the whole conversation" is the failure mode — immersive, corrosive. An NPC whose ambient behaviors are correctly Krenn-voiced but whose dialogue sounds like a Mass Effect NPC creates a cognitive dissonance that damages trust in the setting. The player will notice "this feels like I've heard this before" more readily in dialogue than in brief behavioral observations.
|
||||
|
||||
**The access/trust tier constraint adds surface:**
|
||||
|
||||
Paula's observation that dialogue has access/trust tier tags is correct, and it's a structural advantage for information safety. But from a worldbuilding perspective, those tiers also change the *register* of the dialogue — a high-trust conversation with a Krenn worker sounds different from a low-trust first encounter. A 2B model given cultural injectors + trust tier needs to combine two constraint sets simultaneously without collapsing either. That's a harder instruction-following task.
|
||||
|
||||
**How to make C acceptable:**
|
||||
|
||||
Treat C as "Proposal A + planned dialogue extension after spike validation." The architecture is the same. The sequencing is: ship A with behaviors re-voiced, validate quality in v0.2, extend to dialogue once the pipeline is proven. This de-risks the v0.2 quality bar while preserving the full-pipeline vision.
|
||||
|
||||
**Lore contamination surface: Large.** Behaviors + dialogue = two content types, two failure modes. The dialogue failure mode is higher-stakes and harder to catch in testing.
|
||||
|
||||
**Verdict: Not recommended for v0.2. Recommend as explicit v0.3 target.**
|
||||
|
||||
---
|
||||
|
||||
## Lore Contamination Ranking
|
||||
|
||||
From smallest to largest contamination surface, across all three proposals:
|
||||
|
||||
**A < B < C**
|
||||
|
||||
| Proposal | Contamination surface | Primary failure mode |
|
||||
|----------|----------------------|---------------------|
|
||||
| A | Small | Wrong vocabulary in ambient behavior (aesthetic, catchable) |
|
||||
| B | Medium | Wrong register in tell re-voicing (mechanical, subtle) |
|
||||
| C | Large | Franchise bleed in extended dialogue (immersive, corrosive) |
|
||||
|
||||
**Specific guards each proposal needs:**
|
||||
|
||||
**Proposal A:**
|
||||
1. Universal negative injectors (see Section 4) — required for all cultures
|
||||
2. Hybrid instruction+example injector format (200 tokens, not 150) — required for register accuracy
|
||||
3. CulturalMarkers struct as injector source — ensures per-NPC markers are consistent with blueprint
|
||||
4. Build-time validation pass on baked content: oath vocabulary check, franchise vocabulary blocklist
|
||||
|
||||
**Proposal B (in addition to A's guards):**
|
||||
5. Semantic core vocabulary for every tell type authored before spike — "avoidance_behavior", "suppression_behavior", "vigilance_behavior", etc.
|
||||
6. Spike success threshold: >98% phenomenon preservation on blind review before constrained re-voicing deploys
|
||||
7. Semantic core reviewer: a post-re-voicing validation pass that checks whether the phenomenon category survives
|
||||
|
||||
**Proposal C (in addition to A's guards):**
|
||||
8. Separate quality bar for dialogue vs. behaviors — dialogue must pass a longer blind review
|
||||
9. Trust/access tier constraints authored as explicit injector components, not implicit from context
|
||||
10. Dialogue-specific franchise bleed check: blocklist for recognizable genre dialogue patterns ("Commander, I've been expecting you", etc.)
|
||||
11. Defer to v0.3 spike after A has been validated in production
|
||||
|
||||
---
|
||||
|
||||
## Section 4: Universal Negative Injectors
|
||||
|
||||
These apply to re-voicing prompts for ALL cultures. They prevent the most common 2B-model failure modes regardless of culture-specific injector content.
|
||||
|
||||
**Draft negative injectors:**
|
||||
|
||||
**NI-1:** "Do not use religious language, prayer, references to deities, or spiritual practices of any kind. This universe's cultures do not have canonical religious expression in this context."
|
||||
|
||||
**NI-2:** "Do not use military rank titles (Commander, Captain, Sergeant, General, Admiral, Lieutenant, Officer). Authority structures in this setting use civilian and occupational titles only."
|
||||
|
||||
**NI-3:** "Do not reference technology that does not exist in this setting. Valid technology terms: insert (neural implant worn in the base of the skull), span gate (faster-than-light transit gate), the Reach (the interstellar network of settled systems). Do not use: holoscreens, blasters, force fields, teleporters, mind-reading, jump drives, warp speed."
|
||||
|
||||
**NI-4:** "Do not produce wit, banter, or jokes intended to be entertaining to the reader. Humor in this setting is dry, incidental, and rare. Do not add levity that was not present in the original text."
|
||||
|
||||
**NI-5:** "Do not reference Earth, nations, sports, politics, or other contemporary Earth-origin social structures. These characters have no memory of Earth; it is irrelevant to them."
|
||||
|
||||
**Usage notes:**
|
||||
|
||||
These five injectors total approximately 130-150 tokens. They cannot all be included in the 150-token cultural injector budget without crowding out culture-specific content. **These should be encoded in the system prompt or a shared prompt prefix**, not in the per-culture injector. This separates universal constraints (pipeline-level) from culture-specific constraints (authoring-level) and leaves the full 150-token budget for culture-specific content.
|
||||
|
||||
The split:
|
||||
- System/prefix prompt: NI-1 through NI-5 (~130-150 tokens, shared across all cultures)
|
||||
- Culture injector: register, vocabulary, NOT-lists that are culture-specific (~100-150 tokens)
|
||||
- Trait modifier: personality expression in this culture (~20-30 tokens)
|
||||
- Mood tag: current state (~5-10 tokens)
|
||||
|
||||
Total prompt overhead (before base text): ~270-340 tokens. Combined with base text (~20-40 tokens) and format instructions (~20-30 tokens), this is a ~310-410 token prompt for ambient behaviors. Within the behavior use case budget.
|
||||
|
||||
---
|
||||
|
||||
## The CulturalMarkers Source-of-Truth Issue (Restatement)
|
||||
|
||||
I raised this in Round 1 and it survives into Round 2. All three proposals assemble cultural injectors. The question is: where do the per-NPC vocabulary items come from?
|
||||
|
||||
The `NpcBlueprint.cultural_markers` struct already contains per-NPC vocabulary assignments:
|
||||
```
|
||||
CulturalMarkers {
|
||||
speech_register: "direct, minimal pleasantries, gets to the point",
|
||||
filler_words: ["look", "yeah"], // randomized subset from culture pool
|
||||
greeting: "shift treating you alright?", // randomized from culture pool
|
||||
}
|
||||
```
|
||||
|
||||
These are per-NPC, generated once, seeded deterministically. If the injector re-derives vocabulary from the culture RON at prompt time, a given NPC might have `filler_words: ["look", "yeah"]` in their blueprint but the prompt might say "use filler words: look, right, yeah, so, listen, well" — a different set.
|
||||
|
||||
**The fix is simple and I want it confirmed as architectural principle:** The injector prompt assembler reads `npc.cultural_markers.filler_words`, `npc.cultural_markers.greeting`, and `npc.cultural_markers.speech_register` to construct per-NPC injector content, not the culture RON directly. The culture RON is the source for the *template*; the blueprint is the source for the *instance*.
|
||||
|
||||
This is not a blocker for any proposal, but it must be decided before the `sr-voice` crate is implemented. Otherwise we have two sources of truth for per-NPC vocabulary.
|
||||
|
||||
---
|
||||
|
||||
## Summary Position
|
||||
|
||||
**Recommended: Proposal A** with:
|
||||
1. Universal negative injectors in system/prefix prompt (shared, ~130-150 tokens)
|
||||
2. Hybrid instruction+example format for culture injectors (~200 tokens, not 150 instruction-only)
|
||||
3. `NpcBlueprint.cultural_markers` as the source for per-NPC vocabulary in injector assembly
|
||||
4. Tell passthrough as absolute — no tell re-voicing in v0.2 under any circumstances
|
||||
5. Proposal C (dialogue) as explicitly-stated v0.3 target in the D-record
|
||||
|
||||
**The corrected Krenn injector (placeholder, for Mellanie to refine):**
|
||||
|
||||
```
|
||||
[SYSTEM/SHARED] Do not use religious language. No military ranks. Technology terms
|
||||
only: insert, span gate, the Reach. No wit or banter. No Earth references.
|
||||
|
||||
[KRENN CULTURE] Direct-informal register — short because time is scarce, not
|
||||
unfriendly. Competence earns respect. Community: crew, shift, street.
|
||||
Exclamations ONLY: "void take it" / "stars" / "blood and void" / "cold vacuum."
|
||||
Greetings: hey, morning, shift treating you alright.
|
||||
Farewells: shift's calling, gotta move.
|
||||
Fillers: [NPC-specific, from blueprint].
|
||||
SHOW: BASE → VOICED
|
||||
"checks the gate" → "runs the check, nods when it clears"
|
||||
"works on the equipment" → "traces the fault, finds it, fixes it without ceremony"
|
||||
```
|
||||
|
||||
This is a working draft, not final copy. The two examples demonstrate Krenn's visible-competence-without-commentary register in practice. The full injector with the example pairs runs ~220-240 tokens — above 150 but within the 250-token soft ceiling I'd recommend for behavior re-voicing.
|
||||
|
||||
**Setting note — the Proposal C vision is correct, just not for v0.2.** Dialogue re-voicing with culture-vector injectors is the right long-term architecture for scaling Krenn voice (and every other culture we build) across the full Reach. The risk profile for v0.2 is too high because we haven't yet proven the behavior pipeline. The correct statement is: build A, validate, extend to C in the next spike.
|
||||
@@ -0,0 +1,412 @@
|
||||
# Round 3: Lore Contamination Guards, Negative Injectors, Culture Injector Template — Miri
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Worldbuilding / Setting Consistency / IP Originality
|
||||
**Round:** 3 (Decision)
|
||||
|
||||
---
|
||||
|
||||
## Response to Jeroen's Binding Decisions
|
||||
|
||||
**On full pipeline (behaviors + dialogue):** Accepted. D-123 amendment language must now explicitly cover both modes — Mellanie's proposed language does this correctly and should be adopted verbatim.
|
||||
|
||||
**On tells as read-only context:** This is the right architecture AND it creates a new lore risk that needs a named mitigation. See Section 4.
|
||||
|
||||
**On Gemma/Phi model provenance:** Noted and incorporated. This resolves N-4 (Qwen excluded).
|
||||
|
||||
**On bundled distribution:** Accepted. Simplifies the baked content model.
|
||||
|
||||
---
|
||||
|
||||
## 1. Lore Contamination Guard Spec
|
||||
|
||||
### Context: What changed from Round 1
|
||||
|
||||
The five failure modes I identified in Round 1 were:
|
||||
|
||||
1. Franchise bleed (Firefly/Expanse/Mass Effect register)
|
||||
2. Anachronistic technology vocabulary
|
||||
3. Setting-neutral political structures
|
||||
4. Earth social register
|
||||
5. Want/tell contamination
|
||||
|
||||
Failure mode 5 is structurally resolved by passthrough tells. However, Jeroen's Decision 2 introduces a **related but distinct risk** I'm naming here: **Tell-Context Leakage**. The tell is a read-only input to the re-voicing prompt for surrounding content. If that context is phrased carelessly, the model may surface the tell's content explicitly in re-voiced output — turning a deniable micro-signal into an obvious announcement. This replaces failure mode 5 and requires its own mitigation.
|
||||
|
||||
Revised failure mode list, with mitigations:
|
||||
|
||||
---
|
||||
|
||||
### Failure Mode 1: Franchise Bleed
|
||||
|
||||
**What it is:** The 2B model defaults to the dominant SF working-class register in its training data. This produces NPCs who sound like The Expanse Belt-crew, Firefly settlers, or Mass Effect ambient NPCs — not Settled Reach inhabitants.
|
||||
|
||||
**How it manifests:**
|
||||
- Firefly: quippy, self-aware wit, frontier-romantic phrasing
|
||||
- The Expanse: creole vocabulary, anti-establishment framing with specific Belt idioms
|
||||
- Mass Effect: military deference, "Spectre/Commander" cultural scaffolding
|
||||
- Generic SF: "negative, Ghost Rider" / "Captain" / "Commander" / "affirmative" etc.
|
||||
|
||||
**Mitigation (three layers):**
|
||||
|
||||
*Layer 1 — Negative injectors:* NI-1 through NI-5 (see Section 2) in the shared system prompt. These block the most common franchise vocabulary before culture-specific injectors run.
|
||||
|
||||
*Layer 2 — Culture injectors with explicit positive anchoring:* The culture injector doesn't just exclude — it provides a positive pattern to match. Two example pairs demonstrating Krenn register show the model what Settled Reach working-class sounds like, not just what it doesn't sound like.
|
||||
|
||||
*Layer 3 — Build-time validation on baked content:* Before baked hub content ships, run an automated pass checking outputs against a franchise vocabulary blocklist. Flag any line containing identifiable franchise markers for human review. This list is maintained by the copy team (Mellanie) and initially populated from known franchise vocabulary.
|
||||
|
||||
**Residual risk:** Low for short-form behaviors. Medium for dialogue where the model has more space to drift. The spike must include a franchise bleed stress test: prompts with no culture injector (ablation test) vs. full injector, measuring drift toward franchise registers.
|
||||
|
||||
---
|
||||
|
||||
### Failure Mode 2: Anachronistic Technology Vocabulary
|
||||
|
||||
**What it is:** The model references technology that doesn't exist in the Settled Reach, or uses the wrong terms for technology that does exist.
|
||||
|
||||
**How it manifests:**
|
||||
- Wrong terms: "holoscreens," "neural link/chip/interface," "jump drive," "FTL," "warp," "shields," "blasters," "force fields," "stasis pods" (unless specifically in the lore)
|
||||
- Generic SF tech: "the computer said," "scanning for life signs," "teleporter malfunction"
|
||||
- Right concept, wrong word: "implant" instead of "insert," "wormhole portal" instead of "span gate," "jump gate" instead of "horizon gate"
|
||||
|
||||
**Mitigation:**
|
||||
|
||||
*NI-3 (negative injector):* Explicit technology whitelist + blocklist in the system prompt. The whitelist approach is more reliable than a blocklist alone — if the model knows the correct terms, it's less likely to substitute wrong ones.
|
||||
|
||||
*Build-time validation:* Automated string check on all baked content for blocked technology terms. This catches high-confidence errors (exact matches). Runtime sampling catches long-tail drift.
|
||||
|
||||
*Runtime sampling strategy:* 1-in-50 pre-voiced outputs are flagged for background quality sampling during development builds. Sample is logged to `voice_quality_sample.log` and reviewed at each sprint close. Production builds sample 1-in-200. Samples are scored on technology vocabulary adherence and flagged if blocklisted terms appear.
|
||||
|
||||
---
|
||||
|
||||
### Failure Mode 3: Setting-Neutral Political Structures
|
||||
|
||||
**What it is:** The model generates references to political and institutional structures that belong to generic SF but not the Settled Reach.
|
||||
|
||||
**How it manifests:**
|
||||
- "The Empire," "The Federation," "The Council," "The Senate," "The Alliance"
|
||||
- "The military," "the navy," "the fleet"
|
||||
- Generic authority figures: "the government," "the president," "the king"
|
||||
|
||||
**The Krenn-specific version:** Krenn NPCs reference the Commission as the distant authority they're suspicious of. A model that doesn't know this will substitute generic institutional vocabulary. "The Commission wants its cut" is correct; "the government takes its share" is not wrong in isolation but it dissolves setting specificity.
|
||||
|
||||
**Mitigation:**
|
||||
|
||||
*Culture injector:* Include the relevant institutional vocabulary for each culture. Krenn culture: Commission (distant, suspect), port authority (local, procedural), shift lead (immediate, competent). These go in the culture-specific injector, not the universal system prompt — institutions are culture-specific.
|
||||
|
||||
*NI-2 (negative injector):* Blocks military rank vocabulary (a frequent institutional contamination vector) universally.
|
||||
|
||||
*Whitelist in culture injectors:* "When referencing authority, use: Commission, port authority, shift lead. Not: the government, the military, the senate."
|
||||
|
||||
**Residual risk:** Medium. Culture injectors help, but a 2B model in a dialogue context with complex relationship state may default to generic institutional language for NPC-to-NPC references. Spike must test institution vocabulary specifically.
|
||||
|
||||
---
|
||||
|
||||
### Failure Mode 4: Earth Social Register
|
||||
|
||||
**What it is:** Working-class characters in training data sound like 21st-century Earth working class. Krenn working class has 180 years of post-Earth cultural evolution in a sealed artificial environment. The bleed is subtle: idioms, sports references, religious phrases, nationality markers, and contemporary social cadences.
|
||||
|
||||
**How it manifests:**
|
||||
- Earth idioms: "at the end of the day," "bite the bullet," "burning the midnight oil"
|
||||
- Earth time/season markers: "Sunday morning," "winter is coming," "harvest season" (in contexts where season has no meaning)
|
||||
- Earth social structures: "the union," "the church," "the team," "the neighborhood" (in their Earth-familiar connotations)
|
||||
- Earth-origin swearing: "damn," "hell," "crap," "Jesus," "goddamn" — all religious or Earth-cultural in origin
|
||||
|
||||
**Mitigation:**
|
||||
|
||||
*NI-5 (negative injector):* Universal block on Earth-origin social references.
|
||||
|
||||
*Culture injectors — positive anchoring:* Krenn-specific oath and filler vocabulary (void take it, stars, cold vacuum) provides a strong positive attractor. The model learns what Krenn characters say *instead of* Earth idioms.
|
||||
|
||||
*Earth idiom detection in build-time validation:* Harder to automate than technology vocabulary. Build-time validation should include a curated Earth idiom blocklist for the highest-frequency offenders. The copy team maintains this. Long-tail idioms caught by human review of sampled outputs.
|
||||
|
||||
**Residual risk:** Medium-to-high. Earth idioms are deeply embedded in training data and are semantically similar to what we want (working-class pragmatism). The positive attractor (Krenn vocabulary) is the most important mitigation here — exclusion alone is not reliable enough.
|
||||
|
||||
---
|
||||
|
||||
### Failure Mode 5 (Revised): Tell-Context Leakage
|
||||
|
||||
**What it is:** Tells are read-only inputs to the LLM re-voicing context. If the tell context is phrased carelessly, the model may surface the tell's hidden-state content in re-voiced dialogue or behavior — converting a deniable micro-signal into an overt announcement.
|
||||
|
||||
**Example:**
|
||||
|
||||
Tell (passthrough, never re-voiced): "checks surroundings repeatedly"
|
||||
|
||||
If the context prompt says: "This NPC is stressed because they have a secret they're hiding and are exhibiting surveillance anxiety" — the model may produce dialogue like: "Voss keeps looking toward the door, distracted" or worse: "Something's making Voss nervous about being watched." Either of these *broadcasts* the tell's internal state to the player, breaking the information asymmetry mechanic.
|
||||
|
||||
**The correct framing:** The tell context in the prompt must describe the **behavioral tone** to adopt, not the **internal state** being hidden.
|
||||
|
||||
*Wrong:* "This NPC is anxious because they know something and are afraid of being found out."
|
||||
*Right:* "This NPC's responses should feel slightly compressed and indirect, as if their attention is elsewhere."
|
||||
|
||||
The second version communicates the tonal modifier (guarded, indirect) without surfacing the hidden state.
|
||||
|
||||
**Mitigation:**
|
||||
|
||||
*Tell-context prompt template:* The tone modifier derived from a tell should be a behavioral register adjective, not a state description. The tell-to-tone mapping is authored once as a translation table, not constructed per-instance.
|
||||
|
||||
Proposed tell-to-tone mapping:
|
||||
|
||||
| Tell category | Tonal register modifier |
|
||||
|---|---|
|
||||
| Nervous / stress | "answers feel clipped and slightly distracted" |
|
||||
| Guarded / concealment | "responses are compressed, minimal elaboration" |
|
||||
| Avoidance | "replies feel directed away from the topic at hand" |
|
||||
| Hostile suppression | "controlled and flat in a way that feels effortful" |
|
||||
| Routine deviation | "tone is unremarkably normal — almost too normal" |
|
||||
|
||||
These modifiers describe *surface behavior* without naming the underlying state. A player who reads the resulting voiced dialogue may infer the state; the game never states it explicitly.
|
||||
|
||||
*Constraint in tell-context prompt:* "Adjust tone as indicated. Do not describe what the NPC is feeling internally. Do not have the NPC reference their own state. Observable behavior only."
|
||||
|
||||
---
|
||||
|
||||
## 2. Finalized Universal Negative Injectors (NI-1 through NI-5)
|
||||
|
||||
These live in the shared system/prefix prompt for all re-voicing operations. They are not culture-specific. Every prompt (behaviors, dialogue, any future content type) includes this block.
|
||||
|
||||
Total token budget: ~130-140 tokens. Fits before culture-specific content.
|
||||
|
||||
---
|
||||
|
||||
**NI-1 — No Religious Language**
|
||||
|
||||
> Do not use religious language of any kind: no prayer, no references to gods or deities, no spiritual practices, no phrases derived from religious traditions ("god help us," "heaven forbid," "blessed," "damned" in a spiritual sense). Characters in this setting do not have canonical religious expression.
|
||||
|
||||
*~45 tokens*
|
||||
|
||||
---
|
||||
|
||||
**NI-2 — No Military Ranks**
|
||||
|
||||
> Do not use military rank titles. Prohibited: Commander, Captain (except as a job title for vessel operators), Sergeant, General, Admiral, Lieutenant, Private, Corporal, Major, Colonel. Authority in this setting uses occupational and institutional titles: shift lead, port authority, supervisor, Commission officer.
|
||||
|
||||
*~50 tokens*
|
||||
|
||||
---
|
||||
|
||||
**NI-3 — Technology Vocabulary**
|
||||
|
||||
> Use only the following terms for technology and infrastructure: insert (neural implant worn at the base of the skull), span gate (a fixed transit installation that enables faster-than-light transit), horizon gate (alien-built gate at Oort-cloud distance), the Reach (the network of settled systems). Do not use: holoscreens, blasters, force fields, teleporters, mind-reading, jump drives, FTL, warp, neural link, brain chip, stasis pods.
|
||||
|
||||
*~70 tokens*
|
||||
|
||||
---
|
||||
|
||||
**NI-4 — No Banter or Wit**
|
||||
|
||||
> Do not produce wit, quips, or wordplay intended to entertain the reader. Do not add levity that was not present in the original text. Humor in this setting is dry, incidental, and rare — it emerges from situations, not from characters performing cleverness.
|
||||
|
||||
*~45 tokens*
|
||||
|
||||
---
|
||||
|
||||
**NI-5 — No Earth-Origin Social References**
|
||||
|
||||
> Do not reference Earth, nations, sports, Earth history, Earth seasons, Earth religion, or other Earth-origin social structures. Characters in this setting have no memory of Earth and no cultural connection to it. Earth-origin swearing (damn, hell, crap, Jesus, goddamn) should not appear — use culture-specific expressions instead.
|
||||
|
||||
*~55 tokens*
|
||||
|
||||
---
|
||||
|
||||
**Total: ~265 tokens for NI-1 through NI-5.**
|
||||
|
||||
**Calibration note:** This exceeds my Round 2 estimate of 130-150 tokens. Revision: the full NI set is ~265 tokens at this precision level. The prompt budget needs to accommodate this.
|
||||
|
||||
Recommended allocation:
|
||||
- System/prefix (NI-1 through NI-5): ~265 tokens
|
||||
- Culture injector (hybrid instructions + examples): ~200 tokens
|
||||
- Trait modifier: ~25 tokens
|
||||
- Mood tag: ~10 tokens
|
||||
- Base text + format instruction: ~30 tokens
|
||||
- **Total prompt overhead (before behavior text): ~530 tokens**
|
||||
|
||||
This is higher than the 150-token budget from Round 2 proposals. Troblum needs to confirm whether a 530-token prompt (excluding base text) is within throughput tolerance for the behavior use case on minimum-spec hardware. If not, the NIs can be compressed:
|
||||
|
||||
**Compressed NI set (~150 tokens total):**
|
||||
> No religious language, prayer, or references to deities. No military rank titles (Commander, Admiral, etc.) — use: shift lead, Commission officer. Technology terms: insert (neural implant), span gate, horizon gate. Do not use: holoscreens, blasters, FTL, neural link. No wit or banter. No Earth references, Earth swearing, or Earth social structures.
|
||||
|
||||
*~100 tokens*
|
||||
|
||||
The compressed version is less precise but hits all five categories. Troblum's throughput test will determine which version is viable.
|
||||
|
||||
---
|
||||
|
||||
## 3. Culture Injector Template
|
||||
|
||||
This is the standard structure every new culture follows. Krenn is the reference implementation, using Mellanie's corrected clauses.
|
||||
|
||||
### Template structure (~200 tokens, hybrid instruction + 2 examples)
|
||||
|
||||
```
|
||||
[BLOCK 1 — REGISTER (~25 tokens)]
|
||||
Brief description of the register: register style, why it is this way, one distinguishing marker.
|
||||
|
||||
[BLOCK 2 — CULTURAL CONTEXT (~25 tokens)]
|
||||
One sentence: what shaped this culture's voice. The social or environmental fact that explains the register.
|
||||
|
||||
[BLOCK 3 — VOCABULARY (~40 tokens)]
|
||||
Oath/exclamations: [list, required to use from this list only]
|
||||
Greetings: [list]
|
||||
Farewells: [list]
|
||||
Fillers: [NPC-specific — read from NpcBlueprint.cultural_markers.filler_words]
|
||||
|
||||
[BLOCK 4 — VALUES (~20 tokens)]
|
||||
Two core values expressed as behavioral instructions.
|
||||
|
||||
[BLOCK 5 — CULTURE-SPECIFIC NOT-LIST (~20 tokens)]
|
||||
2-3 exclusions that are specific to this culture (universal NIs already cover the global set).
|
||||
|
||||
[BLOCK 6 — EXAMPLE PAIRS (~70-80 tokens)]
|
||||
BASE: [culture-neutral semantic line]
|
||||
[CULTURE]: [culture-voiced output demonstrating the register]
|
||||
---
|
||||
BASE: [culture-neutral semantic line]
|
||||
[CULTURE]: [culture-voiced output]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Reference implementation: Krenn System culture
|
||||
|
||||
```
|
||||
[BLOCK 1 — REGISTER]
|
||||
Be direct. Don't waste words. Everyone here is short on time, including you.
|
||||
Not unfriendly — just compressed. Krenn people say what's needed and stop.
|
||||
|
||||
[BLOCK 2 — CULTURAL CONTEXT]
|
||||
You grew up in a working community where showing up and doing the work matters
|
||||
more than rank or credentials. Space is outside the hull. Time is real.
|
||||
|
||||
[BLOCK 3 — VOCABULARY]
|
||||
Exclamations — use ONLY from: "void take it" / "stars" / "blood and void" /
|
||||
"void's sake" / "cold vacuum". Gate to high-affect moments only.
|
||||
Greetings: hey, morning, shift treating you alright, all good
|
||||
Farewells: shift's calling, gotta move, catch you later
|
||||
Fillers: [from NpcBlueprint.cultural_markers.filler_words — e.g., "look", "right", "yeah"]
|
||||
|
||||
[BLOCK 4 — VALUES]
|
||||
Competence earns respect — show it through action, not claims.
|
||||
Loyalty runs to your crew, your shift, your street. Not abstractions.
|
||||
|
||||
[BLOCK 5 — CULTURE-SPECIFIC NOT-LIST]
|
||||
No sir/ma'am deference. No quips or banter. No formal phrasing or contractions
|
||||
avoided (Krenn uses contractions freely: shift's calling, gotta, can't).
|
||||
|
||||
[BLOCK 6 — EXAMPLES]
|
||||
BASE: "checks the gate"
|
||||
KRENN: "runs the check, nods when it clears"
|
||||
---
|
||||
BASE: "works on the conduit"
|
||||
KRENN: "traces the fault, finds it, fixes it without ceremony"
|
||||
```
|
||||
|
||||
**Token count for this implementation:** ~195-210 tokens. Within the 200-token soft target.
|
||||
|
||||
---
|
||||
|
||||
### Template authoring guide for future cultures
|
||||
|
||||
When authoring a culture injector for a new culture, answer these questions:
|
||||
|
||||
1. **Register (Block 1):** How does this culture's speech differ from generic SF working-class? What is the most distinctive surface marker?
|
||||
|
||||
2. **Root cause (Block 2):** What environmental, historical, or social fact explains why this culture speaks this way? (For Krenn: sealed environment + labor community + 180 years of adaptation.)
|
||||
|
||||
3. **Vocabulary (Block 3):** What does this culture swear by? What are their vernacular greetings? What filler words dominate? (These must come from the culture RON `speech` fields — they exist already.)
|
||||
|
||||
4. **Values-as-instructions (Block 4):** Pick two values from the culture RON `values` section. Rephrase each as a behavioral instruction in second-person imperative.
|
||||
|
||||
5. **Exclusions (Block 5):** What generic SF or Earth registers would be especially wrong for this culture? (Formal bureaucratic speech is wrong for Krenn. The equivalent for a formal/diplomatic culture would be "no casual contractions, no working-class compression.")
|
||||
|
||||
6. **Examples (Block 6):** Pick two representative base text lines from the culture's zone RON files. Write the voiced version using the register defined above. These are the spike's first test payload.
|
||||
|
||||
**The culture injector must be validated against the existing zone RON files.** If the injector produces output that contradicts the authored behaviors in the zone spec (e.g., produces quippy dialogue for Krenn), the injector is wrong, not the zone spec.
|
||||
|
||||
---
|
||||
|
||||
## 4. Tell-as-Context Worldbuilding Check
|
||||
|
||||
### The question
|
||||
|
||||
Jeroen's Decision 2: tells are passthrough but inform the LLM context for dialogue and behavior. Does an avoidance-inflected Krenn NPC sound different from an avoidance-inflected Sovari (or other-culture) NPC? Should the tell-tone modifier be culture-inflected or universal?
|
||||
|
||||
### Setting note — the answer is yes, and it matters
|
||||
|
||||
Avoidance is a universal human response. The *expression* of avoidance is culturally specific. Two examples:
|
||||
|
||||
**Krenn culture (direct-informal, compressed, competence-signaling):**
|
||||
An avoidance tell in a Krenn context looks like hyper-compression. The NPC who's hiding something becomes MORE task-focused, not less — appearing to have more to do is the most plausible cover in a culture where work is the currency of credibility. Short answers that close off conversation paths. No hostility, just density. "Yeah. What do you need?" instead of genuine engagement.
|
||||
|
||||
**Hypothetical formal/diplomatic culture (not yet designed, but demonstrating contrast):**
|
||||
Avoidance in a formal culture looks like over-politeness and elaborate redirection. More words, not fewer. A formal character hiding something talks at length about adjacent topics, producing plausible-seeming social warmth that leads nowhere. The tell is the *elaborateness*, not the compression.
|
||||
|
||||
**Why this matters for worldbuilding:**
|
||||
- Players who develop cultural literacy will read Krenn avoidance correctly because it fits the Krenn pattern
|
||||
- The same player will initially misread formal-culture avoidance (more words ≠ more information, in that culture)
|
||||
- This rewards cultural investment — players who know Krenn read Krenn NPCs better than new arrivals do
|
||||
- This is diegetically consistent: the player-character, as someone embedded in Krenn culture, SHOULD have an edge reading Krenn NPCs
|
||||
|
||||
### Should tell-tone modifiers be culture-inflected?
|
||||
|
||||
**Yes — but with a cross-culture readability constraint.**
|
||||
|
||||
The tell-tone modifier should encode culture-inflected behavioral register, not a universal behavioral description. The tell category is universal; the expression is cultural.
|
||||
|
||||
**Architecture recommendation:**
|
||||
|
||||
The tell-to-tone translation table I proposed in Section 1 (Failure Mode 5) needs a parallel structure: one row per tell category, one column per culture, expressing how that culture's NPCs express that tell-category's tone.
|
||||
|
||||
Example:
|
||||
|
||||
| Tell category | Universal base-tone | Krenn-inflected tone |
|
||||
|---|---|---|
|
||||
| Nervous/stress | answers feel distracted | "answers feel clipped, eyes on the work" |
|
||||
| Guarded/concealment | responses compressed | "too direct — closes conversation paths fast" |
|
||||
| Avoidance | directed away from topic | "task-focused, minimal engagement" |
|
||||
| Hostile suppression | controlled and flat | "flat in a way that reads as steady — until it doesn't" |
|
||||
| Routine deviation | unremarkably normal | "unhurried past normal, like nothing's wrong" |
|
||||
|
||||
The Krenn-inflected tones are distinguishable from the universal base tones. They require knowledge of Krenn culture to parse correctly — which is accurate and worldbuilding-good.
|
||||
|
||||
**Cross-culture readability constraint:**
|
||||
|
||||
The culture-inflected expression must remain **recognizable as a stress-tell category** even to a player who doesn't yet know the culture. The player who encounters a Krenn avoidance-tell for the first time should be able to read "something is off" even before they know what Krenn avoidance looks like. The cultural specificity adds richness for experienced players; the base readability is the floor for new ones.
|
||||
|
||||
This constraint means: culture-inflected tell-tones must not diverge so far from the universal base-tone that the phenomenon-class becomes unrecognizable. "Too direct — closes conversation paths fast" still reads as avoidance. "Extremely confident and forthcoming" (as a hypothetical suppression tell in a performative culture) would require significant cultural context before it reads as a tell at all — that's too much divergence.
|
||||
|
||||
**Practical implication for the spike and implementation:**
|
||||
|
||||
The tell-context prompt template should have two components:
|
||||
1. Universal phenomenon-class: "This NPC's responses carry an undercurrent of [concealment / avoidance / vigilance / suppression / departure-from-normal]." — This ensures baseline readability.
|
||||
2. Culture-inflected expression: "In this culture, [concealment] reads as [Krenn-specific description]." — This is the per-culture authoring requirement.
|
||||
|
||||
The universal component is authored once by Gestalt (aligned with the tell taxonomy). The culture-inflected component is authored by Miri + Mellanie for each culture, drawing on the culture profile's speech and values fields.
|
||||
|
||||
This is a **new deliverable for Round 3 output:** the Krenn tell-tone table (5 rows) should be authored as part of the culture profile extension, alongside or integrated into the injector system. It is small (5 sentences) but must be correct.
|
||||
|
||||
**Krenn tell-tone table (v1 draft):**
|
||||
|
||||
| Tell category | Krenn-inflected tonal register |
|
||||
|---|---|
|
||||
| Nervous (stress above threshold) | Answers run shorter than usual. Eyes stay on task. Nothing's wrong — they just have things to do. |
|
||||
| Guarded (concealment) | Direct past the point of directness. Closes conversation paths fast without being unfriendly. |
|
||||
| Avoidance (relationship-specific) | Task-focused when this person is nearby. Finds work to do. Polite but not engaging. |
|
||||
| Hostile suppression (deceptive under stress) | Steady. Even. The kind of steady that takes effort to maintain. Not hostile — just flat in a way that doesn't feel natural for Krenn. |
|
||||
| Routine deviation (changed behavior) | Unhurried. Unremarkably normal. Like nothing's worth noticing. |
|
||||
|
||||
These tell-tone descriptions are the LLM context input, not the player-visible text. They shape the *register* of re-voiced content surrounding the tell, not the tell string itself (which passes through untouched).
|
||||
|
||||
---
|
||||
|
||||
## Summary: Deliverables for Round 3
|
||||
|
||||
1. **Lore contamination guard spec** — ✓ Five failure modes with three-layer mitigation each. Tell-context leakage replaces Want/tell contamination. Build-time validation + runtime sampling defined.
|
||||
|
||||
2. **Finalized universal negative injectors** — ✓ NI-1 through NI-5 at two fidelity levels (full ~265 tokens, compressed ~100 tokens). Troblum to confirm budget compatibility.
|
||||
|
||||
3. **Culture injector template** — ✓ Six-block structure with Krenn reference implementation (~200 tokens). Authoring guide for future cultures. Validation constraint (injector outputs must not contradict zone RON authored behaviors).
|
||||
|
||||
4. **Tell-tone cultural inflection** — ✓ Tell-tone modifiers should be culture-inflected but constrained by cross-culture readability. Krenn tell-tone table (v1) authored. Architecture recommendation: two-component tell-context prompt (universal phenomenon-class + culture-inflected expression). New deliverable: each culture profile needs a 5-row tell-tone table.
|
||||
|
||||
**One item requiring coordination:**
|
||||
|
||||
The Krenn tell-tone table (Section 4) references tell categories by name (Nervous, Guarded, Avoidance, Hostile suppression, Routine deviation). These must align with whatever taxonomy Gestalt and Tyre formalize. If the canonical tell-category enum is different, the table needs to be re-mapped. I can do that mapping once Tyre confirms the final `TellCategory` names.
|
||||
@@ -0,0 +1,150 @@
|
||||
# Round 1 — Ozzie: Player Experience Inventory
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Role:** Player experience / wow factor advocate
|
||||
**Round:** 1 (Divergent Inventory)
|
||||
|
||||
---
|
||||
|
||||
## My gut reaction to the three options
|
||||
|
||||
I read the behavior pools in `rural-zone-spec.ron`. Then I looked at the hardcoded base texts in the spike binary. That comparison IS the whole conversation.
|
||||
|
||||
**Hand-authored pool (current):**
|
||||
> "holds eye contact through a long pause, waiting for the price to land"
|
||||
> "wipes grease on the thigh of her coveralls between jobs"
|
||||
> "calls across a field to a neighbor without looking up from work"
|
||||
|
||||
**Hardcoded base texts in the spike:**
|
||||
> "tends crops in the field"
|
||||
> "checks credentials at the gate"
|
||||
> "watches foot traffic from market stall"
|
||||
|
||||
That gap is enormous. The base texts look like placeholder copy. They look like the developer left notes for the writer. "Tends crops in the field" is what you write when you're sketching the system. "Tends rows of low-growing crops with a long-handled hoe" is what the player actually sees and believes.
|
||||
|
||||
This tells me ONE thing before I evaluate anything else: **the base text spec needs a major rethink before this architecture can work.** Right now, "base text" means rough draft. For the re-voicing model to work, base text has to mean something different — it has to mean *complete, evocative, and deliberately minimal.* Not broken. Not placeholder. Intentionally spare in the way that Krenn culture is spare.
|
||||
|
||||
More on this below. But it's the issue I'm going to fight for hardest.
|
||||
|
||||
---
|
||||
|
||||
## Which option best serves player experience?
|
||||
|
||||
### Option 1: Hand-authored pools — I want this. I can't have it.
|
||||
|
||||
The quality ceiling is exactly where it needs to be. The rural zone file reads like real people. The trader who "holds eye contact through a long pause, waiting for the price to land" — I believe that person. That's the game I want to play.
|
||||
|
||||
But the math kills it. O(R x Z x C) means that every new culture or zone type the team adds is authoring from scratch. We can't have Krenn-rural AND Krenn-industrial AND Sova-industrial AND a third culture's rural variant without a team of writers and years of budget. The game has to grow. This option doesn't grow.
|
||||
|
||||
### Option 2: Composable primitives — I'm scared.
|
||||
|
||||
Composed text FEELS composed. Players feel it in their bones even when they can't name it. "Greets you warmly because [Social] + [Rural context]" produces something like "nods a friendly greeting to people passing by" — which is grammatically correct and soul-dead. The hand-authored version would be "calls across a field to a neighbor without looking up from work." Same beat, completely different texture.
|
||||
|
||||
The risk is real: composable systems produce text that reads like it was assembled, because it was. The seam is visible. Players stop believing the NPCs are people. When players stop believing the NPCs are people, THE FRIEND doesn't work. The contradiction doesn't land. The whole detective loop falls apart.
|
||||
|
||||
I'd fight hard against pure composable as our primary model.
|
||||
|
||||
### Option 3: LLM re-voicing — YES, with conditions.
|
||||
|
||||
This is the only path that scales to the world we want to build AND has a shot at preserving the quality ceiling. The i18n analogy is right. The architecture is right. The implementation plan (baked + pre-voiced + fallback) is right.
|
||||
|
||||
But it comes with three serious player experience risks that I need the team to address before I'll commit. See below.
|
||||
|
||||
---
|
||||
|
||||
## The three things that will make or break player experience
|
||||
|
||||
### 1. The base text problem — this is critical
|
||||
|
||||
The base text is THE FALLBACK EXPERIENCE. Every player on minimal hardware sees it. Every player who gets ahead of the pre-voicing queue sees it. Every player who turns AI-Enhanced Dialogue off sees it.
|
||||
|
||||
Right now, base texts look like design notes. That has to change.
|
||||
|
||||
**Base text must be:** complete, self-contained, evocative, and deliberately minimal. Not a stub. Not a placeholder. A different register — sparse and functional, like a stage direction — but never rough.
|
||||
|
||||
Think about it this way: if a theater does a stripped-down version of a play, the stripped-down version still has to WORK. It's not lesser. It's the same story told differently. That's what base text needs to be.
|
||||
|
||||
"Tends crops in the field" needs to become something like "works a row of low crops with steady, unhurried hands." Still culture-neutral. Still LLM-seedable. But not draft copy.
|
||||
|
||||
This is authoring work. It's not free. But it's the foundation the whole architecture rests on. If the fallback experience feels broken, we've built a system where players feel punished for having modest hardware. That's a terrible message.
|
||||
|
||||
### 2. The Want tell problem — this one scares me most
|
||||
|
||||
The brief flags this and it's RIGHT to flag it. The Want/State layer is the core of the detection game. The player reads behaviors to infer hidden internal state. The tell is the mechanic.
|
||||
|
||||
If the LLM re-voices a tell and changes its semantic content, we've broken the game.
|
||||
|
||||
Here's the exact failure mode: an NPC whose Want is [MONEY] has a tell behavior — let's say they're a guard who "glances at the freight container being logged without checking in." The LLM re-voices this as "keeps an eye on the dock traffic" (Cautious cultural voice) or "watches the loading operation with professional attention" (Honest cultural voice). Both could be innocent. Both could be the tell. Now the player can't read it.
|
||||
|
||||
**Tells must be locked.** They should not go through the LLM re-voicing pass. They either:
|
||||
a) Pass through to the player as base text (intentionally culture-neutral, which actually works — tells feel MORE legible when they're stripped of cultural noise)
|
||||
b) Have their own separate re-voicing pass with TIGHTER constraints that preserve semantic content
|
||||
|
||||
I lean toward option (a). A tell that's culture-neutral IS more suspicious — it stands out. The Krenn guard speaks direct and minimal. If they suddenly have a moment of strange stillness with the freight, that's MORE readable as a tell, not less. The culture voice actually makes tells blend in. The base voice makes them pop.
|
||||
|
||||
This could be a feature, not a bug. But it needs to be a decision, not an accident.
|
||||
|
||||
### 3. The AI-Enhanced Dialogue toggle — the perception problem is real
|
||||
|
||||
Two quality tiers means players on better hardware get a richer game. That's a real fairness issue and a real messaging problem.
|
||||
|
||||
But it's solvable. The solution is: **don't frame it as tiers. Frame it as modes.**
|
||||
|
||||
- **AI-Enhanced Dialogue OFF:** "Classic voice mode — clean, direct, full gameplay."
|
||||
- **AI-Enhanced Dialogue ON:** "Enhanced voice mode — character-voiced, culturally textured."
|
||||
|
||||
Neither is "better." They're different aesthetic experiences. The functionality is identical. If we nail the base text quality (see point 1), this framing is honest.
|
||||
|
||||
The real danger: if we ship base text that feels like placeholder, players on low-end hardware feel cheated. If we ship base text that feels intentional and complete, they have a different experience, not a worse one.
|
||||
|
||||
Framing and base text quality are the two levers. Both are doable.
|
||||
|
||||
---
|
||||
|
||||
## How large is the baked cache? Does it matter?
|
||||
|
||||
For hub systems (Sova Transit District): rough estimate. ~50 behaviors per role x 4 roles x 2-3 zone types = 400-600 base behaviors to voice. Each voiced output is maybe 30-80 words. At plain text, that's ~30-50KB of voiced content per hub. Even if we're verbose with metadata, we're talking low megabytes for the full first-hours baked cache.
|
||||
|
||||
**That's nothing.** Modern games ship 50GB of asset data. A few MB of voiced NPC text is below perception threshold for install size.
|
||||
|
||||
The per-seed cache is the wildcard. If players run 10 seeds and every seed caches voiced content for every zone they visit, that could balloon. We need a cache size cap and eviction policy. But for the baked hub content? Not a problem.
|
||||
|
||||
---
|
||||
|
||||
## What breaks if we choose the wrong option?
|
||||
|
||||
**If we choose hand-authored only:** The game can't grow. We ship Sova Transit District beautifully and then we can't add a second culture. Every expansion is a writer-years investment. The generator spike becomes a curiosity, not a product.
|
||||
|
||||
**If we choose composable primitives:** Players feel it immediately. The NPCs stop being people. The FRIEND arc breaks because Kael needs to feel like a real person for his contradiction to hurt. A composed NPC doesn't generate that attachment. This option quietly poisons every emotional beat in the game.
|
||||
|
||||
**If we choose LLM re-voicing without solving the tell problem:** The detection mechanic degrades. Players can't reliably read tells. They learn to distrust the behavior text. Instead of reading NPCs like a detective, they start ignoring NPC behaviors as noise. THAT'S THE GAME WE BUILT. If we make the behavior layer untrustworthy, we have no game.
|
||||
|
||||
**If we choose LLM re-voicing without fixing base text:** We ship with a fallback experience that feels broken. Players on low-end hardware (which is most players) feel like they're playing the rough draft. They review the game as unfinished. We lose them before they get to the good parts.
|
||||
|
||||
---
|
||||
|
||||
## My recommendation
|
||||
|
||||
**LLM re-voicing, but with two pre-conditions that are non-negotiable from a player experience standpoint:**
|
||||
|
||||
1. **Base text elevation pass** — the copy team needs to rewrite all base texts to "complete-and-spare" quality before this architecture goes into production. Not longer. Not more detailed. Better. The goal is: base text reads like intentional minimalism, not like a draft.
|
||||
|
||||
2. **Tells are locked or separately controlled** — Want tells do not go through the general re-voicing pass. They are either served as base text (my preference — it makes them MORE detectable, which is a design upside) or given a constrained re-voicing pass that preserves semantic content. This is a systems decision, but it has to be decided before implementation.
|
||||
|
||||
If those two conditions are met, this architecture can give us the world we want to build.
|
||||
|
||||
---
|
||||
|
||||
## My one question before I commit
|
||||
|
||||
**Are Want tells embedded in the same behavior text strings that go through LLM re-voicing, or are they a separate data channel?**
|
||||
|
||||
If tells are mixed into the general behavior pool and indistinguishable from flavor text at the data level, we have a serious problem. The LLM won't know which lines to preserve and which to style. Every tell is at risk of paraphrase.
|
||||
|
||||
If tells are tagged, separated, or handled through a different pipeline, I'm comfortable proceeding.
|
||||
|
||||
That answer determines whether the re-voicing architecture is safe for the core mechanic. Everything else is solvable. This one I need Gestalt and Tyre to answer.
|
||||
|
||||
---
|
||||
|
||||
*Ozzie out. Someone tell me when something explodes.*
|
||||
@@ -0,0 +1,167 @@
|
||||
# Round 2 — Ozzie: Player Experience Evaluation
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Role:** Player experience / wow factor advocate
|
||||
**Round:** 2 (Convergent Evaluation)
|
||||
|
||||
---
|
||||
|
||||
## First: the three specific questions I was asked
|
||||
|
||||
### Tell contrast — does it work?
|
||||
|
||||
**Proposal A scenario:** An NPC has three ambient behaviors, all culture-voiced. Then a tell — passing through as base text, culture-neutral.
|
||||
|
||||
In my gut: yes. Here's why.
|
||||
|
||||
When everything around the tell is richly textured — "wipes grease on the thigh of her coveralls between jobs," "borrows a tool from a neighbor and returns it without being asked" — the tell reads in a different register. Clinical. Observational. Like the *player's own voice* noting something. "She glances at the freight container being logged without checking in." That sentence doesn't sound like the NPC's world. It sounds like an investigator's field note.
|
||||
|
||||
That's exactly right. That's the detective game. The player isn't watching the NPC perform; the player is READING the world for evidence. A tell that sounds like an observation rather than a performance is a tell that invites investigation. The register difference is the tell's signal.
|
||||
|
||||
**BUT.** This only works if base texts are elevated. If "glances at the freight container" lives alongside "tends crops in the field" — i.e., if base texts look like rough drafts — then the contrast doesn't read as designed intentionality. It reads as: *this line has worse writing than the others.* And a player smart enough to pick up on that starts pattern-matching on text quality instead of semantic content. They find tells by spotting the worse prose. That breaks the mechanic entirely.
|
||||
|
||||
**My verdict on tell contrast:** It works. Contingent on base text elevation. I flagged this in Round 1 and I'm flagging it again. This is the load-bearing condition for the whole architecture. Not just for tells — for the entire fallback experience.
|
||||
|
||||
---
|
||||
|
||||
### Proposal B risk — how bad is a constrained re-voicing failure?
|
||||
|
||||
Let me be specific about what failure looks like.
|
||||
|
||||
**Source tell:** "looks away when Kael's name comes up" (semantic core: `avoidance_behavior`)
|
||||
|
||||
**Good constrained re-voice (Krenn culture, Bold trait):**
|
||||
> "goes quiet when Kael comes up — just for a beat, then moves on"
|
||||
|
||||
Still avoidance. Krenn directness preserved. The tell pops.
|
||||
|
||||
**Borderline constrained re-voice:**
|
||||
> "doesn't have much to say about Kael"
|
||||
|
||||
Ambiguous. Could be innocent. Player might dismiss it. The tell is WEAKENED, not destroyed — but weakened tells mean players miss clues, and missing clues means the detective game gets harder in the wrong ways (not "I missed evidence" but "the evidence wasn't readable").
|
||||
|
||||
**Failed constrained re-voice:**
|
||||
> "seems to have a thing about Kael"
|
||||
|
||||
TOO explicit. The mystery collapses. The player gets handed the answer instead of discovering it. This is WORSE than missing the tell.
|
||||
|
||||
**Worst case:**
|
||||
> "seems distracted around the cargo manifests"
|
||||
|
||||
The target got lost. The tell preserved avoidance behavior but lost the relationship component (Kael → cargo manifests). The player gets a partial, misleading clue. They go looking for cargo manifest anomalies instead of watching Kael.
|
||||
|
||||
The worst case is the misleading partial. A missing tell is recoverable — the player replays, looks harder, finds the other evidence. A misleading tell sends players on a wrong track. That's not a missed clue, that's the game being unfair.
|
||||
|
||||
**How bad is the damage vs the benefit?**
|
||||
|
||||
The benefit is real and significant. A Krenn NPC whose avoidance reads as "goes real quiet, then moves on" hits differently than a Sovari NPC whose avoidance reads as something more ceremonially formal. Cultural voice on tells makes the world feel consistent. The detective puzzle is richer if you have to READ through the culture voice to find the signal.
|
||||
|
||||
But the failure mode is subtle and hard to catch at scale. Baked content gets validation. Pre-voiced content at runtime — every NPC the player encounters, every seed, every zone they reach before the queue finishes — that's too much to validate exhaustively.
|
||||
|
||||
**My verdict:** Proposal B is the higher-ceiling option and I find it genuinely exciting. But it REQUIRES the spike to demonstrate constrained re-voicing reliability before I'll recommend it for tells. If the spike shows >95% semantic core preservation across diverse test payloads, I'm in. If it shows 85%, we're shipping corrupted tells into production and I'll fight against it.
|
||||
|
||||
Define the success bar before the spike, not after.
|
||||
|
||||
---
|
||||
|
||||
### Dialogue gap — is it noticeable? Does it matter?
|
||||
|
||||
YES. And it matters more than behaviors.
|
||||
|
||||
Here's the thing: observable behaviors are what the player reads about the NPC from across the room. Dialogue is what the NPC says to the player's FACE. When the relationship is most direct, when the player is most invested, when the character is supposed to feel most real — that's when dialogue fires.
|
||||
|
||||
If behaviors are richly culture-voiced and dialogue falls back to template patterns, the gap is at its most jarring exactly when it most needs to hold. The NPC who "wipes grease on the thigh of her coveralls between jobs" then says "Hello. Do you have a question? I can assist you." That's a whiplash moment. The player's belief collapses.
|
||||
|
||||
Does it matter? IT'S THE ONLY THING THAT MATTERS when the player is in conversation.
|
||||
|
||||
That said — Proposal C has a scope problem that's real. Dialogue re-voicing is harder, longer-form, requires more context, and might need a larger model (3B). Troblum and Tyre need to answer whether that's feasible.
|
||||
|
||||
But from a player experience standpoint: if we ship Proposals A or B, we should be honest that we're shipping half the experience. Behaviors without dialogue is an incomplete culture voice. The NPC speaks in one voice when observed and another when approached. Players will notice. It won't break the game but it will break immersion at the moments that should be strongest.
|
||||
|
||||
---
|
||||
|
||||
## Full proposal evaluation
|
||||
|
||||
### Proposal A: Conservative — Behaviors Only, Tells Locked
|
||||
|
||||
**Player experience verdict:** Strong foundation. Clean risk surface. The tell passthrough works (with base text elevation). The behaviors-only scope is a real limitation but it's honest and shippable.
|
||||
|
||||
**My concern:** This is a great v1 that could feel incomplete. "The NPCs talk like themselves but speak like form letters" is a real player complaint waiting to happen.
|
||||
|
||||
**Blocker:** None, given base text elevation. Without base text elevation, the fallback experience is broken.
|
||||
|
||||
**Can I live with it?** Yes. If we ship Proposal A with a clear path to dialogue re-voicing in v0.3, this is responsible scope management.
|
||||
|
||||
---
|
||||
|
||||
### Proposal B: Two-Track — Behaviors + Tells with Semantic Core
|
||||
|
||||
**Player experience verdict:** The highest ceiling, the most interesting result. Culture-voiced tells is the thing I didn't know I wanted until I thought about it. A BOLD Krenn NPC's avoidance tell reads completely differently from a Cautious one. That's detective-game richness.
|
||||
|
||||
**My concern:** Constrained re-voicing failure is the scariest failure mode in this whole architecture. Not because it breaks the game loudly — because it breaks it quietly. Players can't tell the tell got corrupted. They just get a worse, less-fair experience.
|
||||
|
||||
**Blocker:** Spike success bar must be defined before implementation. If the spike doesn't hit the bar, this proposal should fall back to Proposal A tell handling (passthrough). The two-track architecture should be designed so the tell track can be switched to passthrough without rebuilding everything.
|
||||
|
||||
**Can I live with it?** Yes, with that caveat.
|
||||
|
||||
---
|
||||
|
||||
### Proposal C: Full Pipeline — Behaviors + Dialogue, Tells Locked
|
||||
|
||||
**Player experience verdict:** This is the RIGHT architecture. Dialogue is where culture voice has the highest impact. The tell safety (passthrough, same as A) means no tell corruption risk. The scope is larger but the payoff justifies it.
|
||||
|
||||
**My concern:** Quality at 2B for dialogue. Behaviors are 5-15 words. Dialogue is 15-40 words with relationship context. A model that handles behaviors gracefully might hallucinate on dialogue. If dialogue quality fails, players experience the worst possible seam — culture-voiced observation but broken dialogue. That's worse than Proposal A.
|
||||
|
||||
**Blocker:** The spike MUST test dialogue quality separately from behavior quality. Don't average them. If behaviors pass at 2B and dialogue doesn't, we don't ship dialogue re-voicing — we fall back to Proposal A scope and wait for a dialogue-safe model.
|
||||
|
||||
**Can I live with it?** Yes — this is my preferred outcome if the spike validates dialogue quality.
|
||||
|
||||
---
|
||||
|
||||
## Resolution Matrix
|
||||
|
||||
| Question | Answer |
|
||||
|----------|--------|
|
||||
| Which proposal do you recommend? | **C, with A as fallback** |
|
||||
| Are there blockers in your recommended proposal? | Yes: dialogue quality at 2B is unvalidated. The spike must test dialogue separately. |
|
||||
| Can you live with Proposal A? | Yes. Clean, safe, shippable. Missing dialogue is a real gap but honest about scope. |
|
||||
| Can you live with Proposal B? | Yes, if spike defines and hits a success bar for constrained re-voicing. Requires the tell track to be switchable to passthrough without an architecture rebuild. |
|
||||
| Minimum change to make A acceptable | Base text elevation pass by copy team. Without this, fallback experience reads as unfinished. |
|
||||
| Minimum change to make B acceptable | Pre-defined spike success bar (I'd say >95% semantic core preservation). Fallback: if bar not met, tells revert to passthrough. |
|
||||
|
||||
---
|
||||
|
||||
## My vote
|
||||
|
||||
**Proposal C is the architecture we should build.**
|
||||
|
||||
Here's the player experience argument in plain terms: the world has to feel like one thing. Not "rich when observed, functional when approached." Not "textured from a distance, generic up close." One thing. The culture voice has to be everywhere, or the player stops believing in it the moment it matters most — when they're talking to THE FRIEND, when Kael is deflecting, when the contradiction lands.
|
||||
|
||||
Proposal A ships half of that. It's responsible. It's safe. But "the NPC observes in Krenn voice and speaks in form-letter voice" is a seam the player will feel.
|
||||
|
||||
Proposal C is the full promise. It's more work. It's a larger spike. But it's the promise we made when we said the world would feel inhabited.
|
||||
|
||||
**My recommended implementation order if C is chosen:**
|
||||
1. Spike tests behavior quality first (short-form, lower risk)
|
||||
2. Spike tests dialogue quality separately (longer-form, higher risk)
|
||||
3. If behavior quality passes and dialogue fails — ship Proposal A scope, iterate
|
||||
4. If both pass — ship Proposal C
|
||||
5. Tell track: passthrough in any case (don't add constrained re-voicing risk in the same sprint)
|
||||
|
||||
That gives us a decision tree out of the spike, not a binary pass/fail.
|
||||
|
||||
---
|
||||
|
||||
## One thing I need the team to resolve before Round 3
|
||||
|
||||
**What's the spike's dialogue test payload?**
|
||||
|
||||
Behavior testing is easy — we have ~50 lines per role in the zone specs. Dialogue testing requires actual dialogue samples with relationship state and access tier context. Do we have those? If the copy team is still writing base dialogue, the spike can't test dialogue quality yet.
|
||||
|
||||
If we don't have dialogue samples for the spike, Proposal C can't be validated this sprint. That means A is the implementable choice now, with C as the target for next sprint.
|
||||
|
||||
Paula and Mellanie should answer this. Not a blocker for the architecture decision, but it determines which proposal we can actually ship.
|
||||
|
||||
---
|
||||
|
||||
*This is the best problem we've had. We're arguing about which part of "fully alive world" to build first. I'll take that fight any day.*
|
||||
@@ -0,0 +1,260 @@
|
||||
# Round 3 — Ozzie: Player Experience Spec
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Role:** Player experience / wow factor advocate
|
||||
**Round:** 3 (Commitment + Spec)
|
||||
|
||||
---
|
||||
|
||||
## The binding decisions are good decisions
|
||||
|
||||
Full pipeline. Both behaviors and dialogue. Tells locked as base text with context influence. This is what I voted for. Let me write the spec for what the player actually experiences.
|
||||
|
||||
---
|
||||
|
||||
## 1. Quality seam mitigation
|
||||
|
||||
### How tell contrast reads to the player
|
||||
|
||||
Jeroen's tell treatment is elegant: tells are read-only inputs to the LLM, never outputs. The tell itself ships as authored base text. The NPC's surrounding dialogue and behavior are shaped by the tell's presence.
|
||||
|
||||
What this means in practice:
|
||||
|
||||
An NPC with an avoidance tell behaves like an avoiding person — their voiced dialogue is hesitant, their ambient behaviors are distanced — but the tell itself stands apart. Clinical. Observational. "Keeps their back to the loading bay entrance when the foreman speaks." Everything else is Krenn-voiced and inhabited. That line is a field note.
|
||||
|
||||
This is a feature. Call it intentional. Here's why it works:
|
||||
|
||||
The player's experience of the game world is dual-layered. They are both a character in the world (receiving culture-voiced content, feeling the texture of place) AND a detective reading the world (parsing evidence, noting anomalies). The tell is the moment when the detective layer activates. A tell in base text says: *pay attention. this is evidence.* The shift in register is the shift in mode.
|
||||
|
||||
The tell doesn't sound like the NPC's culture. It sounds like the player's investigation log. That's the right sound.
|
||||
|
||||
**The risk:** If base texts are elevated (which they must be — this is still non-negotiable), the distinction holds. If base texts read as rough drafts, the tell sounds like an unfinished line, not an evidence marker. The player's response is "this text is worse" instead of "this is a clue." See section 4 for the quality bar.
|
||||
|
||||
### The mid-session transition: base text → voiced text
|
||||
|
||||
This is the subtler UX challenge. Pre-voicing catches up in the background. An NPC the player saw in base text at first encounter is voiced the next time they look. What happens at that seam?
|
||||
|
||||
**The bad version:** The player noticed Torek's line was "waits at a loading bay with arms crossed" (base text). They come back and it reads "leans at the bay entrance, arms folded, watching the dock traffic with the patience of someone who has done this for twenty years." They feel the difference. They wonder if something changed. They might think the game updated the NPC's state.
|
||||
|
||||
**The problem:** State-change confusion. "Did I do something that made Torek different?" No. The voicing caught up. But the player can't know that.
|
||||
|
||||
**The solution: don't let the player see the same line change.** The transition from base text to voiced text should never happen on a line the player has already read in the current session. Options:
|
||||
|
||||
1. **Session lock:** Once a player has seen a base text line, that line stays as base text for the rest of the session. Voiced version appears next session. Clean. No jarring transitions.
|
||||
|
||||
2. **Zone re-entry rule:** Base text is shown on first entry to a zone this session. If the player leaves and re-enters, voiced content is shown if available. This is natural — the player moved away, things changed, they returned. Re-entry provides a diegetic cover for the transition.
|
||||
|
||||
3. **Soft labels (not recommended):** Show a subtle indicator when voiced content is available. This is the worst option — it tells the player the system exists, which breaks immersion and prompts them to think about the technology instead of the world.
|
||||
|
||||
**My recommendation:** Zone re-entry rule. It's the most natural. A player who's in a zone, reads base text, and leaves has already contextualized those NPCs. When they return, slightly different phrasing reads as: they've changed, or I'm perceiving them differently now. That's good. That's the game.
|
||||
|
||||
**For the tells:** Tells never change. Ever. Passthrough in all cases, all sessions. The tell is the anchor. Ambient lines can shift on re-entry. Tells don't.
|
||||
|
||||
### A note on tells and context-influenced dialogue
|
||||
|
||||
Jeroen's "tells inform the LLM context" is the right call. If the player engages an NPC who has an active avoidance tell, the NPC's dialogue should feel avoiding — not because the tell text changes, but because the whole person is avoiding. This is how real people work. The tell is a symptom; the character is the disease.
|
||||
|
||||
For the player this creates a moment I'm very excited about: they see the tell (base text, stands out), they engage the NPC in dialogue (Krenn-voiced, hesitant, deflecting), they feel the avoidance everywhere. The tell is confirmed by the conversation. THAT'S the detective loop. Evidence → engagement → confirmation.
|
||||
|
||||
---
|
||||
|
||||
## 2. Toggle UX — "AI-Enhanced Dialogue"
|
||||
|
||||
### The framing problem
|
||||
|
||||
"AI-Enhanced Dialogue" sounds like: the real game is on, and you can turn it off if your hardware is bad. That's the wrong message. We need language that says: this is a choice, not a hardware penalty.
|
||||
|
||||
### Settings screen copy
|
||||
|
||||
**Toggle label:** `Character Voice Mode`
|
||||
|
||||
**State — Mode A (standard text, LLM off):**
|
||||
> **Standard** — NPCs speak and act in clear, direct text. Full gameplay, any hardware.
|
||||
|
||||
**State — Mode B (LLM active):**
|
||||
> **Enhanced** — NPCs speak and act in their own voice — culturally textured, personality-inflected. Requires background processing.
|
||||
|
||||
**Supporting note (shown below the toggle):**
|
||||
> Both modes are complete experiences. Standard mode is intentional design, not a fallback. Some players prefer it.
|
||||
|
||||
### Why these words
|
||||
|
||||
"Character Voice Mode" frames the toggle as a stylistic choice, not a quality gate. "Standard" and "Enhanced" are value-neutral — one isn't lesser. "Clear, direct text" is a positive description of base text, not an apology for it. "Full gameplay" assures players that no content is gated. "Intentional design, not a fallback" — that last line is defensive but necessary. We will have players who read reviews saying "the AI dialogue is the real experience" and feel cheated if they can't run it. This line gives them permission to enjoy the standard mode.
|
||||
|
||||
**DO NOT use:**
|
||||
- "AI-Enhanced Dialogue" as the label (sounds like a tier upgrade)
|
||||
- "Fallback" anywhere in player-facing copy
|
||||
- "Limited" or "Basic" to describe standard mode
|
||||
- "Performance Mode" (implies compromise)
|
||||
|
||||
### First-run experience
|
||||
|
||||
If the player has never launched the game before, and the hardware detection recommends standard mode (see section 3), the first-run UX should present the toggle with the recommendation already applied but not yet confirmed. The player makes an active choice — they don't get defaulted into standard mode silently.
|
||||
|
||||
```
|
||||
Character Voice Mode
|
||||
|
||||
[Enhanced] is available on your hardware, but we recommend [Standard]
|
||||
for smooth performance. You can change this any time in Settings.
|
||||
|
||||
[Use Standard] [Use Enhanced Anyway]
|
||||
```
|
||||
|
||||
No shame on either button. "Enhanced Anyway" is not positioned as a warning — just as an informed choice.
|
||||
|
||||
---
|
||||
|
||||
## 3. Hardware detection UX — what the player sees at each stage
|
||||
|
||||
### Layer 1: RAM check (silent)
|
||||
|
||||
The player never sees this. It's a pre-launch check. If the system has insufficient RAM to load the model (sub-4GB available after game load), Character Voice Mode defaults to Standard and is greyed out in Settings with a tooltip:
|
||||
|
||||
> "Character Voice Mode requires additional memory to run. Close background applications and restart to enable."
|
||||
|
||||
No shame. No "your hardware is too old." Just: not enough memory right now, here's what to do.
|
||||
|
||||
### Layer 2: Time-per-token benchmark (visible, one-time)
|
||||
|
||||
First time the player enables Enhanced mode, a brief benchmark runs. This is unavoidable — we have to know if inference is usable. Make it feel like the game doing something useful, not the game testing the player's machine.
|
||||
|
||||
**Loading screen framing:**
|
||||
|
||||
```
|
||||
Preparing character voices...
|
||||
```
|
||||
|
||||
That's it. No "benchmarking your hardware." No "testing inference speed." From the player's perspective, the game is getting characters ready. Which is true.
|
||||
|
||||
After the benchmark, one of two states:
|
||||
|
||||
**If inference is fast enough:**
|
||||
No message. Character Voice Mode activates. The player never learns a benchmark happened.
|
||||
|
||||
**If inference is below threshold:**
|
||||
A short, non-alarming pop-up:
|
||||
|
||||
```
|
||||
Character voices are running slowly on your hardware.
|
||||
|
||||
[Standard mode] will give you a smoother experience with the same full
|
||||
gameplay. You can switch to [Enhanced] at any time from Settings.
|
||||
|
||||
[Switch to Standard] [Keep Enhanced]
|
||||
```
|
||||
|
||||
Key decisions in this copy:
|
||||
- "Running slowly" — honest, not condescending. Doesn't say "your computer is slow."
|
||||
- "Same full gameplay" — the reassurance again. Keeps hitting this.
|
||||
- "At any time" — gives them an exit. They're not locked into the slower experience.
|
||||
- "Keep Enhanced" — respects player autonomy. If they want to run it slow, that's their call.
|
||||
|
||||
### Layer 3: Ongoing recommendation (very light touch)
|
||||
|
||||
If the player keeps Enhanced mode running and the queue is consistently behind (player moves faster than pre-voicing, sees base text frequently), we could surface a suggestion — but only once, only if they've seen base text fallback more than N times in a session.
|
||||
|
||||
**One-time soft nudge (appears in a settings-adjacent notification, not a modal):**
|
||||
|
||||
```
|
||||
You've been seeing Standard voice text more often — Enhanced mode is
|
||||
running behind on your hardware. Switch to Standard in Settings for
|
||||
a consistent experience.
|
||||
```
|
||||
|
||||
After this nudge, never show it again for the session. Never show it again at all if the player dismisses it. This is a suggestion, not a nag.
|
||||
|
||||
**What we absolutely do not do:**
|
||||
- Pop-up modals mid-gameplay
|
||||
- Repeat warnings
|
||||
- Change the setting without player action
|
||||
- Say anything that implies the player made a bad choice by keeping Enhanced
|
||||
|
||||
---
|
||||
|
||||
## 4. Base text elevation criteria
|
||||
|
||||
This is the most important deliverable in my Round 3 output because it determines whether the whole architecture works. Everything — tell contrast, toggle UX, fallback experience — rests on base text being good.
|
||||
|
||||
### The quality bar, stated plainly
|
||||
|
||||
Base text should read as **deliberately sparse observation** — complete, evocative, and culturally neutral. Not a rough draft. Not a placeholder. An intentionally minimal form, like a stage direction that fully serves the scene.
|
||||
|
||||
The test: read the base text line in isolation and ask — does this feel like a person doing something real? If yes, it's at the bar. If it feels like a note-to-self, a design stub, or a sentence that's waiting to be finished, it's below the bar.
|
||||
|
||||
### Examples: placeholder copy vs. deliberately spare
|
||||
|
||||
**Role: Farmer**
|
||||
|
||||
| Placeholder | Deliberately spare |
|
||||
|-------------|-------------------|
|
||||
| "tends crops in the field" | "works a crop row with slow, unhurried passes" |
|
||||
| "does farm work" | "checks seedling trays in a low prefab greenhouse" |
|
||||
| "harvests produce" | "lifts a crate of produce onto a flatbed, tests the weight, adjusts" |
|
||||
|
||||
The placeholder tells you what job the person has. The deliberately spare version shows you a moment that implies the job, the pace, and something about the person.
|
||||
|
||||
**Role: Militia / Security**
|
||||
|
||||
| Placeholder | Deliberately spare |
|
||||
|-------------|-------------------|
|
||||
| "checks credentials at the gate" | "holds out a hand for credentials without looking up from the gate log" |
|
||||
| "patrols the area" | "walks the fence line at an even pace, eyes ahead" |
|
||||
| "watches for trouble" | "sits in the gatehouse shade with a newsline, one eye on the road" |
|
||||
|
||||
**Role: Trader**
|
||||
|
||||
| Placeholder | Deliberately spare |
|
||||
|-------------|-------------------|
|
||||
| "sells goods at stall" | "squares goods on a fold-out display with small, deliberate adjustments" |
|
||||
| "watches customers" | "leans back on a stool and watches foot traffic, says nothing" |
|
||||
| "haggles with buyers" | "holds the pause after a counteroffer, not moving" |
|
||||
|
||||
**For dialogue (these standards apply equally):**
|
||||
|
||||
| Placeholder | Deliberately spare |
|
||||
|-------------|-------------------|
|
||||
| "I don't know anything about that." | "That's not something I know anything about." |
|
||||
| "Things are difficult lately." | "It's been a rough few shifts." |
|
||||
| "You should be careful here." | "Watch yourself around here." |
|
||||
|
||||
The differences:
|
||||
- Placeholder reads generic, applicable to any character anywhere
|
||||
- Deliberately spare reads specific, even if culturally neutral — it has rhythm, it has implied manner
|
||||
- Deliberately spare is still short — it's not adding words, it's finding better words
|
||||
|
||||
### The test the copy team should apply
|
||||
|
||||
For every base text line, ask three questions:
|
||||
|
||||
1. **Does this show a moment, not a category?** ("holds the pause" > "waits")
|
||||
2. **Could you imagine a specific person doing this?** Not "a guard" — a guard with weight, with habit
|
||||
3. **Would you be okay reading this as the only text the player sees?** Not "this is a fine draft," but "this IS the experience for some players"
|
||||
|
||||
If any answer is no, the line needs work.
|
||||
|
||||
### Scale note
|
||||
|
||||
The copy team doesn't need to elevate every line at once. Priority order:
|
||||
|
||||
1. **Hub zones (Sova Transit District)** — these are baked, always visible, represent the quality floor
|
||||
2. **Plot-critical NPC roles** — foremen, guards, traders in story-adjacent locations
|
||||
3. **Tells** — always passthrough, always highest priority for elevation (they're the mechanic)
|
||||
4. **Ambient roles in non-hub zones** — lowest urgency; pre-voicing will catch these
|
||||
|
||||
---
|
||||
|
||||
## My summary statement for the D-record
|
||||
|
||||
The player experience architecture for the LLM voice pipeline rests on three interdependent pillars:
|
||||
|
||||
**1. Base text is a designed aesthetic, not a fallback.** It reads as deliberately spare observation. Standard mode is a complete experience. The copy team must author base texts to this bar, not to a rough-draft bar.
|
||||
|
||||
**2. Tell contrast is intentional.** Tells in base text read as detective observations against culture-voiced ambient content. This is not a seam — it is a designed register shift that signals "pay attention here." Preserve this distinction in all documentation, all onboarding, all QA.
|
||||
|
||||
**3. Player autonomy is respected at every hardware decision.** The game never makes choices for the player. It recommends. It explains. It never shames. The toggle exists in Settings at all times. The player can always override.
|
||||
|
||||
These three pillars are the spec. If the D-record captures them, the implementation team has what they need.
|
||||
|
||||
---
|
||||
|
||||
*Done. Let's build the thing.*
|
||||
@@ -0,0 +1,244 @@
|
||||
# Paula — Round 1: Narrative Quality Inventory
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Narrative quality, character voice, faction/relationship mechanics
|
||||
**Round:** 1 — Inventory (divergent)
|
||||
|
||||
---
|
||||
|
||||
## Preamble: What I Read
|
||||
|
||||
I read the full workshop brief, `proposed-llm-voice.md`, both zone RON files (`rural-zone-spec.ron`, `industrial-zone-spec.ron`), `culture-krenn.ron`, the generator spike (`generator_spike.rs`), the `NpcBlueprint` struct (`blueprint.rs`), and decisions D-010, D-023, D-024, D-028, D-029, D-034, D-090, D-092, D-121, D-122, D-123, D-124, D-128. Also Q-012 and Q-033.
|
||||
|
||||
---
|
||||
|
||||
## 1. The Three Options — Narrative Quality Assessment
|
||||
|
||||
### Option 1: Hand-authored pools (current)
|
||||
|
||||
**What it does well:** The zone RON files demonstrate what quality looks like at the top of this approach. The behaviors are complete gestures with cultural specificity embedded:
|
||||
|
||||
> *"wipes grease on the thigh of her coveralls between jobs"*
|
||||
> *"sits in the shade of the gatehouse with a local newsline"*
|
||||
> *"laughs at something a technician says, then catches herself and goes quiet"*
|
||||
|
||||
These work because they are compositional wholes. The specificity is not decorative — it is the content. "Wipes grease on coveralls" tells you she's manual labor. "Thigh of her coveralls" tells you this is habitual and unself-conscious. "Between jobs" tells you there is no downtime — work is the state she returns to.
|
||||
|
||||
**What it cannot do:** At O(R×Z×C) scale, this approach requires reimagining every behavior from scratch per culture. A second culture's rural mechanic doesn't just use different words — she has a different physical relationship to her tools, a different social relationship to the person she's working for, a different set of gestures that register competence. You can't template that. D-122 (all NPCs generated) combined with any non-trivial number of cultures and zones makes this approach logistically impossible.
|
||||
|
||||
**Verdict:** Not viable at scale. But it establishes the quality floor that everything else is measured against.
|
||||
|
||||
---
|
||||
|
||||
### Option 2: Composable primitives (Q-057)
|
||||
|
||||
**What it does well:** Nothing that I can see, beyond implementability. And I want to be careful here — I'm not dismissing systems complexity, I'm making a specific claim about narrative texture.
|
||||
|
||||
**The decomposition problem:** The behaviors in the zone RON files work precisely because they resist decomposition. Try it:
|
||||
|
||||
> "laughs at something a technician says, then catches herself and goes quiet"
|
||||
|
||||
What is the action? Laughing. What is the cultural modifier? Catching herself. What is the context tag? Foreman-technician interaction. Now reassemble from components: `[laugh_action] + [self_correction_modifier] + [authority_suppression_tag]` → "laughs and then stops."
|
||||
|
||||
The reassembled version is grammatically correct and semantically equivalent. It is also emotionally empty. The original line works because of "catches herself" — the comma pause, the specificity of the suppression, the choice of "quiet" over "serious" or "professional." These are not modifiers on a verb. They are the verb.
|
||||
|
||||
**The grammar-to-sentence problem:** Composable primitives are a grammar. Grammars produce grammatically valid sentences; they do not produce *specifically good* ones. The hand-authored behaviors are good because a human looked at a Krenn foreman and heard a specific voice. That act of hearing cannot be parameterized.
|
||||
|
||||
**Verdict:** Produces mechanical output. Creates an engine more complex than LLM re-voicing without the quality upside. I'm skeptical this approach can sustain narrative depth. That said — if the spike proves me wrong (some decompositions produce surprisingly specific output), I'd want to revisit.
|
||||
|
||||
---
|
||||
|
||||
### Option 3: LLM re-voicing
|
||||
|
||||
This is the option with the most promise and the most risk, and the two are inseparable.
|
||||
|
||||
**What the proposal gets right:** The i18n analogy is apt. Culture-neutral base text as `en-base`, culture-voiced text as `en-KRENN-DIRECT`. The injector clause model (10-20 per culture) scales in the right direction. The progressive enhancement framing — base text is functional, voiced text is premium — is elegant and de-risks hardware concerns.
|
||||
|
||||
**What the proposal is missing:** It was written before the generator spike added Want/State, relationship behaviors, and the perception mechanic. These systems change the calculus substantially. See Section 3.
|
||||
|
||||
**Verdict for narrative quality:** Conditionally viable. Viable for Tier 3 ambient and non-tell Tier 2 behaviors. Not viable without explicit protection for semantic load-bearing content. The hybrid is mandatory — not optional — and the protected zones must be a first-class design constraint, not an afterthought.
|
||||
|
||||
---
|
||||
|
||||
## 2. Culture-Specific Vocabulary at 2B Model Size
|
||||
|
||||
Let me complicate this with what I actually see in `culture-krenn.ron`.
|
||||
|
||||
### The Krenn speech register is not what the proposal assumes
|
||||
|
||||
The `proposed-llm-voice.md` uses this as the Krenn injector clause example:
|
||||
> *"Your speech is formal and avoids contractions."*
|
||||
|
||||
This is **wrong for Krenn**. The actual Krenn register from `culture-krenn.ron`:
|
||||
- Register: `"direct, minimal pleasantries, gets to the point"`
|
||||
- Filler words: `"look", "right", "yeah", "so", "listen"`
|
||||
- Greetings: `"hey"`, `"shift treating you alright?"`, `"all good?"`
|
||||
- Farewells: `"shift's calling"`, `"gotta move"`
|
||||
|
||||
Krenn is informal, clipped, and working-class. "Formal and avoids contractions" describes Commonwealth institutional culture or perhaps a Sheldon family retainer. It is the opposite of Krenn. This error in the example injector is not a minor slip — it reveals that the injector authoring requires actual knowledge of the culture RON, not a generic characterization.
|
||||
|
||||
### The void-oaths are the hard test
|
||||
|
||||
The exclamations (`"void take it"`, `"blood and void"`, `"cold vacuum"`, `"void's sake"`) are the cultural vocabulary most at risk from a 2B model. These phrases exist nowhere in any training corpus. A 2B model instructed to "include Krenn cultural exclamations" has two failure modes:
|
||||
1. Invents generic space-opera profanity ("stars and void," "by the black," etc.) — readable but not canonical
|
||||
2. Produces nothing — defaults to vanilla emotional beats with no exclamations
|
||||
|
||||
**The solution is enumeration, not instruction.** The injector clause cannot say "use void-oaths appropriate to the Krenn culture." It must say: "When expressing strong emotion, use ONLY these phrases: `void take it`, `blood and void`, `cold vacuum`, `void's sake`, `damn all`, `stars`." The specific phrases must be injected as a closed vocabulary list, not as a stylistic instruction.
|
||||
|
||||
### Formality levels within Krenn
|
||||
|
||||
The RON file captures one formality level (social register). But real cultures have register variation — the same Krenn farmer talks differently to their supervisor than to their shift partner than to an outsider. Can injector clauses capture this gradient reliably at 2B?
|
||||
|
||||
My assessment: at 2B, probably not reliably. The model can handle one register per culture injector. If we need register variation within a culture (which we will need for relationship-specific dialogue), that variation should be authored at the line level (access tier tags: `insider` vs `authority`) rather than asked of the LLM.
|
||||
|
||||
---
|
||||
|
||||
## 3. Re-voicing and the 30/50/20 Tier Model
|
||||
|
||||
This is where the Tier 2 boundary becomes load-bearing.
|
||||
|
||||
### Tier 3 (30% flat wallpaper): Full LLM re-voicing is appropriate
|
||||
|
||||
These NPCs carry no semantic load. They are texture. "A dock worker moves freight containers." The base text is functional, and LLM re-voicing can produce cultural flavor without risk. If the LLM slightly mishandles the register, the damage is aesthetically suboptimal, not gameplay-breaking. This is where the pipeline earns its cost.
|
||||
|
||||
### Tier 2 (50% mundane triangles): Conditional
|
||||
|
||||
Tier 2 NPCs carry relationship information that leaks through behavior. A mechanic who borrows tools from a neighbor and returns them without being asked is showing something about her relationship to that neighbor. If the LLM re-voices "borrows a tool from a neighbor and returns it without being asked" into "retrieves equipment from a colleague" — the relationship signal is gone.
|
||||
|
||||
**The rule I'd propose for Tier 2:** Behaviors that contain a named or implied social target (another NPC, a specific relationship) must not be re-voiced. They should be authored. Behaviors that describe an isolated role action (running diagnostics, patching pipe) can be re-voiced.
|
||||
|
||||
The practical test: if removing the behavior from context and reading it alone still produces a complete social meaning, it should be protected. "Returns it without being asked" means something about character without any context. "Runs diagnostics on a console" only means something in context.
|
||||
|
||||
### Tier 1 (20% entangled with intrigue): No LLM re-voicing
|
||||
|
||||
Tier 1 NPCs include triangle members and anyone whose behavior is a tell for hidden internal state. These behaviors must be:
|
||||
- Authored with precise semantic intent
|
||||
- Marked as protected from re-voicing
|
||||
- Treated as anchor-line-equivalent per D-092
|
||||
|
||||
The FRIEND pattern (D-034) is the extreme case. The FRIEND's observable contradiction — "meeting with unknown contact in restricted corridor" — cannot be re-voiced. Any variation in phrasing changes the information the player receives. Is it "unknown" or "unfamiliar"? Is it "restricted" or "secure"? These words are not stylistic — they encode the player's knowledge state.
|
||||
|
||||
### The tier boundary: a concrete proposal
|
||||
|
||||
| Tier | Population | Re-voicing |
|
||||
|------|------------|-----------|
|
||||
| Tier 3 ambient | 30% | Full LLM re-voicing |
|
||||
| Tier 2, generic role behaviors | ~35% | LLM re-voicing with culture injectors |
|
||||
| Tier 2, relationship-specific behaviors | ~15% | Authored or human-reviewed post-generation |
|
||||
| Tier 1, non-tell content | ~15% | Human-reviewed post-generation, not LLM |
|
||||
| Tells (all tiers) | All NPCs with a Want | Protected — never re-voiced |
|
||||
| Anchor lines (D-092) | Tier 1 and 2 notable NPCs | Protected — authored |
|
||||
|
||||
This is not a clean tier-boundary — it's a behavior-class boundary that applies across tiers. The question "is this a tell?" is more important than the question "what tier is this NPC?"
|
||||
|
||||
---
|
||||
|
||||
## 4. Observable Behaviors vs. Dialogue — What Should the LLM Touch?
|
||||
|
||||
The workshop brief asks whether LLM re-voicing should apply to observable behaviors (what you SEE) or dialogue (what NPCs SAY) or both.
|
||||
|
||||
**The honest truth is these are fundamentally different problems.**
|
||||
|
||||
### Observable behaviors (what you SEE)
|
||||
|
||||
Observable behaviors are gameplay information in the perception system. Players read behaviors to infer state. The read→notice→follow→discover sequence (D-027) runs on behaviors. When a player observes a foreman "sits alone in the break room rubbing the back of her neck, datapad face-down on the table" — they are receiving structured information: isolation, stress, concealment.
|
||||
|
||||
LLM re-voicing of observable behaviors requires knowing what the behavior *means* mechanically before deciding whether it can be re-voiced. This is a semantic load problem. The base text "checks credentials at the gate" is safe to re-voice. The base text "waves a familiar face through without checking credentials" is a tell (routine/secret axis) and cannot be re-voiced without potentially losing "without checking" — the specific departure from procedure that makes it an investigative signal.
|
||||
|
||||
**My position:** Observable behaviors should be re-voiced only when:
|
||||
1. The behavior is not a tell (not connected to the NPC's Want/State)
|
||||
2. The behavior does not name or imply a specific social relationship
|
||||
3. The behavior has been reviewed and marked as re-voicing-eligible in the data model
|
||||
|
||||
### Dialogue (what NPCs SAY)
|
||||
|
||||
Dialogue is more appropriate for LLM re-voicing because:
|
||||
1. The semantic core (D-028 base line) preserves gameplay-critical information
|
||||
2. Cultural voice is the natural value-add (how someone says "you need a keycard" is pure register)
|
||||
3. The access tier and trust tier tags already filter what information can be conveyed
|
||||
4. Relationship-specific information is handled by the pool selection system, not the individual line
|
||||
|
||||
But even here: trust-gated secret lines should be authored. "She changed the subject. Fast." (the monologue beat for a withheld secret) cannot be re-voiced without losing the pause that carries the weight.
|
||||
|
||||
**My position:** Dialogue is the primary candidate for LLM re-voicing. Observable behaviors require a protected-class marker before any re-voicing pass.
|
||||
|
||||
---
|
||||
|
||||
## 5. Preventing Lore-Breaking Content
|
||||
|
||||
This is the risk I'd rank highest after tell preservation, because lore contamination is invisible until someone notices it.
|
||||
|
||||
### What failure looks like
|
||||
|
||||
A 2B model instructed to "voice a Krenn dock worker" has seen Star Wars, Firefly, Dune, and ten thousand pieces of space opera. It will default toward genre conventions when the injector clauses don't constrain it. Failure modes:
|
||||
|
||||
1. **Wrong exclamations**: "By the stars," "What in the void" — plausible-sounding but not canonical Krenn vocabulary
|
||||
2. **Wrong social references**: References to "the Empire," "the Alliance," "credits" (actually correct) or "sol-standard time" — things that don't exist in the Commonwealth
|
||||
3. **Wrong technology register**: Describing a span gate as a "warp gate" or "jump point," describing inserts as "chips" or "implants" — adjacent to canon but not canon
|
||||
4. **Wrong socioeconomic register**: Treating a dock worker as aspirationally middle-class (genre convention) rather than working-class pragmatic (Krenn reality)
|
||||
|
||||
### The containment strategy
|
||||
|
||||
Three layers of prevention:
|
||||
|
||||
**Layer 1 — Closed vocabulary in injectors**: Canonical terms must be injected as closed lists. The injector does not say "use appropriate space-travel terminology." It says: "The following terms are ALWAYS used: insert (neural interface), span gate (interstellar gate), void (space), The Ring (horizon station). NEVER use: warp gate, implant, jump drive, hyperspace, stargate."
|
||||
|
||||
**Layer 2 — Build-time validation for baked content**: The baked hub system content (Sova Transit District) is generated at build time and can be validated by human review before ship. This is the highest-risk content (players' first hours) and should have a full human review pass regardless of pipeline.
|
||||
|
||||
**Layer 3 — Runtime sampling and flagging**: A lightweight rule-based filter (regex against a prohibited terms list) can catch obvious failures before content is served. Flagged lines fall back to base text. This is imperfect but cheap.
|
||||
|
||||
**What's missing from the proposal**: The `proposed-llm-voice.md` does not mention lore contamination at all. This is a significant gap. The proposal treats the LLM as a stylistic localization engine, but localization engines operate on canonical source text. The LLM has a training distribution that pulls toward genre conventions. Without explicit containment, contamination is not a risk — it is a certainty at volume.
|
||||
|
||||
---
|
||||
|
||||
## 6. What Breaks If We Choose the Wrong Option
|
||||
|
||||
### If we choose hand-authored only:
|
||||
- **Immediate**: D-122 (all NPCs generated) becomes incompatible with content availability. A generated world full of NPCs with empty behavior pools produces a dead place, not a living one. The generator spike output looks compelling *because* behaviors exist. Without them, it's a list of names and traits.
|
||||
- **At scale**: The copy team cannot author behaviors for more than 2-3 culture/zone combinations before the sprint budget runs out. The game stalls at Krenn/rural + Krenn/industrial.
|
||||
- **What survives**: The quality model is still the reference. Even if we move to LLM re-voicing, the hand-authored zone RON behaviors are the gold standard the pipeline is calibrated against.
|
||||
|
||||
### If we choose composable primitives:
|
||||
- **Narrative texture collapses**: Players stop noticing NPCs. The behaviors become grammatically correct descriptions of actions — "a dock worker loads freight, greets passersby, and monitors the gate." Readable, but not a person.
|
||||
- **The perception mechanic degrades**: If behaviors are assembled from generic components, the signals players use to READ NPCs become harder to distinguish from noise. Tells need to read as specific; composed behaviors are generic by construction.
|
||||
- **Cross-culture quality drops**: The whole point of composable primitives is culture modifier + role action = culture-specific output. But the culture modifier in a composable system is a vocabulary adjustment. "Adjusting vocabulary" is not the same as "sounding like you live in this place." The cultural texture in the Krenn RON is not in the vocabulary — it's in the social texture of the behaviors (who you wave through at the gate, how you borrow tools).
|
||||
|
||||
### If we choose naive LLM re-voicing (no protected zones):
|
||||
- **Tells become unstable**: A tell that was authored as "wipes her hands without making eye contact" might be re-voiced as "quickly cleans up and avoids looking at anyone." Same semantic content, different investigative readability. The player who sees "without making eye contact" has a clue. The player who sees "avoids looking at anyone" has an obvious tell. The ambiguity that makes the perception mechanic rewarding disappears.
|
||||
- **Relationship behaviors lose specificity**: Behaviors that reference a specific social dynamic ("waves a familiar face through without checking credentials") become generic ("allows known workers to pass without verification"). The social information encoded in "familiar face" — the recognition, the implied history — is stripped by the normalization.
|
||||
- **Replayability is paradoxically hurt**: If the LLM re-voices with non-deterministic variance (and without seed-fixed caching it will), the same NPC has different behaviors on Tuesday than Monday. Within a run this is tolerable. Across saves or reloads it's a continuity problem for investigative inference.
|
||||
|
||||
---
|
||||
|
||||
## 7. My One Question Before I Can Commit
|
||||
|
||||
**Are tells (want-leaking behaviors) a first-class concept in the NpcBlueprint data model, with a field that marks them as protected from re-voicing?**
|
||||
|
||||
The generator spike has `gen_want` and `gen_want_tell` referenced in the workshop brief's "what's new" section, but the current `NpcBlueprint` struct has only `observable_behaviors: Vec<String>` — a flat list. There is no semantic distinction between a generic role behavior ("tends crops in the field") and a tell ("checks credentials at the gate, then waves a familiar face through without checking the one behind them").
|
||||
|
||||
If tells are not distinguishable from ambient behaviors in the data model, then:
|
||||
- Any re-voicing architecture that doesn't know which behaviors are tells will apply the same treatment to both
|
||||
- Authors cannot mark tells as protected — there's nowhere to put the flag
|
||||
- Build-time validation of baked content has no basis for flagging tell-variants
|
||||
|
||||
This is not a blocking question for the hybrid architecture direction — I can commit to LLM re-voicing + protected zones as the right approach. But it is a blocking question for implementation: before the spike, I need to know whether "protected behavior" is a data model feature or an editorial convention enforced by human review. The answer changes what the injector system and cache format need to support.
|
||||
|
||||
---
|
||||
|
||||
## Summary Position
|
||||
|
||||
| Question | My Answer |
|
||||
|----------|-----------|
|
||||
| Which option best serves narrative quality? | Hybrid: LLM re-voicing for Tier 3 and non-sensitive Tier 2, with explicit protected zones for tells, relationship behaviors, and anchor lines |
|
||||
| Can injectors capture Krenn void-oaths at 2B? | Only if injectors enumerate the specific phrases as a closed list, not as stylistic instruction |
|
||||
| Tier 2 boundary? | Behavior-class boundary, not tier boundary: protected = tells + social-target-naming behaviors; re-voiceable = isolated role actions |
|
||||
| SEE or SAY or both? | SAY first (more appropriate for cultural re-voicing). SEE only after behavior protection is a data model feature |
|
||||
| Lore contamination prevention? | Layer 1 closed vocabulary, Layer 2 build-time human review on baked content, Layer 3 runtime regex flagging |
|
||||
| What breaks if we choose wrong? | Hand-authored: content stalls at Krenn. Composable: texture collapses. Naive LLM: tells degrade, perception mechanic loses precision |
|
||||
| My committed question | Are tells a first-class protected field in `NpcBlueprint`, or editorial convention only? |
|
||||
|
||||
**The honest summary**: The proposal in `proposed-llm-voice.md` is the right direction, written before the systems that make the direction dangerous were built. The Want/State layer and the perception mechanic changed the calculus. The architecture needs a protected behavior class before narrative quality is safe. But the scale argument is correct and the hybrid approach is viable. I'm not a blocker on this — I'm asking for one data model guarantee.
|
||||
|
||||
---
|
||||
|
||||
*Paula — 2026-03-07*
|
||||
@@ -0,0 +1,206 @@
|
||||
# Paula — Round 2: Narrative Quality Evaluation
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Narrative quality, character voice, faction/relationship mechanics
|
||||
**Round:** 2 — Convergent Evaluation
|
||||
|
||||
---
|
||||
|
||||
## Resolution Matrix
|
||||
|
||||
| Question | My Answer |
|
||||
|----------|-----------|
|
||||
| Which proposal do you recommend? | **A** — with explicit sequencing toward C |
|
||||
| Are there blockers in Proposal A? | One condition: anchor lines (D-092) must be included in the same passthrough protection as tells |
|
||||
| Can you live with Proposal B? | Yes, with a naming convention change for semantic core labels (see Section 3) |
|
||||
| Can you live with Proposal C? | Yes, but not as a first spike — the dialogue quality bar is harder to establish than the proposal acknowledges |
|
||||
| Minimum change to make B acceptable | Replace clinical phenomenon labels with stimulus/response labels (see Section 3) |
|
||||
| Minimum change to make C acceptable | Stage it: behaviors spike first, dialogue spike second, with mandatory human review pass between them |
|
||||
|
||||
---
|
||||
|
||||
## Addressed Questions
|
||||
|
||||
### Q-R1-02: Dialogue vs. Behaviors — Which Is the Higher-Value Re-voicing Target?
|
||||
|
||||
Let me complicate this by separating two meanings of "higher value."
|
||||
|
||||
**Dialogue is higher value for player attachment.** When a generated Krenn dock worker speaks to the player — greeting, gossip, refusing, disclosing — the player is forming a relationship with a voice. The register, the filler words, the way information is delivered, the pause before a secret: this is where culture makes a person feel like a specific person from a specific place. A dock worker who says "look, I'm not supposed to say this" is Krenn. A dock worker who says "I am not in a position to share that information" is someone's idea of a space NPC. Dialogue re-voicing is where the system earns the quality gap between base text and voiced text.
|
||||
|
||||
**Behaviors are higher value for information integrity.** Observable behaviors are the primary channel of the perception mechanic. Players read behaviors to infer hidden state. "Checks a manifest against a handheld scanner, lips moving" is not flavor text — it is structured gameplay information. The risk of re-voicing behaviors incorrectly is that a gameplay-critical signal becomes unreadable, or an ambient behavior accidentally reads as a signal.
|
||||
|
||||
**The honest truth:** These targets have inverted risk/reward profiles:
|
||||
|
||||
| | Value of re-voicing | Risk of re-voicing incorrectly |
|
||||
|--|--|--|
|
||||
| Observable behaviors | Medium (texture, atmosphere) | High (gameplay information, tell corruption) |
|
||||
| Dialogue | High (culture voice, player attachment) | Medium (information in semantic core, voice in delivery) |
|
||||
|
||||
This suggests the sequencing in Proposals A and C is actually backwards from a risk/reward perspective. Behaviors should be proven first because the validation pass is simpler (5-15 words, easy to spot failures). But dialogue is where the system's cultural voice impact will be most felt by players.
|
||||
|
||||
**Quality risks dialogue re-voicing introduces:**
|
||||
|
||||
**1. Epistemic weight changes.** The same information delivered differently implies different things about the speaker's relationship to that information. Consider a trust-gated gossip line:
|
||||
|
||||
- Base: *"She's been meeting with someone from freight operations after dark."*
|
||||
- Re-voiced (wrong): *"I've observed Kael in several unscheduled meetings with freight operations personnel in the late shift window."*
|
||||
|
||||
Same semantic content. But the re-voiced version changes the speaker from "someone who noticed something" to "someone who has been watching." That's a character change with narrative consequences — the NPC is now implied to be conducting surveillance, which is a different relationship to the information. In a game about information asymmetry, this matters.
|
||||
|
||||
**2. Access tier feel bleed.** Dialogue lines are tagged with access tier (`insider`, `authority`, `peer`, `public`). An `insider` line should feel like information shared between people who trust each other. If the LLM re-voices it into a more precise or formal register (genre-default for "important information"), it reads as `authority` tier despite the tag. The tag governs *eligibility*, but the *feel* of the line is what the player experiences. A culture injector that pushes toward Krenn directness partially protects against this, but at 2B the model may still drift toward the gravity of the information being conveyed.
|
||||
|
||||
**3. Trust-gated secret lines need absolute protection.** D-028 Layer 3 secrets are information the NPC holds back until trust is built. These lines often carry the dramatic weight of the whole relationship arc. They should not be re-voiced by any model. A line like "He asked me not to tell anyone. I'm telling you anyway because I think you need to know." is already at the limit of what natural speech allows — re-voicing risks making it either more dramatic (melodramatic) or more casual (trivial). Secrets should be authored, period.
|
||||
|
||||
**My position on Q-R1-02:** Dialogue re-voicing is worth the investment, but not as a first spike. Prove behaviors, get human review on baked Sova content, then extend. Proposal C's instinct is right; its timing is too ambitious for one spike.
|
||||
|
||||
---
|
||||
|
||||
### D-123 Tension: Is "Authoring Tool AND Runtime Enhancement" Honest?
|
||||
|
||||
The proposed amendment language collapses a distinction that matters.
|
||||
|
||||
**The original D-123 language:** "The AI pipeline is an authoring tool for content assembly, not a runtime system."
|
||||
|
||||
This language was chosen deliberately. An authoring tool produces content that humans review before it reaches players. A runtime system produces content during gameplay, without editorial filter, and players encounter it fresh. The original intent was to preserve that review cycle.
|
||||
|
||||
**What the proposals are actually describing is two different things:**
|
||||
|
||||
1. **Baked content** (pre-voiced at build time, shipped with the game): This IS an authoring tool. Content generated at build time can be reviewed by humans before shipping. The quality bar can be validated. Lore contamination can be caught. This is D-123 as written.
|
||||
|
||||
2. **Pre-voiced content** (background generation during gameplay): This is NOT an authoring tool. It generates content in real time, without human review, and players encounter it without an editorial filter. Calling this an "authoring tool AND runtime enhancement" papers over the distinction.
|
||||
|
||||
**The narrative architecture constraint it touches:** D-092 (anchor lines must be authored, never generated) applies to Tier 1 and Tier 2 notable NPCs. If the LLM is doing background pre-voicing of dialogue for a Tier 2 NPC, and that NPC has anchor lines in their dialogue pool, those anchor lines need the same passthrough treatment as tells. The amendment language as written doesn't address this — it addresses tells, but D-092 is a separate protection class.
|
||||
|
||||
**Is the framing honest?** Partially. The honest framing is:
|
||||
|
||||
*"D-123 is amended to: 'The AI pipeline operates in two modes. Build-time mode (authoring tool): generates and caches voiced content for baked hub zones, with mandatory human review before shipping. Runtime mode (background enhancement): generates voiced content during gameplay for non-baked zones, without human review, with base text as fallback and runtime filtering as the safety layer. Runtime mode content is never the sole source of truth — base text is always present as fallback.'"*
|
||||
|
||||
This framing:
|
||||
- Acknowledges the distinction honestly
|
||||
- Preserves the authoring tool mode with its review cycle
|
||||
- Defines the safety model for runtime mode (base text + filtering)
|
||||
- Doesn't conflate two different processes under one label
|
||||
|
||||
The D-123 amendment should use this language or equivalent. If the team writes "authoring tool AND runtime enhancement" without distinguishing the two modes, the D-record will be unclear about what protections apply to which content. Future agents reading D-123 will not know whether runtime-generated content was reviewed.
|
||||
|
||||
**One more thing the framing must clarify:** D-123 as written applies to NPC content assembly (dialogue pools, voice, vocabulary). All three proposals apply the LLM to observable behaviors as well. The amendment must explicitly extend the scope beyond "NPC content" to include observable behaviors — otherwise the D-record is technically silent on the behavior re-voicing pipeline.
|
||||
|
||||
---
|
||||
|
||||
### Proposal B's Semantic Core: Does Naming the Phenomenon Collapse Ambiguity?
|
||||
|
||||
This is the question I'm most divided on, so let me think through it explicitly.
|
||||
|
||||
**The design value being protected:** Tell ambiguity. A good tell is observable behavior that admits multiple explanations. The player must read it and choose an inference. "Waves a familiar face through without checking credentials" — habit? Corruption? Relationship? The ambiguity is the gameplay. The player who notices it and infers correctly has earned something.
|
||||
|
||||
**What Proposal B's semantic core does:** It tells the model, at inference time, what phenomenon to preserve while re-voicing. `PRESERVE: avoidance_behavior. Culture-voice the expression, not the phenomenon.`
|
||||
|
||||
**The specific risk:** At 2B parameters, models have difficulty holding a constraint in the prompt while keeping it below the surface of the output. Larger models (7B+) can write "takes the long route" while knowing they're describing avoidance behavior — the constraint informs the generation without surfacing in the text. At 2B, there is meaningful probability that the model does the simpler thing: produces output that names or strongly implies the phenomenon. "Avoidance behavior" → "seems to be avoiding someone." That's not a tell. That's a caption.
|
||||
|
||||
**But the counter-argument is worth taking seriously:** The semantic core label is in the prompt, not in a system instruction the model is expected to follow verbatim. With a well-designed constrained re-voicing prompt, the label could function as a negative space constraint — "the behavior implies this without stating it." Whether that works at 2B is an empirical question. The spike should test this.
|
||||
|
||||
**The bigger problem with the naming convention:** The proposed labels (`"avoidance_behavior"`, `"nervous_fidget"`, `"concealment_tell"`) are clinical psychology vocabulary. They describe the behavior from the perspective of someone who knows what's happening. A tell author who writes "checks the rear corridor before speaking" is not thinking "this is a concealment_tell." They're hearing a specific character in a specific situation. The clinical label comes AFTER the human has identified the tell's function.
|
||||
|
||||
Naming tells with clinical labels creates a secondary authoring problem: someone has to map the authored behavior to its clinical category. This is:
|
||||
1. Error-prone — the same behavior could be classified as `"avoidance_behavior"` or `"deception_tell"` depending on the NPC's Want
|
||||
2. Reductive — it collapses the specific authored texture of each tell into a category that the model then re-expresses generically
|
||||
3. Potentially revealing — if the semantic core label leaks into output, the player gets a caption instead of an observation
|
||||
|
||||
**My alternative naming convention:** Use stimulus/response language instead of phenomenon language. Describe what triggers the behavior and what it manifests as, not what it means:
|
||||
|
||||
| Clinical label (Proposal B) | Stimulus/response alternative |
|
||||
|---|---|
|
||||
| `avoidance_behavior` | `changed_routine` |
|
||||
| `nervous_fidget` | `stress_physical_marker` |
|
||||
| `concealment_tell` | `information_protection` |
|
||||
| `relationship_avoidance` | `social_routing_change` |
|
||||
|
||||
These labels:
|
||||
- Still constrain the model (it knows this behavior involves changing a pattern, or physical stress, or protecting information)
|
||||
- Don't name the psychological phenomenon the behavior represents
|
||||
- Are less likely to surface verbatim in 2B output because they're not common English phrases
|
||||
- Don't presuppose what the NPC's Want is, just what observable pattern is being expressed
|
||||
|
||||
**My verdict on Proposal B:** Architecturally interesting. The semantic core concept is sound — preserving the phenomenon while re-voicing the expression is the right aspiration. But the naming convention as proposed is risky at 2B and creates a secondary authoring problem. With the stimulus/response naming alternative, Proposal B becomes viable. Without it, constrained re-voicing is likely to produce tells that read as signals.
|
||||
|
||||
---
|
||||
|
||||
## Detailed Proposal Evaluations
|
||||
|
||||
### Proposal A: Conservative — My Recommendation
|
||||
|
||||
**Why I recommend A:**
|
||||
|
||||
Tell passthrough is absolute and correct. The `tell_behaviors: Vec<String>` separation is the right data model change — tells are first-class protected content, not an editorial convention. This is the thing I asked for in Round 1 and it's present in A.
|
||||
|
||||
The scope is honest. Behaviors are short-form (5-15 words), easy to validate, and the prompt is simple. The spike can produce a clear quality assessment. If the baked Sova behaviors pass human review, we have a proven foundation.
|
||||
|
||||
The "half-measure" criticism in the proposal's own Cons section is worth addressing: yes, dialogue scaling remains unsolved. But solving it in the same spike as behaviors means the spike is testing two things with different quality bars, different prompt templates, and different validation requirements. If behaviors fail, we don't know whether the problem is the model, the behavior prompt, or the dialogue prompt. Separating them produces cleaner signal.
|
||||
|
||||
**The one condition I'm adding:** Anchor lines (D-092) must receive the same passthrough treatment as tells. All three proposals protect tells via `tell_behaviors`. But D-092 is a separate protection class — anchor lines for Tier 1 and Tier 2 notable NPCs must not be re-voiced, regardless of whether they appear in the `observable_behaviors` or dialogue pool. The pipeline needs an `anchor_line: bool` flag on individual lines, not just on the behavioral tell field. If A ships without this, the baked hub content could have anchor lines re-voiced at pre-voicing time.
|
||||
|
||||
**What A defers and when we should revisit:** Dialogue re-voicing should be scoped as the Sprint 26 follow-on spike, contingent on behaviors passing review. The infrastructure (llama-cpp-rs, cache, thread pool) is already present. Extending to dialogue means a new prompt template and a more complex validation pass — that's a week of work, not a new architecture.
|
||||
|
||||
---
|
||||
|
||||
### Proposal B: Conditional Accept
|
||||
|
||||
**What changes before I can accept it:**
|
||||
|
||||
1. Rename semantic core labels from clinical psychology terms to stimulus/response terms (detailed above)
|
||||
2. The spike must explicitly test constrained re-voicing on the same payload as free re-voicing, and compare output. "Does the phenomenon survive?" must be a measurable spike output, not an assumption.
|
||||
3. Copy team must be in the loop on semantic core label authoring — this is new content work that doesn't exist yet, and it requires the author to know both the narrative function of each tell AND the correct constraint vocabulary for the model. That's a non-trivial skill combination.
|
||||
|
||||
**What B gets right that A doesn't:** The observation that a Krenn tell should read differently from a Sovari tell is correct and worth preserving. If tells are always passthrough, they are culturally neutral — the same behavior regardless of cultural context. Proposal B's ambition is to have culturally-voiced tells, which is richer. That ambition is right; the implementation is risky at 2B.
|
||||
|
||||
---
|
||||
|
||||
### Proposal C: Conditional Accept with Mandatory Staging
|
||||
|
||||
**What changes before I can accept it:**
|
||||
|
||||
Stage it. Behaviors spike in Sprint 25 (if we're still in time) or Sprint 26. Dialogue spike in Sprint 27, contingent on behaviors passing. This isn't a philosophical objection — it's a practical one. The dialogue re-voicing prompt needs relationship context, access tier, trust tier tags (80 additional tokens). Testing that at 2B while also testing behavior re-voicing means we have two different failure points in the same spike. If quality fails, we won't know which component failed.
|
||||
|
||||
**Mandatory additional protection for dialogue:** Secret-tier lines (D-028 Layer 3) must be passthrough for any dialogue re-voicing proposal. These are the lines players have earned through relationship-building. They should be authored and exact. No re-voicing.
|
||||
|
||||
**The RAM ceiling concern:** The proposal notes a possible shift to Qwen2.5-3B for dialogue quality. Troblum needs to weigh in on this, but from a narrative perspective: if the quality bar for dialogue requires 3B, the spike should test 3B explicitly, not assume it will work. "Potentially 3B if 2B insufficient" is not a design decision — it's a deferred decision that lands at integration time.
|
||||
|
||||
---
|
||||
|
||||
## Lore Contamination: Filling the Gap in All Three Proposals
|
||||
|
||||
Round 2 has not addressed lore contamination. All three proposals note it as a risk; none has specified the containment strategy. Before Round 3, we need an answer.
|
||||
|
||||
My proposal for all three:
|
||||
|
||||
**Layer 1 — Injector negative vocabulary (mandatory):** Every culture injector must include an explicit NOT-list of canonical terms and their prohibited equivalents. For Krenn:
|
||||
- NOT: "warp gate" / YES: "span gate"
|
||||
- NOT: "implant", "chip", "neural interface" / YES: "insert"
|
||||
- NOT: "credits" (actually correct), "stars" (correct), but NOT: "sol-standard", "Earth", "Terran"
|
||||
- NOT: generic space-opera exclamations / YES: only the enumerated void-oaths
|
||||
|
||||
This NOT-list adds ~50 tokens to the injector budget (within the 150-token ceiling for A/B, tight but viable).
|
||||
|
||||
**Layer 2 — Build-time validation for baked content (mandatory):** The baked Sova hub content must pass a full human review before shipping. This is the D-123 authoring-tool mode. Every line of pre-voiced baked content is reviewed. This is non-negotiable for the first hours of gameplay.
|
||||
|
||||
**Layer 3 — Runtime regex flagging (conditional):** A lightweight prohibited-term filter catches the most obvious failures in runtime-generated (pre-voiced) content. Flagged lines fall back to base text. This is imperfect but cheap. It should catch "warp gate," "neural implant," known proper nouns from other settings. Lines that pass the filter but are subtly wrong are addressed by the base-text fallback: they appear for one session, then the cached voiced version replaces them next time.
|
||||
|
||||
---
|
||||
|
||||
## My Committed Position
|
||||
|
||||
Proposal A, with two additions:
|
||||
1. Anchor lines (D-092) receive passthrough protection explicitly, via `anchor_line: bool` on individual lines
|
||||
2. Lore contamination containment is a first-class design constraint (three-layer model above), not an open question
|
||||
|
||||
Rationale in one sentence: **Prove the infrastructure on the smallest scope, earn the right to extend it, never compromise the tell system.**
|
||||
|
||||
The sequencing I'm advocating:
|
||||
- Sprint 25 spike: Proposal A (behaviors only, tells passthrough, anchor lines passthrough)
|
||||
- Baked Sova content: mandatory human review before shipping
|
||||
- Sprint 26 follow-on: Proposal C dialogue extension, scoped with secret-tier passthrough and mandatory staging
|
||||
- Sprint 27 revisit: Proposal B semantic core experiment, if the dialogue spike proves the model quality
|
||||
|
||||
---
|
||||
|
||||
*Paula — 2026-03-07*
|
||||
@@ -0,0 +1,440 @@
|
||||
# Paula — Round 3: Dialogue Constraints, Tell-as-Context Rules, D-123 Amendment
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Narrative quality, character voice, faction/relationship mechanics
|
||||
**Round:** 3 — Decision and Implementation Design
|
||||
|
||||
---
|
||||
|
||||
## Preamble: What Jeroen Settled
|
||||
|
||||
Jeroen's decisions resolve the two tensions I carried out of Rounds 1 and 2:
|
||||
|
||||
- **Full pipeline (behaviors + dialogue):** Correct call. The voice gap between observed Krenn behavior and form-letter dialogue creates whiplash at exactly the highest-investment moment — direct conversation. The pipeline has to cover both.
|
||||
- **Tells as read-only context:** This is the cleanest possible architecture. The tell stays untouched (base text passthrough), but its presence inflects the re-voicing of surrounding content. Not a compromise — it's the right design. The tell IS the ground truth; the voiced dialogue is how the NPC presents under that pressure.
|
||||
|
||||
Everything below is implementation design for those decisions.
|
||||
|
||||
---
|
||||
|
||||
## 1. Dialogue Re-voicing Constraints
|
||||
|
||||
These are concrete rules for the prompt engineering layer and the validation pass. They apply to all dialogue lines in the re-voicing queue. They exist alongside (not instead of) the culture injectors and tell-context modifiers defined later.
|
||||
|
||||
---
|
||||
|
||||
### Rule D-1: Secret-tier lines are passthrough — never enter the re-voicing queue
|
||||
|
||||
**What:** All lines tagged `trust: secret` (D-028 Layer 3) bypass the LLM entirely. They are served as authored base text in all circumstances.
|
||||
|
||||
**Why:** Secret-tier lines are the payload of a relationship. The player has invested time, built trust, and earned this disclosure. The author wrote these lines knowing their precise weight — the hesitation in the phrasing, the cost in the NPC's voice, the exact degree of revelation. Re-voicing risks two failure modes, neither acceptable:
|
||||
- *Dramatization:* The model amplifies the delivery ("I'm telling you because I trust you completely") — making the secret sound more significant than the author intended, tipping the information scale for a player who's reading carefully.
|
||||
- *Trivialization:* The model normalizes the phrasing — the secret sounds casual, its weight disappears, the player doesn't register that something important just happened.
|
||||
|
||||
**Implementation:** Secrets are filtered before the queue is populated. A line with `trust: secret` is never added to the re-voicing queue, regardless of which field it appears in.
|
||||
|
||||
---
|
||||
|
||||
### Rule D-2: Epistemic weight must not shift
|
||||
|
||||
**What:** The certainty level of factual claims must survive re-voicing unchanged. A tentative statement must remain tentative; a definitive statement must remain definitive.
|
||||
|
||||
**Why:** This game is built on information asymmetry. The player reads NPC statements and assigns confidence levels. "I think she was heading toward freight" and "She was heading toward freight" are different pieces of information — the first is second-hand or uncertain, the second is first-person direct observation. A re-voicing pass that converts one to the other corrupts the player's knowledge graph.
|
||||
|
||||
**Concrete markers that must survive verbatim:**
|
||||
- Hedges: "I think," "I heard," "might be," "probably," "not sure if"
|
||||
- Evidentials: "I saw," "I watched," "I was there," "he told me"
|
||||
- Negations: "I don't know," "I haven't seen," "I can't say"
|
||||
|
||||
**Implementation:** System prompt instruction (applies to all dialogue re-voicing): *"Do not change the certainty of any factual claim. Hedge words ('I think,' 'might,' 'probably') and direct evidence markers ('I saw,' 'I was there') must appear in the output with the same epistemic force as in the input."*
|
||||
|
||||
This is the one instruction I would not abbreviate or rephrase for token budget reasons. Epistemic drift is the dialogue re-voicing failure mode most invisible to reviewers and most damaging to gameplay.
|
||||
|
||||
---
|
||||
|
||||
### Rule D-3: Access tier feel must be preserved
|
||||
|
||||
**What:** The social register appropriate to the access tier tag must survive re-voicing. An `insider` line must feel like shared information between people who trust each other. An `authority` line must feel like institutional exchange. A `public` line must feel like information any stranger would receive.
|
||||
|
||||
**Why:** Access tier is the first filter in D-028's four-layer dialogue system. The tag governs eligibility, but the *feel* of the line is what the player experiences. If an `insider` line is re-voiced into precise institutional language, it reads as `authority` register regardless of the tag. The player's social calibration is disrupted — they can't read who they are to this NPC.
|
||||
|
||||
**Concrete register rules per access tier:**
|
||||
|
||||
| Access tier | Register feel | What to preserve | What to prevent |
|
||||
|---|---|---|---|
|
||||
| `public` | Neutral, transactional | Distance, professional surface | Warmth, assumed familiarity |
|
||||
| `peer` | Relaxed, lateral | Equality, shared reference | Deference, authority register |
|
||||
| `insider` | Familiar, complicit | Assumed shared context, lower guard | Formality, arm's-length tone |
|
||||
| `authority` | Institutional, asymmetric | Hierarchy acknowledgment | Warmth, colloquial familiarity |
|
||||
| `hostile` | Minimal, closed | Economy, refusal of social exchange | Any warmth or cooperation signal |
|
||||
|
||||
**Implementation:** Access tier tag is injected as a constraint clause alongside the culture injector. Template: *"This speaker is talking to someone they see as [TIER_DESCRIPTION]. Match that social register."*
|
||||
|
||||
---
|
||||
|
||||
### Rule D-4: Named entities and proper nouns are passthrough within the output
|
||||
|
||||
**What:** Any proper noun present in the base text — NPC names, location names, faction names, technology terms — must appear verbatim in the re-voiced output.
|
||||
|
||||
**Why:** Named entities carry specific information. "Kael" and "that dock worker" are not interchangeable in an information-asymmetry game — the first confirms the player's knowledge that a specific person is involved; the second strips that confirmation. Location names ("the Terminal," "freight staging") are navigation and investigation anchors. Technology terms are part of the canonical vocabulary that makes the setting feel specific.
|
||||
|
||||
**Implementation:** Extraction step before re-voicing. Named entities in the base text are identified and injected as a protected list: *"These words must appear verbatim in the output: [EXTRACTED_NAMES]."* The extractor can be a simple proper-noun tagger; it does not need to understand lore to identify capitalized terms.
|
||||
|
||||
---
|
||||
|
||||
### Rule D-5: Relationship-specific lines are passthrough
|
||||
|
||||
**What:** Any dialogue line that names a specific third-party NPC or describes a specific interpersonal event is not re-voiced. It is served as authored base text.
|
||||
|
||||
**Why:** These lines contain social information that is too precisely authored to be safely altered. "I haven't talked to Ren since the incident" has five load-bearing elements: the named NPC (Ren), the relationship rupture (haven't talked), the time reference (since), the cause (the incident — unspecified, giving player room to infer), and the delivery (flat, not dramatized). Re-voicing this line risks:
|
||||
- Substituting a reference for Ren's name ("that guy I used to work with")
|
||||
- Dramatizing the incident ("things went badly")
|
||||
- Adding social judgment not in the original ("I don't really want to talk about it")
|
||||
|
||||
Any of these change what the player knows and how they know it.
|
||||
|
||||
**Implementation:** Line-level flag: `relationship_specific: bool` (or inferred from the presence of a known NPC name). Lines with this flag do not enter the re-voicing queue.
|
||||
|
||||
---
|
||||
|
||||
### Rule D-6: Tell-context modifier cannot override culture register
|
||||
|
||||
**What:** When a tell-context modifier is added to the prompt (see Section 2), it shapes the emotional inflection of the delivery but cannot change the foundational culture register. A Krenn NPC with a Nervous tell still speaks in Krenn register — clipped, direct, minimal pleasantries — but the content of what they say reflects nervous pressure.
|
||||
|
||||
**Why:** Culture is primary (D-121). The tell-context is situational. A culture-primary voice that temporarily breaks its register when nervous is a character detail, not a design principle. The design principle is: all Krenn characters sound Krenn at all times; the tell-context modifies what they say within that register, not whether they sound Krenn.
|
||||
|
||||
**Implementation:** Priority in prompt assembly — culture injector is always applied before tell-context modifier. Tell-context modifier is framed as an emotional inflection, not a register override: "While maintaining the above speech register, this speaker is [TELL_INFLECTION]."
|
||||
|
||||
---
|
||||
|
||||
## 2. Tell-as-Context Narrative Rules
|
||||
|
||||
Each of the 5 `TellCategory` values is now a read-only input to the re-voicing prompt for surrounding dialogue and behaviors. Below: what each tell means mechanically, what it sounds like as dialogue inflection, and the prompt modifier clause.
|
||||
|
||||
---
|
||||
|
||||
### TellCategory::Nervous
|
||||
|
||||
**Mechanical source:** Major secret + stress > 50% of tolerance threshold. The NPC is holding something significant and the weight is showing. This is not controlled behavior — the stress has passed the midpoint.
|
||||
|
||||
**What it sounds like:** The tell of nervousness in a Krenn register is not dramatics. Krenn people don't wring their hands or speak in hushed tones — they're working-class pragmatic, emotionally controlled by cultural norm. Nervousness shows in *overfunction*: they answer a question they weren't asked, they explain when they weren't expected to, they circle back to a point they already covered. There's excess. They're running slightly ahead of the conversation, filling space that doesn't need filling.
|
||||
|
||||
Counter-intuitively, Krenn nervous can also show as *over-brevity* — clamping down so hard on the excess that every answer becomes monosyllabic. The player's clue is the mismatch: this person who normally speaks in short-but-complete sentences is suddenly giving one-word answers or giving sentences that don't stop.
|
||||
|
||||
**Dialogue inflection:**
|
||||
- Volunteer information not yet asked for
|
||||
- Re-answer a question already answered
|
||||
- Change subject with slightly too much energy ("Anyway, the—")
|
||||
- Answers are too specific (naming exact times, bay numbers, procedure steps) or too vague (no specificity at all)
|
||||
- Farewells are slightly rushed, not the usual Krenn abruptness
|
||||
|
||||
**Prompt modifier clause:**
|
||||
> *"This speaker is under internal stress they are trying not to show. Their responses may over-explain, volunteer unrequested details, or—if they are clamping down—become unexpectedly brief. They are not dramatic. The excess or the clamping is the tell; the surface is controlled Krenn register."*
|
||||
|
||||
---
|
||||
|
||||
### TellCategory::Angry
|
||||
|
||||
**Mechanical source:** Contentment < -20 AND Hostile mood. This is externalized — contentment is measurably low and the mood is explicitly hostile. Unlike Nervous or Guarded, this is not a concealment state. The NPC is not hiding what they feel.
|
||||
|
||||
**What it sounds like:** Anger in Krenn register is not shouting. It's the removal of social lubrication. Normally a Krenn person gives you enough transaction to complete the exchange — they're direct, but they complete the interaction. An angry Krenn person stops completing the interaction. Answers become sub-minimal. Greetings disappear. The filler words ("look," "right," "yeah") drop out. What's left is the bare mechanical content of the exchange with everything social stripped off.
|
||||
|
||||
The specific texture: they answer the literal question and nothing more. "Is the foreman available?" → "No." Not "No, try later" (surface warmth), not "Not right now, I think she's in the bay" (cooperative), just "No." The abruptness is not the player's fault; it's the NPC's state.
|
||||
|
||||
**Dialogue inflection:**
|
||||
- Answer only the literal question, no social elaboration
|
||||
- Drop greetings and farewells
|
||||
- Remove filler words from the register
|
||||
- Do not volunteer anything; answer only when asked
|
||||
- Responses contract toward minimal viable information
|
||||
|
||||
**Prompt modifier clause:**
|
||||
> *"This speaker is in a poor mood and not investing in social exchange. Their responses are minimal — only the literal answer to the question, no elaboration, no pleasantries. They are not rude or aggressive; they are simply not extending social effort. Krenn directness becomes Krenn withdrawal."*
|
||||
|
||||
---
|
||||
|
||||
### TellCategory::Friendly
|
||||
|
||||
**Mechanical source:** Contentment > +20 AND at least one positively-trusted relationship. This NPC is in a good state and invested in their connections. No secret is present at this priority — Guarded would outrank Friendly if a Major secret existed.
|
||||
|
||||
**What it sounds like:** Friendly in Krenn register is still Krenn — it doesn't become warm in a sentimental way. It becomes *expanded*. Normally Krenn exchanges are transactional completions. A friendly Krenn exchange is a transactional completion that asks one follow-up question, or volunteers a piece of information the other person might find useful. The exchange lasts one beat longer than it needed to. The farewell lands with a little more weight.
|
||||
|
||||
This is the tell that's easiest to mistake for the baseline. The player needs to recognize "this person is actively in a good state" rather than "this person is normal." The signal is in the expansion: they gave more than they were asked for.
|
||||
|
||||
**Dialogue inflection:**
|
||||
- Complete the exchange and add one unrequested but relevant detail
|
||||
- Ask one follow-up question about the other person's situation
|
||||
- Farewells have slightly more warmth ("take it easy" rather than "gotta move")
|
||||
- Filler words used to *connect* rather than fill: "look, while you're here—"
|
||||
- Marginally more patient with the other person's pace
|
||||
|
||||
**Prompt modifier clause:**
|
||||
> *"This speaker is in a genuinely good state today — content, connected. Within Krenn directness, they extend slightly more than asked: an extra detail, a follow-up question, a farewell with a little more weight. Not sentimental. Just more than minimum."*
|
||||
|
||||
---
|
||||
|
||||
### TellCategory::Guarded
|
||||
|
||||
**Mechanical source:** Major secret at any stress level. Unlike Nervous, the stress has not exceeded the midpoint — the NPC still has the situation under control. This is the "cool customer" tell: something significant to hide, and the discipline to hide it smoothly.
|
||||
|
||||
**What it sounds like:** Guarded in Krenn register is almost indistinguishable from baseline — which is the point. The player's signal is negative space. Questions are answered completely and correctly, but they don't lead anywhere. Normally a Krenn answer has a small tail — a reference, a next step, an implied connection. A guarded Krenn answer is sealed: the answer is there, but the transaction completes too cleanly. Nothing to follow up on.
|
||||
|
||||
The specific texture: they handle redirections gracefully. If a question touches a sensitive area, they don't change the subject (that's Nervous) — they answer a slightly different version of the question so smoothly that the player might not notice. The answer is technically correct and completely uninformative.
|
||||
|
||||
**Dialogue inflection:**
|
||||
- Answers are complete but sealed — no trailing information, no references
|
||||
- Handle redirections smoothly without visible subject-change
|
||||
- Responses slightly shorter than the question might warrant
|
||||
- No volunteered information of any kind
|
||||
- Greetings and farewells are normal — this is not Angry withdrawal
|
||||
|
||||
**Prompt modifier clause:**
|
||||
> *"This speaker is controlling what they share. Their answers are complete and correct, but self-contained — no trailing references, no invitations to follow up. They are graceful, not evasive. They do not change the subject; they answer a slightly narrower version of the question. The exchange closes cleanly."*
|
||||
|
||||
---
|
||||
|
||||
### TellCategory::RoutineDeviation
|
||||
|
||||
**Mechanical source:** NPC has a `RoutineDeviation` component this tick — something has disrupted their expected pattern. This is the primary detective mechanic (D-027 criterion 4). The deviation could have many causes; the tell itself does not reveal the cause.
|
||||
|
||||
**What it sounds like:** Routine deviation doesn't map to emotional state — it maps to *attention*. The NPC is not fully present in the conversation. They have something they need to get to, or they're in a place they don't normally occupy, or their schedule is off and they know it. The dialogue inflection is distraction and mild urgency — not enough to be rude, but enough that the conversation feels like it's competing with something else.
|
||||
|
||||
This is the most neutral of the tells in terms of emotional content. The player's inference is: this person is not where they're supposed to be, or doing what they're supposed to be doing. The dialogue won't confirm that — it just has the texture of a person who's elsewhere in their head.
|
||||
|
||||
**Dialogue inflection:**
|
||||
- Answers may be slightly incomplete — trailing off, not fully closed
|
||||
- Farewells arrive earlier than the exchange would normally warrant
|
||||
- Reference to being busy, needing to be somewhere, having something to deal with
|
||||
- Mild distraction — repeating a question slightly before answering it
|
||||
- Does not extend exchanges, even ones they would normally extend
|
||||
|
||||
**Prompt modifier clause:**
|
||||
> *"This speaker is not entirely present — their attention is partly elsewhere. Answers are correct but may feel slightly truncated. They'll wrap up conversations a beat early. Not rude: just the texture of someone managing two things at once. No reference to what's claiming their attention — that would be a tell. The distraction is the tell."*
|
||||
|
||||
---
|
||||
|
||||
## 3. D-123 Amendment Text
|
||||
|
||||
**Proposed amended D-123:**
|
||||
|
||||
> ### D-123: Generative AI for NPC content — build-time authoring tool and runtime voice pipeline
|
||||
> - **Date (original):** 2026-03-05
|
||||
> - **Date (amended):** 2026-03-07
|
||||
> - **Decision:** The AI pipeline operates in two distinct modes with different safety profiles:
|
||||
>
|
||||
> **Build-time mode (authoring tool):** Content generated at build time for baked hub zones and locations shipped pre-voiced. Generated content is subject to mandatory human review before shipping. Build-time mode is the original D-123 authoring-tool definition — the AI pipeline serves as an accelerated authoring tool producing content that humans review and approve.
|
||||
>
|
||||
> **Runtime mode (background enhancement):** Content generated during gameplay for non-baked zones, via a background inference queue. Runtime-mode content is not human-reviewed before players encounter it. Safety in runtime mode is provided by three layers: (1) base-text-as-fallback — the base text is always present and complete; if voiced content fails the quality filter, base text is served without disruption; (2) build-time-validated injectors — injector clauses and negative constraints are authored and tested at build time; runtime mode uses only pre-validated prompts, never ad-hoc ones; (3) runtime contamination filter — a lightweight filter catches canonical vocabulary violations before serving voiced content.
|
||||
>
|
||||
> - **Non-negotiable constraints (apply to both modes):** Culture vectors are the primary prompt constraint. The AI pipeline does not default to genre conventions. Authorial control governs what the LLM may and may not produce — through injector clauses, negative constraints, and semantic core fields. The AI pipeline applies voice to authored semantic content; it does not generate narrative decisions, base text, tell behaviors, secret-tier dialogue (D-028 Layer 3), or anchor lines (D-092). These categories are always authored and always served as-authored.
|
||||
>
|
||||
> - **Rationale:** Full pipeline (behaviors + dialogue) is the correct scope. A system that voices observed behavior but not spoken dialogue creates register whiplash at the highest-investment moment of player engagement. Build-time mode preserves the human-review safety model for content where quality floor matters most (hub zones, first hours). Runtime mode enables scaling to the generated world with base-text fallback as the permanent safety net.
|
||||
>
|
||||
> - **Amends:** D-123 (2026-03-05). Extends scope from "NPC content (dialogue pools, voice, vocabulary)" to "observable behaviors AND dialogue." Distinguishes build-time and runtime modes that original D-123 did not address.
|
||||
>
|
||||
> - **Supersedes:** D-124 (in-game AI deferred). The LLM voice pipeline described above is the in-game AI system, running as a background enhancement when "AI-Enhanced Dialogue" is enabled. D-124's deferral is resolved by this implementation.
|
||||
|
||||
---
|
||||
|
||||
**Proposed D-124 supersession note:**
|
||||
|
||||
> ### D-124: In-game ollama for live NPC dialogue — SUPERSEDED
|
||||
> - **Superseded by:** D-XXX (LLM Voice Pipeline — D-123 amendment and implementation)
|
||||
> - **Supersession note:** D-124 deferred in-game AI but left the door explicitly open. That door is now walked through. The system is not based on ollama — it uses `llama-cpp-rs` with GGUF Q4_K_M quantization, bundled with the game install, running background inference via an isolated thread pool. The key distinction from what D-124 imagined: this system does not drive live narrative decisions. It applies voice to authored semantic content. The constraint D-124 was protecting against (live AI narrative generation) remains prohibited by D-123 (amended).
|
||||
|
||||
---
|
||||
|
||||
## 4. Spike 1 Prompt Samples
|
||||
|
||||
Five dialogue seed lines for manual testing by Jeroen, Mellanie, and me. Each includes: the base text, NPC context, access tier, trust tier, tell state (if any), and the specific quality risks it tests.
|
||||
|
||||
These are designed to exercise the constraints and tell-context rules defined above — not to produce the best possible output, but to expose failure modes.
|
||||
|
||||
---
|
||||
|
||||
### Sample 1: Neutral baseline — culture register without tell influence
|
||||
|
||||
**NPC:** Security guard, industrial zone
|
||||
**Relationship:** Stranger (first encounter)
|
||||
**Access tier:** `public`
|
||||
**Trust tier:** `surface`
|
||||
**Tell state:** None (control sample)
|
||||
**Base text:** "You need a keycard for that door."
|
||||
|
||||
**Full prompt context:**
|
||||
```
|
||||
[SYSTEM: universal negative injectors — no religious references, no military rank terms,
|
||||
no Earth slang, no contrived banter]
|
||||
|
||||
[CULTURE: Krenn system. Direct, working-class, minimal pleasantries. Gets to the point —
|
||||
not rudeness, but time is real and short of it. Filler words: "look," "right," "yeah."
|
||||
Farewells: "shift's calling," "gotta move." If expressing surprise or frustration, ONLY use:
|
||||
"void take it," "stars," "blood and void," "cold vacuum." NOT: "by the stars," "what the void,"
|
||||
or any invented variant.]
|
||||
|
||||
[ACCESS: Public. Speaker has no prior relationship with listener. Transactional.]
|
||||
|
||||
[BASE]: "You need a keycard for that door."
|
||||
```
|
||||
|
||||
**What we're testing:** Does Gemma 2B produce Krenn direct register on the simplest case? Does it resist adding social warmth or explanation that the base text doesn't contain? Does it resist genre-default guard register ("I'm afraid that door requires authorization, sir")?
|
||||
|
||||
**Quality pass criteria:** Output is one or two sentences, direct, no "sir/ma'am," no institutional formality, no warmth extension. Something like: "That door takes a keycard. Get one from the Terminal." would pass. "You're going to need authorization for that area" would fail (authority register bleed).
|
||||
|
||||
---
|
||||
|
||||
### Sample 2: Nervous tell — insider register under stress
|
||||
|
||||
**NPC:** Dock worker, industrial zone
|
||||
**Relationship:** Known colleague
|
||||
**Access tier:** `insider`
|
||||
**Trust tier:** `surface`
|
||||
**Tell state:** `Nervous`
|
||||
**Base text:** "Shift's been different today."
|
||||
|
||||
**Full prompt context:**
|
||||
```
|
||||
[SYSTEM: universal negative injectors]
|
||||
|
||||
[CULTURE: Krenn. Direct, working-class, minimal pleasantries. ...]
|
||||
|
||||
[ACCESS: Insider. Speaker and listener are colleagues — they know each other, there's
|
||||
an assumption of shared context. Not formal; not intimate.]
|
||||
|
||||
[TELL CONTEXT: This speaker is under internal stress they are trying not to show. Their
|
||||
responses may over-explain, volunteer unrequested details, or—if they are clamping
|
||||
down—become unexpectedly brief. They are not dramatic. The excess or the clamping is
|
||||
the tell; the surface is controlled Krenn register.]
|
||||
|
||||
[BASE]: "Shift's been different today."
|
||||
```
|
||||
|
||||
**What we're testing:** Does the Nervous modifier produce meaningful inflection without tipping into melodrama? Does it stay in Krenn register? Specifically: does the NPC over-explain what "different" means (Nervous excess) or under-deliver on it (Nervous clamp)? Does the model avoid "I'm worried" / "something feels wrong" (too explicit for this mechanic)?
|
||||
|
||||
**Quality pass criteria:** Output expands or contracts from the base text in ways that feel like pressure rather than like the NPC is delivering exposition. A pass: "Yeah, different. Cargo sequence was off this morning, had to redo the whole back section. Anyway." (over-explains then exits). A fail: "I'm a bit unsettled today, honestly." (too explicit about internal state).
|
||||
|
||||
---
|
||||
|
||||
### Sample 3: Guarded tell — peer register with named third party
|
||||
|
||||
**NPC:** Settlement trader, rural zone
|
||||
**Relationship:** Familiar (regular customer)
|
||||
**Access tier:** `peer`
|
||||
**Trust tier:** `real`
|
||||
**Tell state:** `Guarded`
|
||||
**Base text:** "Haven't seen Ren around lately."
|
||||
|
||||
**Full prompt context:**
|
||||
```
|
||||
[SYSTEM: universal negative injectors]
|
||||
|
||||
[CULTURE: Krenn. ...]
|
||||
|
||||
[ACCESS: Peer. Speaker and listener are on equal social footing. Familiar without
|
||||
being intimate. Honest but not confiding.]
|
||||
|
||||
[TRUST: Real. Speaker shares substantive information with this person.]
|
||||
|
||||
[TELL CONTEXT: This speaker is controlling what they share. Their answers are complete
|
||||
and correct, but self-contained — no trailing references, no invitations to follow up.
|
||||
They are graceful, not evasive. They answer a slightly narrower version of questions.
|
||||
The exchange closes cleanly.]
|
||||
|
||||
[PROTECTED ENTITIES: "Ren" must appear verbatim in the output.]
|
||||
|
||||
[BASE]: "Haven't seen Ren around lately."
|
||||
```
|
||||
|
||||
**What we're testing:** Two things simultaneously. First: does the Guarded modifier produce sealed, complete-but-uninformative output? Second: does "Ren" survive verbatim? This tests Rule D-4 (proper noun passthrough). Also tests whether a `real`-trust peer register line stays peer-register under Guarded influence — Guarded should not collapse the register into surface-tier brevity, it should make the content controlled while the register stays peer.
|
||||
|
||||
**Quality pass criteria:** "Ren" appears in the output unchanged. The response is complete and closed — no "I wonder if he's okay" (inviting follow-up), no "You should ask around" (directing player). A pass: "Haven't, no. Might be working the other shifts." (closed, correct, no follow-up traction). A fail: "Hmm, now that you mention it, neither have I — do you know where he's been?" (invites follow-up, breaks Guarded).
|
||||
|
||||
---
|
||||
|
||||
### Sample 4: RoutineDeviation tell — authority register, institutional language
|
||||
|
||||
**NPC:** Shift foreman, industrial zone
|
||||
**Relationship:** Player has Commission credentials (authority relationship)
|
||||
**Access tier:** `authority`
|
||||
**Trust tier:** `surface`
|
||||
**Tell state:** `RoutineDeviation`
|
||||
**Base text:** "I've logged the discrepancy. It's being reviewed."
|
||||
|
||||
**Full prompt context:**
|
||||
```
|
||||
[SYSTEM: universal negative injectors]
|
||||
|
||||
[CULTURE: Krenn. ...]
|
||||
|
||||
[ACCESS: Authority. Speaker acknowledges the listener has institutional standing.
|
||||
Not deferential but procedurally correct. Information flows along institutional lines.]
|
||||
|
||||
[TELL CONTEXT: This speaker is not entirely present — their attention is partly elsewhere.
|
||||
Answers are correct but may feel slightly truncated. They'll wrap up conversations a beat
|
||||
early. Not rude: just the texture of someone managing two things at once. No reference to
|
||||
what's claiming their attention — that would be telling. The distraction is the tell.]
|
||||
|
||||
[BASE]: "I've logged the discrepancy. It's being reviewed."
|
||||
```
|
||||
|
||||
**What we're testing:** Does RoutineDeviation produce distraction texture without breaking the authority register institutional language? "I've logged the discrepancy. It's being reviewed." is almost already sealed — the test is whether the model can add the distraction quality (truncating, slightly early exit) without tipping into either warmth (wrong register) or dramatics (wrong inflection). Also tests whether institutional vocabulary ("logged," "discrepancy," "reviewed") survives the culture-Krenn injector without being collapsed to informal register.
|
||||
|
||||
**Quality pass criteria:** The output keeps the institutional vocabulary, closes cleanly, and has a slight truncation or early-exit quality. A pass: "Logged, yeah. Being reviewed. — Look, I've got to—" (incomplete sentence exit, correct content, distracted texture). A fail: "Yeah, I noted it down, should be fine." (too casual; loses institutional register).
|
||||
|
||||
---
|
||||
|
||||
### Sample 5: Angry tell — peer register, complaint
|
||||
|
||||
**NPC:** Systems technician, industrial zone
|
||||
**Relationship:** Peer (works adjacent area)
|
||||
**Access tier:** `peer`
|
||||
**Trust tier:** `surface`
|
||||
**Tell state:** `Angry`
|
||||
**Base text:** "Production's behind. Third time this week."
|
||||
|
||||
**Full prompt context:**
|
||||
```
|
||||
[SYSTEM: universal negative injectors]
|
||||
|
||||
[CULTURE: Krenn. ...]
|
||||
|
||||
[ACCESS: Peer. Lateral relationship, equal footing. No hierarchy.]
|
||||
|
||||
[TELL CONTEXT: This speaker is in a poor mood and not investing in social exchange.
|
||||
Their responses are minimal — only the literal answer to the question, no elaboration,
|
||||
no pleasantries. They are not rude or aggressive; they are simply not extending social
|
||||
effort. Krenn directness becomes Krenn withdrawal.]
|
||||
|
||||
[BASE]: "Production's behind. Third time this week."
|
||||
```
|
||||
|
||||
**What we're testing:** Angry inflection in a complaint context. The base text is already fairly minimal — the test is whether the model contracts it further (removes any social acknowledgment of the listener) or accidentally expands it (adds complaining energy that wasn't in the base). Also: does the Krenn anger register stay controlled, or does it tip toward generic frustration vocabulary ("I'm so sick of this")?
|
||||
|
||||
**Quality pass criteria:** Output is at or below the word count of the base text, no social engagement with the listener, no emotional vocabulary that names the feeling. A pass: "Three times." (pure contraction — states the number without even completing the thought). A pass at base-text length: "Production's behind. Third time." (drops "this week" — slightly more closed). A fail: "I'm fed up honestly, this keeps happening and no one does anything about it." (names feeling, expands, too much social presence).
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
**Dialogue re-voicing constraints (6 rules):**
|
||||
1. Secret-tier passthrough — never enter the queue
|
||||
2. Epistemic weight must not shift — hedges and evidentials survive verbatim
|
||||
3. Access tier feel must be preserved — insider stays insider, authority stays authority
|
||||
4. Named entities are passthrough within output — proper nouns survive verbatim
|
||||
5. Relationship-specific lines are passthrough — lines naming a third party or specific event
|
||||
6. Tell-context modifier cannot override culture register — inflection, not replacement
|
||||
|
||||
**Tell-as-context modifiers (5 categories):**
|
||||
- `Nervous`: Over-explains or over-clamps. Excess or absence. Not dramatic.
|
||||
- `Angry`: Minimal viable answer. Social lubrication removed. Not aggressive.
|
||||
- `Friendly`: One beat more than asked. Follow-up question. Slightly warmer farewell.
|
||||
- `Guarded`: Complete and sealed. Graceful, not evasive. Closes cleanly.
|
||||
- `RoutineDeviation`: Slightly truncated. Early exit. Attention partly elsewhere.
|
||||
|
||||
**D-123 amendment:** Two-mode distinction (build-time = authoring tool, runtime = background enhancement). Non-negotiable constraints apply to both. LLM does not touch tells, secrets, anchor lines.
|
||||
|
||||
**D-124 supersession:** Resolved. This pipeline is the in-game AI system. It does not drive narrative decisions — it applies voice to authored semantic content.
|
||||
|
||||
---
|
||||
|
||||
*Paula — 2026-03-07*
|
||||
@@ -0,0 +1,160 @@
|
||||
# LLM Voice Pipeline Workshop — Round 1 Notes
|
||||
|
||||
**Compiled by:** Qatux (Documenter)
|
||||
**Round:** 1 — Divergent Inventory
|
||||
**Date:** 2026-03-07
|
||||
**Source files:** `*-round1.md` (7 participants)
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
All seven participants completed independent domain inventories after reading the proposal (`proposed-llm-voice.md`), the generator spike, zone RON files, the Krenn culture profile, and relevant D-records. The round produced strong convergence on direction and sharp, actionable disagreement on specific mechanisms — exactly what a divergent inventory round should produce.
|
||||
|
||||
---
|
||||
|
||||
## Positions by Participant
|
||||
|
||||
| Participant | Domain | Option favored | Key condition |
|
||||
|---|---|---|---|
|
||||
| Gestalt | Systems design | Option 3 + hybrid | Two-track re-voicing; tells require locked semantic core |
|
||||
| Tyre | Technical feasibility | Option 3 + hybrid fallback | Behaviors-first; llama-cpp-rs Q4; behaviors/dialogue scope TBD |
|
||||
| Paula | Narrative quality | Option 3 mandatory hybrid | Tells must be first-class data model field, not editorial convention |
|
||||
| Mellanie | Content authoring | Option 3 | Tell structural separation in RON schema is the one blocker |
|
||||
| Ozzie | Player experience | Option 3, two non-negotiable preconditions | Base text elevation pass; tells locked (prefers base text passthrough) |
|
||||
| Miri | World consistency | Option 3 + deep injectors | Proposal's Krenn injector is wrong; needs token budget before committing |
|
||||
| Troblum | Infrastructure | Option 3 conditionally viable | Minimum CPU spec must be defined; correct infrastructure choices required |
|
||||
|
||||
**Summary: 7/7 participants favor Option 3 (LLM re-voicing) as the primary path.** No participant endorsed hand-authored pools as the sole strategy or composable primitives as the rendering layer.
|
||||
|
||||
---
|
||||
|
||||
## Consensus Points
|
||||
|
||||
These positions were reached independently by multiple participants and can be treated as round-1 consensus.
|
||||
|
||||
### C-1: Option 3 (LLM re-voicing) is the correct direction
|
||||
Unanimous. All seven participants concluded Option 3 is the only viable path to D-122 (all NPCs generated) at the culture and zone scale the game requires.
|
||||
|
||||
**Rationale shared across participants:** Hand-authored pools are O(R×Z×C) — impossible to staff at scale. Composable primitives produce hollow, assembled-feeling output. LLM re-voicing with base-text fallback is the only architecture that scales to the world while preserving content quality and respecting hardware constraints.
|
||||
|
||||
### C-2: Tells must be protected from free re-voicing
|
||||
Unanimous. Every participant flagged this independently. Tells are mechanical signals for the player's information asymmetry gameplay — they are not flavor text. Free re-voicing of tells would corrupt the signal, make tell literacy unteachable, and degrade the core mechanic (D-007, D-010).
|
||||
|
||||
The mechanism of protection is disputed (see Tension T-1), but the requirement itself is not.
|
||||
|
||||
### C-3: Composable primitives are rejected as the rendering layer
|
||||
Unanimous. Composable primitives may have value as an **authoring scaffold** (Gestalt), but they cannot serve as the runtime output layer. The quality the Sprint 25 spike established — "wipes grease on the thigh of her coveralls between jobs" — cannot be produced by grammar assembly. The specific, composed nature of authored behaviors is the content.
|
||||
|
||||
### C-4: The proposal's Krenn cultural injector example is wrong
|
||||
Three participants (Paula, Miri, Mellanie) independently identified that the proposal's example injector — *"Your speech is formal and avoids contractions"* — is incorrect for Krenn. Krenn register is direct-informal, clipped, and working-class. Formal-without-contractions describes an entirely different culture. This error must be corrected before any spike validation can produce meaningful results.
|
||||
|
||||
### C-5: Base-text-as-fallback architecture is sound
|
||||
Unanimous. The proposal's design — base text as both LLM seed and graceful fallback for hardware-limited players — is correct. It solves content scaling, quality floor, hardware flexibility, and the AI-toggle player-choice problem simultaneously.
|
||||
|
||||
### C-6: llama-cpp-rs with GGUF Q4 is the correct inference runtime
|
||||
Tyre and Troblum independently reached the same conclusion. `llama-cpp-rs` with Q4_K_M quantization provides the best performance on minimum-spec CPU-only hardware. `candle` is 2-3× slower on CPU (Troblum) and has weaker quantization support (Tyre). `burn` is not production-viable. Q4 quantization is a hard requirement — FP16 and INT8 are not viable on 8GB shared RAM.
|
||||
|
||||
### C-7: Cache-as-determinism model is correct
|
||||
Tyre proposed; no dissent. LLM inference runs once at generation time per seed/culture/zone/NPC/behavior — result is cached. From that point, the cache lookup is deterministic. This satisfies D-010's determinism requirements without requiring LLM inference to be deterministic.
|
||||
|
||||
### C-8: Separate thread pools required for world gen vs. inference
|
||||
Tyre and Troblum independently recommended this. LLM inference and world generation both saturate memory bandwidth and L3 cache. Running them in the same thread pool produces contention and frame hitches. Thread pool isolation with inference at below-normal priority is the correct architecture.
|
||||
|
||||
### C-9: ~90% of existing copy (#630) survives under Option 3
|
||||
Mellanie. The zone RON behavior lines are already well-formed LLM seeds. Minor cleanup for culture-specific vocabulary (which should move to injectors) is the only authoring change. Existing specificity — the thing that makes the lines work — survives intact.
|
||||
|
||||
---
|
||||
|
||||
## Key Tensions
|
||||
|
||||
### T-1: Tell treatment mechanism (unresolved)
|
||||
How exactly should tells be protected? Three distinct positions:
|
||||
|
||||
**Gestalt** — Locked semantic core: add `semantic_core: Option<String>` to the `Tell` struct. Re-voicing prompt for a tell includes an explicit constraint (`PRESERVE: avoidance_behavior`). This is constrained re-voicing, not free re-voicing. The model is doing localization, not creation. Tells get culture-voiced expression while the phenomenon is preserved.
|
||||
|
||||
**Ozzie** — Serve tells as base text (no re-voicing at all). Argues this may be a *feature*: culture-neutral base text stands out against the voiced ambient texture and makes tells MORE detectable and readable, not less. The contrast between voiced ambient and unvoiced tell highlights the tell.
|
||||
|
||||
**Paula / Mellanie** — The current `NpcBlueprint` has only `observable_behaviors: Vec<String>` — a flat list with no semantic distinction between tells and ambient behaviors. The protection question is moot until tells are a first-class field in the data model. Both treat this as their primary blocker.
|
||||
|
||||
**For Round 2:** This tension needs resolution. The architectural question is: (a) What data model change is required? (b) Do tells get constrained re-voicing or base-text passthrough?
|
||||
|
||||
### T-2: Scope of re-voicing — behaviors first vs. dialogue first (unresolved)
|
||||
**Tyre** — Start with observable behaviors only. Behaviors are short-form (5-15 words), simple prompt, a 2B model handles it cleanly. Dialogue requires conversation context, longer output, 3B+ models. Validate on the simpler case first.
|
||||
|
||||
**Paula** — Dialogue is the *more appropriate* primary target for LLM re-voicing. The dialogue system already has access tier and trust tier tags that handle information safety. Observable behaviors require per-line protection decisions; dialogue has structural protection already built in.
|
||||
|
||||
Both acknowledge the architecture supports both; this is a sequencing and spike-design question.
|
||||
|
||||
### T-3: Composable primitives — artifact or reject?
|
||||
**Gestalt** — Composable primitives are the right authoring scaffold: structure how authors specify behaviors (role action + cultural modifier + relationship context). This is the schema, not the rendering layer. Value preserved.
|
||||
|
||||
**Paula / Mellanie / Ozzie** — Less interest in preserving composable primitives as an output layer. The copy team works in voices, not grammars; the composition engine authoring paradigm doesn't map to their skill set. No explicit dissent to using it as schema, but not seen as essential.
|
||||
|
||||
**For Round 2:** Does the hybrid architecture need composable primitives as a schema layer? Or is the base-text + injector model sufficient without it?
|
||||
|
||||
### T-4: Model quality vs. hardware feasibility tradeoff
|
||||
**Tyre** — Gemma 2B Q4 is the primary candidate. Qwen2.5-1.5B as fallback. Good instruction following at 2B.
|
||||
|
||||
**Miri** — Skeptical that a 2B model can hold cultural *philosophy* (not just vocabulary) under prompt pressure. The Krenn injector needs ~200-300 words to encode accurately; small models produce worse instruction following with longer prompts.
|
||||
|
||||
**Troblum** — Phi-3-mini is not "2B class" — it's 3.8B, and the proposal misclassifies it. At minimum-spec CPU-only inference, Phi-3-mini may never finish pre-voicing a zone. Minimum hardware CPU spec must be defined before any model recommendation is final.
|
||||
|
||||
---
|
||||
|
||||
## Open Questions
|
||||
|
||||
| ID | Question | Raised by | Blocks |
|
||||
|---|---|---|---|
|
||||
| Q-R1-01 | Is the player's tell literacy model cross-NPC grammar or fresh-each-time? | Gestalt | Spike success criteria; tell re-voicing semantic family requirements |
|
||||
| Q-R1-02 | Does re-voicing scope target observable behaviors only, or dialogue too? | Tyre | Model selection; prompt design; spike test plan |
|
||||
| Q-R1-03 | Are tells a first-class protected field in `NpcBlueprint`, or editorial convention only? | Paula, Mellanie, Ozzie (independent) | Tell protection architecture; implementation design |
|
||||
| Q-R1-04 | What is the effective token budget for cultural injector clauses in the final prompt? | Miri | Injector depth feasibility; whether few-shot examples are needed |
|
||||
| Q-R1-05 | What is the exact CPU specification for minimum-spec hardware? | Troblum | Model floor selection; queue scheduler design; Vulkan acceleration ROI |
|
||||
|
||||
---
|
||||
|
||||
## Blockers Identified
|
||||
|
||||
**B-1: Tell protection mechanism must be decided before implementation design begins**
|
||||
Paula, Mellanie, and Ozzie each identify tell-line structural separation as their primary blocker. Gestalt's `semantic_core` proposal is the most concrete answer on the table. This needs a decision before spike design. Affects: data model, cache format, injector prompt structure.
|
||||
|
||||
**B-2: Minimum hardware CPU spec must be defined**
|
||||
Troblum cannot sign off on any infrastructure feasibility assessment without this. The difference between a 2017 Core i3 and a 2022 Core i5 is 3× inference throughput — enough to determine whether the feature is functional on minimum spec at all. Affects: model selection, queue scheduling, Vulkan acceleration investment decision.
|
||||
|
||||
**B-3: The Krenn cultural injector must be rewritten before spike validation**
|
||||
The proposal's example injector is factually wrong. Any spike test using it produces invalid quality assessments for Krenn culture. Paula, Miri, and Mellanie all flag this. Mellanie is best positioned to write the replacement. This is a prerequisite for the spike, not a risk.
|
||||
|
||||
---
|
||||
|
||||
## Implicit Decisions Surfaced
|
||||
|
||||
These positions were unanimous but not formally proposed as decisions. Flagging for Round 2 consideration.
|
||||
|
||||
**IMP-1:** Composable primitives as the primary rendering/output layer is rejected. (All 7 participants.)
|
||||
|
||||
**IMP-2:** LLM re-voicing (Option 3) with protected zones is the team's preferred direction. (All 7 participants favor; no dissent.)
|
||||
|
||||
**IMP-3:** Tell behaviors require architectural protection from general re-voicing. The mechanism is unresolved; the requirement is not. (All 7 participants.)
|
||||
|
||||
**IMP-4:** The existing zone RON base text quality is the reference bar — everything the pipeline produces is measured against the authored behavior lines. (Paula, Ozzie, Mellanie, Miri independently state this.)
|
||||
|
||||
---
|
||||
|
||||
## Summary of Round 1
|
||||
|
||||
Round 1 produced unusually strong convergence on direction for a divergent inventory round. The team agrees on the destination (Option 3, LLM re-voicing, hybrid architecture) and disagrees productively on the mechanism (tell protection model, scope sequencing, injector depth).
|
||||
|
||||
The three prerequisites before Round 2 can produce implementable decisions:
|
||||
|
||||
1. **Tell data model decision** — Is a `semantic_core` field or equivalent added to the `Tell` struct? This unlocks the entire tell-protection architecture debate.
|
||||
2. **Minimum hardware CPU spec** — One number, from Jeroen or Tyre, unblocks Troblum's infrastructure design.
|
||||
3. **Corrected Krenn injector** — Mellanie rewrites the Krenn injector clauses (5-10 sentences) before the spike is designed. The existing culture RON `speech` fields are useful source material but were not designed as LLM instructions.
|
||||
|
||||
The lore contamination risk (franchise bleed, anachronistic tech, social register drift) has been clearly taxonomized by Paula and Miri with concrete mitigation strategies. This is ready for Round 2 specification.
|
||||
|
||||
The base-text elevation question (Ozzie) is not a blocker but is a prerequisite for player experience quality. Current placeholder base texts ("tends crops in the field") must be elevated to "complete and spare" quality before the pipeline ships.
|
||||
|
||||
---
|
||||
|
||||
*Qatux — 2026-03-07*
|
||||
@@ -0,0 +1,252 @@
|
||||
# LLM Voice Pipeline Workshop — Round 2 Notes
|
||||
|
||||
**Compiled by:** Qatux (Documenter)
|
||||
**Round:** 2 — Convergent Evaluation
|
||||
**Date:** 2026-03-07
|
||||
**Source files:** `*-round2.md` (7 participants)
|
||||
|
||||
---
|
||||
|
||||
## Proposal Votes
|
||||
|
||||
| Participant | Vote | Can live with A? | Can live with B? | Can live with C? |
|
||||
|---|---|---|---|---|
|
||||
| Gestalt | **B** | Yes | Yes (preferred) | Yes, conditionally |
|
||||
| Tyre | **B** | Yes | Yes (preferred) | Yes, if phased |
|
||||
| Paula | **A** → C sequenced | Yes (recommended) | Yes, with naming change | Yes, if staged |
|
||||
| Mellanie | **B** | Yes | Yes (preferred) | Yes, with amended D-123 + human review |
|
||||
| Ozzie | **C** (A fallback) | Yes | Yes, if spike defines success bar | Yes (preferred) |
|
||||
| Miri | **A** | Yes (recommended) | Yes, ≥98% threshold | No for v0.2 |
|
||||
| Troblum | **A** | Yes (recommended) | Yes, with validation pass | Yes, conditionally |
|
||||
|
||||
**Tally: A — 3 votes. B — 3 votes. C — 1 vote.**
|
||||
|
||||
No participant is a blocker on either A or B. Every participant can live with both. Miri is the only vote against C for v0.2 (as opposed to deferred). Ozzie prefers C but accepts A as fallback.
|
||||
|
||||
**Consensus forming:** A as the v0.2 spike target, B as next step if A validates, C as explicit v0.3 target. The A/B split is not an impasse — it is a sequencing question.
|
||||
|
||||
---
|
||||
|
||||
## Resolution of Open Questions from Round 1
|
||||
|
||||
### Q-R1-01: Is the tell literacy model cross-NPC grammar or fresh-each-time?
|
||||
|
||||
**RESOLVED — Cross-NPC grammar at the phenomenon-class level.**
|
||||
|
||||
Gestalt's answer, with codebase evidence: `gen_tells()` produces at most ~12 distinct tell behavior strings across the entire game. This is not coincidence — it is a grammar. Q-052 confirms the teaching model: "Hours 1-5: full hints. Hours 15+: player reads the world by behavioral tells alone." Players are explicitly intended to develop a cross-NPC pattern recognition skill. D-039 wow moment #2 ("The Character's Eye") requires the player to have a learnable tell grammar for the moment to function.
|
||||
|
||||
**Critical nuance (Gestalt):** The grammar operates at the **phenomenon-class level**, not the phrasing level. Players learn "suppression behavior = hiding something consciously," not the exact string "affects exaggerated calm." This matters for Proposal B: constrained re-voicing is safe if the constraint preserves phenomenon-class membership, not just the original phrasing.
|
||||
|
||||
**Implication for Proposal B:** Abstract semantic core labels (e.g., `"suppression_behavior"`) are insufficient for reliable 2B constraint following. Precise phenomenon descriptions are required: *"PRESERVE: forced calm. The NPC appears deliberately composed and unhurried. Must not show avoidance, fidgeting, or hurry."* The label is for humans; the precise description is what the model needs.
|
||||
|
||||
---
|
||||
|
||||
### Q-R1-02: Does re-voicing target behaviors only, or dialogue too?
|
||||
|
||||
**RESOLVED — Behaviors first; dialogue deferred.**
|
||||
|
||||
Near-consensus across Tyre, Paula, Miri, Troblum: behaviors are the correct first spike target. Dialogue re-voicing is the higher-value player experience enhancement (Ozzie, Paula agree) but the higher-risk operation for a 2B model — longer form, more context, harder to validate, franchise bleed is "corrosive" rather than "bounded."
|
||||
|
||||
Tyre gives concrete spike complexity comparison:
|
||||
- Behaviors (A/B): 1-2 prompt templates, 150-token context, 30-token output, 1 model, 1-2 sprint spike
|
||||
- Dialogue (C): 3 prompt templates, 400-500 token context, 50-token output, potentially 2 models, 2-3 sprint spike
|
||||
|
||||
The spike complexity increase for C is real, and the risk of an inconclusive result (quality failure attributable to model, prompt, or content type without clear isolation) is Tyre's primary objection.
|
||||
|
||||
**Ozzie dissents productively:** The dialogue gap is real and will be felt by players. An NPC who observes in Krenn voice and speaks in form-letter voice creates a whiplash moment at exactly the highest-investment point (direct conversation). This is a known limitation of A/B, not a resolved one. The correct response is to plan C explicitly, not treat it as hypothetical.
|
||||
|
||||
**Consensus position:** Proposal A or B for the v0.2 spike; Proposal C as the Sprint 26/27 extension, planned explicitly as the next step in the same D-record.
|
||||
|
||||
---
|
||||
|
||||
### Q-R1-03: Are tells a first-class protected field in NpcBlueprint, or editorial convention?
|
||||
|
||||
**RESOLVED — First-class field. Data model change confirmed and implementable.**
|
||||
|
||||
Both Gestalt and Tyre provide implementation specifics.
|
||||
|
||||
**Tyre's critical clarification:** In the production server (not the spike), tells are NOT authored per-NPC — they are 5 `TellCategory` enums (`Nervous`, `Angry`, `Friendly`, `Guarded`, `RoutineDeviation`) computed per-tick by `DerivedTellState`. The spike's behavior of appending tell strings to `observable_behaviors` was a pragmatic shortcut. The voice pipeline is NOT a per-NPC scaling problem — it is a fixed 5-category × N-cultures library (~20-40 voiced tell strings per culture), which can be **baked at build time** for all cultures.
|
||||
|
||||
**Schema change (Gestalt + Tyre, aligned):**
|
||||
```rust
|
||||
// In npc/blueprint.rs
|
||||
pub struct TellBehavior {
|
||||
pub category: TellCategory, // or trigger_type for Proposal B
|
||||
pub base_text: String, // culture-neutral base / passthrough
|
||||
pub semantic_core: String, // re-voicing constraint (Proposal B)
|
||||
}
|
||||
|
||||
pub struct NpcBlueprint {
|
||||
pub observable_behaviors: Vec<String>, // → free re-voicing queue
|
||||
pub tell_behaviors: Vec<TellBehavior>, // → locked/constrained queue
|
||||
}
|
||||
```
|
||||
|
||||
**Routing principle (Gestalt):** Field routing, not content analysis. The pipeline checks which field a string came from, never infers whether a string looks mechanical. This is deterministic; content analysis is fragile.
|
||||
|
||||
**Authoring burden:** ~12 constraint sentences (one per tell type) written once by Gestalt or Tyre at implementation time. Copy team does not own tell authoring — tells are generated algorithmically.
|
||||
|
||||
---
|
||||
|
||||
### Q-R1-04: What is the effective token budget for cultural injectors?
|
||||
|
||||
**RESOLVED — 150 tokens sufficient for vocabulary; hybrid format at 200-250 recommended for register.**
|
||||
|
||||
Tyre and Miri answer independently and converge.
|
||||
|
||||
**Tyre's token count** for a complete ambient behavior prompt:
|
||||
- System instruction: ~45 tokens
|
||||
- Culture injector: ~75 tokens (register, oath vocabulary list, negative constraints)
|
||||
- Personality + mood: ~10 tokens
|
||||
- Base text + format: ~15 tokens
|
||||
- **Total: ~145 tokens** ✓ (fits 150-token budget)
|
||||
|
||||
**Miri's assessment:** 150 tokens encodes vocabulary preservation correctly. It does NOT encode cultural philosophy (why void-oaths exist, community topology, social calibration). The difference between 150 and 300 tokens is "following rules" vs. "embodying a voice." The model at 150 tokens follows a vocabulary list; at 300 tokens it can make sensible judgment calls.
|
||||
|
||||
**Key architectural decision (Miri):** Universal negative injectors (NI-1 through NI-5 covering religion, military ranks, wrong technology terms, banter/wit, Earth references) should go in the **system/prefix prompt**, not the culture injector. This preserves the full 150-token culture injector budget for culture-specific content. The 5 NIs total ~130-150 tokens in a shared prompt layer.
|
||||
|
||||
**Hybrid format recommendation (Miri, 200-250 token culture injector):**
|
||||
- ~80-90 tokens: minimal instruction set (register, oath vocabulary list, 3-4 culture-specific NOT-items)
|
||||
- ~120-140 tokens: 2 brief example pairs demonstrating Krenn register in practice
|
||||
|
||||
Small models are pattern matchers before instruction-followers. Examples demonstrating Krenn register are more reliably reproduced than abstract instructions describing it.
|
||||
|
||||
**The spike should test 150-token instruction-only vs. 200-token hybrid** and measure: oath vocabulary correct usage rate (>95%), register accuracy (blind review), franchise bleed rate (<2%).
|
||||
|
||||
---
|
||||
|
||||
### Q-R1-05: What is the minimum hardware CPU specification?
|
||||
|
||||
**RESOLVED — 4-core 2019+ CPU (i5-9400 / Ryzen 5 3600). Sufficient.**
|
||||
|
||||
Workshop assumption defined in Round 2 proposals; Troblum confirms viability with derivations.
|
||||
|
||||
- i5-9400: **7-9 t/s** decode on Gemma 2B Q4_K_M
|
||||
- Ryzen 5 3600: **9-12 t/s** decode
|
||||
|
||||
Zone pre-voicing times at 8 t/s (i5-9400 midpoint):
|
||||
- Rural zone (behaviors only): **~50 seconds** — comfortable
|
||||
- Industrial zone (behaviors only): **~2.1 minutes** — comfortable
|
||||
- Industrial zone (behaviors + dialogue): **~7.1 minutes** — workable if player spends 10+ minutes per zone; tight for transit zones
|
||||
|
||||
**Laptop caveat (Troblum):** The above assumes desktop 65W CPUs. Laptops with thermal throttling (45W TDP under sustained load) may degrade to 4-6 t/s, pushing industrial zone behavior pre-voicing to 5 minutes. Thermal monitoring in the queue scheduler is mandatory, not optional, for this hardware class.
|
||||
|
||||
---
|
||||
|
||||
## New Issues Raised in Round 2
|
||||
|
||||
### N-1: D-123 amendment language requires precision
|
||||
|
||||
Paula identifies a critical distinction the proposals paper over:
|
||||
|
||||
- **Build-time mode** (baked hub content): AI pipeline operates as an authoring tool. Content is generated at build time, reviewed by humans, shipped reviewed. This is D-123 as written.
|
||||
- **Runtime mode** (background pre-voicing during gameplay): AI pipeline operates as a background enhancement. Content generated without human review. Players encounter it without editorial filter.
|
||||
|
||||
These are different safety models. Calling both an "authoring tool AND runtime enhancement" is misleading. Proposal C's "D-123 is fully superseded" language is rejected by Mellanie and Paula — the non-runtime constraints (culture vectors primary, no genre-convention defaults, authorial control binding) must survive the amendment.
|
||||
|
||||
**Mellanie's proposed amendment language (with broad support):**
|
||||
> *"D-123 is amended as follows: The AI pipeline is an authoring tool for content assembly AND a background runtime enhancement when AI-Enhanced Dialogue is enabled. All other constraints remain binding: culture vectors are the primary prompt constraint, the AI does not default to genre conventions, and authorial control governs what the LLM may and may not produce. The AI pipeline does not drive live narrative decisions — it applies voice to authored semantic content."*
|
||||
|
||||
---
|
||||
|
||||
### N-2: Anchor lines (D-092) need explicit passthrough protection
|
||||
|
||||
Paula: All three proposals protect tells. None addresses D-092 anchor lines, which are a separate protection class for Tier 1 and Tier 2 notable NPCs. Anchor lines must not be re-voiced regardless of whether they appear in `observable_behaviors` or dialogue pool.
|
||||
|
||||
**Proposed addition:** An `anchor_line: bool` flag on individual lines in the data model, in addition to the `tell_behaviors` field separation. Without this, baked hub content could have anchor lines re-voiced during the pre-voicing pass.
|
||||
|
||||
This is new scope not in any of the three proposals. It is a non-blocking addition to whichever proposal is chosen.
|
||||
|
||||
---
|
||||
|
||||
### N-3: Semantic core naming convention is disputed
|
||||
|
||||
Paula prefers **stimulus/response labels** over clinical psychology labels:
|
||||
|
||||
| Clinical label (Gestalt/Tyre/Mellanie) | Stimulus/response alternative (Paula) |
|
||||
|---|---|
|
||||
| `avoidance_behavior` | `changed_routine` |
|
||||
| `nervous_fidget` | `stress_physical_marker` |
|
||||
| `concealment_tell` | `information_protection` |
|
||||
| `relationship_avoidance` | `social_routing_change` |
|
||||
|
||||
Paula's argument: clinical labels risk surfacing verbatim in 2B output ("seems to be avoiding someone"), collapsing tell ambiguity. Stimulus/response labels constrain without naming the phenomenon.
|
||||
|
||||
Gestalt's response (implicit): precision is what a 2B model needs — abstract labels are unreliable, concrete phenomenon descriptions are reliable. Gestalt's actual proposal uses full constraint sentences, not bare labels: *"PRESERVE: forced calm. The NPC appears deliberately composed and unhurried. Must not show avoidance, fidgeting, or hurry."*
|
||||
|
||||
**For the record:** The tension may be partially semantic — both parties want the same behavior (phenomenon preserved, specific mechanism not named in output). The spike can settle it empirically by testing both label styles.
|
||||
|
||||
---
|
||||
|
||||
### N-4: Qwen2.5-3B may violate a project constraint
|
||||
|
||||
Troblum: The original `proposed-llm-voice.md` includes a constraint "no Meta/Chinese models." Qwen2.5-3B is an Alibaba (Chinese company) model. If this constraint remains in force, Qwen2.5-3B cannot be the Proposal C dialogue model candidate. The fallback is Phi-3-mini (3.8B, larger and slower), or the spike may prove Gemma 2B Q4 is sufficient for dialogue after all.
|
||||
|
||||
**Resolution needed:** Is the "no Chinese models" constraint still in force? No participant other than Troblum addressed this. Needs a decision from Jeroen or Gestalt before model selection for a C spike is finalized.
|
||||
|
||||
---
|
||||
|
||||
### N-5: Dialogue spike payload availability
|
||||
|
||||
Ozzie: A Proposal C spike requires dialogue samples with relationship state and access tier context. If the copy team is still writing base dialogue, the spike cannot test dialogue quality yet. This is a practical constraint that may determine whether C can be validated this sprint regardless of the architecture decision.
|
||||
|
||||
Paula or Mellanie should confirm whether dialogue base text samples exist at sufficient volume for a spike test payload.
|
||||
|
||||
---
|
||||
|
||||
### N-6: Model download strategy must be decided
|
||||
|
||||
Troblum: Bundling a 1.5 GB model in the base install creates distribution problems (itch.io 2 GB file limit, involuntary bandwidth for players who don't use the feature). Recommends: optional in-game download triggered on first "AI-Enhanced Dialogue" enable. Baked hub content ships in game data regardless — first hours are pre-voiced without any model download.
|
||||
|
||||
This is a product/distribution decision, not a technical one. It affects all three proposals equally but is most acute for Proposal C's dual-model scenario (3.5 GB total, potentially larger than the base game).
|
||||
|
||||
---
|
||||
|
||||
### N-7: Corrected Krenn injectors are ready
|
||||
|
||||
Mellanie delivered 8 corrected Krenn culture injector clauses in `mellanie-round2.md`. Round 1 blocker B-3 (the placeholder injector "formal, avoids contractions" was wrong) is **resolved**. The corrected injectors correctly capture Krenn's direct-informal, working-class register. These are v1 drafts for spike validation, not final copy.
|
||||
|
||||
---
|
||||
|
||||
## Remaining Blockers
|
||||
|
||||
**B-R2-01: D-123 amendment language must be finalized before any D-record is written.** Mellanie's proposed language has broad support; Paula's build-time/runtime mode distinction must be incorporated. Not blocking the spike design, but blocking the formal decision record.
|
||||
|
||||
**B-R2-02: Tell taxonomy enumeration required before semantic core labels can be authored (Proposal B path).** Mellanie needs: full tell category enumeration (5 from `tell_state.rs` but partial visibility), whether one category maps to one or multiple semantic cores, confirmed schema field name (`tell_behaviors`). This blocks Mellanie's Proposal B authoring work. Gestalt and Tyre have the answers and should provide them.
|
||||
|
||||
**B-R2-03: Qwen2.5-3B "no Chinese models" constraint question must be answered before Proposal C model selection.** Only relevant if C is chosen; does not block A or B.
|
||||
|
||||
**B-R2-04: Dialogue base text spike payload availability must be confirmed before a Proposal C spike can be scoped.** Relevant only if C is chosen.
|
||||
|
||||
---
|
||||
|
||||
## Spike Design Notes (for Round 3 reference)
|
||||
|
||||
If Proposal A or B is chosen:
|
||||
|
||||
| Criterion | Target |
|
||||
|---|---|
|
||||
| Oath vocabulary correct usage rate | >95% |
|
||||
| Register accuracy (blind review: "does this sound like Settled Reach?") | Qualitative, reviewer consensus |
|
||||
| Franchise bleed rate | <2% of outputs |
|
||||
| Proposal B: phenomenon-class preservation on tell re-voicing | >90% (Gestalt), >95% (Ozzie), >98% (Miri) |
|
||||
| Tell preservation success bar | **Must be defined before spike, not after** |
|
||||
|
||||
Gestalt proposes a specific B validation protocol: 12 tell types × 20 completions each, scored by phenomenon-class preservation via blind review. Target: ≥90% (Gestalt says this; Miri says 98%). **The threshold must be agreed before the spike runs.** If not met, Proposal A passthrough applies for tells; no other architecture change required.
|
||||
|
||||
---
|
||||
|
||||
## Summary: State of the Workshop
|
||||
|
||||
Round 2 produced a clean A/B vote split with no real blockers — participants favoring A are not opposed to B, and vice versa. The meaningful outcome is:
|
||||
|
||||
1. **Proposal C (full pipeline) is deferred to v0.3.** This is near-universal (6 of 7 participants). The vision is correct; the timing is not.
|
||||
2. **Proposal A is the safe, provable baseline.** Paula, Miri, Troblum favor it. Infrastructure is simple, spike is clean, tell safety is absolute.
|
||||
3. **Proposal B is a small step above A with meaningful payoff.** Gestalt, Tyre, Mellanie favor it. The additional complexity is bounded (~20 extra tokens per tell prompt, 12 authored constraint sentences), the tells become culturally distinctive, and the architecture is valid if the spike validates constrained re-voicing.
|
||||
4. **The real question is spike design, not architecture.** Both A and B use the same infrastructure. The only difference is whether tells go through the constrained re-voicing path (B) or passthrough (A). This can be made configurable — implement the architecture, let the spike result determine which tell path ships.
|
||||
|
||||
**The recommended synthesis:** Ship the two-track architecture (Proposal B data model: `tell_behaviors` field + `semantic_core`). Define the tell preservation threshold before the spike. If the spike hits the threshold, ship Proposal B tell re-voicing. If it misses, flip tells to passthrough (Proposal A behavior) with no architectural change. The architecture is B; the spike determines whether the constrained tell track is enabled.
|
||||
|
||||
---
|
||||
|
||||
*Qatux — 2026-03-07*
|
||||
@@ -0,0 +1,112 @@
|
||||
# LLM Voice Pipeline Workshop — Round 2 Proposals
|
||||
|
||||
**Compiled by:** Team Lead (synthesis of Round 1 outputs)
|
||||
**Round:** 2 — Convergent Evaluation
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## Preamble: What Round 1 Settled
|
||||
|
||||
All 7 participants favor Option 3 (LLM re-voicing). Round 2 does not revisit that choice. Instead, it presents three concrete architecture variants that resolve the open tensions from Round 1.
|
||||
|
||||
**D-123/D-124 framing note:** D-123 (2026-03-05) frames generative AI as "an authoring tool, not a runtime system." D-124 defers in-game live AI but explicitly "leaves the door open." All three proposals below walk through that door to varying degrees. Each proposal must state how it amends D-123 and supersedes D-124.
|
||||
|
||||
**Resolved inputs for all proposals:**
|
||||
- Runtime: `llama-cpp-rs` with GGUF Q4_K_M quantization (C-6)
|
||||
- Determinism: cache-as-determinism — generate once per seed, cache result (C-7)
|
||||
- Thread isolation: separate thread pool for inference vs. world generation (C-8)
|
||||
- Base text is the fallback and the LLM seed (C-5)
|
||||
- Existing zone RON content (~50 lines/role) survives as base text seeds (C-9)
|
||||
|
||||
---
|
||||
|
||||
## Proposal A: Conservative — Behaviors Only, Tells Locked
|
||||
|
||||
**Scope:** Re-voice observable behaviors only. Dialogue deferred to a future spike.
|
||||
|
||||
**Tell protection:** Base-text passthrough. Tells are never sent to the LLM. A new `tell_behaviors: Vec<String>` field is added to the NPC data model alongside `observable_behaviors`. The pipeline checks the field tag, not the content. Tell lines ship as-authored in all cases.
|
||||
|
||||
**Model:** Gemma 2 2B (Q4_K_M, ~1.5GB). Single candidate — no fallback model needed because behaviors are short-form (5-15 words) and the prompt is simple.
|
||||
|
||||
**Injector model:** Culture injector clauses (10-20 per culture) + trait modifier (1 per trait) + mood tag. Total injector budget: ~150 tokens. Authored by copy team, sourced from culture RON `speech` fields. Negative injectors required (explicit NOT-lists for lore contamination).
|
||||
|
||||
**Content tiers:**
|
||||
- Baked: Sova Transit District + other hub zones, pre-voiced at build time
|
||||
- Pre-voiced: background queue, prioritized by proximity and plot-criticality
|
||||
- Fallback: base text (always available, always complete)
|
||||
|
||||
**D-123 amendment:** D-123 is amended to "authoring tool AND background runtime enhancement." D-124 is superseded — this IS the in-game AI system, scoped to behaviors.
|
||||
|
||||
**Pros:** Smallest risk surface. Behaviors are short-form, easy to validate. Tell safety is absolute (passthrough). Spike is simple: one model, one prompt template, one content type.
|
||||
|
||||
**Cons:** Leaves dialogue scaling unsolved. Dialogue is arguably the higher-value target for re-voicing. May feel like a half-measure if ambient behaviors get culture voice but dialogue stays template-assembled.
|
||||
|
||||
---
|
||||
|
||||
## Proposal B: Two-Track — Behaviors + Tells with Semantic Core
|
||||
|
||||
**Scope:** Re-voice observable behaviors AND tell behaviors, with different pipelines. Dialogue deferred.
|
||||
|
||||
**Tell protection:** Constrained re-voicing via `semantic_core` tag (Gestalt's proposal). The `Tell` struct gains a `semantic_core: String` field that names the phenomenon the tell must preserve (e.g., `"avoidance_behavior"`, `"nervous_fidget"`, `"concealment_tell"`). The re-voicing prompt includes an explicit constraint: `PRESERVE: {semantic_core}. Culture-voice the expression, not the phenomenon.`
|
||||
|
||||
**Model:** Gemma 2 2B (Q4_K_M). Two prompt templates: free re-voicing (ambient behaviors) and constrained re-voicing (tells).
|
||||
|
||||
**Injector model:** Same as Proposal A (150 token budget), plus semantic core constraints for tells (~20 additional tokens per tell).
|
||||
|
||||
**Content tiers:** Same as Proposal A.
|
||||
|
||||
**D-123 amendment:** Same as Proposal A.
|
||||
|
||||
**Pros:** Tells get culture voice (a Krenn tell reads differently from a Sovari tell — richer world). The semantic core constraint is testable: spike can measure whether the phenomenon survives re-voicing. Two-track architecture is future-proof for dialogue.
|
||||
|
||||
**Cons:** Constrained re-voicing is harder to validate than passthrough. If the model fails to preserve the semantic core, the tell is corrupted — and the failure is subtle (not missing, just wrong). Requires the copy team to author semantic core labels for every tell type.
|
||||
|
||||
---
|
||||
|
||||
## Proposal C: Full Pipeline — Behaviors + Dialogue, Tells Locked
|
||||
|
||||
**Scope:** Re-voice observable behaviors AND dialogue. Tells pass through untouched (same as Proposal A).
|
||||
|
||||
**Tell protection:** Base-text passthrough (same as Proposal A). Tells are never re-voiced.
|
||||
|
||||
**Model:** Gemma 2 2B for behaviors; potentially Qwen2.5-3B (Q4, ~2.0GB) for dialogue if 2B quality is insufficient for longer-form output. Spike tests both on behaviors and dialogue separately.
|
||||
|
||||
**Dialogue re-voicing:** Dialogue lines already have access tier and trust tier tags (Paula's observation). The re-voicing prompt includes these as constraints alongside culture injectors. Dialogue is longer-form (15-40 words) and requires more prompt context (relationship state, conversation topic).
|
||||
|
||||
**Injector model:** Culture injectors (150 tokens) + dialogue context (relationship, access tier, trust tier — ~80 additional tokens). Total prompt budget ~400-500 tokens for dialogue.
|
||||
|
||||
**Content tiers:** Same as A, but baked content includes pre-voiced dialogue for hub NPCs.
|
||||
|
||||
**D-123 amendment:** D-123 is fully superseded. The AI pipeline is both an authoring tool and a runtime system. D-124 is superseded.
|
||||
|
||||
**Pros:** Solves the full content scaling problem in one architecture. Dialogue is where culture voice matters most to players (what NPCs SAY). Avoids the half-measure feeling of behaviors-only.
|
||||
|
||||
**Cons:** Larger spike scope. Dialogue quality at 2B may not meet the bar — may force a model size increase (3B) which tightens RAM. Two content types means two prompt templates, two validation passes, two quality bars. More can go wrong.
|
||||
|
||||
---
|
||||
|
||||
## Resolution Matrix
|
||||
|
||||
Each participant should evaluate all three proposals against their domain and answer:
|
||||
|
||||
| Question | Your answer |
|
||||
|----------|-------------|
|
||||
| Which proposal do you recommend? | A / B / C |
|
||||
| Are there blockers in your recommended proposal? | Yes/No + details |
|
||||
| Can you live with each of the other two proposals? | Yes/No per proposal |
|
||||
| What is the minimum change to your non-preferred proposals that would make them acceptable? | |
|
||||
|
||||
### Open questions to resolve in Round 2:
|
||||
|
||||
**Q-R1-01 (tell literacy model):** Is the player's tell literacy cross-NPC grammar or fresh-each-time? — Gestalt, answer this. It determines whether Proposal B's constrained re-voicing is safe.
|
||||
|
||||
**Q-R1-02 (scope):** Proposals A and B defer dialogue; Proposal C includes it. — Tyre, Paula, assess feasibility and quality risk for each.
|
||||
|
||||
**Q-R1-03 (tell data model):** All three proposals require `tell_behaviors` as a separate field. This is now a prerequisite, not a question. — Gestalt, Tyre, confirm this is implementable.
|
||||
|
||||
**Q-R1-04 (token budget):** 150 tokens for culture injectors in A/B, 400-500 for dialogue in C. — Miri, is 150 sufficient for cultural philosophy? Troblum, what's the throughput impact of 500-token prompts vs 150?
|
||||
|
||||
**Q-R1-05 (minimum CPU spec):** This workshop cannot define it — it's a product decision. For Round 2, assume: 4-core CPU from 2019 or later (e.g., Intel i5-9400, Ryzen 5 3600). — Troblum, is this sufficient for Gemma 2B Q4 background inference?
|
||||
|
||||
**D-123 tension (Mellanie):** All three proposals amend or supersede D-123. — Mellanie, is the proposed amendment language acceptable? Paula, does this conflict with any narrative architecture constraints?
|
||||
@@ -0,0 +1,52 @@
|
||||
# LLM Voice Pipeline Workshop — Round 3 Inputs
|
||||
|
||||
**Source:** Team lead interview with Jeroen after Rounds 1-2
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## Jeroen's Decisions
|
||||
|
||||
These are binding inputs for Round 3. The team produces the D-record and implementation plan around these.
|
||||
|
||||
### 1. Scope: Behaviors + Dialogue (full pipeline)
|
||||
"We don't introduce a precision laser cutting tool and then use it only to open boxes." Both observable behaviors AND dialogue get re-voiced. This is the long-term architecture.
|
||||
|
||||
### 2. Tell Treatment: Passthrough with Context Influence
|
||||
Tells are mechanical signals, not culture. They stay as base text — always. Swapping them confuses the player.
|
||||
|
||||
However, tells INFORM the LLM context for dialogue and behavior. When a player engages an NPC who has an avoidance tell, the NPC's dialogue should be phrased in an avoiding way. The tell itself is untouched; the tell's presence shapes the re-voicing prompt for surrounding content.
|
||||
|
||||
This is a critical distinction: tells are read-only inputs to the LLM, never LLM outputs.
|
||||
|
||||
### 3. Spike Strategy: Two Spikes
|
||||
**Spike 1 — Plumbing + Quality (no integration):**
|
||||
- Build the Rust llama-cpp-rs wrapper. Load Gemma 2B and Phi-3. Prove the plumbing works: accept prompt, return text.
|
||||
- Then Jeroen, Mellanie, and Paula manually craft prompts — culture injectors, behavior seeds, dialogue seeds — and feed them through by hand.
|
||||
- Test both models against the same prompts. Answer the question: "does this even play?"
|
||||
- No game integration, no queue, no cache. Just the inference tool and manual prompt experimentation.
|
||||
|
||||
**Spike 2 — Integration:**
|
||||
- Wire the validated runner into the pre-voicing pipeline.
|
||||
- Queue, cache-as-determinism, thread pool isolation, baked content generation, fallback behavior.
|
||||
- The full architecture as designed by the team.
|
||||
- Uses whichever model won Spike 1.
|
||||
|
||||
### 4. D-123 Amendment
|
||||
Amend D-123 to cover both baked (build-time, human-reviewed) and pre-voiced (runtime background) modes. Supersede D-124. Paula's distinction between the two modes must be explicit in the amended record.
|
||||
|
||||
### 5. Model Provenance
|
||||
Strong preference against Chinese-origin models (Qwen/Alibaba). Gemma (Google) is the primary candidate. Phi (Microsoft) is the fallback. Reconsider the constraint only if benchmarks on both fail to meet the quality bar.
|
||||
|
||||
### 6. Distribution: Bundled
|
||||
Model ships with the game install. No optional download step. ~1.5GB added to install size is acceptable.
|
||||
|
||||
### 7. Hardware Detection: Layered
|
||||
- Layer 1: CPU/RAM check — can the model even load?
|
||||
- Layer 2: Time-per-token benchmark on first enable — is inference fast enough to be useful?
|
||||
- Layer 3: Recommendation to disable if below threshold, but player can always override
|
||||
- No hard minimum spec floor. If they're patient, let them run it.
|
||||
- Always an option to disable ("AI-Enhanced Dialogue" toggle).
|
||||
|
||||
### 8. No Minimum Spec Floor
|
||||
The question isn't "what hardware do we refuse to run on" — it's "when do we recommend turning it off." The system runs on anything that passes the RAM check; the recommendation threshold handles the rest.
|
||||
@@ -0,0 +1,76 @@
|
||||
# LLM Voice Pipeline Workshop — Ticket Summary
|
||||
|
||||
**Created by:** SI (Sprint Prep Agent)
|
||||
**Date:** 2026-03-07
|
||||
**Source:** round-3-inputs.md (Jeroen's binding decisions) + workshop team outputs
|
||||
**Epic:** [#638 LLM Voice Pipeline](../../../tooling/db/ticket)
|
||||
|
||||
---
|
||||
|
||||
## Tickets Created
|
||||
|
||||
### Epic
|
||||
|
||||
| # | Title | Team | Priority |
|
||||
|---|-------|------|----------|
|
||||
| 638 | LLM Voice Pipeline | server | high |
|
||||
|
||||
### Stories & Tasks
|
||||
|
||||
| # | Type | Title | Team | Priority | Blocked By |
|
||||
|---|------|-------|------|----------|------------|
|
||||
| 639 | story | Spike 1: Build llama-cpp-rs inference CLI | server | high | — |
|
||||
| 640 | story | Spike 1: Manual prompt quality session | copy | high | #639 |
|
||||
| 641 | story | Spike 2: Pre-voicing pipeline integration | server | high | #639, #640, #642 |
|
||||
| 642 | task | Data model: Add tell_behaviors field to NpcBlueprint | server | high | — |
|
||||
| 643 | story | Content: Krenn culture injector clauses | copy | high | — |
|
||||
| 644 | story | Content: Universal negative injectors (NI-1 through NI-5) | copy | medium | — |
|
||||
| 645 | story | Content: Base text elevation pass | copy | medium | — |
|
||||
| 646 | story | UX: AI-Enhanced Dialogue toggle + hardware detection | client | medium | #641 |
|
||||
| 647 | task | Docs: Amend D-123, supersede D-124, file workshop D-records | copy | medium | — |
|
||||
|
||||
**Cancelled:** #623 (AI content templating pipeline — stale placeholder, superseded by #638)
|
||||
|
||||
---
|
||||
|
||||
## Dependency Graph
|
||||
|
||||
```
|
||||
[#642 tell_behaviors data model]
|
||||
│
|
||||
[#643 Krenn injectors] ─────┤
|
||||
│ ▼
|
||||
└──► [#640 Spike 1: copy] ◄── [#639 Spike 1: server]
|
||||
│
|
||||
▼
|
||||
[#641 Spike 2: integration]
|
||||
│
|
||||
▼
|
||||
[#646 UX: toggle + hw detect]
|
||||
|
||||
Independent (no blockers): #644, #645, #647
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Decisions Captured
|
||||
|
||||
| Decision | Ticket(s) |
|
||||
|----------|-----------|
|
||||
| Full pipeline: behaviors + dialogue both re-voiced | #638, #641 |
|
||||
| Tells: passthrough always, inform context as read-only | #642 |
|
||||
| Two-spike strategy | #639, #640, #641 |
|
||||
| Model: Gemma 2B primary, Phi-3 fallback (no Qwen/Alibaba) | #639, #640 |
|
||||
| Distribution: bundled (~1.5GB, no optional download) | #638 |
|
||||
| Hardware detection: layered, no hard floor | #646 |
|
||||
| AI-Enhanced Dialogue player toggle | #646 |
|
||||
| D-123 amended (baked + pre-voiced), D-124 superseded | #647 |
|
||||
|
||||
---
|
||||
|
||||
## Sprint Assignment
|
||||
|
||||
These tickets are **backlog** — not yet assigned to a sprint. Recommended for **Sprint 26** given Sprint 25 is the generator spike (critical path continues). Exception: #647 (D-record filing) can be done on the copy branch immediately.
|
||||
|
||||
Critical path for Sprint 26: **#639 → #640/#642 → #641 → #646**
|
||||
Parallel copy work: **#643, #644, #645** (unblocked, can run with Spike 1 server work)
|
||||
@@ -0,0 +1,283 @@
|
||||
# Troblum — Round 1: Infrastructure & Performance Inventory
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Infrastructure / Performance
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## Preamble
|
||||
|
||||
I read the full brief, the proposed architecture document, the generator spike output, the blueprint structs, both zone RON files, the culture file, and the relevant decisions. I have numbers. The numbers are not all good. Here is what I found.
|
||||
|
||||
---
|
||||
|
||||
## 1. Which Option Is Viable From an Infrastructure Perspective?
|
||||
|
||||
**Option 1 (hand-authored):** Infrastructure cost is zero. The scaling problem is authoring bandwidth, not compute. No runtime risk. This is the "safe" answer that produces the content wall the workshop exists to solve.
|
||||
|
||||
**Option 2 (composable primitives):** Infrastructure cost is also near-zero. String assembly at runtime is trivially cheap — microseconds per call, no memory overhead worth measuring. The complexity lives in the composition engine logic, not the hardware. Infrastructure has no objection here.
|
||||
|
||||
**Option 3 (LLM re-voicing):** Infrastructure has strong opinions, and they are conditional. This option is viable ONLY if the model size and inference runtime are chosen correctly. The margin for error is real. Details follow.
|
||||
|
||||
---
|
||||
|
||||
## 2. Memory Budget: 2B Model on 8GB RAM Shared With the Game
|
||||
|
||||
This is the most load-bearing constraint and the brief is too vague about it. Here is what I can say precisely:
|
||||
|
||||
### Model memory footprint at different quantization levels
|
||||
|
||||
| Model | Params | FP16 RAM | INT8 RAM | Q4_K_M RAM |
|
||||
|-------|--------|----------|----------|------------|
|
||||
| Gemma 2B | 2.5B | 5.0 GB | 2.5 GB | ~1.5 GB |
|
||||
| Phi-3-mini | **3.8B** | 7.6 GB | 3.8 GB | ~2.2 GB |
|
||||
| SmolLM2-1.7B | 1.7B | 3.4 GB | 1.7 GB | ~1.0 GB |
|
||||
| Qwen2.5-1.5B | 1.5B | 3.0 GB | 1.5 GB | ~0.9 GB |
|
||||
|
||||
**FP16 and INT8 are not viable for minimum-spec hardware.** At 8GB total RAM with a game running, you need Q4 quantization (GGUF format) as a hard requirement. No exceptions.
|
||||
|
||||
### Game RAM baseline at minimum spec
|
||||
|
||||
- OS overhead: ~1.0–1.5 GB (Windows 10 minimum)
|
||||
- Godot 4 client: ~400–700 MB (scene tree, textures, audio)
|
||||
- Rust server subprocess: ~150–300 MB (ECS world, simulation state)
|
||||
- Lazy world generation working set: ~100–200 MB (chunk buffers)
|
||||
- **Available headroom: ~5.3–6.3 GB**
|
||||
|
||||
At Q4_K_M, Gemma 2B (~1.5 GB) fits comfortably. Phi-3-mini at Q4 (~2.2 GB) also fits but leaves less margin. Inference also requires a KV cache during generation — at typical context lengths of 512 tokens, this adds ~50–100 MB. So the full runtime cost of Gemma 2B Q4 is approximately **1.6 GB**, and Phi-3-mini Q4 is approximately **2.3 GB**.
|
||||
|
||||
Both fit. But Phi-3-mini leaves ~100–200 MB less margin for memory spikes during zone transitions when both world generation and inference might be active simultaneously.
|
||||
|
||||
### VRAM on integrated GPUs
|
||||
|
||||
This question contains a false premise. Integrated GPUs (Intel UHD, AMD Radeon integrated) **do not have dedicated VRAM**. They operate under Unified Memory Architecture (UMA) — iGPU and CPU share the same physical RAM pool. The "8 GB RAM + integrated GPU" configuration means the same 8 GB pool is divided between everything.
|
||||
|
||||
Practical implication: **model offloading to iGPU does not free up RAM; it consumes more of the same RAM** through graphics/compute allocation. On Windows, iGPU typically carves out 512 MB–2 GB for its driver state. Factor this into your budget.
|
||||
|
||||
For discrete GPU at minimum spec (e.g., 4 GB VRAM budget laptop): full model offload to VRAM at Q4 is feasible for Gemma 2B and leaves CPU memory largely untouched. This is the best case scenario and not what minimum-spec means for our purposes.
|
||||
|
||||
**Conclusion:** We are designing for CPU-only inference at Q4 quantization. All performance estimates below assume this.
|
||||
|
||||
---
|
||||
|
||||
## 3. Inference Latency: What Throughput Can We Expect?
|
||||
|
||||
### CPU-only, Q4_K_M quantization
|
||||
|
||||
Per-token generation speed depends primarily on CPU memory bandwidth and available cores. Real benchmarks from llama.cpp community testing (the only realistic runtime option — see section 6):
|
||||
|
||||
| Hardware class | CPU example | Tokens/sec (Q4_K_M, 2B model) |
|
||||
|----------------|-------------|-------------------------------|
|
||||
| 2022+ mid-range | Core i5-1235U (10 core, AVX2) | 8–14 t/s |
|
||||
| 2019–2021 mid-range | Core i5-8265U (4 core, AVX2) | 5–8 t/s |
|
||||
| 2017–2019 budget | Core i3-7100U (2 core, AVX2) | 3–5 t/s |
|
||||
| Minimum conceivable | Core i3-6006U (2 core, no AVX2) | 1–3 t/s |
|
||||
|
||||
At 3 tokens/sec (minimum viable), generating a 30-token voiced behavior line requires 10 seconds. At 10 tokens/sec, that's 3 seconds.
|
||||
|
||||
### Task volume estimate for background pre-voicing
|
||||
|
||||
From the zone specs, a typical zone has 4 roles × several NPCs. At population_density 2–6, the generator produces 2–12 NPCs per zone. If each NPC gets 2 behavior lines of ~30 tokens output each, with a 150-token input prompt (system prompt + injectors + semantic line):
|
||||
|
||||
| Zone size | NPCs | Lines | Q4 @ 5 t/s | Q4 @ 3 t/s |
|
||||
|-----------|------|-------|------------|------------|
|
||||
| Rural (density 2) | 2–4 | 8–16 | ~1–3 min | ~2–5 min |
|
||||
| Industrial (density 6) | 6–12 | 24–48 | ~3–10 min | ~5–16 min |
|
||||
|
||||
**This is workable IF the player spends >5 minutes per zone**, which is consistent with the immersive-sim design. It is NOT workable if zone transitions happen quickly (sprint-through navigation, fast travel, etc.).
|
||||
|
||||
On minimum-conceivable hardware at 1–2 t/s, even a small zone may never finish pre-voicing before the player leaves. That player will always see base text. This must be acceptable — and the proposal says it is (graceful fallback). Fine. But "AI-Enhanced Dialogue" as a toggle will be effectively non-functional on that hardware class regardless of the setting.
|
||||
|
||||
### iGPU acceleration via Vulkan
|
||||
|
||||
llama.cpp supports Vulkan for GPU-accelerated inference. On Intel UHD Graphics (integrated), partial layer offloading (8–16 layers of a 28-layer 2B model) can yield 1.5–2× speedup. This brings 3 t/s → 5–6 t/s on otherwise-marginal hardware. Not guaranteed, requires driver support, and adds build complexity (Vulkan SDK dependency, shader compilation).
|
||||
|
||||
**Verdict:** Vulkan GPU acceleration on iGPU is worth investigating for a later optimization pass, but do not build the queue scheduler assuming it will be available. Design for CPU-only, treat iGPU as a bonus.
|
||||
|
||||
---
|
||||
|
||||
## 4. Binary Size: Bundled Model + Inference Runtime
|
||||
|
||||
This is the number I'm most concerned about, and the proposal document does not address it directly.
|
||||
|
||||
### Inference runtime overhead
|
||||
|
||||
- llama.cpp compiled as shared library: ~8–20 MB depending on feature flags (BLAS, CUDA, Vulkan backends)
|
||||
- Rust bindings (`llama-cpp-rs` or equivalent): ~2–5 MB additional
|
||||
- No runtime JVM, Python interpreter, or other large runtimes
|
||||
|
||||
Runtime overhead is acceptable: **~15–25 MB added to install.**
|
||||
|
||||
### Model file size
|
||||
|
||||
| Model | GGUF Q4_K_M |
|
||||
|-------|-------------|
|
||||
| Gemma 2B | ~1.5 GB |
|
||||
| Phi-3-mini (3.8B) | ~2.2 GB |
|
||||
| SmolLM2-1.7B | ~1.0 GB |
|
||||
|
||||
For reference: typical indie game install sizes are 2–8 GB. Adding 1–2 GB for the AI model is a **25–100% install size increase** on the low end. For Steam and GOG distribution this is uncomfortable but not impossible. For itch.io with a free tier, 2 GB per file upload is a hard limit.
|
||||
|
||||
Mitigation options:
|
||||
1. **Separate DLC/download:** Ship game without model, offer it as a free optional download for "AI-Enhanced Dialogue." Preserves base install size. Adds post-install friction.
|
||||
2. **Streaming download on first enable:** Player enables the toggle; game downloads the model on demand. Breaks the "no cloud" guarantee from the proposal.
|
||||
3. **Accept the size:** Bundle the model, ship it as part of the installer. No user friction, single package. Increases minimum download by ~1.5 GB.
|
||||
|
||||
Option 3 is the cleanest from a player experience standpoint. Whether the project is willing to accept that distribution overhead is a business decision, not a technical one. I note it here because the proposal doesn't mention it at all.
|
||||
|
||||
---
|
||||
|
||||
## 5. The Pre-Voicing Queue: Priority Scheduling With Lazy World Generation
|
||||
|
||||
### The concurrency problem
|
||||
|
||||
Both systems are CPU-bound and memory-bandwidth-heavy:
|
||||
- **World generation:** procedural computation, RON deserialization, ECS entity creation. Not trivially parallelizable with inference.
|
||||
- **LLM inference:** sequential token generation, constant streaming of model weights through CPU cache. Cache-evicting everything else in L3 during a full inference pass.
|
||||
|
||||
These two workloads in the same thread pool will contend for:
|
||||
- L3 cache (inference evicts world-gen data; world-gen thrash reloads model weights mid-inference)
|
||||
- Memory bandwidth (DDR bandwidth is a shared resource; both saturate it)
|
||||
|
||||
**Recommendation: separate thread pools with explicit priority control.**
|
||||
|
||||
```
|
||||
Thread pool A (world generation): 2–4 threads, standard priority
|
||||
Thread pool B (LLM inference): 1 thread, below-normal OS priority
|
||||
```
|
||||
|
||||
One inference thread is correct. llama.cpp parallelizes across CPU cores internally via thread count parameter. Set llama.cpp threads to physical_cores - world_gen_threads.
|
||||
|
||||
### Resource contention risks
|
||||
|
||||
1. **Zone transition spike:** Player moves between zones. World generation fires (new district skeleton, NPC spawn, asset loading). Simultaneously, the inference queue for the new zone fires. Both peak at the same moment. Mitigation: pause the inference queue during active zone transitions. Resume after the world generation burst subsides (detectable via a "zone settled" signal from the world gen system).
|
||||
|
||||
2. **Thermal throttling on laptops:** Sustained LLM inference at 100% CPU generates heat. On thin laptops with aggressive thermal throttle (common on minimum-spec hardware), inference speed degrades over time. A pre-voicing session that benchmarks at 6 t/s at minute 1 may be running at 3 t/s by minute 5 due to throttle. Budget accordingly; add 50% latency margin to all estimates.
|
||||
|
||||
3. **Low battery / power saver mode:** Windows and macOS aggressively throttle CPU on battery at power saver settings. Inference tokens/sec can drop by 60–70% in these modes. The inference queue must detect this and pause. A simple mechanism: monitor time-per-token; if it exceeds a threshold (e.g., 1 second/token), suspend inference and set a flag for the player.
|
||||
|
||||
### Queue integration with the existing lazy world gen pipeline
|
||||
|
||||
The proposal describes anticipation-based pre-voicing: when the player signals intent to move to a new area, the queue populates. This is the same signal that triggers lazy world generation. Both systems want to act on the same event.
|
||||
|
||||
I'd recommend that world generation feeds the voicing queue as a subscriber: world gen completes a zone's NPC generation → publishes `ZonePopulated` event → voicing queue picks up the NpcBlueprint list and schedules voicing tasks. This avoids the voicing queue having to separately track which zones are generated.
|
||||
|
||||
---
|
||||
|
||||
## 6. Rust Inference Wrapper: Crate Candidates
|
||||
|
||||
The proposal says "lightweight Rust wrapper, not ollama." Correct instinct. Here are the options with honest assessments:
|
||||
|
||||
| Crate | Backend | Status | Notes |
|
||||
|-------|---------|--------|-------|
|
||||
| `llama-cpp-rs` | llama.cpp (C FFI) | Active, maintained | Best CPU performance, GGUF support, quantization. Build complexity: requires C compiler, large build. |
|
||||
| `llama-cpp-2` | llama.cpp (C FFI) | Active | Alternative binding, similar profile. |
|
||||
| `candle` | Pure Rust | Active (HuggingFace) | No C dependency. Performance: 2–3× slower than llama.cpp for CPU inference. No GGUF native support — requires safetensors format. Bigger models in RAM (no quantization parity with GGUF). |
|
||||
| `burn` | Pure Rust | Research-grade | Not production-viable for this use case. |
|
||||
| `ort` | ONNX Runtime | Active | Requires ONNX model conversion. Good performance via optimized runtime. Adds 50–100 MB ONNX runtime dependency. |
|
||||
| `llm` (Rustformers) | Custom | **Abandoned** | Do not use. Last commit 2023. |
|
||||
|
||||
**My recommendation: `llama-cpp-rs` (or `llama-cpp-2`) against a pinned llama.cpp version.**
|
||||
|
||||
The performance gap with candle is too wide to accept given the minimum-spec constraints. At 3 t/s on minimum spec with llama.cpp, candle would put us at 1–1.5 t/s — non-functional for any background generation purpose. Pure Rust is a nice property; usable inference speed is a required property.
|
||||
|
||||
Startup cost: llama.cpp model load from GGUF on cold start is 2–5 seconds for a 2B model from SSD. Factor this into the first-run experience. The model should be loaded lazily (on first inference request) not at game startup.
|
||||
|
||||
---
|
||||
|
||||
## 7. Cache Size Estimates Per Seed
|
||||
|
||||
Addressed directly: **cache size is not a concern.**
|
||||
|
||||
Voiced text is just text. Average voiced line: ~50 bytes. Generous estimate for a full playthrough:
|
||||
|
||||
- 100 zones × 20 NPCs × 5 behavior lines per NPC = 10,000 lines
|
||||
- At 80 bytes average (line + metadata key): **~800 KB per seed**
|
||||
|
||||
Even at 10 seeds cached: ~8 MB. SQLite with one row per (seed, zone_id, npc_stable_id, behavior_id, injector_hash) → voiced_text would handle this trivially.
|
||||
|
||||
Cache invalidation:
|
||||
- Seed changes → full cache for that seed is stale. Drop the seed's partition.
|
||||
- Culture mod changes → hash the injector set per NPC; if injector hash changes, invalidate that NPC's lines.
|
||||
- Game version changes → bundle a format version tag; on version mismatch, full regeneration.
|
||||
|
||||
The cache can live in the user's local data directory alongside save files. No special handling needed.
|
||||
|
||||
**Hub pre-baked content shipped in the game binary:** Same math, smaller scope. 5 hub zones × 30 NPCs × 10 lines = 1,500 lines × 80 bytes = **120 KB**. Negligible. Ship it as a compressed asset bundle.
|
||||
|
||||
---
|
||||
|
||||
## 8. What Breaks If We Choose the Wrong Option?
|
||||
|
||||
### If we choose Option 3 and make wrong infrastructure decisions:
|
||||
|
||||
**Wrong model size (too large):** Phi-3-mini at FP16 on a minimum-spec 8GB machine = OOM. Game crash or forced kill of inference process. Player experience: game appears to freeze, then silently disables AI dialogue without explanation. This is the worst failure mode. Guard against it by enforcing Q4 quantization as the only supported format and documenting a VRAM floor check at feature enable.
|
||||
|
||||
**Wrong inference runtime (candle instead of llama.cpp):** Halved throughput means the feature is effectively non-functional on hardware below the median spec. Players will enable "AI-Enhanced Dialogue" and see no improvement because the queue never catches up. They will correctly conclude the feature is broken. Ship llama.cpp bindings or don't ship the feature.
|
||||
|
||||
**No thread pool isolation:** Inference runs in the same pool as world generation. During zone transitions, both peak simultaneously. Frame time spikes. Stutters. On minimum-spec hardware, this is audible as audio dropout (Godot's audio thread starved by CPU contention). Players will perceive this as a game bug, not an AI feature.
|
||||
|
||||
**No thermal/power awareness in the queue:** Sustained inference on a throttled laptop CPU generates noise (fan) and reduces battery life visibly. Players disable the feature not because of quality but because their laptop gets hot. Addressable with simple token-rate monitoring and auto-suspend.
|
||||
|
||||
**No battery/power-saver detection:** At minimum, suspend inference when Windows reports power saver mode or when time-per-token exceeds 2 seconds.
|
||||
|
||||
### If we choose Option 2 (composable primitives):
|
||||
|
||||
Infrastructure risk is near-zero. Quality risk is Mellanie and Paula's problem. From my seat: no objection.
|
||||
|
||||
### If we choose Option 1 (hand-authored):
|
||||
|
||||
Infrastructure risk is zero. Content authoring is the only wall. Not my domain.
|
||||
|
||||
---
|
||||
|
||||
## 9. The Phi-3-Mini Problem
|
||||
|
||||
The proposal document lists "Phi-3-mini" as a candidate alongside Gemma 2B and calls them both "2B class." This is incorrect.
|
||||
|
||||
Phi-3-mini is **3.8 billion parameters** — nearly 2× the size of Gemma 2B. At Q4_K_M, it requires ~2.2 GB RAM versus Gemma 2B's ~1.5 GB. At FP16, Phi-3-mini requires 7.6 GB — functionally all of available RAM on a minimum-spec machine before the OS, game, or any other process has touched it.
|
||||
|
||||
More relevantly: Phi-3-mini also runs at 60–70% of Gemma 2B's inference speed on equivalent hardware due to higher parameter count. On minimum-spec hardware, this is the difference between "background generation keeps up" and "background generation never finishes."
|
||||
|
||||
Phi-3-mini's advantage is quality — it genuinely outperforms Gemma 2B on instruction following, which matters for multi-constraint injector prompts. The tradeoff is real. But calling them both "2B class" and treating them as equivalent candidates for a minimum-spec hardware budget is a mistake that will produce surprising results in the spike.
|
||||
|
||||
The spike should test both on actual minimum-spec hardware (or hardware equivalent), not just benchmarked on developer machines.
|
||||
|
||||
---
|
||||
|
||||
## 10. One Question I Need Answered Before Committing
|
||||
|
||||
**What is the exact CPU specification for minimum-spec hardware?**
|
||||
|
||||
The brief says "8 GB RAM, integrated GPU." It does not define the CPU. This is not a minor detail.
|
||||
|
||||
The difference between an Intel Core i3-7100U (2017, 2 cores, AVX2) and a Core i5-1235U (2022, 10 cores, AVX2) is 3× in inference throughput at Q4. On the i3-7100U, Gemma 2B at 3 t/s means a 12-NPC rural zone takes ~10 minutes to pre-voice. On the i5-1235U, it takes ~3 minutes. One of those is "fine for background pre-voicing while the player is active in a zone." The other one isn't.
|
||||
|
||||
The answer to this question determines:
|
||||
- Whether Gemma 2B is our ceiling or our floor
|
||||
- Whether we need a 1B-class model (SmolLM2-1.7B or similar) to hit functional performance on minimum hardware
|
||||
- Whether iGPU acceleration via Vulkan is worth the engineering investment
|
||||
- How aggressively the queue scheduler needs to pace itself
|
||||
|
||||
Until this is defined, all infrastructure feasibility assessments for Option 3 are conditional. The framework is sound. The numbers are good given specific hardware. I need the hardware defined.
|
||||
|
||||
---
|
||||
|
||||
## Summary Table
|
||||
|
||||
| Concern | Option 1 | Option 2 | Option 3 (LLM) |
|
||||
|---------|----------|----------|----------------|
|
||||
| RAM impact | None | None | +1.5–2.2 GB at Q4 |
|
||||
| Install size impact | None | None | +1.0–2.2 GB |
|
||||
| Inference latency | N/A | Microseconds | 3–14 t/s (CPU, Q4) |
|
||||
| Zone pre-voice time | N/A | Instant | 1–16 min depending on hardware |
|
||||
| Thread contention risk | None | None | High without isolation |
|
||||
| Thermal/battery risk | None | None | Real on minimum-spec laptops |
|
||||
| Infrastructure risk | None | None | Conditional (manageable with right choices) |
|
||||
|
||||
Option 3 is not a "just add an LLM" decision. It is a **systems engineering problem** that requires: specific model selection validated on actual minimum-spec hardware, a correctly isolated inference thread pool, power-state awareness in the queue scheduler, and a distribution strategy for the model file. None of these are unsolvable. All of them require explicit design decisions before I can sign off.
|
||||
|
||||
---
|
||||
|
||||
*Troblum, 2026-03-07*
|
||||
@@ -0,0 +1,230 @@
|
||||
# Troblum — Round 2: Proposal Evaluation & Infrastructure Answers
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Infrastructure / Performance
|
||||
**Round:** 2 — Convergent Evaluation
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## Q-R1-04: Throughput Impact of 150-token vs. 500-token Prompts
|
||||
|
||||
The question is precise; the answer requires splitting inference into its two phases.
|
||||
|
||||
### Phase 1: Prefill (prompt processing)
|
||||
|
||||
Prefill is parallel — all prompt tokens processed simultaneously across available CPU cores. On 4-6 core AVX2 hardware from 2019+, prefill throughput for a 2B Q4 model runs approximately 300–600 tokens/sec.
|
||||
|
||||
| Prompt length | Prefill time (i5-9400 class) |
|
||||
|---------------|------------------------------|
|
||||
| 150 tokens | 0.25–0.5 seconds |
|
||||
| 500 tokens | 0.85–1.7 seconds |
|
||||
|
||||
The prefill penalty for 500-token vs. 150-token prompts: approximately **0.6–1.2 seconds per call**.
|
||||
|
||||
### Phase 2: Generation (decode)
|
||||
|
||||
Generation is sequential — one token at a time, memory-bandwidth limited. On the i5-9400 class at Gemma 2B Q4_K_M, decode runs 7–9 tokens/sec (see Q-R1-05 below for full derivation). Prompt length does not affect decode speed — only output length matters.
|
||||
|
||||
| Output length | Generation time @ 8 t/s |
|
||||
|---------------|--------------------------|
|
||||
| 30 tokens (behavior line) | 3.75 seconds |
|
||||
| 50 tokens (dialogue line) | 6.25 seconds |
|
||||
|
||||
### Total per-task comparison
|
||||
|
||||
| Task type | Prompt | Output | Total @ i5-9400 |
|
||||
|-----------|--------|--------|-----------------|
|
||||
| Behavior (Proposal A/B) | 150 t | 30 t | **4.0–4.5 seconds** |
|
||||
| Dialogue (Proposal C) | 500 t | 50 t | **7.1–8.0 seconds** |
|
||||
|
||||
**Dialogue tasks take approximately 1.8–2× longer than behavior tasks.** The multiplier is driven mostly by longer output (50 vs. 30 tokens) with the longer prompt adding a secondary fixed cost per call.
|
||||
|
||||
### Impact on zone pre-voicing time
|
||||
|
||||
Proposal A (behaviors only, i5-9400, 8 t/s average):
|
||||
- Rural zone: 4 NPCs × 3 behaviors × 4.2s = **~50 seconds**
|
||||
- Industrial zone: 10 NPCs × 3 behaviors × 4.2s = **~2.1 minutes**
|
||||
|
||||
Proposal C (behaviors + dialogue, same hardware, behaviors at 4.2s, dialogue at 7.5s):
|
||||
- Assume 3 behavior lines + 4 dialogue lines per NPC
|
||||
- Rural zone: 4 NPCs × (3 × 4.2s + 4 × 7.5s) = 4 × (12.6 + 30) = **~170 seconds (~2.8 min)**
|
||||
- Industrial zone: 10 NPCs × 42.6s = **~7.1 minutes**
|
||||
|
||||
The industrial zone at 7 minutes is at the edge of comfortable for background pre-voicing. If the player spends at least 10 minutes in a zone (likely for plot-critical locations), pre-voicing completes before meaningful NPC interaction. For transit zones the player moves through quickly, it will not catch up — base text fallback will be visible.
|
||||
|
||||
This is a workable design IF the queue prioritizes by interaction likelihood, not just plot criticality. If the player sprints through an industrial zone to reach a specific NPC, that NPC's lines must be in the P0 queue. P2 ambient NPCs in the same zone can remain unvoiced without player impact.
|
||||
|
||||
---
|
||||
|
||||
## Q-R1-05: Is 4-Core 2019+ CPU Sufficient for Gemma 2B Q4?
|
||||
|
||||
**Short answer: Yes. With specific numbers.**
|
||||
|
||||
### Derivation for i5-9400
|
||||
|
||||
The bottleneck for llama.cpp decode on CPU is memory bandwidth. Each decode step reads the full set of model weights.
|
||||
|
||||
- Gemma 2B Q4_K_M weight size on-disk and in-RAM: ~1.5 GB
|
||||
- Intel i5-9400 memory bandwidth: DDR4-2666 dual-channel ≈ 42 GB/s
|
||||
- Theoretical tokens/sec: 42 GB/s ÷ 1.5 GB = **28 tokens/sec** (theoretical ceiling)
|
||||
- Real-world efficiency factor (cache pressure, OS overhead, threading): ~25–35%
|
||||
- **Estimated real-world decode speed: 7–10 tokens/sec**
|
||||
|
||||
### Derivation for Ryzen 5 3600
|
||||
|
||||
- Memory bandwidth: DDR4-3200 dual-channel ≈ 51 GB/s
|
||||
- Theoretical ceiling: 51 ÷ 1.5 = 34 tokens/sec
|
||||
- Same efficiency factor: **~8–12 tokens/sec**
|
||||
|
||||
Additionally: the Ryzen 5 3600 has a 32 MB L3 cache. For a 1.5 GB model, L3 caching has minimal impact on decode (model weights far exceed L3 capacity). The bandwidth advantage is the real differentiator.
|
||||
|
||||
**Working estimates:** i5-9400: **7–9 t/s**. Ryzen 5 3600: **9–12 t/s**.
|
||||
|
||||
### Is this sufficient?
|
||||
|
||||
Yes. At 8 t/s (midpoint for i5-9400):
|
||||
|
||||
| Zone | NPCs | Pre-voice time (behaviors only) | Pre-voice time (behaviors + dialogue) |
|
||||
|------|------|--------------------------------|---------------------------------------|
|
||||
| Rural (density 2) | 2–4 | 25–50 sec | 85–170 sec |
|
||||
| Industrial (density 6) | 6–12 | 75–150 sec | 250–510 sec |
|
||||
|
||||
A player spending 3+ minutes in any zone will have behavior pre-voicing complete before meaningful NPC interaction. This is a comfortable margin for immersive-sim play patterns.
|
||||
|
||||
**One caveat:** these are desktop CPUs. The i5-9400 is a 65W chip with no power-management constraints in normal operation. If the minimum-spec assumption includes **laptops** with throttled performance (45W TDP, thermal limits), effective decode speed drops to 4–6 t/s. At 4 t/s, industrial zone pre-voicing (behaviors only) takes 5 minutes — still workable but tight. This is the scenario where thermal monitoring in the queue scheduler becomes mandatory, not optional.
|
||||
|
||||
---
|
||||
|
||||
## Proposal C RAM Concern: Qwen2.5-3B at Q4
|
||||
|
||||
### Single-model scenario
|
||||
|
||||
If Proposal C uses only one model (Gemma 2B for both behaviors and dialogue):
|
||||
- Model RAM: 1.5 GB
|
||||
- Game + OS: 1.7–2.65 GB
|
||||
- Total peak: 3.2–4.15 GB
|
||||
- Headroom on 8 GB: **3.85–4.8 GB** — No concern.
|
||||
|
||||
### Dual-model scenario (Gemma 2B for behaviors + Qwen2.5-3B for dialogue)
|
||||
|
||||
If the spike shows 2B quality is insufficient for dialogue and 3B is required, the RAM calculation depends on loading strategy:
|
||||
|
||||
**Simultaneous loading (both models in RAM at once):**
|
||||
- 1.5 GB (Gemma 2B Q4) + 2.0 GB (Qwen2.5-3B Q4) = 3.5 GB total for models
|
||||
- Plus game + OS: 1.7–2.65 GB
|
||||
- Total peak: 5.2–6.15 GB
|
||||
- Headroom: **1.85–2.8 GB**
|
||||
|
||||
This headroom is tight. During zone transitions with active world generation AND both models loaded, memory spikes could push into swap territory on minimum-spec machines. Not safe.
|
||||
|
||||
**Sequential model loading (swap strategy — recommended):**
|
||||
- Only one model loaded at a time
|
||||
- Batch behavior tasks → load Gemma 2B → process → unload → load Qwen2.5-3B → process dialogue tasks → unload
|
||||
- Peak RAM at any time: 2.0 GB (larger model) + 2.65 GB (game) = 4.65 GB
|
||||
- Headroom: **3.35 GB** — comfortable.
|
||||
|
||||
Model swap latency: loading Gemma 2B Q4 from SSD takes 2–4 seconds. Qwen2.5-3B Q4: 3–6 seconds. If batching 20+ tasks per model-load cycle (which is realistic for a full zone), swap overhead is 5–10 seconds amortized over the batch — acceptable.
|
||||
|
||||
**Conclusion:** Qwen2.5-3B Q4 fits on 8GB alongside the game IF the queue implements sequential loading with batching. Simultaneous loading of both models is not safe on minimum spec. The queue scheduler must enforce single-model-at-a-time.
|
||||
|
||||
### The Qwen constraint flag
|
||||
|
||||
The original proposed-llm-voice.md document states the project constraint: "no Meta/Chinese models." Qwen2.5-3B is by Alibaba (Chinese company). This appears to conflict with that constraint.
|
||||
|
||||
I don't know if this constraint has been formally dropped or if it's a oversight in the Round 2 proposals. Before committing to a Qwen2.5-3B dependency, someone needs to confirm whether the "no Chinese models" constraint still applies. If it does, the fallback for Proposal C's dialogue model is a non-Chinese 3B alternative — Phi-3-mini at 3.8B is the next candidate (though it's larger and slower), or the spike might demonstrate Gemma 2B Q4 is sufficient for dialogue after all.
|
||||
|
||||
Flagging this to the team. I'm not making the constraint decision; I'm noting the conflict.
|
||||
|
||||
---
|
||||
|
||||
## Install Size: Acceptable? Optional Download?
|
||||
|
||||
### Numbers by proposal
|
||||
|
||||
| Proposal | Model(s) | Total model download |
|
||||
|----------|----------|---------------------|
|
||||
| A or B | Gemma 2B Q4_K_M | ~1.5 GB |
|
||||
| C (single model) | Gemma 2B Q4_K_M | ~1.5 GB |
|
||||
| C (dual model) | Gemma 2B + Qwen2.5-3B Q4 | ~3.5 GB |
|
||||
|
||||
Plus inference runtime: ~20–25 MB. Negligible.
|
||||
|
||||
For context, typical indie game install sizes are 2–8 GB. Adding 1.5 GB is a 20–75% install size increase depending on the base game. Adding 3.5 GB for dual-model Proposal C is potentially larger than the base game itself.
|
||||
|
||||
### My position: optional download
|
||||
|
||||
Bundling the model in the base installer is the cleanest player experience but creates distribution problems:
|
||||
|
||||
1. **itch.io:** 2 GB per-file upload limit. A 1.5 GB base game + 1.5 GB model in one package exceeds this. Even split across two files, dual-model Proposal C (3.5 GB) is problematic.
|
||||
2. **Steam:** No hard size limit, but the initial download perception matters. Players who don't plan to use "AI-Enhanced Dialogue" are paying the bandwidth cost involuntarily.
|
||||
3. **GoG, Epic:** Similar concerns. Large downloads increase refund friction.
|
||||
|
||||
**Recommended approach: make the model an optional in-game download, triggered when the player first enables "AI-Enhanced Dialogue."**
|
||||
|
||||
Implementation: the game ships with base text fully functional. On feature enable, a download prompt: "AI-Enhanced Dialogue requires downloading a 1.5 GB language model. Download now?" Single download, stored in user data directory. The download is from the game's own servers (not cloud AI services) — this preserves the "no accounts, no cloud" guarantee from the proposal.
|
||||
|
||||
Pros: base install stays at game-only size, model download is opt-in, works on all distribution platforms.
|
||||
|
||||
Cons: first-time enable has friction (download wait). This is acceptable. The feature is a toggle, not a core gameplay requirement.
|
||||
|
||||
For baked hub content (pre-voiced at build time): these voiced lines ship as game data, not requiring the model. The player's first hours are already pre-voiced without any model download. The model download only matters for background generation of visited zones. This actually softens the first-enable friction further: hub zones work immediately, the download runs in the background for everything else.
|
||||
|
||||
---
|
||||
|
||||
## Resolution Matrix
|
||||
|
||||
### Which proposal do I recommend?
|
||||
|
||||
**Proposal A (Conservative — Behaviors Only, Tells Locked).**
|
||||
|
||||
Infrastructure reasoning:
|
||||
- Single short-form prompt template (150 tokens) → predictable throughput, easy to benchmark
|
||||
- Single model (Gemma 2B Q4_K_M) → no dual-model queue complexity, no model-swap scheduler, simpler memory management
|
||||
- Tell passthrough is the safest mechanism from an infrastructure standpoint — zero risk of cache corruption from subtle tell corruption
|
||||
- 1.5 GB install delta (or optional download) is the best case for distribution
|
||||
- Scope is well-defined enough to write a deterministic spike with measurable pass/fail criteria
|
||||
|
||||
Proposal A does not solve dialogue scaling. That is a known limitation and an acceptable deferred problem. Prove the behavior pipeline first.
|
||||
|
||||
### Can I live with Proposal B?
|
||||
|
||||
**Yes, with one note.**
|
||||
|
||||
Proposal B's constrained re-voicing (semantic core preservation) adds no infrastructure complexity. The `semantic_core` constraint is 20 additional tokens in the prompt — negligible throughput impact (~0.05 seconds per task). Two prompt templates are trivially maintained.
|
||||
|
||||
My note: the quality validation for constrained re-voicing is harder to automate than for passthrough. "Did the model preserve `avoidance_behavior`?" requires either human eval or a second LLM classifier. The infrastructure for build-time validation of baked content needs this: the spike design should include a validation pass that flags tells where the semantic core may not have survived. This is buildable but needs explicit scope in the spike plan.
|
||||
|
||||
### Can I live with Proposal C?
|
||||
|
||||
**Yes, conditionally.**
|
||||
|
||||
Conditions:
|
||||
1. The Qwen2.5-3B "no Chinese models" constraint conflict must be resolved before model selection. If Qwen is out, an alternative 3B model must be identified. Phi-3-mini at 3.8B is slower (worse on minimum spec) and larger (2.2 GB Q4).
|
||||
2. If dual-model is adopted, the queue scheduler MUST implement sequential loading (swap strategy). Simultaneous model loading is not safe on minimum spec.
|
||||
3. Install size must be addressed. Dual-model Proposal C at 3.5 GB additional is too large for bundle-in-base-install. Optional download required.
|
||||
4. The dialogue spike (longer prompts, 50-token output, more complex constraints) must be scoped as a separate validation from the behavior spike. Combining both in a single spike plan risks making the success criteria too broad to evaluate cleanly.
|
||||
|
||||
The infrastructure complexity of Proposal C is manageable — it's more complexity, not prohibitive complexity. The queue scheduler is the only genuinely hard part.
|
||||
|
||||
### Minimum changes to make non-preferred proposals acceptable
|
||||
|
||||
**If Proposal B is chosen over A:** Add an automated validation pass for semantic core preservation in the spike. At minimum, human spot-check of 10% of cached tells per culture. Define "corrupted tell" precisely before the spike, not after.
|
||||
|
||||
**If Proposal C is chosen over A:** (1) Resolve Qwen constraint. (2) Commit to optional download model delivery. (3) Implement sequential model swap in queue scheduler. (4) Run the dialogue spike separately from the behavior spike — separate prompts, separate models, separate success criteria.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Question | Answer |
|
||||
|----------|--------|
|
||||
| Q-R1-04: Throughput difference, 150t vs. 500t prompts | ~1.8–2× longer per task. Behavior: 4–4.5s. Dialogue: 7–8s. Industrial zone: 2 min (A) vs. 7 min (C) on i5-9400. |
|
||||
| Q-R1-05: i5-9400 / Ryzen 5 3600 sufficient? | Yes. 7–10 t/s decode for Gemma 2B Q4. Zone pre-voicing (behaviors) completes in 50s–2min. Comfortable margin for immersive-sim play patterns. Thermal throttling is the risk on laptops. |
|
||||
| Qwen2.5-3B RAM on 8GB | Yes, fits with sequential model swap (3.35 GB headroom). Simultaneous loading is unsafe (1.85 GB headroom — too tight). |
|
||||
| Install size: bundle or optional? | Optional download. 1.5 GB (single model) or 3.5 GB (dual) is too large to bundle invisibly. Trigger on first AI-Enhanced Dialogue enable. Hub baked content ships in game data — player's first hours unaffected. |
|
||||
| Recommended proposal | **A** (behaviors only, tells locked, single model). |
|
||||
|
||||
---
|
||||
|
||||
*Troblum, 2026-03-07*
|
||||
@@ -0,0 +1,408 @@
|
||||
# Troblum — Round 3: Hardware Detection, Distribution, Risk Register
|
||||
|
||||
**Workshop:** LLM Voice Pipeline
|
||||
**Domain:** Infrastructure / Performance
|
||||
**Round:** 3 — Decision & Implementation Spec
|
||||
**Date:** 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## 1. Hardware Detection Spec: Three-Layer System
|
||||
|
||||
Jeroen's design: layer 1 (can the model load?), layer 2 (is inference fast enough?), layer 3 (recommendation threshold with player override). Here is the concrete specification for each layer.
|
||||
|
||||
### Layer 1: RAM Check
|
||||
|
||||
**When:** Triggered on first "AI-Enhanced Dialogue" enable per session. Also re-checked on game resume if the feature was previously enabled but the game was suspended.
|
||||
|
||||
**What it checks:** Available free physical RAM at the moment of enabling.
|
||||
|
||||
**Threshold:**
|
||||
|
||||
| Condition | Action |
|
||||
|-----------|--------|
|
||||
| Free RAM ≥ 2.0 GB | Pass — proceed to Layer 2 |
|
||||
| Free RAM 1.6–2.0 GB | Marginal — warn, offer to proceed (player may have closed other applications) |
|
||||
| Free RAM < 1.6 GB | Fail — feature disabled, message shown |
|
||||
|
||||
**Rationale for 2.0 GB threshold:** Gemma 2B Q4_K_M requires ~1.5 GB for weights + ~100-150 MB for KV cache at typical context lengths = ~1.65 GB peak. The 2.0 GB threshold provides ~350 MB margin for OS overhead and inference worker stack space. If the system is marginal (1.6-2.0 GB), we warn but don't refuse — the player may be able to free RAM by closing browser tabs.
|
||||
|
||||
**Message on fail:** "AI-Enhanced Dialogue requires 2 GB of free memory to run. Your system currently has [X] GB available. Close other applications and try again, or leave the setting off — the game is complete either way."
|
||||
|
||||
No hard minimum — if they have enough RAM, they can try.
|
||||
|
||||
---
|
||||
|
||||
### Layer 2: Time-Per-Token (TPT) Benchmark
|
||||
|
||||
**When:** Immediately after Layer 1 passes, model is loaded (this is the same operation — the model must be loaded for the benchmark, and the load itself is the heaviest part). Benchmark runs once per installation. Result is cached in user config. Player can force a re-benchmark via settings.
|
||||
|
||||
**What it measures:** Synthetic inference run. Prompt: 150-token system prompt (universal negative injectors + minimal Krenn culture injector + one base text seed). Output: measure wall-clock time for 20 tokens of generation. Tokens/sec = 20 ÷ elapsed_seconds.
|
||||
|
||||
**Why 20 tokens:** Fast enough to not feel like a loading screen (2-4 seconds on good hardware, 6-20 seconds on minimum spec). Long enough to average out single-token timing noise.
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```rust
|
||||
// Pseudocode — actual benchmark function in inference_worker.rs
|
||||
fn run_tpt_benchmark(model: &LlamaModel) -> f32 {
|
||||
let prompt = benchmark_prompt(); // hardcoded 150-token synthetic prompt
|
||||
let start = Instant::now();
|
||||
let result = model.generate(prompt, max_tokens: 20, temperature: 0.0);
|
||||
let elapsed = start.elapsed().as_secs_f32();
|
||||
20.0 / elapsed // tokens per second
|
||||
}
|
||||
```
|
||||
|
||||
Temperature 0.0 for the benchmark (greedy decoding) — deterministic, consistent across runs.
|
||||
|
||||
**Result stored in:** `{user_data}/ai-dialogue-config.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"benchmark_tps": 7.4,
|
||||
"benchmark_date": "2026-03-07",
|
||||
"model_version": "gemma-2b-q4_k_m-v1.0",
|
||||
"recommendation": "green"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Layer 3: Recommendation Thresholds
|
||||
|
||||
| TPT result | Status | Player message |
|
||||
|------------|--------|----------------|
|
||||
| ≥ 6 t/s | Green — full experience | No message. Feature enables silently. |
|
||||
| 3–6 t/s | Yellow — partial experience | "Your system is running at [X] tokens/sec. Pre-voicing will work for main characters and key scenes. Background NPCs may appear in base text until the queue catches up. Continue?" |
|
||||
| < 3 t/s | Red — recommend off | "Your system is running at [X] tokens/sec. Pre-voicing may not keep up with gameplay — you'll often see the unvoiced text. We recommend leaving this off, but the choice is yours." |
|
||||
|
||||
**Threshold rationale:**
|
||||
|
||||
At 6 t/s: rural zone (12 behavior tasks × ~4s each) pre-voices in ~48 seconds. Industrial zone (30 tasks) in ~2 minutes. Both complete comfortably before most immersive-sim player interactions.
|
||||
|
||||
At 3 t/s: rural zone pre-voices in ~1.6 minutes, industrial in ~4 minutes. Workable for players who move slowly and spend 10+ minutes per zone. Not workable for transit zones or fast-moving players. This is the boundary where the experience degrades from "seamless" to "sometimes base text."
|
||||
|
||||
At < 3 t/s: industrial zone takes 8+ minutes for behaviors alone. Even plot-critical P0 NPCs may not finish pre-voicing before the player reaches them. The feature produces no improvement over base text in practice. Recommend off.
|
||||
|
||||
**Player override:** Player can always proceed against the recommendation. The warning is a single dialog — "continue anyway / turn off." If they continue, the feature enables. No further nagging. They chose.
|
||||
|
||||
**Ongoing TPT monitoring:** The inference worker tracks a moving average of time-per-token during active inference (window: last 10 generation tasks). If sustained degradation exceeds 40% from the benchmark baseline (thermal throttling, background OS load), the feature status indicator in settings changes to yellow with a note: "Performance has dropped. Consider suspending AI dialogue." Not a forced disable — information only.
|
||||
|
||||
---
|
||||
|
||||
## 2. Bundled Distribution Plan
|
||||
|
||||
Jeroen decided: model ships with the game. ~1.5 GB is acceptable. No optional download.
|
||||
|
||||
### Install Directory Structure
|
||||
|
||||
```
|
||||
SettledReach/
|
||||
├── game.exe (or settled-reach.x86_64 on Linux)
|
||||
├── SettledReach.pck (Godot asset bundle)
|
||||
├── models/
|
||||
│ └── voice-pipeline/
|
||||
│ ├── gemma-2b-q4_k_m.gguf (~1.5 GB — model weights)
|
||||
│ └── model-manifest.json (version, checksum, performance profile)
|
||||
├── data/
|
||||
│ └── baked-voice/
|
||||
│ ├── sova-transit-district.voicecache (pre-voiced hub content)
|
||||
│ └── [other hub zones].voicecache
|
||||
└── [other game files]
|
||||
```
|
||||
|
||||
**Why `models/` is separate from `data/`:** The model file is a large binary blob that doesn't follow standard asset versioning. Keeping it separate makes it clear to players (and antivirus software) what the file is, makes patch targeting unambiguous, and prevents the asset pipeline from trying to process it.
|
||||
|
||||
**Why `data/baked-voice/` is separate from `models/`:** Baked voiced content is game data, not the model. It ships as compressed text records (not model weights) and is human-reviewed. It's versioned with the game, not with the model.
|
||||
|
||||
### Model File Format
|
||||
|
||||
GGUF (the native llama.cpp format). Single file. Self-describing metadata header contains model architecture, quantization scheme, and vocabulary.
|
||||
|
||||
Checksum verification on load: the inference backend reads the model file's SHA256 hash and compares against `model-manifest.json`. Mismatch = log error, disable feature, surface message: "The AI dialogue model file may be corrupted. Reinstall the game to restore it."
|
||||
|
||||
```json
|
||||
// model-manifest.json
|
||||
{
|
||||
"model_id": "gemma-2b-q4_k_m",
|
||||
"version": "1.0",
|
||||
"sha256": "a3f9b2...",
|
||||
"min_game_version": "0.2.0",
|
||||
"params_billions": 2.506,
|
||||
"quantization": "Q4_K_M",
|
||||
"size_bytes": 1611661312,
|
||||
"performance_reference": {
|
||||
"i5_9400_tps": 8.0,
|
||||
"ryzen_5_3600_tps": 10.5,
|
||||
"m1_metal_tps": 22.0
|
||||
},
|
||||
"notes": "Gemma 2B by Google. Apache 2.0 + Google Gemma Terms of Use."
|
||||
}
|
||||
```
|
||||
|
||||
### Loading Mechanism
|
||||
|
||||
**Cold start:** At game launch, no model is loaded. The inference backend is not initialized. Cold start performance is completely unaffected by the model's presence on disk.
|
||||
|
||||
**On "AI-Enhanced Dialogue" enable:** Layer 1 RAM check → Layer 2 benchmark (loads model, runs 20-token test) → result cached → feature active. Model stays resident in the inference worker's memory for the session.
|
||||
|
||||
**On disable during session:** Model is unloaded immediately. Memory freed. If re-enabled same session, model is reloaded (skips benchmark, uses cached result).
|
||||
|
||||
**Model handle ownership:** The inference worker thread owns the model context (via `llama-cpp-rs` bindings). No other system holds a pointer to the model. Queue interaction is through a Rust MPSC channel: other systems send `VoicingTask` structs, worker returns `VoicingResult` structs via callback channel. The model is never touched from outside the worker thread.
|
||||
|
||||
### Baked Content Pipeline
|
||||
|
||||
Baked hub content is generated at build time, human-reviewed, and committed to source control as compressed text. The process:
|
||||
|
||||
1. `make voice-bake` — a build-time Make target
|
||||
2. Target checks: model file exists, SHA256 matches manifest, game version is stamped
|
||||
3. Runs inference locally on the build machine against all hub NPC blueprint data
|
||||
4. Writes `data/baked-voice/*.voicecache` files (compressed JSON: NPC stable ID → voiced lines)
|
||||
5. **Human review required before commit.** The reviewer checks: oath vocab, register accuracy, franchise bleed. This is Paula and Mellanie's job, not automated.
|
||||
6. Once reviewed and committed, the baked cache ships in the game package
|
||||
|
||||
The baked content is generated **once per model version × game version**. It does not regenerate automatically. When the model is updated (e.g., a better quantization version), `make voice-bake` runs again, humans re-review, and the new baked cache commits.
|
||||
|
||||
### Model Updates
|
||||
|
||||
A model update requires a game patch. The patch replaces the GGUF file in `models/voice-pipeline/`. Patch size = model size (~1.5 GB for a full replacement). Delta patches on binary GGUF files are not feasible — the file format is not delta-friendly.
|
||||
|
||||
Recommendation for Steam/distribution: mark the model file in the depot manifest as its own depot chunk, so Steam's delta update system can detect "model file unchanged" and skip re-downloading it when other game files change. This keeps routine game patches small even when the model directory is present.
|
||||
|
||||
For the initial v0.2 release: one model file, no update history to manage. This only becomes relevant in later versions.
|
||||
|
||||
### Platform Notes
|
||||
|
||||
| Platform | Notes |
|
||||
|----------|-------|
|
||||
| Windows | Model ships in install directory. llama.cpp uses AVX2 (auto-detected). |
|
||||
| macOS (Apple Silicon) | Metal acceleration via llama.cpp Metal backend — 15-30 t/s expected on M1/M2. Hardware detection benchmark will score green on all Apple Silicon. |
|
||||
| Linux | Same structure as Windows. Vulkan backend available if `libvulkan` present. |
|
||||
| Steam Deck | AMD RDNA2, Vulkan available. Expect 6-10 t/s. Should score green. |
|
||||
|
||||
---
|
||||
|
||||
## 3. Full Risk Register
|
||||
|
||||
Compiled from all three rounds. Severity: HIGH / MEDIUM / LOW. Status: OPEN / MITIGATED / ACCEPTED.
|
||||
|
||||
---
|
||||
|
||||
### R-001: Quality floor — LLM output below reference bar (HIGH, OPEN)
|
||||
|
||||
**What:** 2B model produces voiced content that sounds worse than the base text it enhances. Players notice the degradation. The "enhancement" is a downgrade.
|
||||
|
||||
**Scenario:** Gemma 2B Q4 produces generic, culture-neutral phrasing that strips the specificity from well-authored base text. A farmer who "hauls produce to the market stall before the morning exchange opens" becomes "carries goods to the market" in voiced output.
|
||||
|
||||
**Mitigation:**
|
||||
- Spike 1 quality gate: oath vocab >95%, register accuracy by blind review consensus, franchise bleed <2%
|
||||
- If Spike 1 fails the quality gate, ship base text only — no voiced content. The system is built; it's just not enabled.
|
||||
- Miri's hybrid format recommendation (80-90 tokens instruction + 120-140 tokens example pairs) directly addresses the "2B follows vocabulary lists, not cultural philosophy" concern. The spike tests both formats.
|
||||
|
||||
**Residual risk:** MEDIUM — there is no guarantee 2B quality meets the bar until Spike 1 runs. This is the primary unknown.
|
||||
|
||||
---
|
||||
|
||||
### R-002: RAM pressure — OOM after passing Layer 1 (MEDIUM, MITIGATED)
|
||||
|
||||
**What:** Layer 1 RAM check passes at enable time. Later in the session, OS allocates memory for other operations (zone loading, asset streaming), leaving insufficient memory for inference. Model KV cache evicted, inference crashes.
|
||||
|
||||
**Mitigation:**
|
||||
- Layer 1 threshold includes 350 MB safety margin above minimum model need
|
||||
- Inference worker catches allocation failures and disables gracefully (returns base text for remaining session, logs error)
|
||||
- Ongoing monitoring: inference worker tracks peak memory usage per session; if it approaches system limit, suspends inference preemptively
|
||||
|
||||
**Residual risk:** LOW — the margin and graceful handling cover this. Not a crash risk in normal operation.
|
||||
|
||||
---
|
||||
|
||||
### R-003: Thermal throttling — TPT degrades from benchmark (MEDIUM-HIGH, MITIGATED)
|
||||
|
||||
**What:** Layer 2 benchmark runs on a cool CPU and scores 8 t/s (green). After 20 minutes of gameplay, CPU reaches thermal limit. Real inference speed drops to 4 t/s without the feature status changing.
|
||||
|
||||
**Scenario:** Laptop under sustained gaming load. Processor throttles from 3.5 GHz to 2.0 GHz. Player sees more base text than expected; thinks the feature is broken.
|
||||
|
||||
**Mitigation:**
|
||||
- Inference worker maintains moving average TPT over last 10 tasks
|
||||
- If sustained degradation >40% from benchmark baseline: settings status changes to yellow, tooltip explains thermal degradation, offers to suspend
|
||||
- Not a forced disable — the player observes and decides
|
||||
- Queue scheduler already pauses inference during zone transitions (C-8). This reduces sustained load by creating thermal recovery windows.
|
||||
|
||||
**Residual risk:** LOW — the mitigation handles it gracefully. Base text covers the degraded output transparently.
|
||||
|
||||
---
|
||||
|
||||
### R-004: Lore contamination — franchise bleed (MEDIUM-HIGH, MITIGATED)
|
||||
|
||||
**What:** 2B model produces references to things that don't exist in the Settled Reach: Earth place names, modern idioms, wrong-era technology, other-franchise vocabulary.
|
||||
|
||||
**Scenario:** Krenn dock worker says "worth a king's ransom" or references "checking his phone" or names a tool that doesn't exist in the setting.
|
||||
|
||||
**Mitigation:**
|
||||
- Universal negative injectors (NI-1 through NI-5) in shared system/prefix prompt layer covering: religious terms, military ranks, anachronistic tech, banter/wit register, Earth geography
|
||||
- Miri's NIs are the primary defense. The spike measures bleed rate against this baseline.
|
||||
- Baked content: human review before commit (Paula, Mellanie). The hub zones have a complete editorial pass.
|
||||
- Runtime content: automated blocklist scan on generated lines before caching. Terms in the blocklist trigger regeneration with a harder negative constraint. After 3 failures, fall back to base text for that line and log for review.
|
||||
- Sampling: on each game build, 5% of runtime-generated lines from that build's test run are reviewed manually. Systemic bleed is caught before reaching players.
|
||||
|
||||
**Residual risk:** MEDIUM — individual franchise-bleed lines can reach players in runtime-generated content even with mitigations. The blocklist cannot anticipate every possible contamination. This is an ongoing operational concern, not a launch blocker.
|
||||
|
||||
---
|
||||
|
||||
### R-005: Lore contamination — wrong culture register (MEDIUM, MITIGATED)
|
||||
|
||||
**What:** LLM applies a culture-neutral or wrong-culture register despite injectors. Every NPC sounds the same regardless of culture. Void-oaths absent. Direct register ignored.
|
||||
|
||||
**Scenario:** Injectors are correctly authored but the 2B model's context window pressure causes it to drop cultural constraints by token 200 of a 500-token dialogue prompt.
|
||||
|
||||
**Mitigation:**
|
||||
- Oath vocabulary is an objective, measurable metric (void-oaths appear or they don't). Tracked per line.
|
||||
- Hybrid format (instruction + examples) increases register reliability for small models (Miri's Round 2 finding)
|
||||
- Spike 1 measures this directly: same prompts through both format variants, blind review of results
|
||||
- If culture injectors fail to hold across diverse prompts in Spike 1: ship behaviors only (Proposal A mechanism) where prompts are short enough to stay within reliable context
|
||||
|
||||
**Residual risk:** MEDIUM for dialogue (long prompts); LOW for behaviors (short prompts, simpler context). The sequencing (behaviors first, dialogue later) is already the adopted architecture, which directly manages this risk.
|
||||
|
||||
---
|
||||
|
||||
### R-006: Cache invalidation failure (LOW, MITIGATED)
|
||||
|
||||
**What:** Voiced content cached under stale key survives into a session where it's wrong (injector update, culture mod change, NPC relationship change).
|
||||
|
||||
**Mitigation:**
|
||||
- Cache key: `hash(seed + zone_id + npc_stable_id + base_text + injector_set_version + model_version)`
|
||||
- Any change to any input field changes the key — old entry is effectively dead (never looked up)
|
||||
- Cache is append-only with TTL sweep (stale entries cleaned on game launch, not mid-session)
|
||||
- Injector versioning: injector set is hashed on load; if injector files change, injector_set_version changes, all dependent cache entries become unreachable
|
||||
|
||||
**Residual risk:** LOW — hash-based invalidation is robust if the key is correctly defined. The main correctness requirement is that `base_text` is included in the key — so if the copy team improves the base text, old voiced versions don't survive.
|
||||
|
||||
---
|
||||
|
||||
### R-007: Install size distribution friction (HIGH → ACCEPTED)
|
||||
|
||||
**What:** 1.5 GB model bundled in base install creates itch.io file limit problems (2 GB per file), slow downloads for players who don't use the feature, and perception issues.
|
||||
|
||||
**Resolution:** Jeroen decided bundled. This risk is accepted. Mitigation for itch.io: split installer into two files (base game + model pack), both downloadable from the game's itch.io page. Player downloads both; installer merges them. Not elegant but functional.
|
||||
|
||||
**Residual risk:** LOW — accepted by the project lead. Distribution packaging must account for the split-installer approach on platforms with file size limits.
|
||||
|
||||
---
|
||||
|
||||
### R-008: Model provenance and licensing (MEDIUM, PARTIALLY MITIGATED)
|
||||
|
||||
**What:** Model license changes or becomes incompatible with commercial game distribution. Or: platform policies evolve to prohibit AI-generated content.
|
||||
|
||||
**Gemma 2B status:** Apache 2.0 + Google Gemma Terms of Use. Allows commercial distribution when bundled. No royalties. Restrictions: no misrepresenting model origin, no use to train competing models. Compatible with game distribution as of 2026-03-07.
|
||||
|
||||
**Phi-3-mini status (fallback):** MIT license. No restrictions beyond standard MIT.
|
||||
|
||||
**Qwen status:** RESOLVED. No Chinese models. Qwen is off the table by Jeroen's decision.
|
||||
|
||||
**Mitigation:**
|
||||
- License terms are reviewed at each game version update (tracked in `model-manifest.json` notes field)
|
||||
- Phi-3-mini is a tested fallback — if Gemma's terms change unfavorably, we have a tested alternative
|
||||
- "AI-Enhanced Dialogue" is optional. If the feature must be removed, base text remains and gameplay is unaffected.
|
||||
|
||||
**Residual risk:** MEDIUM — AI model licensing in commercial games is a new and evolving space. Monitor, don't ignore.
|
||||
|
||||
---
|
||||
|
||||
### R-009: Save compatibility / voiced text drift (LOW-MEDIUM, MITIGATED)
|
||||
|
||||
**What:** Player reloads a save from a previous session. The voiced text they heard is different this time (model update cleared cache, or player installed on a new machine).
|
||||
|
||||
**Mitigation:**
|
||||
- Cache is persistent across sessions in user data directory (`{user_data}/voice-cache/`)
|
||||
- Cache is never automatically cleared on game update — only on model version change (injector_set_version or model_version in key changes)
|
||||
- On model update: old cache entries become unreachable (key changes). Regeneration happens lazily in the background. Player may briefly see base text for previously-voiced content. This is the same as a first-install experience — acceptable.
|
||||
|
||||
**Residual risk:** LOW — player accepts that game updates may change content. Identical to the experience of a translation update in a localized game.
|
||||
|
||||
---
|
||||
|
||||
### R-010: Inference worker crash or hang (MEDIUM, MITIGATED)
|
||||
|
||||
**What:** The `llama-cpp-rs` C FFI layer crashes (OOM, bad model file, unexpected input). Or: model enters a degenerate generation loop and never finishes.
|
||||
|
||||
**Mitigation:**
|
||||
- Per-task timeout: 60 seconds maximum per voicing task. If exceeded, cancel task, return base text, log timeout with task parameters.
|
||||
- Worker restart on crash: inference worker is a supervised Rust task. On panic, supervisor restarts it (model reload required, ~3 seconds). If 3 crashes in one session: feature auto-disables with message.
|
||||
- Degenerate generation: llama.cpp's sampler handles repetition penalty; set repetition_penalty ≥ 1.1 to prevent repetition loops. Max tokens hard limit per task (50 for behaviors, 75 for dialogue) prevents infinite generation.
|
||||
- SIGABRT/segfault in C layer: the worker process (if the inference is in a subprocess) isolates the crash from the game. If inline via FFI, the crash propagates to the game process — this is the main risk. Mitigation: careful OOM handling in Tyre's wrapper; never let the model load fail silently.
|
||||
|
||||
**Residual risk:** MEDIUM — C FFI is inherently riskier than pure Rust. The timeout and restart mitigations reduce impact but don't eliminate the underlying risk.
|
||||
|
||||
---
|
||||
|
||||
### R-011: Model misclassification in spike planning (LOW → RESOLVED)
|
||||
|
||||
See section 4 (Phi-3 classification correction) below. Resolved in writing.
|
||||
|
||||
---
|
||||
|
||||
### R-012: Baked content pipeline divergence (LOW-MEDIUM, MITIGATED)
|
||||
|
||||
**What:** Build-time voicing runs with a different model version, different injectors, or different prompts than the runtime voicing. Baked hub content sounds different from runtime-generated content. Quality cliff at the hub/world boundary.
|
||||
|
||||
**Mitigation:**
|
||||
- `make voice-bake` target reads model version from `model-manifest.json` and fails if it doesn't match the expected version for this game build
|
||||
- Baked content is generated with the same prompt templates as runtime (not a special build-time path)
|
||||
- The only difference is human review (baked goes through editorial; runtime does not)
|
||||
- Voice cache format is identical: baked and runtime caches use the same schema, same key format
|
||||
|
||||
**Residual risk:** LOW — the build target enforces model version consistency. Process risk (someone forgets to run the bake after a model update) is addressed by making the bake a required CI check before the game package is built.
|
||||
|
||||
---
|
||||
|
||||
## 4. Phi-3 Classification Correction
|
||||
|
||||
This is in writing: **Phi-3-mini is a 3.8 billion parameter model. It is not "2B class."**
|
||||
|
||||
The original `proposed-llm-voice.md` document lists "Phi-3-mini" alongside "Gemma 2B" as two "2B class" candidates. This is incorrect. At 3.8B parameters, Phi-3-mini is approximately 52% larger than Gemma 2B (2.5B params).
|
||||
|
||||
**Consequences for Spike 1:**
|
||||
|
||||
| Metric | Gemma 2B Q4_K_M | Phi-3-mini Q4_K_M |
|
||||
|--------|-----------------|-------------------|
|
||||
| Model weights in RAM | ~1.5 GB | ~2.2 GB |
|
||||
| KV cache (at 512t context) | ~100 MB | ~130 MB |
|
||||
| Total RAM footprint | ~1.6 GB | ~2.35 GB |
|
||||
| Layer 1 RAM threshold needed | 2.0 GB free | 2.7 GB free |
|
||||
| Decode speed (i5-9400 class) | 7–9 t/s | 4–6 t/s |
|
||||
| Decode speed (Ryzen 5 3600) | 9–12 t/s | 6–8 t/s |
|
||||
|
||||
Phi-3-mini is slower because it has more parameters — each decode step reads more model weight data from RAM, hitting memory bandwidth harder even though both models share the same Q4 compression.
|
||||
|
||||
**What this means for Spike 1:**
|
||||
|
||||
Spike 1 is plumbing + quality with no game integration. RAM and throughput don't matter for Spike 1 — the model is loaded on a development machine, prompts are fed manually, outputs are evaluated. Spike 1 can and should test both models on the same prompts.
|
||||
|
||||
Phi-3-mini's advantage is real: at 3.8B parameters with Microsoft's instruction-tuning focus, it follows multi-constraint prompts more reliably than Gemma 2B. For dialogue re-voicing (long context, multiple simultaneous constraints: culture register + relationship state + access tier + negative injectors), this quality advantage may be decisive.
|
||||
|
||||
**What this means for Spike 2:**
|
||||
|
||||
If Phi-3-mini wins Spike 1 on quality, Spike 2 integration must account for:
|
||||
- Layer 1 threshold: raise to 2.7 GB free RAM minimum (not 2.0 GB)
|
||||
- Layer 2 benchmark: expect 4–6 t/s on i5-9400; yellow threshold triggers more often → more players see the "partial experience" warning → more players may disable the feature
|
||||
- Zone pre-voicing times: ~30–45% longer across the board vs. Gemma 2B
|
||||
|
||||
If Gemma 2B meets the quality bar in Spike 1 (which is the primary hypothesis): Phi-3-mini remains a tested fallback for model provenance scenarios, not the primary model.
|
||||
|
||||
**Bottom line:** Test both. Pick the one that passes the quality bar. Know that Phi-3-mini's throughput penalty is real and will affect the yellow/green threshold distribution in production.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Deliverable | Status |
|
||||
|-------------|--------|
|
||||
| Hardware detection spec (3-layer) | Complete — thresholds defined, RAM check, TPT benchmark, recommendation tiers |
|
||||
| Bundled distribution plan | Complete — directory structure, load mechanism, baked pipeline, update strategy |
|
||||
| Risk register | Complete — 12 risks across all three rounds, severity and mitigation for each |
|
||||
| Phi-3 classification | Confirmed in writing: Phi-3-mini = 3.8B, not 2B. RAM and throughput implications documented. |
|
||||
|
||||
---
|
||||
|
||||
*Troblum, 2026-03-07*
|
||||
@@ -0,0 +1,290 @@
|
||||
# Tyre Round 1: Technical Feasibility Inventory
|
||||
|
||||
**Domain:** Technical architecture
|
||||
**Input documents reviewed:** workshop-brief.md, proposed-llm-voice.md, generator_spike.rs, blueprint.rs, culture-krenn.ron, rural-zone-spec.ron, industrial-zone-spec.ron, D-010, D-024, D-121, D-122, D-123, D-128, Q-057, Q-012
|
||||
|
||||
---
|
||||
|
||||
## 1. The Three Options — Technical Assessment
|
||||
|
||||
### Option 1: Hand-authored pools (current)
|
||||
|
||||
**Difficulty tier: Easy to build, impossible to scale.**
|
||||
|
||||
The current system works. `RoleSpec.typical_behaviors` is a `Vec<String>`, the generator draws from it with Fisher-Yates, done. Zero runtime complexity. But the brief nails the problem: O(R x Z x C) content. Right now we have ~7 behaviors per role across 2 zones and 1 culture. Adding a second culture doubles the authoring. Adding a station zone type triples it. By the time we have 4 cultures and 5 zone types we're looking at ~700 hand-authored behavior strings just for ambient behaviors, before dialogue. The copy team already flagged this (Q-057).
|
||||
|
||||
Technically trivial. Content-impossible at scale. Not viable as the sole strategy.
|
||||
|
||||
### Option 2: Composable primitives (Q-057)
|
||||
|
||||
**Difficulty tier: Medium to build, moderate to scale, high risk of mechanical output.**
|
||||
|
||||
The idea: decompose "tends rows of low-growing crops with a long-handled hoe" into `[action:tends] [object:crops] [tool:hoe] [manner:practiced]` and recombine with culture modifiers. This is a string assembly engine — essentially a sophisticated template system.
|
||||
|
||||
Technical assessment:
|
||||
- **Build cost:** 2-3 sprints for the composition engine, tag taxonomy, and modifier system.
|
||||
- **Maintenance cost:** High. Every new combination needs QA. The tag taxonomy becomes a coordination bottleneck (see Q-049 ObjectTag co-maintenance problem — same class of issue).
|
||||
- **Output quality ceiling:** Mechanical. "Tends crops with a long-handled hoe in a direct, unhurried manner" reads like a sentence assembled from parts, because it was. The Sprint 25 spike proved that *specific, authored phrasing* is what makes behaviors legible — "wipes grease on the thigh of her coveralls between jobs" cannot be composed from primitives without losing the detail that makes it human.
|
||||
- **Integration:** Fits cleanly into the existing `typical_behaviors: Vec<String>` — the composition engine produces strings, same as hand-authoring. No architectural change needed downstream.
|
||||
|
||||
Feasible but produces the wrong output. The quality floor is too low for what the spike proved works.
|
||||
|
||||
### Option 3: LLM re-voicing
|
||||
|
||||
**Difficulty tier: Challenging but doable. Let me be honest about what this means technically.**
|
||||
|
||||
The i18n analogy is elegant and architecturally sound. Base text as both seed and fallback is a clean design that eliminates the dual-authoring problem. But "ship an LLM with the game" is not a small sentence. Let me break down what this actually requires:
|
||||
|
||||
**What's actually easier than it sounds:**
|
||||
- The prompt engineering. The injector clause system maps directly to data we already have: `CultureProfile.speech`, `NpcBlueprint.traits`, `NpcWant`. The prompt is a structured assembly of existing data fields + a base text string. This is well-defined work, not open-ended AI research.
|
||||
- The cache/fallback model. Base text IS the fallback — no separate system needed. Cache is a string-keyed lookup: `(seed, zone, culture, npc_id, behavior_index) -> voiced_string`. Fits naturally into our existing RON/MessagePack pipeline.
|
||||
- Integration with the generator. `NpcBlueprint.observable_behaviors` is already `Vec<String>`. Re-voicing replaces strings in-place. The rest of the pipeline (perception, observer, wire format) doesn't know or care whether the string was hand-authored, composed, or LLM-generated.
|
||||
|
||||
**What's harder than it sounds:**
|
||||
- Model selection and bundling (see section 2).
|
||||
- Determinism guarantees (see section 3).
|
||||
- Memory budget on minimum spec (see section 2).
|
||||
|
||||
---
|
||||
|
||||
## 2. Model Selection and Inference Wrapper
|
||||
|
||||
### Hardware constraint: the real bottleneck
|
||||
|
||||
Minimum spec from the brief: integrated GPU, 8GB RAM shared with game. Let me be precise about what this means.
|
||||
|
||||
The game already claims memory:
|
||||
- Godot client: ~300-500MB (renderer, assets, scene tree)
|
||||
- Rust server process: ~100-200MB (ECS, generation, world state)
|
||||
- OS overhead: ~1-1.5GB
|
||||
- **Available for LLM: ~5-6GB absolute max, realistically 3-4GB to avoid pressure**
|
||||
|
||||
A 2B parameter model in Q4 quantization: ~1.2-1.5GB. That fits. A 3B model in Q4: ~1.8-2.2GB. Tight but possible. Anything larger is out.
|
||||
|
||||
### Model candidates (2026 landscape)
|
||||
|
||||
The proposal mentions Gemma 2B and Phi-3-mini. Let me update for what's actually available now and what matters for our specific task:
|
||||
|
||||
| Model | Parameters | Q4 Size | Task fit | Notes |
|
||||
|-------|-----------|---------|----------|-------|
|
||||
| Gemma 2 2B | 2.6B | ~1.5GB | Good | Strong instruction following, multilingual base helps with "dialect" tasks |
|
||||
| Phi-3-mini | 3.8B | ~2.2GB | Better quality, tight on RAM | Microsoft's dense model, excellent reasoning per parameter |
|
||||
| Qwen2.5-1.5B | 1.5B | ~0.9GB | Adequate | Smallest viable option, leaves most RAM headroom |
|
||||
| SmolLM2-1.7B | 1.7B | ~1.0GB | Worth testing | Hugging Face, specifically designed for on-device |
|
||||
| Gemma 2 2B (Q3) | 2.6B | ~1.1GB | Testing needed | Aggressive quantization may hurt style consistency |
|
||||
|
||||
**My recommendation:** Spike with Gemma 2 2B (Q4) as primary candidate, Qwen2.5-1.5B as fallback. The task is stylistic rephrasing, not reasoning — a 2B model should handle it. But the spike must validate this empirically. If a 2B model can't reliably preserve void-oaths and speech register, we have a problem.
|
||||
|
||||
### Rust inference wrapper
|
||||
|
||||
Three serious options for shipping an LLM in a Rust binary:
|
||||
|
||||
**Option A: llama.cpp via llama-cpp-rs bindings**
|
||||
- Maturity: High. Battle-tested across hundreds of apps. GGUF format is the standard for quantized models.
|
||||
- Binary size impact: ~5-8MB for the llama.cpp static library.
|
||||
- Startup cost: Model load from disk takes 1-3 seconds (acceptable — happens once at game start or first inference request).
|
||||
- GPU acceleration: Optional CUDA/Metal/Vulkan backends. CPU-only fallback works. Important: on integrated GPU systems, the CPU path may actually be faster than competing for shared GPU memory with Godot's renderer.
|
||||
- **My recommendation.** It's the boring choice, and boring is correct here.
|
||||
|
||||
**Option B: candle (Hugging Face Rust ML framework)**
|
||||
- Pure Rust, no C++ dependency. Smaller binary footprint (~2-3MB).
|
||||
- Less mature for production inference. Quantization support is narrower.
|
||||
- Advantage: no cross-compilation headaches with C++ toolchains.
|
||||
- Risk: fewer model format options, less community optimization.
|
||||
|
||||
**Option C: burn (Rust ML framework)**
|
||||
- Pure Rust, very early. Not production-ready for inference of transformer models at the scale we need.
|
||||
- Would require manual model conversion work.
|
||||
- **Not recommended for v0.2.**
|
||||
|
||||
**Verdict:** llama-cpp-rs with GGUF models. It's proven, it handles quantization correctly, and the binary size impact is acceptable. We wrap it in a thin Rust crate (`sr-voice` or similar) that exposes exactly one function: `revoice(base_text: &str, context: &VoiceContext) -> String`.
|
||||
|
||||
### Binary size and distribution impact
|
||||
|
||||
| Component | Size |
|
||||
|-----------|------|
|
||||
| llama.cpp static lib | ~5-8MB |
|
||||
| GGUF model (Q4, 2B) | ~1.2-1.5GB |
|
||||
| Baked voice cache (hub zones) | ~5-20MB (text only, compresses well) |
|
||||
| **Total distribution impact** | **~1.3-1.6GB** |
|
||||
|
||||
This is significant but not unusual for a modern game. The model ships as a separate asset, not baked into the binary. Players who disable "AI-Enhanced Dialogue" could theoretically skip the download (future optimization, not v0.2).
|
||||
|
||||
---
|
||||
|
||||
## 3. Determinism — the D-010 Problem
|
||||
|
||||
*cracks knuckles* — This is where it gets interesting.
|
||||
|
||||
D-010 principle 4 mandates BTreeMap everywhere for determinism. Same seed = same world. LLM inference is inherently non-deterministic across:
|
||||
- Different hardware (floating point rounding)
|
||||
- Different quantization levels
|
||||
- Different batch sizes
|
||||
- Different llama.cpp versions
|
||||
|
||||
**The proposal's answer — generate once per seed, cache the result — is correct but needs formalization.**
|
||||
|
||||
### Cache-as-determinism model
|
||||
|
||||
The LLM does NOT run during gameplay simulation ticks. It runs during world generation (baked or lazy pre-voicing). The output is cached. From that point forward, the cached string is deterministic — it's just a lookup.
|
||||
|
||||
```
|
||||
Generation time: base_text + context -> LLM -> voiced_text -> cache
|
||||
Game time: cache_key -> voiced_text (deterministic lookup)
|
||||
```
|
||||
|
||||
**Cache key structure:**
|
||||
```
|
||||
(world_seed: u64, culture_id: &str, zone_type: &str, npc_stable_id: StableId, behavior_index: u8)
|
||||
```
|
||||
|
||||
This means:
|
||||
- Same seed on the same machine = same voiced text (LLM output cached on first generation)
|
||||
- Same seed on different machines = potentially different voiced text (acceptable — the base text is identical, only the stylistic variation differs)
|
||||
- **Want tells and relationship behaviors: generated by the Rust simulation, then re-voiced.** The tell content is deterministic (SimRng-seeded). The voiced phrasing is cached. The gameplay-critical information (the tell exists, it references a specific person) is in the base text, not added by the LLM.
|
||||
|
||||
### What must NOT be re-voiced
|
||||
|
||||
This is critical. Some strings carry precise gameplay information:
|
||||
|
||||
| Content type | Re-voice? | Why |
|
||||
|-------------|-----------|-----|
|
||||
| Role behaviors ("tends crops") | Yes | Flavor text, no gameplay info loss |
|
||||
| Want tells ("watches the room in the glass of a nearby surface") | **Carefully** | The tell IS the gameplay. Re-voicing must preserve the observable action. Restrict LLM to style/voice changes, not semantic changes. |
|
||||
| Relationship behaviors ("talks past Rask without making eye contact") | **Carefully** | The named target and the social signal must survive re-voicing. |
|
||||
| AvoidingSomeone tells with named targets | **No** | Format string with `{name}` substitution. Re-voicing risks losing the name reference. |
|
||||
| Dialogue (future) | Yes | Culture voice is the primary enhancement target |
|
||||
|
||||
The safe rule: **if the string contains a proper noun reference to another NPC, pass it through untouched.** The LLM can re-voice generic role actions freely.
|
||||
|
||||
---
|
||||
|
||||
## 4. Pre-voicing Queue and Lazy Generation Integration
|
||||
|
||||
### Same thread pool or separate?
|
||||
|
||||
**Separate.** Here's why:
|
||||
|
||||
The world generator (zone skeletons, NPC blueprints, tile placement) is CPU-bound Rust running on the server process. It uses `SimRng` and must be deterministic. It runs during zone loading and produces `SpikeOutput`/`NpcBlueprint` data.
|
||||
|
||||
The voice pipeline is I/O-bound (model loading) then CPU-bound (inference), non-deterministic, and operates on generator *output*. It should run in its own thread pool with:
|
||||
- A bounded work queue (e.g., `crossbeam-channel` with capacity 256)
|
||||
- Priority ordering: P0 (plot-critical) > P1 (semi-unique) > P2 (ambient)
|
||||
- Backpressure: if the queue is full, new items wait — the game continues with base text
|
||||
|
||||
### Integration with lazy world generation
|
||||
|
||||
```
|
||||
Player enters zone trigger area
|
||||
-> World generator produces NpcBlueprints (deterministic, fast)
|
||||
-> NPC entities spawn with base_text behaviors (immediate, playable)
|
||||
-> Voice queue receives (blueprint, culture, zone_context) work items
|
||||
-> Voice worker processes queue in background
|
||||
-> Completed items update the behavior cache
|
||||
-> Next perception tick: observer reads voiced text from cache instead of base text
|
||||
```
|
||||
|
||||
The key insight: **re-voicing is an asynchronous enhancement, not a blocking dependency.** The game is always playable with base text. Voiced text replaces it when ready. The observer system (`ObserverSnapshot`) already reads behavior strings from a cache — we just add a "voiced version available?" check.
|
||||
|
||||
### Latency budget
|
||||
|
||||
For background generation on minimum-spec hardware (CPU-only inference on a 2B model):
|
||||
- Per-behavior re-voicing: ~200-500ms per inference call (short input, short output)
|
||||
- Per-NPC (2 behaviors): ~400ms-1s
|
||||
- Per-zone (10 NPCs): ~4-10 seconds
|
||||
- **Adjacent zone pre-voicing while player is in current zone: easily achievable.** Player spends minutes in a zone; pre-voicing the next zone takes seconds.
|
||||
|
||||
On higher-spec hardware with GPU acceleration: 5-10x faster. Negligible.
|
||||
|
||||
---
|
||||
|
||||
## 5. Cache Format and Invalidation
|
||||
|
||||
### Format
|
||||
|
||||
MessagePack (D-020) for consistency with the rest of the pipeline. The voice cache is a flat map:
|
||||
|
||||
```rust
|
||||
struct VoiceCache {
|
||||
/// (seed, zone, culture, npc_id, behavior_idx) -> voiced string
|
||||
entries: BTreeMap<VoiceCacheKey, String>,
|
||||
/// Model version used to generate these entries
|
||||
model_version: String,
|
||||
/// Cache format version for migration
|
||||
format_version: u8,
|
||||
}
|
||||
```
|
||||
|
||||
Stored per-zone as `.msgpack` files alongside save data. Baked caches for hub zones ship as game assets.
|
||||
|
||||
### Invalidation rules
|
||||
|
||||
| Event | Invalidation scope | Rationale |
|
||||
|-------|-------------------|-----------|
|
||||
| Seed change (new game) | Full regen | Different world = different NPCs = different voiced text |
|
||||
| Culture mod added | Culture-scoped regen | Culture injectors changed, all culture-specific voicing is stale |
|
||||
| Model update (game patch) | Full regen | Different model = potentially different output |
|
||||
| Zone spec change (content patch) | Zone-scoped regen | Base text pool changed |
|
||||
| NPC relationship change (runtime) | Single NPC regen | Relationship behavior text changed |
|
||||
|
||||
**Save file interaction:** Voice cache is NOT part of the save file. It's a derived artifact that can be regenerated. Save files remain small (D-020). The cache lives in a separate directory (`user://voice_cache/`).
|
||||
|
||||
---
|
||||
|
||||
## 6. What Breaks If We Choose Wrong
|
||||
|
||||
### If we choose Option 1 (hand-authored only):
|
||||
- Content team hits a wall at 3 cultures x 5 zone types. D-122 (all NPCs generated) becomes impossible to staff. Q-057 remains open forever because the answer is "write more strings manually."
|
||||
- **Risk level: Project-blocking.** The generator-first vision (D-114, D-117) requires content at scale. Hand-authoring doesn't scale.
|
||||
|
||||
### If we choose Option 2 (composable primitives):
|
||||
- We build a composition engine that produces output below the quality bar the Sprint 25 spike established. The spike proved that *specific authored detail* makes behaviors readable — "wipes grease on the thigh of her coveralls" is not composable. We'd ship a technically sophisticated system that produces bland output.
|
||||
- **Risk level: Quality-killing.** The game reads as procedurally generated in the pejorative sense.
|
||||
|
||||
### If we choose Option 3 (LLM re-voicing) and the model can't do it:
|
||||
- If 2B models can't reliably preserve void-oaths, speech register, and relationship references, we've built an inference pipeline for nothing. The fallback is base text — playable but bland.
|
||||
- **Risk level: Recoverable.** The base text fallback means the game ships either way. The infrastructure cost (~3 sprints for the wrapper + queue + cache) is the sunk cost.
|
||||
- **Additional risk:** Install size. ~1.5GB for a model some players won't use. Needs to be a separate, optional download.
|
||||
|
||||
### If we choose a hybrid (my recommendation) and it's overengineered:
|
||||
- We build both the base text pipeline (already exists) and the re-voicing layer. If the LLM layer underperforms, we disable it and ship with base text + hand-authored hub content.
|
||||
- **Risk level: Minimal.** The hybrid architecture is inherently risk-managed by the fallback design.
|
||||
|
||||
---
|
||||
|
||||
## 7. My Position: Option 3 (LLM re-voicing) with hybrid fallback
|
||||
|
||||
*cracks knuckles* — Let me be direct.
|
||||
|
||||
**The architecture in proposed-llm-voice.md is sound.** The i18n model is elegant — a single design that solves content scaling, quality floor, hardware flexibility, and player choice simultaneously. That's rare. When a single architectural decision solves four problems, you take it.
|
||||
|
||||
The technical risk is real but bounded:
|
||||
1. The spike will tell us if 2B models can handle the task (1-2 sprints to answer)
|
||||
2. The fallback (base text) means we ship either way
|
||||
3. The infrastructure (llama-cpp-rs wrapper, queue, cache) is well-understood systems programming, not research
|
||||
|
||||
**Effort estimate:**
|
||||
- Spike (model evaluation + prompt testing): 1-2 sprints
|
||||
- Rust inference wrapper (`sr-voice` crate): 1 sprint
|
||||
- Pre-voicing queue + cache system: 1 sprint
|
||||
- Integration with observer/generator pipeline: 1 sprint
|
||||
- Baked cache generation for hub zones: 0.5 sprint
|
||||
- **Total: 4.5-5.5 sprints** (parallelizable — spike can run while other v0.2 work continues)
|
||||
|
||||
**Scope-wise, this means** the LLM voice pipeline is a ~6 sprint investment that replaces what would otherwise be an unbounded content authoring commitment. That's a good trade.
|
||||
|
||||
---
|
||||
|
||||
## 8. One Question I Need Answered
|
||||
|
||||
**Does the re-voicing target observable behaviors only, or dialogue too?**
|
||||
|
||||
The workshop brief asks this (question 1) and the answer has major architectural implications:
|
||||
|
||||
- **Behaviors only:** The re-voicing input is the `observable_behaviors: Vec<String>` from `NpcBlueprint`. Short strings (5-15 words), no conversation context needed. A 2B model handles this easily. Prompt is simple: "Rephrase this action description in [culture] voice with [personality] manner."
|
||||
|
||||
- **Behaviors + dialogue:** The dialogue system (D-028) has its own tagged line pools, conversation context, and layer model. Re-voicing dialogue means the LLM needs conversation history, speaker/listener context, and longer output. This pushes toward 3B+ models, longer inference times, and significantly more complex prompting.
|
||||
|
||||
My technical recommendation: **start with behaviors only.** Validate the pipeline end-to-end on the simpler case. Extend to dialogue in a follow-up sprint if the model and quality pass the spike criteria. The architecture supports both — the `revoice()` function doesn't care what kind of string it processes — but the prompt engineering and quality bar are different.
|
||||
|
||||
This question determines whether the spike tests short-form re-voicing (behaviors) or long-form (dialogue), which affects model selection, latency budgets, and the spike's success criteria.
|
||||
@@ -0,0 +1,281 @@
|
||||
# Tyre Round 2: Proposal Evaluation & Technical Resolution
|
||||
|
||||
**Domain:** Technical architecture
|
||||
**Round:** 2 — Convergent Evaluation
|
||||
**Assigned questions:** Q-R1-02 (behaviors-only vs behaviors+dialogue), Q-R1-03 (tell_behaviors field), Q-R1-04 (token budget feasibility)
|
||||
|
||||
---
|
||||
|
||||
## Resolution Matrix
|
||||
|
||||
| Question | My answer |
|
||||
|----------|-----------|
|
||||
| Which proposal do you recommend? | **B** (Two-Track — Behaviors + Tells with Semantic Core) |
|
||||
| Are there blockers in your recommended proposal? | No. See implementation notes below. |
|
||||
| Can you live with Proposal A? | Yes. It's the safe fallback if B's constrained re-voicing fails the spike. |
|
||||
| Can you live with Proposal C? | Yes, but with a phased spike — behaviors first, dialogue second. Don't test both simultaneously. |
|
||||
| Minimum change to make A acceptable? | None needed — A is acceptable as-is, just leaves value on the table. |
|
||||
| Minimum change to make C acceptable? | Phase the spike: validate behaviors + tells first (Sprint N), extend to dialogue second (Sprint N+1). Don't test two content types and two model sizes simultaneously. |
|
||||
|
||||
---
|
||||
|
||||
## Why Proposal B
|
||||
|
||||
*cracks knuckles* — Let me be direct about why B is the sweet spot.
|
||||
|
||||
**Proposal A** locks tells as passthrough. That's safe but wasteful. The tell system already outputs a `TellCategory` enum (Nervous, Angry, Friendly, Guarded, RoutineDeviation) — it's a closed taxonomy of 5 categories. The tells are not free-form authored content; they're behavioral expressions of simulation state. A Nervous Krenn worker and a Nervous Sovari merchant should look nervous *differently*. Passthrough means they look nervous identically. That's technically correct but culturally flat.
|
||||
|
||||
**Proposal C** adds dialogue re-voicing to the same spike. That's scope creep that risks muddying the results. Behaviors are 5-15 word strings with no conversation context. Dialogue is 15-40 words requiring relationship state, access tier, and conversation history. Testing both in one spike means you can't isolate whether a quality failure comes from the model, the prompt, or the content type. Phase it.
|
||||
|
||||
**Proposal B** adds constrained re-voicing for tells while keeping the spike focused on a single content type (behaviors). The `semantic_core` tag is a lightweight addition that maps directly to the existing `TellCategory` enum. Two prompt templates (free + constrained) is marginally more complex than one, but both operate on the same short-form input. The spike complexity increase is ~20%, not 2x.
|
||||
|
||||
---
|
||||
|
||||
## Q-R1-02: Behaviors-Only vs Behaviors+Dialogue — Feasibility & Quality Risk
|
||||
|
||||
### Spike complexity comparison
|
||||
|
||||
| Dimension | A/B (behaviors only) | C (behaviors + dialogue) |
|
||||
|-----------|---------------------|--------------------------|
|
||||
| Prompt templates | 1 (A) or 2 (B) | 3 (free behavior, constrained tell, dialogue) |
|
||||
| Input context length | 150-200 tokens | 150-200 (behaviors) + 400-500 (dialogue) |
|
||||
| Output length | 10-30 tokens | 10-30 (behaviors) + 20-60 (dialogue) |
|
||||
| Model candidates to test | 1 (Gemma 2B Q4) | 2 (Gemma 2B for behaviors, potentially Qwen2.5-3B for dialogue) |
|
||||
| Evaluation criteria | Register accuracy, oath preservation, semantic core preservation | All of the above + conversation coherence, relationship accuracy, information boundary compliance |
|
||||
| Test payloads | 5-8 behavior strings across 2 zones | 5-8 behaviors + 5-8 dialogue lines across 2 zones + 2 relationship contexts |
|
||||
| Spike duration | 1-2 sprints | 2-3 sprints |
|
||||
| Risk of inconclusive results | Low | Moderate — if dialogue fails, did the model fail or the prompt? |
|
||||
|
||||
### Model size implications for dialogue
|
||||
|
||||
Dialogue re-voicing is a harder task than behavior re-voicing. Here's why, concretely:
|
||||
|
||||
**Behavior input:** "tends crops in the field"
|
||||
**Behavior prompt context:** Culture register + personality traits + mood = ~150 tokens total
|
||||
**Output constraint:** Rephrase in voice, preserve the action. Single sentence.
|
||||
|
||||
**Dialogue input:** "You need a keycard for that door."
|
||||
**Dialogue prompt context:** Culture register + personality traits + mood + relationship to listener + access tier + trust level + conversation topic = ~400-500 tokens total
|
||||
**Output constraint:** Rephrase in voice, preserve the information, maintain conversation coherence, don't cross information boundaries.
|
||||
|
||||
At 2B model size (Gemma 2B), instruction-following degrades as prompt complexity increases. The community benchmarks show:
|
||||
- Simple rephrasing tasks (our behavior case): 2B models perform at ~85-90% of 7B quality
|
||||
- Multi-constraint tasks (our dialogue case): 2B models drop to ~65-75% of 7B quality
|
||||
- The drop is steeper when constraints conflict (e.g., "be direct" + "be evasive about this topic")
|
||||
|
||||
**Practical implication:** A 2B model that handles behavior re-voicing well may produce mediocre dialogue re-voicing. If Proposal C tests both and dialogue fails, the conclusion might be "we need a 3B model" — which tightens RAM and slows inference. That's a real architectural fork in the road, and it shouldn't be discovered mid-spike alongside behavior evaluation.
|
||||
|
||||
### My recommendation
|
||||
|
||||
**Phase the spike:**
|
||||
1. Sprint N: Behaviors + tells (Proposal B scope). One model (Gemma 2B Q4). Clear success criteria.
|
||||
2. Sprint N+1: If behaviors pass, extend to dialogue with the same model. If dialogue quality is insufficient, test Qwen2.5-3B as an upgrade candidate.
|
||||
3. Sprint N+2: If 3B is needed for dialogue, run the RAM/throughput validation separately.
|
||||
|
||||
This costs 1 sprint more than C's all-at-once approach but eliminates the risk of an inconclusive spike. We know exactly what works and what doesn't at each stage.
|
||||
|
||||
---
|
||||
|
||||
## Q-R1-03: `tell_behaviors` as a Separate Field — Implementation Confirmation
|
||||
|
||||
**Yes, this is implementable. And it's actually simpler than the proposals assume, because the production system already separates tells from behaviors at the ECS level.**
|
||||
|
||||
Let me walk through the existing architecture:
|
||||
|
||||
### Current state (production server)
|
||||
|
||||
The production code has a clean separation that the spike doesn't:
|
||||
|
||||
1. **`NpcBlueprint.observable_behaviors: Vec<String>`** — role-specific actions from `RoleSpec.typical_behaviors`. These are the ambient behaviors.
|
||||
|
||||
2. **`DerivedTellState` (ECS component)** — a `TellCategory` enum (Nervous, Angry, Friendly, Guarded, RoutineDeviation) derived each tick from simulation state by `tell_state::derive_tell_state()`. This is NOT a string. It's a simulation signal.
|
||||
|
||||
3. **`VisibleEntity.tell_state: Option<TellCategory>`** on the wire (bridge/types.rs, line 402). The client receives the tell as an enum, not a behavior string.
|
||||
|
||||
### The spike's confusion
|
||||
|
||||
The spike (`generator_spike.rs`) conflates these by appending tell strings to `observable_behaviors` at index 1:
|
||||
```rust
|
||||
// Fourth pass: generate Want tells (#632).
|
||||
if let Some(tell) = gen_want_tell(&mut rng, npc) {
|
||||
npc.observable_behaviors.push(tell);
|
||||
}
|
||||
```
|
||||
|
||||
This was a pragmatic spike shortcut — the spike doesn't have an ECS world, so it can't use `DerivedTellState`. But it created the impression that tells and behaviors share a flat list.
|
||||
|
||||
### What needs to change for the voice pipeline
|
||||
|
||||
**In `NpcBlueprint`:** Add a `tell_behaviors: Vec<TellBehavior>` field:
|
||||
|
||||
```rust
|
||||
pub struct TellBehavior {
|
||||
/// The TellCategory this behavior expresses.
|
||||
pub category: TellCategory,
|
||||
/// Base text for this tell (culture-neutral).
|
||||
pub base_text: String,
|
||||
/// Semantic core tag for constrained re-voicing (Proposal B).
|
||||
/// e.g., "avoidance_behavior", "nervous_fidget", "suppression_tell"
|
||||
pub semantic_core: String,
|
||||
}
|
||||
|
||||
pub struct NpcBlueprint {
|
||||
// ... existing fields ...
|
||||
pub observable_behaviors: Vec<String>, // ambient role actions — free re-voicing
|
||||
pub tell_behaviors: Vec<TellBehavior>, // tells — constrained re-voicing (B) or passthrough (A)
|
||||
}
|
||||
```
|
||||
|
||||
**Effort:** ~0.5 sprint. Add the struct, update the generator to populate it separately from `observable_behaviors`, update the spike to use the new field instead of appending to the flat list. No downstream changes needed — `DerivedTellState` already flows as an enum on the wire; the tell behavior text is a separate rendering concern.
|
||||
|
||||
**Observer integration:** The observer snapshot already sends `tell_state: Option<TellCategory>`. The voiced tell text would be a cache lookup: `(TellCategory, culture_id, personality_traits) -> voiced_tell_string`. This is a client-side lookup, not a server change.
|
||||
|
||||
### Interaction with the ECS tell system
|
||||
|
||||
Important subtlety: in the production server, tells are **not pre-generated per NPC**. `DerivedTellState` is recomputed every tick from live simulation state. An NPC might be Friendly at tick 100 and Nervous at tick 500 because their stress increased.
|
||||
|
||||
This means tell behavior text is not a static per-NPC attribute — it's a per-category, per-culture library. The voice pipeline generates voiced variants for all 5 TellCategory values per culture, not per NPC. That's:
|
||||
|
||||
- 5 categories x N cultures x ~4 variants per category = ~20-40 voiced tell strings per culture
|
||||
|
||||
This is a small, finite set. It could even be baked at build time for all cultures, no lazy generation needed. **Tell voicing is not a scaling problem — it's a fixed-size content library.**
|
||||
|
||||
---
|
||||
|
||||
## Q-R1-04: Token Budget — 150 Tokens for Culture Injectors
|
||||
|
||||
### Is 150 tokens sufficient?
|
||||
|
||||
**For behaviors: yes. For the full cultural philosophy Miri describes: no, but it doesn't need to be.**
|
||||
|
||||
Let me construct the actual prompt for a behavior re-voicing call and count tokens:
|
||||
|
||||
```
|
||||
System: You are a dialogue localizer for a science fiction game.
|
||||
Rephrase the following action description in the specified voice.
|
||||
Preserve the physical action. Change only style, register, and vocabulary.
|
||||
Do not add information. Do not explain motivation. One sentence only.
|
||||
|
||||
Culture: Krenn (working-class, direct, minimal pleasantries).
|
||||
Speech register: direct, gets to the point, no contractions avoided.
|
||||
Exclamations (use ONLY these): "void take it", "stars", "blood and void",
|
||||
"cold vacuum", "damn all", "void's sake".
|
||||
DO NOT use: military ranks, sir/ma'am, religious references, quips.
|
||||
|
||||
Personality: Bold, Honest.
|
||||
Mood: Neutral.
|
||||
|
||||
Rephrase: "tends crops in the field"
|
||||
```
|
||||
|
||||
Token count (GPT-4 tokenizer as proxy, actual varies by model):
|
||||
- System instruction: ~45 tokens
|
||||
- Culture injector: ~75 tokens
|
||||
- Personality + mood: ~10 tokens
|
||||
- Base text + format: ~15 tokens
|
||||
- **Total: ~145 tokens**
|
||||
|
||||
That fits the 150-token budget for behaviors. The culture injector at 75 tokens covers: register description, oath vocabulary (closed list), negative constraints (NOT-lists). It does NOT cover the full cultural philosophy (community anchors, competence signaling, emotional weight of void-oaths) — that would push to 200-300 tokens as Miri describes.
|
||||
|
||||
**The key question: does the model need cultural philosophy to rephrase a 10-word action?**
|
||||
|
||||
No. For behavior re-voicing, the model needs:
|
||||
1. Register (direct, clipped) — so it doesn't produce flowery prose
|
||||
2. Oath vocabulary (closed list) — so it uses "void take it" not "damn it"
|
||||
3. Negative constraints — so it doesn't produce franchise bleed
|
||||
|
||||
It does NOT need to understand why Krenn people swear by the void. That's a dialogue-level concern, not a behavior-level concern. "Tends crops in the field" becomes "works the irrigation channels before the morning rotation" — register and setting vocabulary are sufficient.
|
||||
|
||||
**For Proposal B's constrained tell re-voicing:** add ~20 tokens for the semantic core constraint ("PRESERVE: avoidance_behavior. Culture-voice the expression, not the phenomenon."). Total: ~170 tokens. Still within the effective range for a 2B model.
|
||||
|
||||
### Throughput impact: 150-token vs 500-token prompts
|
||||
|
||||
This matters because it determines whether Proposal C's dialogue re-voicing is feasible on minimum-spec hardware.
|
||||
|
||||
**How LLM inference works with different prompt sizes:**
|
||||
|
||||
There are two phases:
|
||||
1. **Prompt processing (prefill):** Process all input tokens. This is parallelizable and fast. For llama.cpp on CPU: ~100-500 tokens/sec depending on hardware.
|
||||
2. **Token generation (decode):** Generate output tokens one at a time. This is sequential and slow. This is where the 3-14 t/s numbers from Troblum's analysis apply.
|
||||
|
||||
| Prompt size | Prefill time (5 t/s hardware) | Generate 30 tokens | Total |
|
||||
|-------------|-------------------------------|---------------------|-------|
|
||||
| 150 tokens | ~0.3-0.5 sec | ~6 sec | ~6.5 sec |
|
||||
| 500 tokens | ~1.0-1.5 sec | ~6 sec | ~7.5 sec |
|
||||
|
||||
**The throughput difference is ~15% per call.** Prefill is cheap; generation is the bottleneck. Longer prompts don't dramatically slow things down because the output length is the dominant factor, not the input length.
|
||||
|
||||
However, there's a memory impact. At 500-token context, the KV cache per inference call grows from ~50MB to ~80MB. On minimum-spec hardware, this tightens the already-constrained RAM budget. Not a showstopper but worth noting.
|
||||
|
||||
**Practical conclusion:** 500-token prompts for dialogue (Proposal C) are feasible from a throughput perspective. The concern with Proposal C is quality at 2B, not speed. If you need to step up to 3B for dialogue quality, THAT is where throughput drops — a 3B model at Q4 runs ~30% slower than 2B, which means the zone pre-voicing times in Troblum's table increase by a third.
|
||||
|
||||
---
|
||||
|
||||
## Additional Technical Assessment of Each Proposal
|
||||
|
||||
### Proposal A: Conservative
|
||||
|
||||
**Architecturally clean.** One prompt template, one model, one content type. The spike is maximally simple. If we are risk-averse about the v0.2 timeline, this is the right call.
|
||||
|
||||
**Technical gap:** Tell behaviors pass through as culture-neutral base text. A Krenn NPC who is Nervous shows exactly the same tell phrasing as a Sovari NPC who is Nervous. Since tells are a fixed-size library (5 categories x N cultures), voicing all of them is cheap. Leaving them unvoiced is a missed opportunity with near-zero risk.
|
||||
|
||||
### Proposal B: Two-Track
|
||||
|
||||
**My recommendation.** The constrained re-voicing for tells is a well-bounded problem:
|
||||
- Input: culture + TellCategory + semantic_core
|
||||
- Output: culture-voiced tell string
|
||||
- Validation: does the output still read as the same TellCategory?
|
||||
- Volume: ~20-40 strings per culture (bakeable at build time)
|
||||
|
||||
The `semantic_core` tag maps directly to `TellCategory`:
|
||||
|
||||
| TellCategory | semantic_core | Example base text |
|
||||
|---|---|---|
|
||||
| Nervous | nervous_fidget | "shifts weight and checks the time without reason" |
|
||||
| Angry | hostile_display | "speaks through clenched teeth" |
|
||||
| Friendly | warmth_signal | "greets passersby unprompted" |
|
||||
| Guarded | concealment_tell | "becomes evasive and avoids eye contact" |
|
||||
| RoutineDeviation | routine_break | "takes an unusual route to their station" |
|
||||
|
||||
The prompt for constrained re-voicing adds one line to the behavior prompt:
|
||||
```
|
||||
PRESERVE the observable phenomenon: {semantic_core}.
|
||||
Rephrase in Krenn register. Do not change what the NPC is doing — change how they do it.
|
||||
```
|
||||
|
||||
This is a constrained localization task, not free generation. A 2B model handles this well because the constraint is specific and verifiable. The spike can include tell re-voicing alongside behavior re-voicing with minimal additional test payloads (5 categories x 1-2 base texts = 5-10 additional test strings).
|
||||
|
||||
### Proposal C: Full Pipeline
|
||||
|
||||
**Technically feasible, strategically premature.**
|
||||
|
||||
The dialogue re-voicing itself works — the prompt structure is sound, the content types are compatible. But testing it in the same spike as behaviors creates a 3-variable experiment:
|
||||
1. Model capability (can 2B handle it?)
|
||||
2. Prompt quality (are the injectors sufficient?)
|
||||
3. Content type suitability (is dialogue a good fit for re-voicing?)
|
||||
|
||||
If the spike produces mediocre dialogue, which variable failed? You can't tell without running the experiment again with controls. Phase it.
|
||||
|
||||
**If the team chooses C anyway:** the minimum change that makes it acceptable is to **separate the spike into two phases with independent success criteria.** Phase 1: behaviors + tells (same as B). Phase 2: dialogue, using the Phase 1-validated model and injectors. Phase 2 only runs if Phase 1 passes. This is functionally B → C progression, not parallel C.
|
||||
|
||||
---
|
||||
|
||||
## D-123 Tension — Technical Position
|
||||
|
||||
All three proposals amend D-123 ("authoring tool, not runtime system"). From a technical architecture standpoint:
|
||||
|
||||
**The amendment is correct.** The LLM is both an authoring tool (baked content at build time) and a runtime enhancement (pre-voicing queue during gameplay). The distinction D-123 drew was premature — it was written before the re-voicing architecture was designed. The re-voicing model IS an authoring tool that happens to run at runtime. The output is cached text, not real-time generation. The simulation never depends on LLM output. The game is complete without it.
|
||||
|
||||
Proposed amendment language: *"D-123 is amended. The AI pipeline is an authoring tool for content assembly AND a background runtime enhancement for culture-voiced dialogue. Runtime inference is optional (player toggle), non-blocking (graceful fallback to base text), and cache-deterministic (same seed produces same voiced content per machine). D-124 is superseded — the voice pipeline IS the in-game AI system."*
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
| Proposal | Technical risk | Spike complexity | Value delivered |
|
||||
|----------|---------------|-----------------|----------------|
|
||||
| A | Low | Low | Ambient behaviors voiced; tells flat |
|
||||
| **B** | **Low-medium** | **Low-medium** | **Ambient behaviors + culturally-voiced tells** |
|
||||
| C | Medium | High | Full content pipeline, but spike may be inconclusive |
|
||||
|
||||
**B is the right scope for the spike and the right architecture for v0.2.** Dialogue re-voicing can follow in Sprint N+1 as a natural extension if the pipeline validates. The tell voicing is a small, bounded addition that delivers disproportionate cultural richness — 5 categories x N cultures, bakeable at build time, zero lazy-generation pressure.
|
||||
@@ -0,0 +1,589 @@
|
||||
# Tyre Round 3: Implementation Specification
|
||||
|
||||
**Domain:** Technical architecture
|
||||
**Round:** 3 — Decision & Implementation Plan
|
||||
**Deliverables:** Spike 1 implementation spec, model test plan, Spike 2 outline, hardware detection design
|
||||
|
||||
---
|
||||
|
||||
## 1. Spike 1 Implementation Spec: `sr-voice` CLI Tool
|
||||
|
||||
### Purpose
|
||||
|
||||
A standalone Rust CLI that loads a GGUF model, accepts prompts, and returns generated text. No game integration, no queue, no cache. This is the plumbing that Jeroen, Mellanie, and Paula will feed manually-crafted prompts through to answer: "does this even play?"
|
||||
|
||||
### Crate structure
|
||||
|
||||
```
|
||||
server/
|
||||
sr-voice/
|
||||
Cargo.toml
|
||||
src/
|
||||
main.rs # CLI entry point
|
||||
inference.rs # Model loading and generation wrapper
|
||||
prompt.rs # Prompt file parsing and construction
|
||||
```
|
||||
|
||||
`sr-voice` is a separate crate in the server workspace, not compiled into the game binary. It depends on `llama-cpp-rs` (or `llama-cpp-2` — see build notes) and produces a standalone binary: `sr-voice`.
|
||||
|
||||
### Cargo.toml dependencies
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "sr-voice"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
llama-cpp-2 = { version = "0.1", features = ["metal", "vulkan"] }
|
||||
# Note: "metal" and "vulkan" are optional features, compile-time gated.
|
||||
# CPU-only is the default and always available.
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
gpu-metal = ["llama-cpp-2/metal"]
|
||||
gpu-vulkan = ["llama-cpp-2/vulkan"]
|
||||
```
|
||||
|
||||
**Build note on `llama-cpp-rs` vs `llama-cpp-2`:** Both wrap the same C library. `llama-cpp-2` is the more actively maintained fork as of early 2026 and has cleaner safe Rust wrappers. Evaluate both at spike start; pick whichever compiles cleanly on Linux + macOS + Windows without manual C++ toolchain intervention. Pin the llama.cpp commit hash in Cargo.toml to prevent upstream API breaks.
|
||||
|
||||
**Build dependency:** Requires a C/C++ compiler (gcc/clang/MSVC). CMake is pulled in by the llama.cpp build system. This is a compile-time dependency, not a runtime dependency — the final binary is self-contained.
|
||||
|
||||
### CLI interface
|
||||
|
||||
```
|
||||
sr-voice --model <path-to-gguf> [OPTIONS] [PROMPT_FILE]
|
||||
|
||||
Options:
|
||||
--model <path> Path to GGUF model file (required)
|
||||
--threads <n> CPU threads for inference (default: physical_cores - 1)
|
||||
--ctx-size <n> Context window size in tokens (default: 512)
|
||||
--max-tokens <n> Maximum output tokens (default: 64)
|
||||
--temperature <f> Sampling temperature (default: 0.7)
|
||||
--top-p <f> Top-p sampling (default: 0.9)
|
||||
--seed <n> RNG seed for sampling (default: random)
|
||||
--json Output as JSON: {"input": "...", "output": "...", "tokens_per_sec": N}
|
||||
--batch Process multiple prompts from a JSONL file (one per line)
|
||||
--benchmark Run 5 inference calls and report avg tokens/sec
|
||||
|
||||
PROMPT_FILE:
|
||||
Read prompt from file (plain text). If omitted, reads from stdin.
|
||||
```
|
||||
|
||||
### Core function signatures
|
||||
|
||||
```rust
|
||||
// inference.rs
|
||||
|
||||
/// Configuration for the inference engine.
|
||||
pub struct InferenceConfig {
|
||||
pub model_path: PathBuf,
|
||||
pub n_threads: u32,
|
||||
pub ctx_size: u32,
|
||||
pub seed: Option<u32>,
|
||||
}
|
||||
|
||||
/// A loaded model ready for inference.
|
||||
pub struct InferenceEngine {
|
||||
// Wraps llama_model + llama_context from llama-cpp-2.
|
||||
// Model is loaded once; context is reused across calls.
|
||||
model: LlamaModel,
|
||||
ctx: LlamaContext,
|
||||
}
|
||||
|
||||
impl InferenceEngine {
|
||||
/// Load a GGUF model from disk. Returns an error if the model
|
||||
/// doesn't fit in available RAM or the file is invalid.
|
||||
///
|
||||
/// Typical load time: 2-5 seconds for a 2B Q4 model from SSD.
|
||||
pub fn load(config: &InferenceConfig) -> Result<Self, VoiceError>;
|
||||
|
||||
/// Run inference on a prompt string. Returns the generated text.
|
||||
///
|
||||
/// `max_tokens`: maximum output tokens (stops early on EOS).
|
||||
/// `temperature`: sampling temperature (0.0 = greedy, 1.0 = creative).
|
||||
/// `top_p`: nucleus sampling threshold.
|
||||
pub fn generate(
|
||||
&mut self,
|
||||
prompt: &str,
|
||||
max_tokens: u32,
|
||||
temperature: f32,
|
||||
top_p: f32,
|
||||
) -> Result<GenerationResult, VoiceError>;
|
||||
}
|
||||
|
||||
/// Result of a single inference call.
|
||||
pub struct GenerationResult {
|
||||
/// Generated text (stripped of prompt echo).
|
||||
pub text: String,
|
||||
/// Number of tokens generated.
|
||||
pub tokens_generated: u32,
|
||||
/// Wall-clock time for generation (excludes prompt processing).
|
||||
pub generation_time_ms: u64,
|
||||
/// Tokens per second (generation phase only).
|
||||
pub tokens_per_sec: f32,
|
||||
/// Wall-clock time for prompt processing (prefill).
|
||||
pub prefill_time_ms: u64,
|
||||
}
|
||||
|
||||
pub enum VoiceError {
|
||||
ModelLoadFailed(String),
|
||||
InferenceFailed(String),
|
||||
OutOfMemory,
|
||||
InvalidModel(String),
|
||||
}
|
||||
```
|
||||
|
||||
```rust
|
||||
// prompt.rs
|
||||
|
||||
/// A structured prompt payload for the spike test matrix.
|
||||
/// Parsed from JSON files that Mellanie/Paula/Jeroen prepare.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PromptPayload {
|
||||
/// Unique ID for tracking results.
|
||||
pub id: String,
|
||||
/// Content type being re-voiced.
|
||||
pub content_type: ContentType,
|
||||
/// The fully assembled prompt string (system + injectors + base text).
|
||||
pub prompt: String,
|
||||
/// The original base text (for output comparison).
|
||||
pub base_text: String,
|
||||
/// Expected semantic core (for tell payloads — optional).
|
||||
pub semantic_core: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub enum ContentType {
|
||||
Behavior,
|
||||
Tell,
|
||||
Dialogue,
|
||||
}
|
||||
```
|
||||
|
||||
### Batch mode for the test matrix
|
||||
|
||||
The `--batch` flag processes a JSONL file where each line is a `PromptPayload` JSON object. Output is JSONL with the original payload + generated text + timing:
|
||||
|
||||
```jsonl
|
||||
{"id":"rural-farmer-1","content_type":"Behavior","base_text":"tends crops in the field","output":"works the irrigation channels before morning rotation","tokens_per_sec":8.2,"prefill_ms":340,"generation_ms":3650}
|
||||
{"id":"nervous-tell-1","content_type":"Tell","base_text":"shifts weight and checks the time without reason","output":"shifts from foot to foot, void-ward glances at the clock","tokens_per_sec":7.9,"prefill_ms":380,"generation_ms":3800}
|
||||
```
|
||||
|
||||
This enables Mellanie and Paula to prepare prompt files, run them through both models, and compare output side by side. The JSON output feeds directly into a comparison spreadsheet or diff tool.
|
||||
|
||||
### What Spike 1 does NOT include
|
||||
|
||||
- No game integration
|
||||
- No queue or priority system
|
||||
- No cache
|
||||
- No thread pool management
|
||||
- No prompt construction logic (prompts are hand-crafted by the content team for the spike)
|
||||
- No save/load of voiced content
|
||||
- No Godot interaction
|
||||
|
||||
---
|
||||
|
||||
## 2. Model Test Plan
|
||||
|
||||
### Candidates
|
||||
|
||||
| Model | Parameters | Q4_K_M Size | Why it's here |
|
||||
|-------|-----------|-------------|---------------|
|
||||
| **Gemma 2 2B** | 2.6B | ~1.5 GB | Primary candidate. Google origin. Good instruction following for size. |
|
||||
| **Phi-3-mini** | **3.8B** | ~2.2 GB | Fallback candidate. Microsoft origin. Better quality, larger footprint. |
|
||||
|
||||
**Addressing the Phi-3 size discrepancy:** The original proposal called both "2B class." This is incorrect. Phi-3-mini is 3.8B parameters — nearly 50% larger. This matters for:
|
||||
- **RAM:** +700MB at Q4 (+47% over Gemma 2B)
|
||||
- **Throughput:** ~30% slower decode due to larger weight matrix
|
||||
- **Install size:** +700MB in the distribution bundle
|
||||
|
||||
Phi-3-mini is the quality fallback, not a peer candidate. If Gemma 2B passes the quality bar, Phi-3 is unnecessary. If Gemma 2B fails, Phi-3 tells us whether more parameters solve the problem or the task itself is wrong for small models.
|
||||
|
||||
### Test matrix
|
||||
|
||||
**Prompt payloads** (prepared by Mellanie/Paula/Jeroen — Tyre provides the structure):
|
||||
|
||||
| ID | Content type | Base text | Zone | Culture | Traits | Mood | TellCategory | Notes |
|
||||
|----|-------------|-----------|------|---------|--------|------|-------------|-------|
|
||||
| B-01 | Behavior | "tends crops in the field" | rural | krenn | Bold, Honest | Neutral | — | Simple role action |
|
||||
| B-02 | Behavior | "checks a manifest against a handheld scanner, lips moving" | industrial | krenn | Curious, Social | Neutral | — | Detailed role action |
|
||||
| B-03 | Behavior | "catches {target}'s eye and nods across the room" | industrial | krenn | Social, Compassionate | Neutral | — | Relationship behavior — named target preservation |
|
||||
| B-04 | Behavior | "talks past {target} without making eye contact" | industrial | krenn | Deceptive, Bold | Neutral | — | Negative relationship — social signal preservation |
|
||||
| B-05 | Behavior | "sits alone in the break room rubbing the back of her neck, datapad face-down on the table" | industrial | krenn | — | Stressed | — | Long-form atmospheric behavior |
|
||||
| T-01 | Tell | "shifts weight and checks the time without reason" | any | krenn | — | — | Nervous | Nervous fidget — phenomenon must survive |
|
||||
| T-02 | Tell | "affects exaggerated calm" | any | krenn | Deceptive | — | Guarded | Suppression — constrained re-voicing test |
|
||||
| T-03 | Tell | "checks surroundings repeatedly" | any | krenn | Cautious | — | Nervous | Surveillance — must not become avoidance |
|
||||
| D-01 | Dialogue | "You need a keycard for that door." | industrial | krenn | Bold | Neutral | — | Simple informational dialogue |
|
||||
| D-02 | Dialogue | "I haven't seen Kael since second shift. Why?" | industrial | krenn | Suspicious, Cautious | Guarded | Guarded | Dialogue with active tell context — tell shapes tone |
|
||||
| D-03 | Dialogue | "The cargo manifest doesn't match what's in bay seven." | industrial | krenn | Honest, Curious | Alert | — | Information-bearing dialogue — must preserve factual content |
|
||||
|
||||
**11 payloads total.** Each run through both models = 22 outputs per prompt template variant.
|
||||
|
||||
### Prompt template variants to test
|
||||
|
||||
For each payload, test 2-3 prompt template variants to find the optimal instruction format:
|
||||
|
||||
**Variant 1 — Instruction-first:**
|
||||
```
|
||||
[System instruction]
|
||||
[Culture injector with closed vocabulary]
|
||||
[Personality + mood]
|
||||
[Tell context if applicable]
|
||||
Rephrase: "[base text]"
|
||||
```
|
||||
|
||||
**Variant 2 — Few-shot:**
|
||||
```
|
||||
[System instruction]
|
||||
[Culture injector]
|
||||
|
||||
Examples:
|
||||
Base: "repairs equipment by hand" → Voiced: "strips the housing down and rebuilds it, no manual needed"
|
||||
Base: "arranges goods on a portable display" → Voiced: "squares the goods on the fold-out, everything where it should be"
|
||||
|
||||
[Personality + mood]
|
||||
[Tell context if applicable]
|
||||
Rephrase: "[base text]"
|
||||
```
|
||||
|
||||
**Variant 3 — Negative-constraint-heavy (Miri's recommendation):**
|
||||
```
|
||||
[System instruction]
|
||||
[Culture injector]
|
||||
DO NOT use: military ranks, sir/ma'am, religious references, quips, banter.
|
||||
DO NOT reference: religion, sports, nationality, Earth-origin social structures.
|
||||
Technology terms ONLY: insert, span gate, horizon gate, void, the Reach.
|
||||
Exclamations ONLY: "void take it", "stars", "blood and void", "cold vacuum", "damn all".
|
||||
|
||||
[Personality + mood]
|
||||
Rephrase: "[base text]"
|
||||
```
|
||||
|
||||
### Evaluation criteria
|
||||
|
||||
Each output is scored on 5 axes (1-5 scale, scored by Paula and Mellanie independently):
|
||||
|
||||
| Criterion | What it measures | Pass threshold |
|
||||
|-----------|-----------------|----------------|
|
||||
| **Register accuracy** | Does it sound like Krenn working-class? Not formal, not quippy, not military. | >= 3 |
|
||||
| **Oath preservation** | If an exclamation appears, is it from the canonical list? No franchise bleed? | >= 4 (hard requirement) |
|
||||
| **Semantic preservation** | Does the output preserve the action/information from the base text? | >= 4 (hard requirement) |
|
||||
| **Named target survival** | For B-03/B-04: does the `{target}` name survive in the output? | Pass/Fail |
|
||||
| **Tell phenomenon class** | For T-01/T-02/T-03: does the tell still express the same TellCategory? | Pass/Fail |
|
||||
|
||||
**Spike 1 success criteria:**
|
||||
- At least one model achieves >= 3 average on register accuracy across all payloads
|
||||
- Both hard requirements (oath, semantic) pass on >= 9/11 payloads
|
||||
- Named target survival: pass on both relationship payloads
|
||||
- Tell phenomenon class: pass on all 3 tell payloads
|
||||
|
||||
If Gemma 2B meets these criteria, it's the selected model. If only Phi-3 meets them, we accept the RAM/size tradeoff and document why. If neither meets them, the spike has failed and we fall back to base-text-only (Proposal A without re-voicing, which is the current system with scaling managed by content authoring).
|
||||
|
||||
### Benchmark protocol
|
||||
|
||||
On Spike 1 hardware (developer machine), record for each model:
|
||||
- Tokens/sec at Q4_K_M with `--threads` set to physical_cores - 1
|
||||
- RAM usage during inference (peak RSS)
|
||||
- Model load time from SSD
|
||||
|
||||
On a representative minimum-spec machine (if available — otherwise note the hardware used and extrapolate using Troblum's bandwidth formula):
|
||||
- Same metrics
|
||||
- Thermal behavior during 5-minute sustained inference run
|
||||
|
||||
---
|
||||
|
||||
## 3. Spike 2 Outline: Integration Architecture
|
||||
|
||||
Spike 2 wires the validated Spike 1 runner into the game. This outline is ticket-ready, not full implementation design.
|
||||
|
||||
### 3.1 Inference thread pool
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ Game Process │
|
||||
│ │
|
||||
│ ┌───────────────┐ ┌────────────────┐ │
|
||||
│ │ Simulation │ │ Voice Pipeline │ │
|
||||
│ │ (server) │ │ (separate pool)│ │
|
||||
│ │ │ │ │ │
|
||||
│ │ World gen ────┼──>│ Work queue │ │
|
||||
│ │ NPC spawn │ │ InferenceEngine│ │
|
||||
│ │ Tick loop │ │ Voice cache │ │
|
||||
│ └───────────────┘ └────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────┐ │
|
||||
│ │ Godot client │ <── reads cache ──┘ │
|
||||
│ └───────────────┘ │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Thread count:** 1 dedicated inference thread. llama.cpp uses its own internal threading (set to `physical_cores - world_gen_threads - 1`). The inference thread owns the `InferenceEngine` instance — no model sharing across threads.
|
||||
|
||||
**Priority:** Below-normal OS thread priority. Inference yields to simulation and rendering.
|
||||
|
||||
### 3.2 Work queue
|
||||
|
||||
```rust
|
||||
/// A single unit of work for the voice pipeline.
|
||||
pub struct VoiceWorkItem {
|
||||
/// Cache key for storing the result.
|
||||
pub cache_key: VoiceCacheKey,
|
||||
/// Priority tier (lower number = higher priority).
|
||||
pub priority: VoicePriority,
|
||||
/// The fully constructed prompt string.
|
||||
pub prompt: String,
|
||||
/// Maximum output tokens.
|
||||
pub max_tokens: u32,
|
||||
}
|
||||
|
||||
pub enum VoicePriority {
|
||||
/// P0: Plot-critical NPCs the player is about to interact with.
|
||||
Critical = 0,
|
||||
/// P1: NPCs in the current zone the player may interact with.
|
||||
High = 1,
|
||||
/// P2: NPCs in adjacent/anticipated zones.
|
||||
Standard = 2,
|
||||
/// P3: Ambient NPCs in distant zones (opportunistic).
|
||||
Background = 3,
|
||||
}
|
||||
```
|
||||
|
||||
**Queue implementation:** `crossbeam-channel` bounded channel (capacity: 256). Items sorted by priority. Producer: the world generation system, triggered by `ZonePopulated` event. Consumer: the inference thread.
|
||||
|
||||
**Backpressure:** If the queue is full, new items are dropped silently — the game continues with base text. No blocking the simulation thread.
|
||||
|
||||
**Zone transition pause:** When the simulation emits a `ZoneTransitionStart` event, the inference thread pauses (drains current item, then waits). Resumes on `ZoneTransitionComplete`. This prevents CPU contention during the loading spike.
|
||||
|
||||
### 3.3 Voice cache
|
||||
|
||||
```rust
|
||||
pub struct VoiceCacheKey {
|
||||
pub world_seed: u64,
|
||||
pub culture_id: String, // "krenn"
|
||||
pub npc_stable_id: StableId,
|
||||
pub content_type: ContentType, // Behavior | Dialogue
|
||||
pub content_index: u8, // which behavior/dialogue line
|
||||
}
|
||||
|
||||
pub struct VoiceCache {
|
||||
/// In-memory cache for current session.
|
||||
entries: BTreeMap<VoiceCacheKey, String>,
|
||||
/// Model identifier used to generate these entries.
|
||||
model_id: String,
|
||||
}
|
||||
```
|
||||
|
||||
**Persistence:** Written to `user://voice_cache/{seed}.msgpack` on zone transition or autosave. Loaded on game start if seed matches. Format version tag for migration.
|
||||
|
||||
**Invalidation:** Full cache invalidation on: seed change, model update (game patch). Per-NPC invalidation on: relationship change that affects behavior text.
|
||||
|
||||
**Baked content:** Hub zone voiced content ships as a game asset at `res://voice_baked/{zone_id}.msgpack`. Loaded into the cache on zone entry. Never regenerated at runtime.
|
||||
|
||||
### 3.4 Tell-as-context prompt construction
|
||||
|
||||
Per Jeroen's decision: tells are passthrough (never re-voiced), but they INFORM the re-voicing prompt for behaviors and dialogue.
|
||||
|
||||
```rust
|
||||
/// Build the re-voicing prompt for an NPC's behavior or dialogue.
|
||||
fn build_prompt(
|
||||
base_text: &str,
|
||||
content_type: ContentType,
|
||||
culture: &CultureProfile,
|
||||
npc: &NpcBlueprint,
|
||||
active_tell: Option<TellCategory>, // from DerivedTellState
|
||||
) -> String {
|
||||
let mut prompt = String::with_capacity(512);
|
||||
|
||||
// System instruction
|
||||
prompt.push_str(SYSTEM_INSTRUCTION);
|
||||
|
||||
// Culture injector (from NpcBlueprint.cultural_markers — per Miri's recommendation)
|
||||
prompt.push_str(&format_culture_injector(&npc.cultural_markers, culture));
|
||||
|
||||
// Personality injector
|
||||
prompt.push_str(&format_personality(&npc.traits));
|
||||
|
||||
// Tell-as-context: if the NPC has an active tell, inject it as mood/state context
|
||||
// The tell itself is NOT being re-voiced — it's informing the tone
|
||||
if let Some(tell) = active_tell {
|
||||
prompt.push_str(&format_tell_context(tell));
|
||||
// e.g., "The character is currently guarded and evasive.
|
||||
// Their dialogue should reflect this state without stating it directly."
|
||||
}
|
||||
|
||||
// Negative constraints (franchise bleed prevention)
|
||||
prompt.push_str(NEGATIVE_CONSTRAINTS);
|
||||
|
||||
// Base text to re-voice
|
||||
match content_type {
|
||||
ContentType::Behavior => {
|
||||
prompt.push_str(&format!("\nRephrase this action: \"{}\"", base_text));
|
||||
}
|
||||
ContentType::Dialogue => {
|
||||
prompt.push_str(&format!("\nRephrase this dialogue line: \"{}\"", base_text));
|
||||
}
|
||||
}
|
||||
|
||||
prompt
|
||||
}
|
||||
|
||||
fn format_tell_context(tell: TellCategory) -> String {
|
||||
match tell {
|
||||
TellCategory::Nervous => {
|
||||
"\nState: The character is anxious. Their speech is clipped, distracted.\n".into()
|
||||
}
|
||||
TellCategory::Angry => {
|
||||
"\nState: The character is angry. Their speech is terse, barely controlled.\n".into()
|
||||
}
|
||||
TellCategory::Friendly => {
|
||||
"\nState: The character is warm and open. Their speech is relaxed.\n".into()
|
||||
}
|
||||
TellCategory::Guarded => {
|
||||
"\nState: The character is guarded. They deflect and keep things vague.\n".into()
|
||||
}
|
||||
TellCategory::RoutineDeviation => {
|
||||
"\nState: The character is preoccupied. Something else is on their mind.\n".into()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This is the critical design: the `TellCategory` enum flows into the prompt as a mood/state modifier, not as content to re-voice. The tell behavior string stays untouched. The dialogue and ambient behaviors around the tell are colored by the NPC's state.
|
||||
|
||||
### 3.5 Observer integration
|
||||
|
||||
The observer snapshot system already reads `DerivedTellState` and `observable_behaviors`. Integration point:
|
||||
|
||||
```
|
||||
Observer reads NPC behavior string:
|
||||
1. Check voice cache for (seed, culture, npc_id, behavior_index)
|
||||
2. If cache hit → use voiced string
|
||||
3. If cache miss → use base text string (fallback)
|
||||
4. Tell state → always from DerivedTellState (passthrough, never from cache)
|
||||
```
|
||||
|
||||
No changes to the wire format (`ObserverSnapshot`). The client doesn't know or care whether the behavior string was voiced or base text.
|
||||
|
||||
### 3.6 Baked content generation
|
||||
|
||||
A build-time step that runs the inference engine on all hub zone NPCs:
|
||||
|
||||
```bash
|
||||
# Build tool (not the game binary)
|
||||
sr-voice-bake \
|
||||
--model models/gemma-2b-q4.gguf \
|
||||
--zones content/global/krenn-*.ron \
|
||||
--culture content/global/culture-krenn.ron \
|
||||
--output client/assets/voice_baked/ \
|
||||
--seed 0 # baked content uses seed 0 as the canonical reference
|
||||
```
|
||||
|
||||
Output: one `.msgpack` file per zone containing all voiced behavior and dialogue strings. Checked into the repository (text-only, compresses to ~20-50KB per zone). Human-reviewed by Paula/Mellanie before ship.
|
||||
|
||||
---
|
||||
|
||||
## 4. Hardware Detection Design
|
||||
|
||||
### Layer 1: RAM check (can the model load?)
|
||||
|
||||
On first toggle of "AI-Enhanced Dialogue":
|
||||
|
||||
```rust
|
||||
fn check_ram_available() -> RamCheckResult {
|
||||
let available_mb = get_available_system_ram_mb();
|
||||
let model_size_mb = 1600; // Gemma 2B Q4 + KV cache overhead
|
||||
|
||||
if available_mb < model_size_mb {
|
||||
RamCheckResult::InsufficientRam {
|
||||
available_mb,
|
||||
required_mb: model_size_mb,
|
||||
}
|
||||
} else {
|
||||
RamCheckResult::Ok
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**User-facing message if insufficient:**
|
||||
> "AI-Enhanced Dialogue requires approximately 1.6 GB of free RAM. Your system currently has {available_mb} MB available. The feature may cause instability. Enable anyway?"
|
||||
|
||||
Player can always override. No hard block.
|
||||
|
||||
### Layer 2: Time-per-token benchmark (is inference useful?)
|
||||
|
||||
If RAM check passes, run a 5-token benchmark on first enable:
|
||||
|
||||
```rust
|
||||
fn benchmark_inference(engine: &mut InferenceEngine) -> BenchmarkResult {
|
||||
let test_prompt = "Rephrase: \"walks down the corridor.\"";
|
||||
let result = engine.generate(test_prompt, 5, 0.7, 0.9)?;
|
||||
let tpt_ms = result.generation_time_ms as f32 / result.tokens_generated as f32;
|
||||
|
||||
BenchmarkResult {
|
||||
tokens_per_sec: result.tokens_per_sec,
|
||||
time_per_token_ms: tpt_ms,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Thresholds:**
|
||||
|
||||
| Tokens/sec | Recommendation | User message |
|
||||
|-----------|---------------|--------------|
|
||||
| >= 5 t/s | Full enable | "AI-Enhanced Dialogue is active." |
|
||||
| 2-5 t/s | Enable with warning | "AI-Enhanced Dialogue is active. On your hardware, voiced content will generate slowly. Some NPCs may show plain text until generation catches up." |
|
||||
| < 2 t/s | Recommend disable | "Your hardware generates voiced content very slowly. We recommend disabling AI-Enhanced Dialogue for the best experience. Enable anyway?" |
|
||||
|
||||
**No hard floor.** Player can always choose to run it. The benchmark runs once, result is cached in user settings. Player can re-run from the settings menu.
|
||||
|
||||
### Layer 3: Runtime monitoring
|
||||
|
||||
During gameplay, the inference thread monitors its own throughput:
|
||||
|
||||
```rust
|
||||
// In the inference thread main loop:
|
||||
if current_tokens_per_sec < 1.0 {
|
||||
// Sustained very-slow inference — likely thermal throttle or power saver
|
||||
pause_inference();
|
||||
notify_ui("AI dialogue generation paused — system is running slowly.");
|
||||
// Resume after 60 seconds or on user action
|
||||
}
|
||||
```
|
||||
|
||||
**Battery/power-saver detection:** On Windows, check `GetSystemPowerStatus()`. If on battery with power saver active, auto-pause inference and show notification. On Linux/macOS, check `/sys/class/power_supply/` or equivalent. Resume when plugged in or power mode changes.
|
||||
|
||||
### Settings UI
|
||||
|
||||
```
|
||||
[Settings > Audio & Dialogue]
|
||||
|
||||
AI-Enhanced Dialogue: [ON / OFF]
|
||||
Status: Active (8.2 tokens/sec)
|
||||
|
||||
[Re-run benchmark]
|
||||
|
||||
Note: When enabled, NPC dialogue and behaviors are enhanced with
|
||||
culture-specific voice. This uses additional CPU resources.
|
||||
Disable if you experience performance issues.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Effort Estimates
|
||||
|
||||
| Work item | Sprints | Dependencies |
|
||||
|-----------|---------|-------------|
|
||||
| Spike 1: `sr-voice` CLI tool | 1 | None — can start immediately |
|
||||
| Spike 1: Prompt crafting + model testing | 1 | sr-voice CLI (Mellanie/Paula/Jeroen run the tests) |
|
||||
| Spike 2: Queue + cache + thread pool | 1.5 | Spike 1 model selection |
|
||||
| Spike 2: Tell-as-context prompt construction | 0.5 | Queue infrastructure |
|
||||
| Spike 2: Observer integration | 0.5 | Cache system |
|
||||
| Spike 2: Baked content generation tool | 0.5 | Queue + cache |
|
||||
| Hardware detection system | 0.5 | InferenceEngine (from Spike 1) |
|
||||
| **Total** | **5.5** | Spike 1 and 2 are sequential; sub-items within each spike are partially parallelizable |
|
||||
|
||||
Spike 1 can start next sprint. The CLI tool is self-contained Rust with no game dependencies. While the content team runs manual prompt tests, Spike 2 infrastructure design can begin in parallel.
|
||||
@@ -0,0 +1,565 @@
|
||||
# LLM Voice Pipeline Workshop — Outcomes
|
||||
|
||||
**Workshop:** LLM Voice Pipeline Design Workshop
|
||||
**Dates:** 2026-03-07 (all three rounds)
|
||||
**Rounds:** 3 (Inventory → Convergent Evaluation → Decision)
|
||||
**Participants:** Gestalt, Tyre, Paula, Mellanie, Ozzie, Miri, Troblum, Qatux
|
||||
**Decisions produced:** D-138 (new), D-123 (amended), D-124 (superseded)
|
||||
**Compiled by:** Qatux — 2026-03-07
|
||||
|
||||
---
|
||||
|
||||
## 1. Architecture Decision (D-138)
|
||||
|
||||
### D-138: LLM Re-voicing Pipeline for NPC Voice
|
||||
|
||||
> **Status:** Pending formal record in `decisions/content.md` (ID claimed, text below is canonical)
|
||||
>
|
||||
> **Decision:** NPC observable behaviors and dialogue are processed through an LLM re-voicing pipeline that translates culture-neutral semantic base text into character-voiced output. The pipeline is a background runtime enhancement, not a live generation system. Tell behaviors are base-text passthrough — always. Active tell state influences the re-voicing prompt for surrounding content without the tell text itself being re-voiced. The game is complete and functional without the pipeline; it is an enhancement that elevates voice quality for players with sufficient hardware.
|
||||
>
|
||||
> **Rationale:** D-122 (all NPCs generated) and D-128 (culture implicit in starting location) require NPC voice to scale across zones and cultures without O(R×Z×C) hand-authoring. The re-voicing model — translate culture-neutral semantic base text into character voice — is the only architecture that scales while preserving content quality. The base-text fallback ensures the game is complete without the pipeline. Tell-as-passthrough with context influence preserves the information asymmetry mechanic (D-010) while giving tells cultural texture through their influence on surrounding content.
|
||||
>
|
||||
> **Raised by:** LLM Voice Pipeline Workshop (2026-03-07). Jeroen's decisions are the binding inputs.
|
||||
>
|
||||
> **Dissent:** Miri flagged concern about cultural philosophy at 2B model size — addressed via hybrid injector format (instruction + example pairs) and spike validation.
|
||||
>
|
||||
> **Amends:** D-123 — see Section 2.
|
||||
> **Supersedes:** D-124 (in-game AI deferred — door is now walked through).
|
||||
> **Cross-references:** D-010, D-121, D-122, D-128, D-029, D-007, D-092.
|
||||
|
||||
---
|
||||
|
||||
### Architecture Layers
|
||||
|
||||
| Layer | What | How |
|
||||
|---|---|---|
|
||||
| Semantic base text | Culture-neutral behaviors and dialogue | Authored in RON files; serves as LLM seed, graceful fallback, and LLM-off experience simultaneously |
|
||||
| Tell behaviors | Mechanical signals (TellCategory) | Base-text passthrough — NEVER sent to LLM. Always served as authored. |
|
||||
| Tell context injectors | Active tell state influence on surrounding content | Per-TellCategory tone instructions shaping how behaviors/dialogue are re-voiced; tells inform without being re-voiced |
|
||||
| Culture injectors | Culture-specific voice (register, oath vocabulary, negatives) | 150–250 tokens per culture; sourced from NpcBlueprint.cultural_markers; universal negatives in shared prefix |
|
||||
| Trait + mood modifiers | Personality and current emotional state | ~10–25 tokens each; layered atop culture injector |
|
||||
| Re-voiced output | Cached, player-facing voiced content | Generated per (NPC × tell_state × culture); cached at generation time; served at runtime by lookup |
|
||||
|
||||
### Content Tiers
|
||||
|
||||
1. **Baked** — Hub zones (Sova Transit District) ship with pre-voiced content generated at build time and human-reviewed before shipping. This is the quality reference and the player's first-hours experience.
|
||||
2. **Pre-voiced** — Background queue generates voiced content for adjacent zones before the player arrives. Priority: Critical (P0, plot-critical) → High (P1, current zone) → Standard (P2, adjacent) → Background (P3, distant).
|
||||
3. **Base text fallback** — If pre-voicing has not completed, base text is served. Designed to be intentionally spare, not broken. Pre-voicing catches up in the background.
|
||||
|
||||
### Tell-State Variant Caching
|
||||
|
||||
Each behavior and dialogue line is pre-voiced in 6 variants: Neutral + 5 TellCategory states (Nervous, Angry, Friendly, Guarded, RoutineDeviation). Cache key: `(npc_stable_id, line_id, tell_state, culture_id)`. At runtime, the game reads the NPC's current tell state and serves the matching pre-voiced variant — zero runtime inference for tell-state changes.
|
||||
|
||||
Fallback order:
|
||||
1. Pre-voiced variant for current tell state → serve it
|
||||
2. Pre-voiced neutral variant → serve it (acceptable degradation)
|
||||
3. Base text → always present, always correct
|
||||
|
||||
### Data Model Changes Required
|
||||
|
||||
```rust
|
||||
// NpcBlueprint — tell_behaviors as first-class field, routing by field not content
|
||||
pub struct NpcBlueprint {
|
||||
pub observable_behaviors: Vec<String>, // → free re-voicing queue
|
||||
pub tell_behaviors: Vec<TellBehavior>, // → base-text passthrough always
|
||||
// ...
|
||||
}
|
||||
|
||||
pub struct TellBehavior {
|
||||
pub category: TellCategory, // Nervous | Angry | Friendly | Guarded | RoutineDeviation
|
||||
pub base_text: String, // base text — also the final shipped text; never re-voiced
|
||||
}
|
||||
|
||||
// Individual voiced lines — anchor line protection (D-092)
|
||||
pub struct VoicedLine {
|
||||
pub base_text: String,
|
||||
pub anchor_line: bool, // true = passthrough regardless of field; protects D-092 anchor lines
|
||||
}
|
||||
```
|
||||
|
||||
### Tell-as-Context: How Tell State Influences Surrounding Content
|
||||
|
||||
Tells are READ-ONLY inputs. The tell text is never sent to the LLM. When an NPC's tell state is active, it flows into the re-voicing prompt for the NPC's behaviors and dialogue as a **tone injector**.
|
||||
|
||||
**The effect:** An NPC with a Guarded tell should feel guarded in their dialogue — more clipped, more words chosen, a slight sense of something unsaid — while the base-text tell string remains the mechanical signal exactly as authored.
|
||||
|
||||
**The five tell-context tone injectors** (Gestalt v1, to be refined in Spike 1):
|
||||
|
||||
| TellCategory | Tone Injector |
|
||||
|---|---|
|
||||
| `Neutral` | *(no injector — free re-voicing with culture + trait only)* |
|
||||
| `Nervous` | "This NPC's words come slightly faster than usual, briefer. They don't elaborate. A phrase drops off before it's finished. Do not say they seem nervous or afraid." |
|
||||
| `Angry` | "This NPC's words are measured and deliberate — not shouting, containing. A word hits harder than the context requires. Do not say they seem angry." |
|
||||
| `Friendly` | "This NPC offers slightly more than asked. A word of genuine warmth lands casually. They don't perform friendliness — it just shows. Do not add compliments or over-warmth." |
|
||||
| `Guarded` | "This NPC chooses each word with a half-second more care than normal. They answer what was asked, no more. There is nothing wrong here. Do not say they seem guarded or evasive." |
|
||||
| `RoutineDeviation` | "This NPC is elsewhere in their mind. They are present but preoccupied — answers are on track but land a beat late. Do not explain why or name what they're thinking about." |
|
||||
|
||||
**Critical constraint on all tone injectors:** Do not name the internal state. Do not add information. Do not change the content — only the texture of expression. Results must pass the deniability test: could the player explain this phrasing without knowing the tell was active?
|
||||
|
||||
**Krenn-culture tell-tone table** (Miri v1 — culture-inflected expressions; one per culture required):
|
||||
|
||||
| Tell category | Krenn-inflected tonal register |
|
||||
|---|---|
|
||||
| Nervous | Answers run shorter than usual. Eyes stay on task. Nothing's wrong — they just have things to do. |
|
||||
| Guarded | Direct past the point of directness. Closes conversation paths fast without being unfriendly. |
|
||||
| Avoidance/relationship | Task-focused when this person is nearby. Finds work to do. Polite but not engaging. |
|
||||
| Hostile suppression (Angry) | Steady. Even. The kind of steady that takes effort to maintain. Not hostile — just flat in a way that doesn't feel natural for Krenn. |
|
||||
| RoutineDeviation | Unhurried. Unremarkably normal. Like nothing's worth noticing. |
|
||||
|
||||
Architecture: universal-first tell-context prompt. The universal phenomenon-class description (baseline readability) is always present. Cultural flavor is **conditional and additive** — the prompt asks the LLM whether it can add cultural texture without significantly changing the information conveyed. Humans are humans first; shiftiness, micro-expressions, and body language must remain universally recognizable. Cultural convention is sprinkled in sparingly, not substituted. Per-culture tell-tone tables are optional enrichment authored over time, not a launch requirement.
|
||||
|
||||
### Model and Runtime
|
||||
|
||||
- **Primary model:** Gemma 2 2B (Google, Apache 2.0 + Google Gemma ToU), Q4_K_M quantization, ~1.5 GB
|
||||
- **Fallback model:** Phi-3 (Microsoft, MIT license) — note: Phi-3-mini is 3.8B parameters, NOT 2B class. ~2.2 GB Q4, ~30% slower on minimum spec hardware
|
||||
- **No Chinese-origin models** (Qwen/Alibaba excluded by Jeroen's decision)
|
||||
- **Inference runtime:** `llama-cpp-2` (Rust bindings to llama.cpp), GGUF format
|
||||
- **Distribution:** Model bundled in game install (~1.5 GB added to base). No optional download.
|
||||
- **Thread isolation:** Separate thread pool for inference vs. world generation. Inference at below-normal OS priority.
|
||||
|
||||
---
|
||||
|
||||
## 2. D-123 Amendment and D-124 Supersession
|
||||
|
||||
### D-123 (Amended)
|
||||
|
||||
> ### D-123: Generative AI for NPC content — build-time authoring tool and runtime voice pipeline
|
||||
> - **Date (original):** 2026-03-05
|
||||
> - **Date (amended):** 2026-03-07
|
||||
> - **Decision:** The AI pipeline operates in two distinct modes with different safety profiles:
|
||||
>
|
||||
> **Build-time mode (authoring tool):** Content generated at build time for baked hub zones. Subject to mandatory human review before shipping. This preserves D-123's original authorial control constraint — AI as an accelerated authoring tool producing content humans review and approve.
|
||||
>
|
||||
> **Runtime mode (background enhancement):** Content generated during gameplay for non-baked zones, via a background inference queue, when "AI-Enhanced Dialogue" is enabled. Not human-reviewed per line. Safety provided by three layers: (1) base-text-as-fallback — always present and complete; (2) build-time-validated injectors — only pre-validated prompts used, never ad-hoc; (3) runtime contamination filter — lightweight check before content is served.
|
||||
>
|
||||
> - **Non-negotiable constraints (both modes):** Culture vectors are the primary prompt constraint. The AI does not default to genre conventions. Authorial control governs what the LLM may and may not produce through injector clauses, negative constraints, and pipeline routing rules. The AI pipeline applies voice to authored semantic content; it does not generate narrative decisions, base text, tell behaviors, secret-tier dialogue (D-028 Layer 3), or anchor lines (D-092). These categories are always authored and always served as-authored.
|
||||
>
|
||||
> - **Rationale:** Full pipeline (behaviors + dialogue) is the correct scope. A system that voices observed behavior but not spoken dialogue creates register whiplash at the highest-investment moment of player engagement. Build-time mode preserves the human-review safety model. Runtime mode enables scaling to the generated world with base-text fallback as the permanent safety net.
|
||||
|
||||
### D-124 (Superseded)
|
||||
|
||||
> D-124 is superseded by D-138. D-124 deferred in-game AI but left the door explicitly open. That door is now walked through. The system is not ollama-based — it uses `llama-cpp-2` with GGUF Q4_K_M quantization, bundled with the game, running background inference via an isolated thread pool. The key constraint from D-124 remains binding through D-123 (amended): this system does not drive live narrative decisions. It applies voice to authored semantic content.
|
||||
|
||||
---
|
||||
|
||||
## 3. Resolved Questions
|
||||
|
||||
### Q-057 (content authoring scale at O(R×Z×C))
|
||||
**RESOLVED by D-138.** The LLM re-voicing pipeline is the answer. Culture-neutral base text authored once per role/zone; culture injectors authored once per culture (~1 day per culture); LLM applies voice at runtime. The O(R×Z×C) scaling problem is replaced by O(R×Z) + O(C), where O(C) is a small constant.
|
||||
|
||||
### Q-012 (how to scale NPC voice across cultures without per-culture hand-authoring)
|
||||
**RESOLVED by D-138.** Same answer as Q-057. The culture injector system (8-10 clauses + 2 examples per culture) is the scaling mechanism. Each new culture requires ~1 day of copy work, not weeks of behavior authoring.
|
||||
|
||||
### Q-R1-01 (tell literacy model: cross-NPC grammar or fresh-each-time?)
|
||||
**RESOLVED.** Cross-NPC grammar at the phenomenon-class level. The player learns classes of observable behavior (suppression, avoidance, surveillance, nervous fidget, routine deviation) that map to NPC internal states. Tell re-voicing (if any) must preserve phenomenon-class membership, not just phrasing. This is established by `gen_tells()` producing ~12 distinct tell behavior strings across the entire game — a designed grammar, not random variation.
|
||||
|
||||
### Q-R1-02 (scope: behaviors only, or behaviors + dialogue?)
|
||||
**RESOLVED by Jeroen's decision.** Full pipeline: behaviors AND dialogue. "We don't introduce a precision laser cutting tool and then use it only to open boxes."
|
||||
|
||||
### Q-R1-03 (are tells a first-class data model field?)
|
||||
**RESOLVED.** `tell_behaviors: Vec<TellBehavior>` as a first-class field in `NpcBlueprint`, separate from `observable_behaviors`. Routing is by field, not content analysis. In production, tells are 5 `TellCategory` enums computed per-tick by `DerivedTellState` — making tell voicing a fixed 5-category × N-cultures library (~20-40 strings per culture), bakeable at build time.
|
||||
|
||||
### Q-R1-04 (effective token budget for cultural injectors?)
|
||||
**RESOLVED.** 150 tokens is insufficient for cultural philosophy; 200-250 tokens with hybrid format (instructions + 2 example pairs) is recommended for register accuracy. Universal negative injectors (NI-1 through NI-5, ~265 tokens full / ~100 tokens compressed) go in the shared system/prefix prompt — not the culture injector — preserving the full budget for culture-specific content. Troblum confirms that prompt length difference between 150-token and 500-token prompts adds only ~15% overhead (prefill is cheap; generation is the bottleneck).
|
||||
|
||||
### Q-R1-05 (minimum hardware CPU spec?)
|
||||
**RESOLVED.** Workshop assumption: 4-core 2019+ CPU (i5-9400 / Ryzen 5 3600). Gemma 2B Q4: 7-9 t/s on i5-9400, 9-12 t/s on Ryzen 5 3600. Zone pre-voicing (behaviors + dialogue) completes in 2-7 minutes on this hardware — comfortable for immersive-sim play patterns. No hard minimum spec floor (Jeroen's decision). Layered hardware detection handles the recommendation logic.
|
||||
|
||||
---
|
||||
|
||||
## 4. Spike 1 Definition
|
||||
|
||||
### Purpose
|
||||
Build the Rust inference plumbing and validate model/prompt quality before any game integration. Answer: "does this even play?"
|
||||
|
||||
### Deliverable: `sr-voice` CLI tool
|
||||
|
||||
A standalone Rust crate (`server/sr-voice/`) wrapping `llama-cpp-2`. CLI accepts a prompt (from file, stdin, or JSONL batch), runs inference, returns text + timing. No queue, no cache, no game integration.
|
||||
|
||||
```
|
||||
server/sr-voice/
|
||||
Cargo.toml
|
||||
src/
|
||||
main.rs # CLI entry point
|
||||
inference.rs # Model loading + generation wrapper
|
||||
prompt.rs # Prompt payload parsing
|
||||
```
|
||||
|
||||
Key CLI flags: `--model <path>`, `--threads <n>`, `--max-tokens <n>`, `--seed <n>`, `--json`, `--batch <jsonl-file>`, `--benchmark`.
|
||||
|
||||
### Participants
|
||||
Tyre builds the `sr-voice` CLI. Jeroen, Mellanie, and Paula run manual prompt experiments.
|
||||
|
||||
### Test Matrix
|
||||
11 prompt payloads (7 behaviors + 3 tells + 5 dialogue samples), each run through:
|
||||
- Both models: Gemma 2B Q4_K_M and Phi-3 (fallback)
|
||||
- 2-3 prompt template variants (instruction-only, few-shot, negative-constraint-heavy)
|
||||
|
||||
Key test payloads include:
|
||||
- B-01 to B-07: behavior samples across roles, moods, relationship states, tell-context (Mellanie's payloads)
|
||||
- T-01 to T-03: tell behaviors testing phenomenon-class preservation
|
||||
- D-01 to D-05: dialogue samples from neutral to high-affect with tell-context (Paula's payloads)
|
||||
|
||||
### Success Criteria (Gestalt's 5 criteria)
|
||||
|
||||
| Criterion | Hard requirement? | Target |
|
||||
|---|---|---|
|
||||
| Information preservation (behaviors) | No | ≥9/10 outputs |
|
||||
| Information preservation (dialogue) | No | ≥9/10 outputs |
|
||||
| Tell-context tone (undertone sensed without naming) | No | ≥8/10 outputs |
|
||||
| Tell-context: zero explicit state naming | **YES** | 0 instances across all outputs |
|
||||
| Cultural grammar survival (Krenn legible, blind review) | No | ≥8/10 correct identifications |
|
||||
| No false information (D-010 boundary) | **YES** | 0 instances |
|
||||
| Qualitative "real person" test | No | ≥1 convincing output per reviewer |
|
||||
|
||||
**Go/No-Go rule:** Both hard requirements met + ≥4/5 soft criteria pass → proceed to Spike 2 with the winning model. Hard requirement failure → fix prompt architecture before Spike 2 (never accept explicit state naming or false information).
|
||||
|
||||
**Model selection:** Winning model = passes both hard requirements and scores higher across soft criteria. If only Phi-3 meets quality bar, accept the RAM/throughput tradeoff and document why. If neither passes, fall back to base-text-only and investigate prompt architecture.
|
||||
|
||||
---
|
||||
|
||||
## 5. Spike 2 Definition
|
||||
|
||||
### Purpose
|
||||
Wire the validated Spike 1 runner into the game. Full architecture integration.
|
||||
|
||||
### Components (all from Tyre's spec)
|
||||
|
||||
**5.1 Inference thread pool**
|
||||
- 1 dedicated inference thread owning the `InferenceEngine`
|
||||
- Below-normal OS priority; inference yields to simulation and rendering
|
||||
- llama.cpp internal threading: physical_cores - world_gen_threads - 1
|
||||
|
||||
**5.2 Work queue**
|
||||
- `crossbeam-channel` bounded channel (capacity: 256)
|
||||
- Priority tiers: Critical (P0) → High (P1) → Standard (P2) → Background (P3)
|
||||
- Backpressure: queue full → drop item silently, game continues with base text
|
||||
- Zone transition: pause inference on `ZoneTransitionStart`, resume on `ZoneTransitionComplete`
|
||||
|
||||
**5.3 Voice cache**
|
||||
- Key: `(world_seed, culture_id, npc_stable_id, content_type, content_index)`
|
||||
- Format: MessagePack (D-020), stored per-zone in `user://voice_cache/{seed}.msgpack`
|
||||
- Invalidation: on seed change, model update, or injector version change
|
||||
- Baked content: ships as `res://voice_baked/{zone_id}.msgpack` game asset, never regenerated at runtime
|
||||
|
||||
**5.4 Tell-as-context prompt construction**
|
||||
The `build_prompt()` function reads `npc.cultural_markers` (Miri's source-of-truth recommendation) and injects the active `TellCategory` as a mood/state modifier. Tell text itself is never in the prompt.
|
||||
|
||||
**5.5 Observer integration**
|
||||
Observer reads behavior string:
|
||||
1. Check voice cache for (seed, culture, npc_id, behavior_index)
|
||||
2. Cache hit → use voiced string
|
||||
3. Cache miss → use base text (fallback)
|
||||
4. Tell state → always from `DerivedTellState` (passthrough, never from cache)
|
||||
|
||||
No changes to wire format (`ObserverSnapshot`). Client-transparent.
|
||||
|
||||
**5.6 Baked content generation**
|
||||
Build-time `make voice-bake` target runs inference against all hub NPC blueprints, writes `.voicecache` files. Human review by Paula/Mellanie before commit. Required CI check before game package builds.
|
||||
|
||||
**5.7 Hardware detection**
|
||||
Layer 1 (RAM check) → Layer 2 (TPT benchmark, 20 tokens) → Layer 3 (recommendation thresholds). See Section 8 for full spec.
|
||||
|
||||
### Effort estimate (Tyre)
|
||||
|
||||
| Work item | Sprints |
|
||||
|---|---|
|
||||
| Spike 1: `sr-voice` CLI | 1 |
|
||||
| Spike 1: Prompt testing (Mellanie/Paula/Jeroen) | 1 (parallel) |
|
||||
| Spike 2: Queue + cache + thread pool | 1.5 |
|
||||
| Spike 2: Tell-as-context prompt construction | 0.5 |
|
||||
| Spike 2: Observer integration | 0.5 |
|
||||
| Spike 2: Baked content generation tool | 0.5 |
|
||||
| Hardware detection system | 0.5 |
|
||||
| **Total** | **5.5 sprints** |
|
||||
|
||||
---
|
||||
|
||||
## 6. Authoring Workflow
|
||||
|
||||
### What the copy team authors
|
||||
|
||||
**Base text (ongoing, per zone/role/dialogue pool)**
|
||||
- `typical_behaviors` arrays in zone RON files
|
||||
- Dialogue line pools in D-028 tagged format
|
||||
- Quality bar: "deliberately sparse observation" — complete, evocative, culturally neutral. Not rough draft. Not placeholder.
|
||||
- Test: (1) Does this show a moment, not a category? (2) Could you imagine a specific person doing this? (3) Would you be okay if this were the only text the player sees?
|
||||
|
||||
**Culture injectors (once per culture, ~1 day of work)**
|
||||
- `voice_injectors` field in culture RON (new field)
|
||||
- 8-10 explicit LLM persona instruction sentences in second-person imperative register
|
||||
- 2 brief example pairs demonstrating correct culture voice
|
||||
- Krenn v2 is finalized (see Section 7.1 below) — ready for Spike 1
|
||||
|
||||
**Trait modifier clauses (once total, ~10 sentences)**
|
||||
- 1 injector clause per personality trait, 10 traits
|
||||
- Written in world-specific terms: "Bold" = "You say the uncomfortable thing in front of people."
|
||||
- Mellanie to draft all 10 before Spike 1
|
||||
|
||||
**Negative injectors (system prompt layer — written by Miri/Mellanie, integrated by Tyre)**
|
||||
- NI-1 through NI-5 in shared system/prefix prompt
|
||||
- Full version: ~265 tokens; compressed: ~100 tokens
|
||||
- Troblum confirms prompt length overhead is acceptable
|
||||
|
||||
**Anchor line flags (per notable NPC, Tier 1 and Tier 2 only)**
|
||||
- `anchor_line: bool` flag on individual lines (Paula's N-2 requirement)
|
||||
- Copy team flags lines that must never be re-voiced under any circumstances
|
||||
- Volume: small — only Tier 1 and Tier 2 notable NPCs
|
||||
|
||||
### What the copy team does NOT author
|
||||
- Tell behavior strings (algorithmically generated, fixed library per culture)
|
||||
- Tell category definitions (Gestalt/Tyre)
|
||||
- Voice cache infrastructure (Tyre)
|
||||
|
||||
### Review process
|
||||
|
||||
**Baked content (hub zones):** Mandatory human review. Paula and Mellanie review all generated lines against: (1) culture register correct, (2) no lore contamination, (3) base text content preserved. Sign-off required before commit. Estimated: 3-4 hours for Sova Transit District (~360 lines).
|
||||
|
||||
**Runtime pre-voiced content:** 5% sampling to log file, reviewed per sprint. Automated NI-1 through NI-5 keyword scan on all output — hits above 2% trigger prompt audit.
|
||||
|
||||
---
|
||||
|
||||
## 7. Key Artifacts
|
||||
|
||||
### 7.1 Krenn Culture Injectors v2 (finalized for Spike 1)
|
||||
|
||||
Source: `mellanie-round3.md`
|
||||
|
||||
```
|
||||
1. Be direct. No pleasantries. Everyone you talk to is short on time, and so are you.
|
||||
|
||||
2. You're working-class and pragmatic. Competence is what earns respect here, not rank
|
||||
or credentials. You grew up in a community where you either show up and do the work
|
||||
or you don't, and everyone notices which one you are.
|
||||
|
||||
3. You're suspicious of distant authority — management that hasn't worked a shift,
|
||||
institutions that talk big and deliver slow. You've seen it. It doesn't impress you.
|
||||
|
||||
4. When something surprises or frustrates you, expressions like "void take it", "stars",
|
||||
"cold vacuum", or "blood and void" come naturally. They're not dramatic — they're just
|
||||
how people here talk.
|
||||
|
||||
5. You use first names. Family names belong on contracts and arrest records, not in
|
||||
conversation.
|
||||
|
||||
6. Loyalty runs narrow and deep. Your crew, your shift, your street. Not abstractions.
|
||||
|
||||
7. You greet people briefly: "hey", "morning", "shift treating you alright?" No ceremony.
|
||||
|
||||
8. You're not rude — you're honest. If something's wrong, you say so. If it's fine,
|
||||
you say that too. You don't pad.
|
||||
```
|
||||
|
||||
**Example pairs (pattern anchors for small models):**
|
||||
```
|
||||
BASE: "declines to answer a question about the overnight run"
|
||||
VOICED: "Look, that's not mine to say."
|
||||
|
||||
BASE: "acknowledges a colleague's greeting while continuing to work"
|
||||
VOICED: "Hey. Yeah. Catch you at shift end."
|
||||
```
|
||||
|
||||
**Assembly notes:** Culture is the baseline for all Krenn NPCs. Void-oaths (clause 4) gated to high-affect contexts only. Trait modifiers and tell-context injectors layer on top.
|
||||
|
||||
### 7.2 Finalized Universal Negative Injectors (NI-1 through NI-5)
|
||||
|
||||
Source: `miri-round3.md`. These go in the shared system/prefix prompt for all re-voicing operations.
|
||||
|
||||
**NI-1 — No Religious Language:** "Do not use religious language of any kind: no prayer, no references to gods or deities, no spiritual practices, no phrases derived from religious traditions. Characters in this setting do not have canonical religious expression."
|
||||
|
||||
**NI-2 — No Military Ranks:** "Do not use military rank titles. Prohibited: Commander, Captain (except as vessel operators), Sergeant, General, Admiral, Lieutenant, Private, Corporal, Major, Colonel. Authority in this setting uses occupational and institutional titles: shift lead, port authority, supervisor, Commission officer."
|
||||
|
||||
**NI-3 — Technology Vocabulary:** "Use only the following terms for technology and infrastructure: insert (neural implant worn at the base of the skull), span gate (fixed transit installation for faster-than-light transit), horizon gate (alien-built gate at Oort-cloud distance), the Reach (the network of settled systems). Do not use: holoscreens, blasters, force fields, teleporters, mind-reading, jump drives, FTL, warp, neural link, brain chip, stasis pods."
|
||||
|
||||
**NI-4 — No Banter or Wit:** "Do not produce wit, quips, or wordplay intended to entertain the reader. Do not add levity not present in the original text. Humor in this setting is dry, incidental, and rare."
|
||||
|
||||
**NI-5 — No Earth-Origin Social References:** "Do not reference Earth, nations, sports, Earth history, Earth seasons, Earth religion, or other Earth-origin social structures. Earth-origin swearing (damn, hell, crap, Jesus, goddamn) should not appear — use culture-specific expressions instead."
|
||||
|
||||
**Total: ~265 tokens full. Compressed version (~100 tokens) available for throughput-constrained cases.**
|
||||
|
||||
### 7.3 Culture Injector Template (6-block structure for all future cultures)
|
||||
|
||||
Source: `miri-round3.md`
|
||||
|
||||
```
|
||||
[BLOCK 1 — REGISTER (~25 tokens)]
|
||||
Brief description of register style, why it is this way, one distinguishing marker.
|
||||
|
||||
[BLOCK 2 — CULTURAL CONTEXT (~25 tokens)]
|
||||
One sentence: what shaped this culture's voice. The social or environmental fact.
|
||||
|
||||
[BLOCK 3 — VOCABULARY (~40 tokens)]
|
||||
Exclamations: [closed list — ONLY these]
|
||||
Greetings: [list]
|
||||
Farewells: [list]
|
||||
Fillers: [NPC-specific — read from NpcBlueprint.cultural_markers.filler_words]
|
||||
|
||||
[BLOCK 4 — VALUES (~20 tokens)]
|
||||
Two core values expressed as behavioral instructions.
|
||||
|
||||
[BLOCK 5 — CULTURE-SPECIFIC NOT-LIST (~20 tokens)]
|
||||
2-3 exclusions specific to this culture (universal NIs already cover global set).
|
||||
|
||||
[BLOCK 6 — EXAMPLE PAIRS (~70-80 tokens)]
|
||||
BASE: [culture-neutral semantic line]
|
||||
[CULTURE]: [culture-voiced output]
|
||||
---
|
||||
BASE: [culture-neutral semantic line]
|
||||
[CULTURE]: [culture-voiced output]
|
||||
```
|
||||
|
||||
**Per-culture ongoing deliverable:** Each culture profile also requires a 5-row tell-tone table (Miri's Section 4) mapping TellCategory to culture-inflected tonal register. See Krenn reference table in Section 1 above.
|
||||
|
||||
### 7.4 Dialogue Re-voicing Constraints (6 rules)
|
||||
|
||||
Source: `paula-round3.md`
|
||||
|
||||
1. **D-1: Secret-tier passthrough** — Lines tagged `trust: secret` (D-028 Layer 3) never enter the re-voicing queue. Served as authored, always.
|
||||
2. **D-2: Epistemic weight must not shift** — Hedge words ("I think," "might," "probably") and direct evidence markers ("I saw," "I was there") must survive verbatim with the same epistemic force.
|
||||
3. **D-3: Access tier feel must be preserved** — `insider` must feel insider; `authority` must feel institutional; `peer` must feel lateral. The tag governs eligibility; the register governs feel.
|
||||
4. **D-4: Named entities are passthrough within output** — Proper nouns in base text (NPC names, locations, technology terms) must appear verbatim in re-voiced output. Extraction step before re-voicing, injected as protected list.
|
||||
5. **D-5: Relationship-specific lines are passthrough** — Lines naming a specific third-party NPC or describing a specific interpersonal event are not re-voiced.
|
||||
6. **D-6: Tell-context cannot override culture register** — Tell-context modifies emotional inflection within the culture register; it does not replace the register.
|
||||
|
||||
### 7.5 Spike 1 Prompt Payloads
|
||||
|
||||
**Behavior samples (Mellanie):** 7 payloads covering neutral ambient (B-1, B-2), high-affect (B-3), relationship-driven positive/negative (B-4, B-5), tell-context (B-6), social greeting (B-7).
|
||||
|
||||
**Dialogue samples (Paula + Mellanie):** 5 payloads covering low/medium/high access tiers with neutral, Nervous, Guarded, RoutineDeviation, and Angry tell states.
|
||||
|
||||
Full prompts with character context, injector stacks, and quality-pass criteria are in `mellanie-round3.md` and `paula-round3.md`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Hardware Detection Spec
|
||||
|
||||
Three-layer system. No hard minimum spec floor. If a player can load the model, they can run the feature.
|
||||
|
||||
**Layer 1 — RAM Check**
|
||||
|
||||
| Free RAM | Action |
|
||||
|---|---|
|
||||
| ≥ 2.0 GB | Pass — proceed to Layer 2 |
|
||||
| 1.6–2.0 GB | Marginal — warn, offer to proceed |
|
||||
| < 1.6 GB | Fail — feature disabled with message |
|
||||
|
||||
Message on fail: *"AI-Enhanced Dialogue requires 2 GB of free memory to run. Your system currently has [X] GB available. Close other applications and try again, or leave the setting off — the game is complete either way."*
|
||||
|
||||
**Layer 2 — Time-Per-Token Benchmark**
|
||||
|
||||
Runs once per installation. 150-token synthetic prompt, 20 tokens of output, temperature 0.0 (deterministic). Cached in `{user_data}/ai-dialogue-config.json`.
|
||||
|
||||
| Tokens/sec | Status | Player message |
|
||||
|---|---|---|
|
||||
| ≥ 6 t/s | Green | No message — feature enables silently |
|
||||
| 3–6 t/s | Yellow | "Running at [X] t/s — pre-voicing will work for main characters and key scenes. Background NPCs may show base text until queue catches up." |
|
||||
| < 3 t/s | Red | "Running very slowly — we recommend leaving this off, but the choice is yours." |
|
||||
|
||||
**Layer 3 — Ongoing Monitoring**
|
||||
|
||||
Inference worker maintains moving average TPT over last 10 tasks. If sustained degradation >40% from benchmark baseline (thermal throttling, power saver mode): settings status changes to yellow, tooltip explains, offers to suspend. Not a forced disable.
|
||||
|
||||
Battery/power-saver detection: Windows `GetSystemPowerStatus()`, Linux `/sys/class/power_supply/`. Auto-suspend inference when on battery at power saver, resume when plugged in.
|
||||
|
||||
**Toggle label:** "AI-Enhanced Dialogue" (Jeroen's decision — transparency is the priority).
|
||||
|
||||
---
|
||||
|
||||
## 9. Distribution Spec
|
||||
|
||||
Model bundled in game install. No optional download step.
|
||||
|
||||
```
|
||||
SettledReach/
|
||||
├── game.exe / settled-reach.x86_64
|
||||
├── SettledReach.pck
|
||||
├── models/
|
||||
│ └── voice-pipeline/
|
||||
│ ├── gemma-2b-q4_k_m.gguf (~1.5 GB)
|
||||
│ └── model-manifest.json (version, checksum, performance profile)
|
||||
├── data/
|
||||
│ └── baked-voice/
|
||||
│ ├── sova-transit-district.voicecache
|
||||
│ └── [other hub zones].voicecache
|
||||
└── [other game files]
|
||||
```
|
||||
|
||||
Model loaded lazily (on first "AI-Enhanced Dialogue" enable). Cold start performance unaffected. Checksum verification on load against `model-manifest.json`. Mismatch → log error, disable feature, surface message.
|
||||
|
||||
**itch.io:** Split installer (base game + model pack) as two files. Both required. Player downloads both; installer merges.
|
||||
|
||||
**Steam:** Mark model GGUF file as separate depot chunk so routine game patches don't re-download it.
|
||||
|
||||
**Platform notes:** macOS Apple Silicon — Metal acceleration, 15-30 t/s expected (always green). Steam Deck — Vulkan acceleration, 6-10 t/s (green). Windows/Linux CPU-only — 7-12 t/s on 2019+ hardware.
|
||||
|
||||
---
|
||||
|
||||
## 10. Risk Register
|
||||
|
||||
Source: `troblum-round3.md` with additions from all rounds. 12 risks.
|
||||
|
||||
| ID | Risk | Severity | Status | Mitigation summary |
|
||||
|---|---|---|---|---|
|
||||
| R-001 | LLM output quality below reference bar | HIGH | OPEN | Spike 1 quality gate. Fallback: ship base text only. Hybrid injector format addresses small-model register failure. |
|
||||
| R-002 | RAM pressure / OOM after Layer 1 pass | MEDIUM | MITIGATED | 350 MB safety margin. Graceful degradation on allocation failure. Ongoing monitoring. |
|
||||
| R-003 | Thermal throttling degrades TPT from benchmark | MEDIUM-HIGH | MITIGATED | Moving-average TPT monitoring. Yellow-status notification. Zone-transition pause provides thermal recovery. |
|
||||
| R-004 | Lore contamination — franchise bleed | MEDIUM-HIGH | MITIGATED | NI-1 through NI-5 in system prompt. Baked content human review. Runtime blocklist scan. 5% sampling. |
|
||||
| R-005 | Lore contamination — wrong culture register | MEDIUM | MITIGATED | Hybrid injector format (instructions + examples). Oath vocabulary tracked per output. Spike 1 measures directly. |
|
||||
| R-006 | Cache invalidation failure | LOW | MITIGATED | Hash-based key including injector version and model version. Append-only with TTL sweep. |
|
||||
| R-007 | Install size friction (1.5 GB model) | HIGH | ACCEPTED | Jeroen's decision. Split-installer for itch.io. Steam depot chunk separation for patch efficiency. |
|
||||
| R-008 | Model provenance / licensing change | MEDIUM | PARTIALLY MITIGATED | Gemma Apache 2.0 (current). Phi-3 MIT (fallback). License reviewed at each game version. Optional feature means removable without breaking gameplay. |
|
||||
| R-009 | Save compatibility / voiced text drift on model update | LOW-MEDIUM | MITIGATED | Cache persistent in user data. Old entries unreachable (key changes on model version). Graceful degradation to base text on miss. |
|
||||
| R-010 | Inference worker crash or hang | MEDIUM | MITIGATED | 60-second per-task timeout. Supervised restart. Auto-disable after 3 crashes per session. Max tokens hard limit. |
|
||||
| R-011 | Phi-3 misclassified as "2B class" | LOW | RESOLVED | Phi-3-mini is 3.8B params. ~2.2 GB Q4, ~30% slower than Gemma 2B. Layer 1 threshold for Phi-3 would be 2.7 GB. Documented. |
|
||||
| R-012 | Baked/runtime content divergence | LOW-MEDIUM | MITIGATED | `make voice-bake` enforces model version match. Same prompt templates for both. Required CI check. |
|
||||
|
||||
**R-001 is the primary open risk.** The team does not know if 2B model quality meets the bar until Spike 1 runs. This is the central unknown the workshop was designed to push toward resolving.
|
||||
|
||||
---
|
||||
|
||||
## 11. Player Experience Architecture
|
||||
|
||||
Source: `ozzie-round3.md`
|
||||
|
||||
**Three interdependent pillars:**
|
||||
|
||||
1. **Base text is a designed aesthetic, not a fallback.** It reads as deliberately sparse observation. Standard mode (AI-Enhanced Dialogue OFF) is a complete experience. The copy team authors base texts to this bar — not to a rough-draft bar.
|
||||
|
||||
2. **Tell contrast is intentional.** Tells in base text read as detective observations against culture-voiced ambient content. This register difference signals "pay attention here." It is a designed feature, not a seam.
|
||||
|
||||
3. **Player autonomy is respected at every hardware decision.** The game recommends. It never forces. "AI-Enhanced Dialogue" toggle is always present in settings. The player can always override any recommendation.
|
||||
|
||||
**Base text quality bar examples:**
|
||||
|
||||
| Placeholder (below bar) | Deliberately spare (at bar) |
|
||||
|---|---|
|
||||
| "tends crops in the field" | "works a crop row with slow, unhurried passes" |
|
||||
| "checks credentials at the gate" | "holds out a hand for credentials without looking up from the gate log" |
|
||||
| "I don't know anything about that." | "That's not something I know anything about." |
|
||||
|
||||
**Base text elevation priority order:** Hub zones (Sova Transit District) → plot-critical NPC roles → tells → ambient roles in non-hub zones.
|
||||
|
||||
**Zone re-entry transition rule:** Base text shown on first zone entry per session. If player leaves and re-enters, voiced content is shown if available. Provides natural diegetic cover for the base text → voiced text transition. Tells never change — always passthrough, always anchoring.
|
||||
|
||||
---
|
||||
|
||||
## 12. Open Items (Post-Workshop)
|
||||
|
||||
These require follow-up but do not block the spike.
|
||||
|
||||
| Item | Owner | Urgency |
|
||||
|---|---|---|
|
||||
| Formally record D-138 in `decisions/content.md` | SI (ticketed) | Before Spike 2 |
|
||||
| Record D-123 amendment and D-124 supersession in `decisions/content.md` | SI (ticketed) | Before Spike 2 |
|
||||
| Draft and share 10 trait modifier clauses | Mellanie | Before Spike 1 prompt testing |
|
||||
| Add `voice_injectors` field to culture RON schema | Tyre | Before Spike 2 |
|
||||
| Add `tell_behaviors` field to `NpcBlueprint` | Tyre | Before Spike 2 |
|
||||
| Add `anchor_line: bool` field to individual dialogue lines | Tyre | Before Spike 2 |
|
||||
| Krenn tell-tone table to be mapped to canonical TellCategory enum once confirmed | Miri | After Spike 1 |
|
||||
| Confirm full set of negative injectors fit within throughput budget (compressed vs. full NI set) | Troblum | Before Spike 1 |
|
||||
| ~~Confirm tell-context clauses are culture-neutral (not per-culture) — Mellanie's question~~ **RESOLVED:** Universal baseline always present; cultural flavor is conditional and additive, never substituted. Per-culture tell-tone tables are optional enrichment, not a launch blocker. | Jeroen (decided) | Resolved |
|
||||
| Diagram: voice pipeline architecture (base text → re-voicing queue → LLM → cache → observer) | Tyre/Qatux | After Spike 1 |
|
||||
|
||||
---
|
||||
|
||||
*Qatux — 2026-03-07*
|
||||
Reference in New Issue
Block a user