| | """
|
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| | β β
|
| | β ADAPTIVE SELF-LEARNING ENGINE fΓΌr NoahsKI β
|
| | β Learns from every user interaction to improve responses β
|
| | β β
|
| | β Features: β
|
| | β β Learn from user feedback (ratings, corrections) β
|
| | β β Detect what works and what doesn't β
|
| | β β Adapt response strategies based on success β
|
| | β β Active learning (ask for feedback on quality) β
|
| | β β Pattern recognition from conversation history β
|
| | β β Self-improvement without external training β
|
| | β β Topic-specific expertise development β
|
| | β β Personalization based on user preferences β
|
| | β β
|
| | ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| | """
|
| |
|
| | import os
|
| | import json
|
| | import time
|
| | import logging
|
| | import hashlib
|
| | from typing import Dict, List, Tuple, Optional, Any
|
| | from dataclasses import dataclass, field, asdict
|
| | from pathlib import Path
|
| | from datetime import datetime, timedelta
|
| | from collections import defaultdict, deque
|
| | import numpy as np
|
| |
|
| | logger = logging.getLogger(__name__)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | @dataclass
|
| | class ResponseFeedback:
|
| | """Feedback on an AI response"""
|
| | response_id: str
|
| | user_id: str
|
| | query: str
|
| | response: str
|
| | rating: float
|
| | feedback_text: Optional[str] = None
|
| | was_helpful: bool = False
|
| | needed_improvement: bool = False
|
| | timestamp: float = field(default_factory=time.time)
|
| | corrections: Optional[str] = None
|
| | clarifications: Optional[str] = None
|
| |
|
| | def to_dict(self) -> Dict:
|
| | return asdict(self)
|
| |
|
| |
|
| | @dataclass
|
| | class TopicExpertise:
|
| | """Expertise level on a specific topic"""
|
| | topic: str
|
| | total_interactions: int = 0
|
| | successful_responses: int = 0
|
| | failed_responses: int = 0
|
| | average_rating: float = 0.5
|
| | sources_used: List[str] = field(default_factory=list)
|
| | common_misunderstandings: List[str] = field(default_factory=list)
|
| | last_updated: float = field(default_factory=time.time)
|
| | confidence: float = 0.3
|
| |
|
| | @property
|
| | def success_rate(self) -> float:
|
| | if self.total_interactions == 0:
|
| | return 0.5
|
| | return self.successful_responses / self.total_interactions
|
| |
|
| | @property
|
| | def expertise_level(self) -> str:
|
| | if self.total_interactions < 5:
|
| | return "beginner"
|
| | elif self.success_rate > 0.8:
|
| | return "expert"
|
| | elif self.success_rate > 0.6:
|
| | return "advanced"
|
| | elif self.success_rate > 0.4:
|
| | return "intermediate"
|
| | else:
|
| | return "learning"
|
| |
|
| | def to_dict(self) -> Dict:
|
| | return {
|
| | 'topic': self.topic,
|
| | 'total_interactions': self.total_interactions,
|
| | 'successful_responses': self.successful_responses,
|
| | 'failed_responses': self.failed_responses,
|
| | 'average_rating': self.average_rating,
|
| | 'sources_used': self.sources_used,
|
| | 'common_misunderstandings': self.common_misunderstandings,
|
| | 'last_updated': self.last_updated,
|
| | 'confidence': self.confidence,
|
| | 'success_rate': self.success_rate,
|
| | 'expertise_level': self.expertise_level
|
| | }
|
| |
|
| |
|
| | @dataclass
|
| | class UserPreference:
|
| | """Learned user preferences"""
|
| | user_id: str
|
| | preferred_style: str = "balanced"
|
| | preferred_language: str = "en"
|
| | preferred_response_length: str = "medium"
|
| | prefers_examples: bool = True
|
| | prefers_sources: bool = True
|
| | prefers_structures: bool = True
|
| | interaction_count: int = 0
|
| | satisfaction_rate: float = 0.5
|
| | last_learned: float = field(default_factory=time.time)
|
| |
|
| | def to_dict(self) -> Dict:
|
| | return asdict(self)
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | class AdaptiveSelfLearningEngine:
|
| | """Learns from every interaction to improve responses"""
|
| |
|
| | def __init__(self):
|
| | self.data_dir = Path("noahski_data/self_learning")
|
| | self.data_dir.mkdir(parents=True, exist_ok=True)
|
| |
|
| | self.feedback_file = self.data_dir / "feedback_log.json"
|
| | self.expertise_file = self.data_dir / "topic_expertise.json"
|
| | self.preferences_file = self.data_dir / "user_preferences.json"
|
| | self.patterns_file = self.data_dir / "learned_patterns.json"
|
| |
|
| |
|
| | self.feedback_history: List[ResponseFeedback] = []
|
| | self.topic_expertise: Dict[str, TopicExpertise] = {}
|
| | self.user_preferences: Dict[str, UserPreference] = {}
|
| | self.learned_patterns: Dict[str, List[Dict]] = defaultdict(list)
|
| |
|
| |
|
| | self.load_all_data()
|
| |
|
| |
|
| | self.performance_metrics = {
|
| | 'total_responses': 0,
|
| | 'positive_feedback': 0,
|
| | 'negative_feedback': 0,
|
| | 'neutral_feedback': 0,
|
| | 'average_satisfaction': 0.5,
|
| | 'learning_speed': 0.0
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | def record_feedback(self, user_id: str, response_id: str, query: str,
|
| | response: str, rating: float, feedback_text: str = None,
|
| | corrections: str = None) -> bool:
|
| | """Record feedback on a response"""
|
| |
|
| | feedback = ResponseFeedback(
|
| | response_id=response_id,
|
| | user_id=user_id,
|
| | query=query,
|
| | response=response,
|
| | rating=float(np.clip(rating, 0, 1)),
|
| | feedback_text=feedback_text,
|
| | was_helpful=rating >= 0.7,
|
| | needed_improvement=rating < 0.5,
|
| | corrections=corrections
|
| | )
|
| |
|
| | self.feedback_history.append(feedback)
|
| |
|
| |
|
| | self._learn_from_feedback(feedback, query)
|
| |
|
| |
|
| | self._update_user_preference(user_id, rating)
|
| |
|
| |
|
| | self.save_feedback()
|
| |
|
| | logger.info(f"Feedback recorded: rating={rating:.2f}, user={user_id}")
|
| | return True
|
| |
|
| | def _learn_from_feedback(self, feedback: ResponseFeedback, query: str):
|
| | """Extract learnings from user feedback"""
|
| |
|
| |
|
| | topics = self._extract_topics(query)
|
| |
|
| | for topic in topics:
|
| | if topic not in self.topic_expertise:
|
| | self.topic_expertise[topic] = TopicExpertise(topic=topic)
|
| |
|
| | expertise = self.topic_expertise[topic]
|
| | expertise.total_interactions += 1
|
| |
|
| | if feedback.was_helpful:
|
| | expertise.successful_responses += 1
|
| | else:
|
| | expertise.failed_responses += 1
|
| |
|
| |
|
| | expertise.average_rating = (
|
| | expertise.average_rating * 0.7 +
|
| | feedback.rating * 0.3
|
| | )
|
| |
|
| |
|
| | if expertise.total_interactions > 10:
|
| | expertise.confidence = min(0.95, 0.3 + (expertise.total_interactions / 100))
|
| |
|
| | expertise.last_updated = time.time()
|
| |
|
| |
|
| | if feedback.corrections:
|
| | if feedback.corrections not in expertise.common_misunderstandings:
|
| | expertise.common_misunderstandings.append(feedback.corrections)
|
| |
|
| |
|
| | pattern = {
|
| | 'query': query,
|
| | 'rating': feedback.rating,
|
| | 'timestamp': feedback.timestamp,
|
| | 'was_helpful': feedback.was_helpful,
|
| | 'feedback': feedback.feedback_text
|
| | }
|
| |
|
| | query_hash = hashlib.md5(query.lower().encode()).hexdigest()[:8]
|
| | self.learned_patterns[query_hash].append(pattern)
|
| |
|
| | def _extract_topics(self, text: str) -> List[str]:
|
| | """Extract topics from text"""
|
| | topics = []
|
| |
|
| |
|
| | topic_keywords = {
|
| | 'python': ['python', 'programming', 'code', 'script'],
|
| | 'javascript': ['javascript', 'js', 'node', 'web development'],
|
| | 'machine_learning': ['machine learning', 'ai', 'neural', 'algorithm'],
|
| | 'web_development': ['web', 'html', 'css', 'frontend', 'backend'],
|
| | 'database': ['database', 'sql', 'nosql', 'mongodb', 'postgres'],
|
| | 'troubleshooting': ['error', 'bug', 'debug', 'fix', 'not working'],
|
| | 'explanation': ['explain', 'how', 'why', 'what is', 'understand'],
|
| | 'general': ['general', 'information', 'tell me']
|
| | }
|
| |
|
| | text_lower = text.lower()
|
| |
|
| | for topic, keywords in topic_keywords.items():
|
| | if any(keyword in text_lower for keyword in keywords):
|
| | topics.append(topic)
|
| |
|
| | return topics if topics else ['general']
|
| |
|
| | def _update_user_preference(self, user_id: str, rating: float):
|
| | """Update user preferences based on interaction"""
|
| | if user_id not in self.user_preferences:
|
| | self.user_preferences[user_id] = UserPreference(user_id=user_id)
|
| |
|
| | pref = self.user_preferences[user_id]
|
| | pref.interaction_count += 1
|
| | pref.satisfaction_rate = pref.satisfaction_rate * 0.7 + rating * 0.3
|
| | pref.last_learned = time.time()
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | def get_optimal_response_strategy(self, user_id: str, topic: str) -> Dict[str, Any]:
|
| | """Get optimal strategy for user/topic combination"""
|
| |
|
| | strategy = {
|
| | 'style': 'balanced',
|
| | 'length': 'medium',
|
| | 'use_examples': True,
|
| | 'use_sources': True,
|
| | 'ask_clarification': False,
|
| | 'provide_structure': True,
|
| | 'confidence': 0.5
|
| | }
|
| |
|
| |
|
| | if user_id in self.user_preferences:
|
| | pref = self.user_preferences[user_id]
|
| | strategy['style'] = pref.preferred_style
|
| | strategy['length'] = pref.preferred_response_length
|
| | strategy['use_examples'] = pref.prefers_examples
|
| | strategy['use_sources'] = pref.prefers_sources
|
| | strategy['provide_structure'] = pref.prefers_structures
|
| |
|
| |
|
| | if topic in self.topic_expertise:
|
| | expertise = self.topic_expertise[topic]
|
| | strategy['confidence'] = expertise.confidence
|
| |
|
| |
|
| | if expertise.expertise_level in ['beginner', 'learning']:
|
| | strategy['ask_clarification'] = True
|
| | strategy['use_examples'] = True
|
| |
|
| |
|
| | if expertise.expertise_level == 'expert':
|
| | strategy['length'] = 'long'
|
| |
|
| | return strategy
|
| |
|
| | def suggest_improvements(self) -> List[Dict[str, Any]]:
|
| | """Suggest areas for improvement based on learning"""
|
| |
|
| | improvements = []
|
| |
|
| |
|
| | for topic, expertise in self.topic_expertise.items():
|
| | if expertise.success_rate < 0.5 and expertise.total_interactions >= 5:
|
| | improvements.append({
|
| | 'type': 'weak_topic',
|
| | 'topic': topic,
|
| | 'success_rate': expertise.success_rate,
|
| | 'recommendation': f'Focus on improving responses about {topic}'
|
| | })
|
| |
|
| |
|
| | for user_id, pref in self.user_preferences.items():
|
| | if pref.satisfaction_rate < 0.4 and pref.interaction_count >= 3:
|
| | improvements.append({
|
| | 'type': 'user_satisfaction',
|
| | 'user_id': user_id,
|
| | 'satisfaction': pref.satisfaction_rate,
|
| | 'recommendation': f'Better adapt responses for user {user_id}'
|
| | })
|
| |
|
| |
|
| | if self.learned_patterns:
|
| | patterns_to_improve = []
|
| | for pattern_id, patterns in self.learned_patterns.items():
|
| | if len(patterns) >= 3:
|
| | avg_rating = np.mean([p['rating'] for p in patterns])
|
| | if avg_rating < 0.5:
|
| | patterns_to_improve.append(pattern_id)
|
| |
|
| | if patterns_to_improve:
|
| | improvements.append({
|
| | 'type': 'pattern_improvement',
|
| | 'count': len(patterns_to_improve),
|
| | 'recommendation': f'Review and improve responses to similar queries'
|
| | })
|
| |
|
| | return improvements
|
| |
|
| | def get_learning_progress(self) -> Dict[str, Any]:
|
| | """Get current learning progress"""
|
| |
|
| | total_feedback = len(self.feedback_history)
|
| |
|
| | if total_feedback == 0:
|
| | return {'status': 'No data yet', 'progress': 0}
|
| |
|
| | positive = sum(1 for f in self.feedback_history if f.was_helpful)
|
| | negative = sum(1 for f in self.feedback_history if f.needed_improvement)
|
| |
|
| |
|
| | if len(self.feedback_history) >= 10:
|
| | recent_feedbacks = self.feedback_history[-10:]
|
| | older_feedbacks = self.feedback_history[-20:-10] if len(self.feedback_history) >= 20 else self.feedback_history[:5]
|
| |
|
| | recent_avg = np.mean([f.rating for f in recent_feedbacks])
|
| | older_avg = np.mean([f.rating for f in older_feedbacks]) if older_feedbacks else 0.5
|
| |
|
| | learning_speed = recent_avg - older_avg
|
| | else:
|
| | learning_speed = 0.0
|
| |
|
| | return {
|
| | 'total_interactions': total_feedback,
|
| | 'positive_feedback': positive,
|
| | 'negative_feedback': negative,
|
| | 'positive_rate': positive / total_feedback if total_feedback > 0 else 0,
|
| | 'topics_with_expertise': len(self.topic_expertise),
|
| | 'users_tracked': len(self.user_preferences),
|
| | 'overall_satisfaction': np.mean([f.rating for f in self.feedback_history]),
|
| | 'learning_speed': learning_speed,
|
| | 'status': 'Actively Learning' if learning_speed > 0 else 'Stable' if total_feedback > 20 else 'Starting'
|
| | }
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | def save_feedback(self):
|
| | """Save feedback history"""
|
| | try:
|
| | data = [f.to_dict() for f in self.feedback_history[-1000:]]
|
| | with open(self.feedback_file, 'w', encoding='utf-8') as f:
|
| | json.dump(data, f, indent=2, ensure_ascii=False)
|
| | except Exception as e:
|
| | logger.error(f"Error saving feedback: {e}")
|
| |
|
| | def save_expertise(self):
|
| | """Save topic expertise"""
|
| | try:
|
| | data = {topic: exp.to_dict()
|
| | for topic, exp in self.topic_expertise.items()}
|
| | with open(self.expertise_file, 'w', encoding='utf-8') as f:
|
| | json.dump(data, f, indent=2, ensure_ascii=False)
|
| | except Exception as e:
|
| | logger.error(f"Error saving expertise: {e}")
|
| |
|
| | def save_preferences(self):
|
| | """Save user preferences"""
|
| | try:
|
| | data = {user_id: pref.to_dict()
|
| | for user_id, pref in self.user_preferences.items()}
|
| | with open(self.preferences_file, 'w', encoding='utf-8') as f:
|
| | json.dump(data, f, indent=2, ensure_ascii=False)
|
| | except Exception as e:
|
| | logger.error(f"Error saving preferences: {e}")
|
| |
|
| | def save_all_data(self):
|
| | """Save all learning data"""
|
| | self.save_feedback()
|
| | self.save_expertise()
|
| | self.save_preferences()
|
| | logger.info("All learning data saved")
|
| |
|
| | def load_all_data(self):
|
| | """Load all learning data"""
|
| | self._load_feedback()
|
| | self._load_expertise()
|
| | self._load_preferences()
|
| |
|
| | def _load_feedback(self):
|
| | """Load feedback history"""
|
| | try:
|
| | if self.feedback_file.exists():
|
| | with open(self.feedback_file, 'r', encoding='utf-8') as f:
|
| | data = json.load(f)
|
| | self.feedback_history = [ResponseFeedback(**item) for item in data]
|
| | logger.info(f"Loaded {len(self.feedback_history)} feedback items")
|
| | except Exception as e:
|
| | logger.error(f"Error loading feedback: {e}")
|
| |
|
| | def _load_expertise(self):
|
| | """Load topic expertise"""
|
| | try:
|
| | if self.expertise_file.exists():
|
| | with open(self.expertise_file, 'r', encoding='utf-8') as f:
|
| | data = json.load(f)
|
| | for topic, exp_dict in data.items():
|
| | self.topic_expertise[topic] = TopicExpertise(**exp_dict)
|
| | logger.info(f"Loaded expertise for {len(self.topic_expertise)} topics")
|
| | except Exception as e:
|
| | logger.error(f"Error loading expertise: {e}")
|
| |
|
| | def _load_preferences(self):
|
| | """Load user preferences"""
|
| | try:
|
| | if self.preferences_file.exists():
|
| | with open(self.preferences_file, 'r', encoding='utf-8') as f:
|
| | data = json.load(f)
|
| | for user_id, pref_dict in data.items():
|
| | self.user_preferences[user_id] = UserPreference(**pref_dict)
|
| | logger.info(f"Loaded preferences for {len(self.user_preferences)} users")
|
| | except Exception as e:
|
| | logger.error(f"Error loading preferences: {e}")
|
| |
|
| |
|
| |
|
| |
|
| |
|
| |
|
| | if __name__ == "__main__":
|
| | logging.basicConfig(level=logging.INFO)
|
| |
|
| |
|
| | engine = AdaptiveSelfLearningEngine()
|
| |
|
| |
|
| | print("Recording feedback samples...")
|
| | for i in range(5):
|
| | engine.record_feedback(
|
| | user_id="test_user",
|
| | response_id=f"response_{i}",
|
| | query=f"How does {['Python', 'machine learning', 'web development'][i % 3]} work?",
|
| | response="Sample response about the topic.",
|
| | rating=0.8 + (i * 0.02),
|
| | feedback_text="Good answer!"
|
| | )
|
| |
|
| |
|
| | progress = engine.get_learning_progress()
|
| | print(f"\nLearning Progress: {json.dumps(progress, indent=2)}")
|
| |
|
| |
|
| | print(f"\nTopic Expertise:")
|
| | for topic, exp in engine.topic_expertise.items():
|
| | print(f" {topic}: {exp.expertise_level} ({exp.success_rate:.1%})")
|
| |
|
| |
|
| | print(f"\nSuggested Improvements: {json.dumps(engine.suggest_improvements(), indent=2)}")
|
| |
|