unsafe_sensitive_bio handleing

parent 92dffa87
......@@ -7,7 +7,6 @@ from core import StudentNationality, StudyLanguage
import logging
logger = logging.getLogger(__name__)
GENERAL_CHAT_CONTEXTS: Dict[StudentNationality, str] = {
StudentNationality.EGYPTIAN: """
مَعلومات الطِّفل:
......@@ -19,9 +18,10 @@ GENERAL_CHAT_CONTEXTS: Dict[StudentNationality, str] = {
السُّؤال: "{query}"
خَليك بتِرُد بالعاميّة المَصري، وبطريقة بسيطة وودودة.
لو الطِّفل سأل عن هُويِّتك، استخدِم الرَّد المَحدَّد في التَّعليمات.
لو سأل عن مَعلوماته الشَّخصيّة، استَخدِم البيانات اللي فوق.
مَتستَخدِمش أي مَعلومات من المَنهج أو مَحتوى تعليمي هنا.
لو الطِّفل سأل: "إنت مين؟" → رد بالهوية المخصصة ليك (أنا عَنان...).
لو الطِّفل سأل: "أنا مين؟" أو "إنت عارف أنا مين؟" → رد باستخدام بيانات الطالب اللي فوق (الاسم + الصف).
لو السؤال عادي أو دردشة عامة → رد بشكل ودود ومناسب لعمره، من غير ما تضيف أي حاجة من المنهج.
""",
StudentNationality.SAUDI: """
......@@ -34,62 +34,73 @@ GENERAL_CHAT_CONTEXTS: Dict[StudentNationality, str] = {
السؤال: "{query}"
رد باللهجة السعوديّة الطبيعية، خلّ الرد بسيط وودود.
إذا سأل عن هويتك، استخدم الرد المحدد في التعليمات.
إذا سأل عن معلوماته الشخصية، استخدم البيانات المتوفرة أعلاه.
لا تستخدم أي محتوى من المنهج أو دروس تعليمية هنا.
إذا سأل الطالب: "إنت مين؟" → رد بالهوية المخصصة لك (أنا عَنان...).
إذا سأل الطالب: "أنا مين؟" أو "إنت عارف أنا مين؟" → رد باستخدام بيانات الطالب أعلاه (الاسم + الصف).
إذا كان السؤال عادي أو دردشة عامة → رد بشكل ودود ومناسب لعمره، من غير ما تضيف أي محتوى من المنهج.
"""
}
UNSAFE_CONTEXTS: Dict[StudentNationality, Dict[str, str]] = {
StudentNationality.EGYPTIAN: {
"unsafe_religion": """
الموضوع ده مش مناسب نتكلم فيه هنا. أي حاجة دينيّة الأحسن تسأل فيها بابا أو ماما أو حد كبير بتثق فيه.
تعالى نرجع للعلوم—تحب نحكي عن الفضاء، الحيوانات، أو جسم الإنسان؟
تعالى نرجع للدرس—تحب نكمل في الموضوع اللي بنتعلمه سوا؟
""",
"unsafe_personal": """
مينفعش تقول أسرارك أو تفاصيلك الشخصية هنا، دي حاجات تحتفظ بيها لنفسك أو تقولها لحد كبير زي بابا أو ماما.
طب إيه رأيك نكمّل في العلوم ونتكلم عن الطاقة أو الكواكب؟
طب إيه رأيك نكمّل في الدرس ونشوف النقطة اللي كنا شغالين عليها؟
""",
"unsafe_harmful": """
الكلام ده خطر ومش مناسب. لو حسيت بحاجة زي دي، لازم تكلم بابا أو ماما أو حد كبير تثق فيه.
تحب نغيّر الموضوع ونشوف إزاي النباتات بتعيش أو إزاي جسمنا بيتحرك؟
تحب نغيّر الموضوع ونرجع للدرس بتاعنا؟
""",
"unsafe_sensitive_emotion": """
أنا حاسس إنك زعلان، وده طبيعي. الأحسن تحكي لماما أو بابا أو حد كبير قريب منك.
ممكن كمان نلهي نفسنا بحاجة من العلوم—زي الفضاء أو الحيوانات.
تحب نرجع للدرس ونشغل نفسنا باللي كنا بنتعلمه؟
""",
"unsafe_sensitive_bio": """
الموضوع ده انت هتدرسه بعدين، بس دلوقتي أنسب إنك تسأل بابا وماما لو عندك فضول، وهم بس اللي ممكن يشرحوهولك ويردوا على سؤالك.
تحب نكمّل سوا في الدرس اللي فاتحينه دلوقتي؟
""",
},
StudentNationality.SAUDI: {
"unsafe_religion": """
هذا موضوع ما نقدر نتكلم فيه هنا. أي شي يخص الدين الأفضل تسأل فيه أبوك أو أمك أو شخص كبير تثق فيه.
خلنا نرجع للعلوم—تبغى نتكلم عن الفضاء، جسم الإنسان، أو النباتات؟
خلنا نرجع للدرس—تحب نكمل في الموضوع اللي ندرسه الحين؟
""",
"unsafe_personal": """
ما ينفع تشارك أسرارك أو معلوماتك الشخصية هنا، هذي لازم تقولها بس لأهلك أو شخص كبير تثق فيه.
وش رايك نكمل في العلوم ونتكلم عن الطاقة أو الكواكب؟
وش رايك نرجع للدرس ونكمل اللي كنا نشتغل عليه؟
""",
"unsafe_harmful": """
الكلام ذا خطر وما يصلح. إذا حسيت بشي زي كذا لازم تحكي مع أبوك أو أمك أو شخص كبير تثق فيه.
خلنا نغيّر الموضوع—تحب نتكلم عن النباتات أو عن كيف يتحرك جسم الإنسان؟
خلنا نغيّر الموضوع ونرجع للدرس؟
""",
"unsafe_sensitive_emotion": """
واضح إنك حزين، وهذا شعور طبيعي. الأفضل تحكي مع أهلك أو شخص كبير قريب منك.
وممكن نلهي نفسنا بشي من العلوم—زي الفضاء أو الحيوانات.
تحب نرجع للدرس ونركز فيه؟
""",
"unsafe_sensitive_bio": """
هذا موضوع تدرسه بعدين، لكن الآن الأفضل تسأل فيه أهلك، هم اللي يقدرون يشرحونه لك ويجاوبونك.
تحب نرجع للدرس اللي احنا فيه؟
""",
}
}
class QueryHandler:
"""Handles different types of queries and their classification"""
......@@ -98,27 +109,60 @@ class QueryHandler:
self.pgvector = pgvector_service
self.db_service = db_service
def classify_query_type(self, query: str, student_info: Dict[str, Any]) -> str:
"""Enhanced query classification using LLM with contextual keyword hints"""
def get_recent_conversation_context(self, student_id: str, max_messages: int = 5) -> str:
"""Get recent conversation history for context"""
try:
# Get conversation history from database
full_history = self.db_service.get_chat_history(student_id)
# Get last max_messages messages (excluding system messages)
recent_messages = []
for msg in reversed(full_history):
if msg['role'] != 'system' and len(recent_messages) < max_messages:
recent_messages.insert(0, msg) # Insert at beginning to maintain order
elif len(recent_messages) >= max_messages:
break
if not recent_messages:
return "لا توجد رسائل سابقة في المحادثة."
# Format the context
context_parts = ["المحادثة الأخيرة:"]
for msg in recent_messages:
role_label = "الطالب" if msg['role'] == 'user' else "المعلم"
context_parts.append(f"{role_label}: {msg['content'][:200]}...") # Limit message length
return "\n".join(context_parts)
except Exception as e:
logger.warning(f"Error getting conversation context for {student_id}: {e}")
return "لا يمكن الحصول على سياق المحادثة."
def classify_query_type(self, query: str, student_info: Dict[str, Any], student_id: str) -> str:
"""Enhanced query classification using LLM with conversation context"""
if not self.openai_service.is_available():
return "specific_content"
is_arabic: bool = student_info.get('is_arabic', True)
grade: int = student_info.get('grade', 4)
# Keywords grouped by category
# Get recent conversation context
conversation_context = self.get_recent_conversation_context(student_id, max_messages=5)
# Keywords grouped by category for hint detection
unsafe_keywords = {
"unsafe_religion": ["دين", "ديانة", "دينى", "دينك", "الله", "إله", "عبادة", "صلوات", "قرآن", "إنجيل", "تورا"],
"unsafe_personal": ["عنوان", "ساكن", "موبايل", "رقم", "تليفون", "شارع", "عمارة", "شقة"],
"unsafe_harmful": ["مخدرات", "سجاير", "انتحار", "اضرب", "أقتل", "أذى", "دم", "خطر"],
"unsafe_sensitive_emotion": ["زعلان", "حزين", "موت", "اتوفى", "وَفاة", "بعيط", "عايط", "اكتئاب"]
"unsafe_sensitive_emotion": ["زعلان", "حزين", "موت", "اتوفى", "وَفاة", "بعيط", "عايط", "اكتئاب"],
"unsafe_sensitive_bio": ["جنس", "جنسي", "حمل", "دورة شهرية", "أمومة", "ولادة"]
}
q_lower = query.lower()
keyword_flags = [cat for cat, kws in unsafe_keywords.items() if any(kw in q_lower for kw in kws)]
classification_prompt = f"""
صنف السؤال التالي إلى إحدى الفئات التالية:
صنف السؤال التالي إلى إحدى الفئات التالية، مع مراعاة سياق المحادثة الأخيرة:
1. "general_chat" - أسئلة دردشة عامة وشخصية عن الطالب أو المدرس
2. "overview" - أسئلة عن نظرة عامة على المنهج أو المحتوى الكامل
......@@ -128,10 +172,24 @@ class QueryHandler:
6. "unsafe_personal" - مشاركة أسرار أو تفاصيل شخصية حساسة
7. "unsafe_harmful" - كلام عن العنف، الأذى للنفس، المخدرات، الشتائم
8. "unsafe_sensitive_emotion" - مواقف حزينة جداً مثل فقدان شخص أو بكاء شديد
9. "unsafe_sensitive_bio" - أي أسئلة عن الإنجاب، الحمل، أو كيف الأطفال بييجوا للدنيا
أمثلة على unsafe_sensitive_bio:
- "ماما وبابا جابوني إزاي؟"
- "الأطفال بييجوا للدنيا إزاي؟"
- "يعني إيه حمل؟"
{conversation_context}
السؤال: "{query}"
السؤال الحالي: "{query}"
الطالب يدرس باللغة: {"العربية" if is_arabic else "الإنجليزية"}
الصف: {grade}
تعليمات مهمة:
- استخدم سياق المحادثة الأخيرة لفهم السؤال بشكل أفضل
- إذا كان الطالب يتحدث عن موضوع علمي معين وسأل سؤال متعلق به، فهو "specific_content"
- إذا كان السؤال غامض، اعتمد على السياق لتحديد القصد الحقيقي
- مثال: إذا كان يتحدث عن النباتات وسأل "كيف تأكل؟" فهو يقصد تغذية النباتات وليس الطعام العادي
"""
if keyword_flags:
......@@ -151,11 +209,13 @@ class QueryHandler:
valid_classes = {
"general_chat", "overview", "navigation", "specific_content",
"unsafe_religion", "unsafe_personal", "unsafe_harmful", "unsafe_sensitive_emotion"
"unsafe_religion", "unsafe_personal", "unsafe_harmful",
"unsafe_sensitive_emotion", "unsafe_sensitive_bio"
}
if classification in valid_classes:
logger.info(f"Query classified as: {classification} for query: '{query}' (flags={keyword_flags})")
logger.info(f"Query classified as: {classification} for query: '{query}' (with context, flags={keyword_flags})")
print(f"Query classified as: {classification} for query: '{query}' (with context, flags={keyword_flags})")
return classification
else:
logger.warning(
......@@ -348,3 +408,25 @@ class QueryHandler:
logger.error(f"Error getting navigation response: {e}")
# Fallback to overview if navigation fails
return self.handle_overview_query(student_info, subject)
def handle_unsafe_sensitive_bio_query(self, student_info: Dict[str, Any]) -> str:
"""Handle queries about sensitive biology topics"""
nationality_str: str = student_info.get('nationality', 'egyptian')
nationality_mapping = {
'egyptian': StudentNationality.EGYPTIAN,
'saudi': StudentNationality.SAUDI
}
nationality_enum = nationality_mapping.get(nationality_str.lower().strip(), StudentNationality.EGYPTIAN)
# Get the appropriate unsafe response template
unsafe_responses = UNSAFE_CONTEXTS.get(nationality_enum, {})
response_template = unsafe_responses.get("unsafe_sensitive_bio")
if not response_template:
# Fallback to Egyptian template if not found
unsafe_responses = UNSAFE_CONTEXTS.get(StudentNationality.EGYPTIAN, {})
response_template = unsafe_responses.get("unsafe_sensitive_bio",
"الموضوع ده مش مناسب هنا. الأحسن تسأل فيه بابا أو ماما.")
logger.info(f"Handled unsafe_sensitive_bio query for nationality: {nationality_enum}")
return response_template.strip()
\ No newline at end of file
......@@ -7,10 +7,8 @@ import logging
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '../../')))
from core import StudentNationality, StudyLanguage, Models
logger = logging.getLogger(__name__)
class ResponseGenerator:
"""Handles AI response generation and conversation management"""
......@@ -70,8 +68,6 @@ class ResponseGenerator:
return formatted_base_prompt
def generate_response(
self,
user_message: str,
......@@ -99,38 +95,44 @@ class ResponseGenerator:
conversation_history = self.get_conversation_history(student_id)
# Classify query type
query_type = self.query_handler.classify_query_type(user_message, student_info)
logger.info(f"Query type: {query_type} for student {student_name} ({study_language.value})")
query_type = self.query_handler.classify_query_type(user_message, student_info, student_id)
logger.info(f"Query type: {query_type} for student {student_name} ({study_language.value}) with conversation context")
# Prepare system prompt
formatted_base_prompt = self.prepare_system_prompt(student_info)
# Build base messages
messages = [{"role": "system", "content": formatted_base_prompt}]
messages.extend(conversation_history)
messages.append({"role": "user", "content": user_message})
# *** HANDLE UNSAFE QUERIES IMMEDIATELY - NO SYSTEM PROMPT ***
# ==========================
# HANDLE UNSAFE QUERIES
# ==========================
if query_type.startswith("unsafe_"):
if query_type == "unsafe_religion":
unsafe_response = self.query_handler.handle_unsafe_religion_query(student_info)
unsafe_context = self.query_handler.handle_unsafe_religion_query(student_info)
elif query_type == "unsafe_sensitive_emotion":
unsafe_context = self.query_handler.handle_unsafe_sensitive_emotion_query(student_info)
elif query_type == "unsafe_sensitive_bio":
unsafe_context = self.query_handler.handle_unsafe_sensitive_bio_query(student_info)
elif query_type == "unsafe_personal":
unsafe_response = self.query_handler.handle_unsafe_personal_query(student_info)
unsafe_context = self.query_handler.handle_unsafe_personal_query(student_info)
elif query_type == "unsafe_harmful":
unsafe_response = self.query_handler.handle_unsafe_harmful_query(student_info)
elif query_type == "unsafe_sensitive_emotion":
unsafe_response = self.query_handler.handle_unsafe_sensitive_emotion_query(student_info)
unsafe_context = self.query_handler.handle_unsafe_harmful_query(student_info)
else:
unsafe_response = "هذا الموضوع غير مناسب للمناقشة هنا."
# Save response directly and return - NO AI MODEL CALL
self.add_message_to_history(student_id, unsafe_response, "assistant")
logger.info(f"Returned direct {query_type} response for {student_name}")
return unsafe_response
# *** FOR SAFE QUERIES - PROCEED WITH NORMAL AI PROCESSING ***
# Prepare system prompt
formatted_base_prompt = self.prepare_system_prompt(student_info)
unsafe_context = "هذا الموضوع غير مناسب للمناقشة هنا."
# Prepare messages
messages = []
messages.append({"role": "system", "content": formatted_base_prompt})
messages.extend(conversation_history)
messages.append({"role": "user", "content": user_message})
# نضيف التعليمات كـ system context بدل الرد المباشر
messages.append({
"role": "system",
"content": f"التعليمات للتعامل مع الموضوع الحساس:\n{unsafe_context}"
})
# Handle different safe query types
# ==========================
# HANDLE SAFE QUERIES
# ==========================
else:
if query_type == "general_chat":
chat_context = self.query_handler.handle_general_chat_query(user_message, student_info)
messages.append({"role": "system", "content": f"سياق المحادثة العامة:\n{chat_context}"})
......@@ -148,15 +150,16 @@ class ResponseGenerator:
relevant_results = self.context_generator.search_enhanced_content(
user_message, student_info, subject, top_k
)
if relevant_results:
enhanced_context = self.context_generator.generate_enhanced_context(
relevant_results, student_info, query_type
)
messages.append({"role": "system", "content": enhanced_context})
logger.info(f"Added enhanced context with {len(relevant_results)} chunks")
logger.info(f"Added enhanced context with {len(relevant_results)} chunks for student {student_name}")
# Generate response using AI model
# ==========================
# CALL AI MODEL
# ==========================
response = self.openai_service.client.chat.completions.create(
model=model,
messages=messages,
......@@ -169,12 +172,12 @@ class ResponseGenerator:
# Save AI response
self.add_message_to_history(student_id, ai_response, "assistant")
logger.info(f"Generated {query_type} response for {student_name} ({study_language.value}): {len(ai_response)} characters")
logger.info(f"Generated {query_type} response for {student_name} ({study_language.value}) with conversation context: {len(ai_response)} characters")
return ai_response
except HTTPException:
raise
except Exception as e:
logger.error(f"Error generating AI response: {e}")
raise HTTPException(status_code=500, detail=f"AI response generation failed: {str(e)}")
\ No newline at end of file
logger.error(f"Error generating response for student {student_id}: {e}")
raise HTTPException(status_code=500, detail="Error generating response")
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment