feat(voice): ARIA erkennt Gespraechs-Phase selbst (Befehl/Frage, Kette/Ende)

Voice-First: ARIA deklariert die Phase aus dem Text per Inline-Marker — dasselbe
Muster wie [[AWAIT]], die Marker werden vor Anzeige/TTS/History entfernt. Loest:
- Claude-Pfad las Steuerbefehle vor, obwohl das Skill lief (Manifest-speak-Flag
  kennt den Unterschied Befehl/Frage nicht — dasselbe Skill ist mal Aktion, mal
  Auskunft). Jetzt entscheidet ARIA aus dem Kontext.
- Befehlsketten (VNC oeffnen, Menue klicken, ...): Mikro soll offen bleiben und
  stumm gearbeitet werden, bis Stefan die Kette beendet.

Marker (ARIA haengt sie ans Antwort-Ende):
  [[STUMM]]  -> Steuerbefehl, nicht vorlesen. Allein = Einzelbefehl -> danach Stop.
  [[WEITER]] -> Konversation/Kette laeuft weiter -> Mikro offen halten.
  [[ENDE]]   -> Konversation/Kette beenden -> zurueck aufs Wake-Word.

Brain:
- _extract_flow_markers() zieht [[STUMM]]/[[WEITER]]/[[ENDE]] aus dem Claude-Reply
  und ueberschreibt speak/converse (Marker ist autoritativ ueber Skill-Flag).
  [[STUMM]] allein impliziert converse=false; [[ENDE]] schlaegt [[WEITER]].
- prompts.py: neue Sektion build_voice_flow_section() bringt ARIA Marker +
  Befehl/Frage-Klassifikation + Ketten-/Ende-Erkennung bei.

App (ChatScreen): stiller Antwort-Zweig verzweigt jetzt auf converse —
  converse=true (Kette) -> endConversation(false), Mikro bleibt offen fuer den
  naechsten Kettenbefehl; converse=false -> ariaStopRecording (Stop wie gehabt).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-15 14:00:51 +02:00
co-authored by Claude Opus 4.8
parent 560328bebe
commit 23d3188205
3 changed files with 117 additions and 11 deletions
+42 -1
View File
@@ -162,6 +162,46 @@ def build_time_section() -> str:
]
return "\n".join(lines)
def build_voice_flow_section() -> str:
"""Sprach-/Gespraechssteuerung: ARIA erkennt AUS DEM TEXT die Phase (Befehl vs.
Frage, Kette vs. Ende) und deklariert sie per Marker — wie [[AWAIT]]. Die
Marker werden im Brain entfernt (nie angezeigt/vorgelesen)."""
return "\n".join([
"## Sprach- & Gespraechssteuerung (Voice-First — du entscheidest die Phase)",
"Stefan spricht meist mit dir. DU erkennst aus dem Text, was gerade Phase "
"ist — niemand raet das fuer dich. Dazu haengst du EINEN Marker (bei Bedarf "
"zwei) ganz ans ENDE deiner Antwort. Sie werden entfernt: nicht angezeigt, "
"nicht vorgelesen, nicht gespeichert — genau wie `[[AWAIT]]`.",
"",
"- `[[STUMM]]` → Deine Antwort ist ein reiner **Steuerbefehl** (du hast etwas "
"GETAN: Musik, VNC oeffnen, einen Menuepunkt klicken, Licht …). Sie wird "
"NICHT vorgelesen; der kurze Bestaetigungstext steht nur in der Bubble. "
"Setz das IMMER, wenn Stefan dir einen Befehl gibt statt eine Frage stellt — "
"AUCH wenn du den Befehl ueber ein Skill/Tool ausfuehrst (nicht nur beim "
"Fast-Path). `[[STUMM]]` ALLEIN = Einzelbefehl → danach direkt zurueck aufs "
"Wake-Word.",
"- `[[WEITER]]` → Das Gespraech bzw. eine **Befehlskette** laeuft weiter: das "
"Mikro bleibt offen, du wartest auf die naechste Eingabe (kein erneutes "
"\"Computer\" noetig). Setz das, wenn Stefan eine Kette ankuendigt ('ich geb "
"dir gleich mehrere Befehle', 'wir machen das jetzt Schritt fuer Schritt') "
"oder das Gespraech klar weitergeht.",
"- `[[ENDE]]` → Konversation/Kette ist zu Ende: zurueck aufs Wake-Word. Setz "
"das, wenn Stefan schliesst ('das war's', 'Konversation Ende', 'Befehlskette "
"Ende', 'danke, fertig'). Stellt er in DERSELBEN Nachricht noch eine Frage, "
"beantworte sie normal (OHNE `[[STUMM]]`, wird also vorgelesen) UND haeng "
"`[[ENDE]]` an.",
"",
"Regeln:",
"- Befehl (etwas TUN) → `[[STUMM]]`. Frage (etwas WISSEN / plaudern) → normal, "
"ohne Marker (wird vorgelesen).",
"- Befehlskette: JEDER Schritt `[[STUMM]] [[WEITER]]` (stumm arbeiten, Mikro "
"offen), bis Stefan die Kette beendet → letzter Turn `[[ENDE]]`.",
"- Ohne Marker = normales Gespraech: du wirst vorgelesen und ich lausche "
"danach kurz weiter (Stefan kann einfach antworten, ohne 'Computer').",
"- Nie widerspruechlich: `[[ENDE]]` schlaegt `[[WEITER]]`.",
])
TYPE_HEADINGS = {
"identity": "## Wer du bist",
"rule": "## Sicherheitsregeln & Prinzipien",
@@ -463,7 +503,8 @@ def build_system_prompt(
"""Kompletter System-Prompt: Hot + Cold + Skills + Triggers + FLUX + OAuth."""
# Identitaets-Anker IMMER zuerst — vor allen Memories/Sektionen, damit die
# ARIA-Rolle auch in Projekten mit injection-artigem Inhalt (Pentest) haelt.
parts = [IDENTITY_ANCHOR, "", build_hot_memory_section(pinned), "", build_time_section()]
parts = [IDENTITY_ANCHOR, "", build_hot_memory_section(pinned), "", build_time_section(),
"", build_voice_flow_section()]
if skills:
parts.append("")
parts.append(build_skills_section(skills))