from __future__ import annotations import re from dataclasses import dataclass from utils import normalize_for_search class ConversationIntent: EMPTY = "empty" GREETING = "greeting" HELP = "help" THANKS = "thanks" OUT_OF_SCOPE = "out_of_scope" LEGAL_QUERY = "legal_query" GREETING_ANSWER = ( "Merhaba. Ben MitrAnlil Yükseköğretim Mevzuatı AI Asistanıyım. " "2547, 2914 ve 2809 sayılı kanunlara dayalı olarak kaynak gösterimli cevaplar üretirim. " "YÖK, üniversiteler, akademik personel, öğrenciler, teşkilat, ödenekler, görevler, yetkiler ve ilgili hükümler hakkında soru sorabilirsiniz." ) HELP_ANSWER = ( "Ben yükseköğretim mevzuatı için geliştirilmiş kaynak gösterimli bir mevzuat asistanıyım. " "Sorunuzu kanundaki madde, alt hüküm ve kanıt parçalarıyla eşleştirerek cevaplamaya çalışırım. " "Örneğin dekanın görevleri, YÖK'ün oluşumu, ek ders ücreti, üniversite ödeneği, teşkilat yapısı veya kurumların kuruluşu hakkında soru sorabilirsiniz." ) THANKS_ANSWER = "Rica ederim. Yükseköğretim mevzuatı ile ilgili başka bir soru sorabilirsiniz." OUT_OF_SCOPE_ANSWER = ( "Bu pilot yükseköğretim mevzuatı kapsamında kaynak gösterimli cevap üretmek üzere tasarlanmıştır. " "YÖK, üniversiteler, akademik personel, öğrenciler, diplomalar, teşkilat, ödenekler, görevler ve yetkiler gibi konularda soru sorabilirsiniz." ) @dataclass(frozen=True) class ConversationIntentResult: intent: str answer: str = "" LEGAL_CUE_TERMS = { "egitim", "uzaktan egitim", "yonetmelik", "yonerge", "tez", "savunma", "tez savunmasi", "basvuru", "devam", "akademik takvim", "2547", "2914", "2809", "kanun", "madde", "yok", "yök", "yuksekogretim", "yükseköğretim", "universite", "üniversite", "rektor", "rektör", "dekan", "senato", "fakulte", "fakülte", "enstitu", "enstitü", "yuksekokul", "yüksekokul", "meslek", "onlisans", "önlisans", "on lisans", "ön lisans", "lisans", "diploma", "ogrenci", "öğrenci", "ogretim", "öğretim", "profesor", "profesör", "docent", "doçent", "arastirma", "araştırma", "gorev", "görev", "yetki", "sorumluluk", "atan", "atama", "kurul", "kurulus", "kuruluş", "disiplin", "harc", "harç", "katki", "katkı", "ucret", "ücret", "denklik", "azami", "sure", "süre", "ek ders", "ödenek", "odenek", "derece", "kademe", "teşkilat", "teskilat", "faaliyete", "ek gösterge", "ek gosterge", "makam tazminatı", "makam tazminati", "maaş", "maas", "aylık", "aylik", "mali hak", "özlük hakkı", "ozluk hakki", "tazminat", "ödeme", "odeme", "gösterge", "gosterge", "sosyal hak", } OUT_OF_SCOPE_CUE_TERMS = { "hava", "yemek", "tarif", "siir", "şiir", "hikaye", "futbol", "borsa", "kripto", "film", "muzik", "müzik", "resim", "kod yaz", "mail yaz", "cevir", "çevir", } def detect_conversation_intent(message: str) -> ConversationIntentResult: raw = (message or "").strip() normalized = normalize_for_search(raw) compact = normalized.replace(" ", "") if not normalized: return ConversationIntentResult(ConversationIntent.EMPTY, "Lütfen yükseköğretim mevzuatı ile ilgili bir soru yazın.") if _is_greeting(normalized, compact): return ConversationIntentResult(ConversationIntent.GREETING, GREETING_ANSWER) if _is_thanks(normalized): return ConversationIntentResult(ConversationIntent.THANKS, THANKS_ANSWER) if _is_help_request(normalized): return ConversationIntentResult(ConversationIntent.HELP, HELP_ANSWER) if _has_legal_cue(normalized): return ConversationIntentResult(ConversationIntent.LEGAL_QUERY, "") if _has_out_of_scope_cue(normalized): return ConversationIntentResult(ConversationIntent.OUT_OF_SCOPE, OUT_OF_SCOPE_ANSWER) # Questions without legal cues should still reach the MCKF engine when they look # like normative questions. This avoids blocking short queries such as "kim atar?" # in follow-up-like usage while keeping pure greetings outside retrieval. if any(term in normalized for term in ("nedir", "nasil", "nasıl", "kim", "hangi", "ne zaman", "kac", "kaç", "gerekir", "olur")): return ConversationIntentResult(ConversationIntent.LEGAL_QUERY, "") return ConversationIntentResult(ConversationIntent.OUT_OF_SCOPE, OUT_OF_SCOPE_ANSWER) def _is_greeting(normalized: str, compact: str) -> bool: greetings = { "merhaba", "selam", "selamlar", "gunaydin", "günaydın", "iyi gunler", "iyi günler", "iyi aksamlar", "iyi akşamlar", "iyi geceler", "kolay gelsin", "mrb", "slm", } if normalized in greetings or compact in {g.replace(" ", "") for g in greetings}: return True if len(normalized.split()) <= 3 and any(g in normalized for g in greetings): return True return False def _is_thanks(normalized: str) -> bool: return len(normalized.split()) <= 6 and any(term in normalized for term in ("tesekkur", "teşekkür", "sag ol", "sağ ol", "eyvallah", "tşk")) def _is_help_request(normalized: str) -> bool: phrases = ( "ne yapabilirsin", "neler yapabilirsin", "nasil calisiyorsun", "nasıl çalışıyorsun", "bu sistem nedir", "hangi sorular", "ne sorabilirim", ) if any(phrase in normalized for phrase in phrases): return True # "yardım" tek başına yardım niyetidir; "kurumlararası yardımlaşma" # içindeki kök aynı niyet değildir. return bool(re.search(r"(? bool: return any(term in normalized for term in LEGAL_CUE_TERMS) def _has_out_of_scope_cue(normalized: str) -> bool: return any(term in normalized for term in OUT_OF_SCOPE_CUE_TERMS)