feat(simulation): voice pipeline integration — Factual bypass, observer wiring, tell iteration (#650 #652 #651)
ContentType::Factual bypasses LLM for fact-bearing lines (numbers, denials). Observer enrichment systems rewrite dialogue/conversation text with voiced versions before snapshot assembly. Friendly and RoutineDeviation tells get concrete surface-pattern examples. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,13 @@ pub enum ContentType {
|
||||
Dialogue,
|
||||
/// Observable behavior description — re-voiced with "Describe".
|
||||
Behavior,
|
||||
/// Fact-bearing line — numbers, causal chains, denials.
|
||||
///
|
||||
/// Factual lines bypass the LLM entirely and are served as base text.
|
||||
/// Spike 2 showed 2B models corrupt quantitative content ("14 crates in
|
||||
/// bay seven" → "fourteen crates are missing") and invert denials.
|
||||
/// The simulation is the truth layer — the LLM only handles register.
|
||||
Factual,
|
||||
}
|
||||
|
||||
/// Result of prompt building, including which injections fired.
|
||||
@@ -57,6 +64,15 @@ OUTPUT CONSTRAINTS:\n\
|
||||
/// "make sentences shorter" instruction causes destructive compression that
|
||||
/// strips facts. Long-Angry instead preserves the full claim and focuses
|
||||
/// intensity on one sentence.
|
||||
///
|
||||
/// ## Iteration history for Friendly and RoutineDeviation
|
||||
///
|
||||
/// Spike 2 showed these two tells produce output indistinguishable from neutral
|
||||
/// on Gemma 2B. Tracked against a 3-iteration budget (#651):
|
||||
///
|
||||
/// - **Iteration 1** (Sprint 26): Replaced abstract instructions with concrete
|
||||
/// surface-pattern examples. Friendly: closing aside pattern. RoutineDeviation:
|
||||
/// self-correction/echo pattern. Both more imitable for a 2B model.
|
||||
fn tell_injector(category: TellCategory) -> &'static str {
|
||||
match category {
|
||||
TellCategory::Nervous => {
|
||||
@@ -70,9 +86,12 @@ fn tell_injector(category: TellCategory) -> &'static str {
|
||||
Do not say they seem angry."
|
||||
}
|
||||
TellCategory::Friendly => {
|
||||
"TONE: Add one small extra detail or aside that wasn't strictly necessary. \
|
||||
Example: \"Inspection's tomorrow — should be fine, though.\" \
|
||||
Do not add compliments or forced warmth."
|
||||
// Iteration 1: replaced abstract "add detail" with a concrete closing-aside
|
||||
// pattern. 2B models need a recognisable syntactic target, not a description
|
||||
// of intent. "actually" / "so that's something" are learnable short tokens.
|
||||
"TONE: End with a brief unprompted aside — a phrase the person didn't need to say. \
|
||||
Example: \"Parts are in, so that's something.\" or \"Shift's been quiet, actually.\" \
|
||||
Do not use praise or warm adjectives. One short beat after the main point."
|
||||
}
|
||||
TellCategory::Guarded => {
|
||||
"TONE: Use formal, precise words. Answer exactly what was asked, nothing extra. \
|
||||
@@ -80,9 +99,12 @@ fn tell_injector(category: TellCategory) -> &'static str {
|
||||
Do not say they seem guarded."
|
||||
}
|
||||
TellCategory::RoutineDeviation => {
|
||||
"TONE: Start the sentence on topic, then add a brief unfinished thought about something else. \
|
||||
Example: \"Pressure's fine. I was going to — anyway, it's logged.\" \
|
||||
Do not explain what they were thinking about."
|
||||
// Iteration 1: replaced the two-step interrupted-thought pattern (too complex
|
||||
// for 2B) with a simpler self-correction / word-echo pattern. The model only
|
||||
// needs to repeat a key word or phrase — one concrete surface action.
|
||||
"TONE: Repeat a key word or phrase, as if catching mid-thought. \
|
||||
Example: \"Logged it. Got it logged, yeah.\" or \"Pressure's — pressure's holding.\" \
|
||||
Do not explain. Just the echo."
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -269,6 +291,11 @@ pub fn build_prompt(
|
||||
"Describe the following action as a third-person observer. \
|
||||
Preserve all individual actions in sequence. Do not extract conclusions"
|
||||
}
|
||||
ContentType::Factual => {
|
||||
// Factual lines must be intercepted before reaching build_prompt.
|
||||
// If this branch is hit, the caller has a bug.
|
||||
unreachable!("ContentType::Factual must not reach build_prompt — bypass at worker")
|
||||
}
|
||||
};
|
||||
parts.push(String::new());
|
||||
parts.push(format!("TASK: {}.", task_verb));
|
||||
@@ -370,6 +397,7 @@ mod tests {
|
||||
TellCategory::Friendly,
|
||||
],
|
||||
}],
|
||||
behavior_modifiers: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,6 +427,7 @@ mod tests {
|
||||
voice_persona: None,
|
||||
voice_examples: vec![],
|
||||
occasional_injections: vec![],
|
||||
behavior_modifiers: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user