feat(voice): "Konversation fortführen" hält offen — auch bei Fast-Path-Befehl
Stefans Repro: "spiel Spotify auf Smartphone ab ABER Konversation fortführen" → die Kette endete trotzdem. Grund: "spiel spotify" trifft den Fast-Path (Regex), der den Satz-Rest nicht versteht → converse=false aus dem Skill-Manifest; ARIAs [[WEITER]]-Marker lebt in Claude, der wurde uebersprungen. Fix: _user_wants_conversation_continue() — Gegenstueck zu _user_wants_conversation_ end(). Erkennt "Konversation/Gespraech fortfuehren/weiterfuehren/offen halten/nicht beenden", "weiter reden/sprechen" etc. und erzwingt converse=true an ALLEN Returns (fast-path/local/claude), auch wenn das Manifest false sagt. [[ENDE]]/_wants_end hat Vorrang bei Widerspruch. Getestet inkl. Negativfaelle (sofort/spotify ab). Deploy: Brain-Neustart. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+38
-5
@@ -1418,6 +1418,30 @@ def _user_wants_conversation_end(text: str) -> bool:
|
||||
return bool(_END_CONVERSATION_RE.search(_strip_leading_hint_blocks(text)))
|
||||
|
||||
|
||||
# Gegenstueck zu _END: expliziter "Konversation OFFEN halten / fortfuehren"-Wunsch.
|
||||
# Wichtig fuer BEFEHLE die den Fast-Path treffen: "spiel Spotify ab ABER Konversation
|
||||
# fortfuehren" — der Fast-Path (Regex) versteht den Satz-Rest nicht und wuerde mit
|
||||
# converse=false schliessen. Dieser Detektor erzwingt converse=true, auch am
|
||||
# Fast-Path, egal was das Skill-Manifest sagt. Nomen+Verb in einem Satzteil, plus
|
||||
# "weiter reden/sprechen" ohne Nomen.
|
||||
_CONT_VERB = (r"(?:fortf[uü]hr\w*|fortsetz\w*|weiterf[uü]hr\w*|weiter\s*mach\w*|"
|
||||
r"weiter\b|fort\b|offen\s+(?:halten|lassen)|nicht\s+beenden|weiterlauf\w*)")
|
||||
_CONTINUE_CONVERSATION_RE = re.compile(
|
||||
rf"\b{_CONV_NOUN}\b[^.!?]{{0,20}}\b{_CONT_VERB}"
|
||||
rf"|\b{_CONT_VERB}[^.!?]{{0,20}}\b{_CONV_NOUN}\b"
|
||||
rf"|\bweiter\s*(?:reden|sprechen|quatschen|plaudern|labern)\b",
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
|
||||
def _user_wants_conversation_continue(text: str) -> bool:
|
||||
"""True, wenn der User explizit weiter im Gespraech bleiben will (converse=true
|
||||
erzwingen — auch bei einem Fast-Path-Befehl). [[ENDE]]/_wants_end hat Vorrang."""
|
||||
if not text:
|
||||
return False
|
||||
return bool(_CONTINUE_CONVERSATION_RE.search(_strip_leading_hint_blocks(text)))
|
||||
|
||||
|
||||
def _normalize_for_fast_match(text: str) -> str:
|
||||
norm = _strip_leading_hint_blocks(text).lower()
|
||||
norm = _fold_umlauts(norm)
|
||||
@@ -1826,10 +1850,13 @@ class Agent:
|
||||
if not user_message:
|
||||
raise ValueError("Leere Nachricht")
|
||||
|
||||
# Expliziter "Konversation/Kette beenden"-Wunsch (deterministisch). Wird an
|
||||
# JEDEM Return auf converse=false angewendet — unabhaengig davon, ob ARIA
|
||||
# den [[ENDE]]-Marker setzt. Stefans fester Trigger "Konversation Ende".
|
||||
# Explizite Gespraechs-Steuerung vom USER (deterministisch, an JEDEM Return
|
||||
# angewendet — auch am Fast-Path, den die LLM-Marker nicht erreichen):
|
||||
# _wants_end → converse=false ("Konversation Ende")
|
||||
# _wants_continue → converse=true ("... aber Konversation fortfuehren")
|
||||
# End hat Vorrang bei Widerspruch.
|
||||
_wants_end = _user_wants_conversation_end(user_message)
|
||||
_wants_continue = (not _wants_end) and _user_wants_conversation_continue(user_message)
|
||||
|
||||
# Events vom letzten Turn weglassen
|
||||
self._pending_events = []
|
||||
@@ -1860,6 +1887,8 @@ class Agent:
|
||||
converse = bool(getattr(self, "_fast_path_converse", False))
|
||||
if _wants_end:
|
||||
converse = False
|
||||
elif _wants_continue:
|
||||
converse = True
|
||||
# Fast-Path = reiner Steuerbefehl, nie eine Rueckfrage → awaiting=False.
|
||||
return fast_reply, "fast-path", speak, converse, False
|
||||
|
||||
@@ -1881,6 +1910,8 @@ class Agent:
|
||||
converse = getattr(self, "_local_turn_converse", True)
|
||||
if _wants_end:
|
||||
converse = False
|
||||
elif _wants_continue:
|
||||
converse = True
|
||||
# Local ist tool-loses Reden; blockierende Rueckfragen macht Claude.
|
||||
return local_reply, "local", speak, converse, False
|
||||
|
||||
@@ -2129,10 +2160,12 @@ class Agent:
|
||||
speak = _speak_ov
|
||||
if _conv_ov is not None:
|
||||
converse = _conv_ov
|
||||
# Expliziter User-Wunsch "Konversation beenden" gewinnt IMMER — Frage wird
|
||||
# noch beantwortet (speak bleibt), aber danach zurueck aufs Wake-Word.
|
||||
# Explizite User-Woerter gewinnen ueber Marker/Manifest: "Konversation
|
||||
# beenden" → zu; "... fortfuehren" → offen halten. End hat Vorrang.
|
||||
if _wants_end:
|
||||
converse = False
|
||||
elif _wants_continue:
|
||||
converse = True
|
||||
# awaiting_reply = ARIA stellt eine blockierende Rueckfrage (Queue pausiert).
|
||||
return (final_reply, "claude", speak, converse, awaiting_reply)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user