my-gradio-app / agents /core /context_analyzer.py
Nguyen Trong Lap
Recreate history without binary blobs
eeb0f9c
"""
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