File size: 5,947 Bytes
273b770
 
 
 
eff511c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c534a8
 
 
 
 
eff511c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273b770
 
 
 
 
 
 
 
 
 
eff511c
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
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"(?<![a-z0-9])yardim(?![a-z0-9])", normalized))


def _has_legal_cue(normalized: str) -> 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)