""" Context Analyzer - Understands user intent and needs Determines what type of response is most appropriate """ from typing import Dict, List, Optional class ContextAnalyzer: """ Optimized Context Analyzer - Balanced between simplicity and coverage Handles 90% of scenarios with 50% less complexity """ # Define patterns once as class variables (better performance) # Use compound patterns to reduce false positives URGENT_PATTERNS = [ 'đang đau', 'đau quá', 'khó chịu', 'không chịu nổi', 'cấp cứu', 'khẩn cấp', 'gấp', 'ngay', 'đau không thể', 'không aguanta được' # Strong pain indicators ] # Context-specific urgent combinations (must have BOTH) URGENT_COMBINATIONS = [ ('làm sao', ['hết đau', 'giảm đau', 'đỡ', 'ngay']), ('giúp tôi', ['đau', 'khó chịu', 'không chịu']), ('phải làm gì', ['đau', 'khó chịu', 'ngay', 'gấp']), ('cần gì', ['giảm đau', 'hết đau', 'cấp cứu']) ] INFO_PATTERNS = [ 'tại sao', 'nguyên nhân', 'có phải', 'bị gì', 'là gì', 'thế nào', 'như thế nào' ] PREVENTION_PATTERNS = [ 'phòng ngừa', 'tránh', 'không bị', 'hạn chế', 'làm sao để không', 'để khỏi' ] @staticmethod def is_vague_query(user_query: str) -> bool: """ Detect if user query is too vague/ambiguous """ vague_patterns = [ 'không khỏe', 'mệt', 'khó chịu', 'không ổn', 'giúp tôi', 'cần giúp', 'phải làm sao', 'không biết', 'chẳng hiểu', 'làm gì bây giờ' ] query_lower = user_query.lower() # Check if query is very short and vague if len(user_query.split()) <= 3: if any(pattern in query_lower for pattern in vague_patterns): return True # Check if query has no specific symptom/goal has_specifics = any(word in query_lower for word in [ 'đau', 'sốt', 'ho', 'buồn nôn', # Symptoms 'giảm cân', 'tăng cân', 'tập', # Goals 'ăn', 'thực đơn', 'calo' # Nutrition ]) if not has_specifics and any(pattern in query_lower for pattern in vague_patterns): return True return False @staticmethod def analyze_user_intent(user_query: str, chat_history: List) -> Dict: """ Simplified but effective intent analysis with context awareness Returns only what's needed for response generation """ query_lower = user_query.lower() # Check if query is too vague is_vague = ContextAnalyzer.is_vague_query(user_query) # Smart urgency check with context urgency = 'medium' # default # Check direct urgent patterns if any(p in query_lower for p in ContextAnalyzer.URGENT_PATTERNS): urgency = 'high' # Check combination patterns (need both parts) else: for trigger, contexts in ContextAnalyzer.URGENT_COMBINATIONS: if trigger in query_lower: if any(ctx in query_lower for ctx in contexts): urgency = 'high' break # If not urgent, check if it's informational if urgency != 'high' and any(p in query_lower for p in ContextAnalyzer.INFO_PATTERNS): urgency = 'low' # Determine primary intent (simplified) intent = 'general' # default if urgency == 'high': intent = 'immediate_relief' elif any(p in query_lower for p in ContextAnalyzer.INFO_PATTERNS): intent = 'information' elif any(p in query_lower for p in ContextAnalyzer.PREVENTION_PATTERNS): intent = 'prevention' # Simple decision: solution vs education needs_solution = urgency in ['high', 'medium'] needs_education = urgency == 'low' or intent in ['information', 'prevention'] # Add conversation stage for compatibility conversation_stage = len(chat_history) if chat_history else 0 return { 'intent': intent, 'urgency': urgency, 'needs_solution': needs_solution, 'needs_education': needs_education, 'is_vague': is_vague, 'needs_clarification': is_vague, 'conversation_stage': conversation_stage } @staticmethod def determine_response_structure(context: Dict) -> Dict[str, any]: """ Determine how to structure the response based on context Returns dict with: - structure: 'solution_first', 'assessment_first', 'education_first' - include_immediate: bool - include_prevention: bool - include_referral: bool """ if context['urgency'] == 'high': return { 'structure': 'solution_first', 'include_immediate': True, 'include_prevention': True, # But after solution 'include_referral': True, 'tone': 'supportive_urgent' } elif context['intent'] == 'diagnosis': return { 'structure': 'assessment_first', 'include_immediate': False, 'include_prevention': True, 'include_referral': False, 'tone': 'informative' } elif context['intent'] == 'prevention': return { 'structure': 'education_first', 'include_immediate': False, 'include_prevention': True, 'include_referral': False, 'tone': 'educational' } else: # Default balanced return { 'structure': 'solution_first', 'include_immediate': True, 'include_prevention': True, 'include_referral': True, 'tone': 'balanced' } @staticmethod def format_contextual_response( symptom_assessment: str, solutions: List[str], preventions: List[str], response_structure: Dict ) -> str: """ Format response based on context and user needs """ response = "" if response_structure['structure'] == 'solution_first': # IMMEDIATE RELIEF FIRST if response_structure['include_immediate'] and solutions: response += "**Để giảm triệu chứng ngay, bạn có thể:**\n" for i, solution in enumerate(solutions[:3], 1): # Top 3 immediate actions response += f"{i}. {solution}\n" response += "\n" # MEDICATION OPTIONS (if urgent) if response_structure['tone'] == 'supportive_urgent': response += "**Về thuốc:**\n" response += "- Có thể dùng thuốc kháng acid (Maalox, Gaviscon) nếu đau do acid\n" response += "- Thuốc chống co thắt (Buscopan) nếu đau quặn\n" response += "⚠️ *Nên tham khảo dược sĩ trước khi dùng*\n\n" # WARNING SIGNS if response_structure['include_referral']: response += "**⚠️ Đi khám ngay nếu:**\n" response += "- Đau không giảm sau 2 giờ\n" response += "- Kèm sốt, nôn, tiêu chảy\n" response += "- Đau dữ dội tăng dần\n\n" # PREVENTION (after immediate care) if response_structure['include_prevention'] and preventions: response += "**Sau khi đỡ, để phòng tránh:**\n" for prevention in preventions[:3]: response += f"• {prevention}\n" elif response_structure['structure'] == 'assessment_first': # Start with assessment/diagnosis response += symptom_assessment + "\n\n" # Then solutions if solutions: response += "**Cách xử lý:**\n" for solution in solutions: response += f"• {solution}\n" elif response_structure['structure'] == 'education_first': # Start with education/prevention if preventions: response += "**Để phòng ngừa hiệu quả:**\n" for prevention in preventions: response += f"• {prevention}\n" return response @staticmethod def should_ask_followup(context: Dict, chat_history: List) -> bool: """ Determine if we should ask follow-up questions Rules: - Don't ask if urgency is high (give solutions first) - Don't ask if we already have enough info - Don't ask more than 2 questions total """ # High urgency = no questions, give help if context['urgency'] == 'high': return False # Already asked 2+ questions = enough if chat_history and len(chat_history) >= 2: bot_questions = 0 for _, bot_msg in chat_history: if bot_msg and '?' in bot_msg: bot_questions += 1 if bot_questions >= 2: return False # First interaction and not urgent = can ask 1 question if context['conversation_stage'] == 0: return True return False