# Conflicts: # CHANGELOG.md # content/_meta/README.md # content/_meta/npc-authoring-style-guide.md # wiki/_templates/cultural-group.md # wiki/_templates/institution.md # wiki/_templates/star-system.md # wiki/characters/devra.md # wiki/characters/drin.md # wiki/characters/harek.md # wiki/characters/lera-sessik.md # wiki/characters/maret-korr.md # wiki/characters/naia-tamm.md # wiki/characters/nils-davan.md # wiki/characters/pell.md # wiki/characters/renn.md # wiki/characters/resha.md # wiki/characters/sabel.md # wiki/characters/sera-venn.md # wiki/characters/torek-lintar.md # wiki/characters/voss.md # wiki/star-systems/krenn/index.md
18 KiB
title, description, type, status, workshop, agent, round, created
| title | description | type | status | workshop | agent | round | created |
|---|---|---|---|---|---|---|---|
| Tyre Round 2: Proposal Evaluation and Technical Resolution | Technical resolution of behaviours scope, tell_behaviors field, and token budgets | workshop | archived | llm-voice-pipeline | tyre | 2 | 2026-03-07 |
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 Van Maanen's Star 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:
- Sprint N: Behaviors + tells (Proposal B scope). One model (Gemma 2B Q4). Clear success criteria.
- 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.
- 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:
-
NpcBlueprint.observable_behaviors: Vec<String>— role-specific actions fromRoleSpec.typical_behaviors. These are the ambient behaviors. -
DerivedTellState(ECS component) — aTellCategoryenum (Nervous, Angry, Friendly, Guarded, RoutineDeviation) derived each tick from simulation state bytell_state::derive_tell_state(). This is NOT a string. It's a simulation signal. -
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:
// 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:
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: Van Maanen's Star (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:
- Register (direct, clipped) — so it doesn't produce flowery prose
- Oath vocabulary (closed list) — so it uses "void take it" not "damn it"
- Negative constraints — so it doesn't produce franchise bleed
It does NOT need to understand why Van Maanen's Star 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:
- Prompt processing (prefill): Process all input tokens. This is parallelizable and fast. For llama.cpp on CPU: ~100-500 tokens/sec depending on hardware.
- 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 Van Maanen's Star 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 Van Maanen's Star 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:
- Model capability (can 2B handle it?)
- Prompt quality (are the injectors sufficient?)
- 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.