Spaces:
Runtime error
Runtime error
| """ | |
| 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' | |
| ] | |
| 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 | |
| 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 | |
| } | |
| 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' | |
| } | |
| 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 | |
| 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 | |