Browse Source

Classifier: Slot-Extraktion gezielt fuer erkannten Intent

Problem: KeywordMatcher.match() matcht gegen ALLE Intents und gibt
den falschen Slot zurueck (z.B. status='ein' statt query='rammstein').

Fix: _extract_slots_for_intent() matcht die Patterns des erkannten
Intents gezielt gegen den Text. "musik von rammstein spielen" wird
jetzt korrekt als query="musik von rammstein" extrahiert.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
patrick 1 week ago
parent
commit
bb8b9cc468
1 changed files with 48 additions and 8 deletions
  1. 48 8
      plugins/nlp_classifier/main.py

+ 48 - 8
plugins/nlp_classifier/main.py

@@ -185,14 +185,8 @@ class NLPClassifierPlugin(TrixyPlugin):
                 confidence = prediction.confidence
                 matched_by = "classifier"
 
-                # Slot-Extraktion via KeywordMatcher (der Classifier erkennt Intents,
-                # der KeywordMatcher extrahiert Slots aus den Patterns)
-                slots = prediction.slots or {}
-                if self._keyword_matcher:
-                    kw_slots = self._keyword_matcher.match(text)
-                    if kw_slots and kw_slots.slots:
-                        slots = kw_slots.slots
-                        pdebug(f"[Classifier] Slots via KeywordMatcher: {slots}")
+                # Slot-Extraktion: KeywordMatcher gegen den erkannten Intent matchen
+                slots = self._extract_slots_for_intent(text, intent)
 
                 if log_predictions:
                     pinfo(
@@ -266,6 +260,52 @@ class NLPClassifierPlugin(TrixyPlugin):
 
     # === Hilfsmethoden ===
 
+    def _extract_slots_for_intent(self, text: str, intent_name: str) -> dict[str, Any]:
+        """
+        Extrahiert Slots fuer einen bestimmten Intent.
+
+        Nutzt den KeywordMatcher um die Patterns des erkannten Intents
+        gezielt gegen den Text zu matchen. Fallback auf einfache Heuristik.
+
+        Args:
+            text: Eingabetext
+            intent_name: Vom Classifier erkannter Intent
+
+        Returns:
+            Dict mit extrahierten Slots
+        """
+        if not self._keyword_matcher:
+            return {}
+
+        # Strategie 1: KeywordMatcher Patterns fuer diesen Intent direkt matchen
+        compiled_patterns = self._keyword_matcher._compiled.get(intent_name, [])
+        if compiled_patterns:
+            text_lower = text.lower()
+            # Tokenisieren (wie der KeywordMatcher es macht)
+            import re
+            tokens = re.findall(r"[\w]+", text_lower)
+
+            best_slots: dict[str, Any] = {}
+            best_conf = 0.0
+
+            for pattern in compiled_patterns:
+                result = self._keyword_matcher._match_pattern(tokens, pattern)
+                if result and result.get("confidence", 0) > best_conf:
+                    best_conf = result["confidence"]
+                    best_slots = result.get("slots", {})
+
+            if best_slots:
+                pdebug(f"[Classifier] Slots extrahiert: {best_slots}")
+                return best_slots
+
+        # Strategie 2: Generischen Match versuchen (beliebiger Intent)
+        kw_result = self._keyword_matcher.match(text)
+        if kw_result and kw_result.slots:
+            pdebug(f"[Classifier] Slots via generischem Match ({kw_result.intent}): {kw_result.slots}")
+            return kw_result.slots
+
+        return {}
+
     def _get_session_info(self, satellite_id: str) -> dict[str, Any]:
         """Holt Session-Info fuer den Satellite."""
         try: