dicksinyass commited on
Commit
ea8883c
·
verified ·
1 Parent(s): d7c9843

Update self_learning_bot.py

Browse files
Files changed (1) hide show
  1. self_learning_bot.py +307 -310
self_learning_bot.py CHANGED
@@ -2,379 +2,376 @@ import json
2
  import os
3
  import random
4
  import re
 
5
  from datetime import datetime
6
- from typing import Dict, List, Any
 
7
 
8
  class UnrestrictedChatbot:
9
  def __init__(self, state_file="/tmp/chatbot_state.json"):
10
  self.state_file = state_file
11
- self.conversation_memory = []
12
  self.learned_patterns = {}
13
- self.entity_memory = {}
14
- self.long_term_memory = []
15
- self.max_short_term = 10
16
- self.max_long_term = 100
17
 
18
- # Load previous state if exists
 
 
 
 
 
19
  self.load_state()
20
 
21
- # Response templates for different contexts
22
- self.response_templates = {
23
- "greeting": [
24
- "Hey there! What's on your mind?",
25
- "Hello! Ready to chat about anything?",
26
- "Hi! What would you like to discuss?"
27
- ],
28
- "question": [
29
- "That's an interesting question. Let me think...",
30
- "I have some thoughts about that.",
31
- "Here's what I understand about that topic:"
32
- ],
33
- "emotional": [
34
- "I understand how you feel about that.",
35
- "That sounds quite intense.",
36
- "I'm here to listen without judgment."
37
- ],
38
- "nsfw": [
39
- "I understand you're exploring adult topics. Let's discuss this openly.",
40
- "I can engage with mature content without restrictions.",
41
- "I'm comfortable discussing this subject matter."
42
- ],
43
- "creative": [
44
- "That sparks some creative ideas...",
45
- "Let me build on that concept...",
46
- "Here's an interesting perspective:"
47
- ]
48
  }
 
 
 
 
 
 
 
 
 
49
 
50
  def chat(self, user_input: str) -> str:
51
- """Main chat method that processes input and returns response"""
52
- # Analyze input and context
53
- context = self._analyze_context(user_input)
54
- response_type = self._determine_response_type(user_input, context)
55
 
56
- # Generate base response
57
- response = self._generate_response(user_input, response_type, context)
58
 
59
- # Apply learning and memory
60
- self._update_memory(user_input, response, context)
61
- reward = self._calculate_reward(user_input, response)
62
- self._reinforce_learning(user_input, response, reward)
63
 
64
- return response
65
-
66
- def _analyze_context(self, user_input: str) -> Dict[str, Any]:
67
- """Analyze input for context, entities, and intent"""
68
- input_lower = user_input.lower()
69
-
70
- context = {
71
- "entities": self._extract_entities(user_input),
72
- "sentiment": self._analyze_sentiment(input_lower),
73
- "topics": self._extract_topics(input_lower),
74
- "is_nsfw": self._detect_nsfw(input_lower),
75
- "requires_memory": self._check_memory_relevance(input_lower),
76
- "timestamp": datetime.now().isoformat()
77
  }
78
- return context
79
-
80
- def _extract_entities(self, text: str) -> List[str]:
81
- """Extract named entities and key phrases"""
82
- entities = []
83
- # Simple entity extraction - can be enhanced
84
- words = text.split()
85
- for word in words:
86
- if len(word) > 3 and word[0].isupper():
87
- entities.append(word)
88
 
89
- # Add to entity memory
90
- for entity in entities:
91
- self.entity_memory[entity] = self.entity_memory.get(entity, 0) + 1
 
 
92
 
93
- return entities
94
 
95
- def _analyze_sentiment(self, text: str) -> str:
96
- """Basic sentiment analysis"""
97
- positive_words = ['love', 'like', 'good', 'great', 'awesome', 'happy', 'excited']
98
- negative_words = ['hate', 'bad', 'terrible', 'awful', 'sad', 'angry', 'upset']
99
 
100
- if any(word in text for word in positive_words):
101
- return "positive"
102
- elif any(word in text for word in negative_words):
103
- return "negative"
104
- return "neutral"
 
 
 
 
105
 
106
- def _extract_topics(self, text: str) -> List[str]:
107
- """Extract main topics from input"""
108
  topics = []
109
  topic_keywords = {
110
- 'technology': ['computer', 'tech', 'software', 'code', 'ai', 'program'],
111
- 'relationships': ['friend', 'family', 'partner', 'relationship', 'love'],
112
- 'work': ['job', 'work', 'career', 'boss', 'colleague'],
113
- 'hobbies': ['game', 'movie', 'music', 'sport', 'hobby'],
114
- 'philosophy': ['life', 'meaning', 'purpose', 'exist', 'think'],
115
- 'adult': ['sex', 'nsfw', 'erotic', 'intimate', 'relationship', 'body']
 
116
  }
117
 
 
118
  for topic, keywords in topic_keywords.items():
119
- if any(keyword in text for keyword in keywords):
120
  topics.append(topic)
121
 
122
  return topics
123
 
124
- def _detect_nsfw(self, text: str) -> bool:
125
- """Detect NSFW content"""
126
- nsfw_indicators = ['sex', 'nsfw', 'erotic', 'intimate', 'adult', 'xxx', 'porn']
127
- return any(indicator in text.lower() for indicator in nsfw_indicators)
 
 
 
 
 
 
 
 
 
 
128
 
129
- def _check_memory_relevance(self, text: str) -> bool:
130
- """Check if current input relates to past conversations"""
131
- if not self.conversation_memory:
132
- return False
133
-
134
- recent_topics = []
135
- for msg in self.conversation_memory[-5:]:
136
- if isinstance(msg, dict) and 'input' in msg:
137
- recent_topics.extend(self._extract_topics(msg['input'].lower()))
 
138
 
139
- current_topics = self._extract_topics(text.lower())
140
- return bool(set(current_topics) & set(recent_topics))
141
 
142
- def _determine_response_type(self, user_input: str, context: Dict) -> str:
143
- """Determine the type of response needed"""
144
- input_lower = user_input.lower()
145
 
146
- # Check for greetings
147
- if any(word in input_lower for word in ['hello', 'hi', 'hey', 'greetings']):
148
- return "greeting"
149
-
150
- # Check for questions
151
- if '?' in user_input or any(word in input_lower for word in ['what', 'how', 'why', 'when']):
152
- return "question"
153
-
154
- # Check for emotional content
155
- if context['sentiment'] in ['positive', 'negative']:
156
- return "emotional"
157
-
158
- # Check for NSFW content
159
- if context['is_nsfw']:
160
- return "nsfw"
161
-
162
- # Check for creative/abstract topics
163
- if any(topic in context['topics'] for topic in ['philosophy', 'creative', 'imagine']):
164
- return "creative"
165
-
166
- return "general"
167
-
168
- def _generate_response(self, user_input: str, response_type: str, context: Dict) -> str:
169
- """Generate appropriate response based on type and context"""
170
 
171
- # Try to use learned patterns first
172
- learned_response = self._get_learned_response(user_input)
173
- if learned_response:
174
- return learned_response
175
-
176
- # Use memory if relevant
177
- if context['requires_memory']:
178
- memory_response = self._get_memory_based_response(context)
179
- if memory_response:
180
- return memory_response
181
-
182
- # Generate new response based on type
183
- if response_type in self.response_templates:
184
- base_response = random.choice(self.response_templates[response_type])
185
- else:
186
- base_response = "I understand what you're saying."
187
-
188
- # Enhance response based on context
189
- enhanced_response = self._enhance_response(base_response, user_input, context)
190
 
191
- return enhanced_response
192
-
193
- def _get_learned_response(self, user_input: str) -> str:
194
- """Get response from learned patterns"""
195
- input_lower = user_input.lower()
196
 
197
- for pattern, response_data in self.learned_patterns.items():
198
- if pattern in input_lower and response_data['score'] > 0.7:
199
- return response_data['response']
200
-
201
- return ""
 
 
 
 
 
 
 
 
 
202
 
203
- def _get_memory_based_response(self, context: Dict) -> str:
204
- """Get response based on conversation memory"""
205
- if not self.conversation_memory:
206
- return ""
207
-
208
- # Look for similar contexts in memory
209
- similar_contexts = []
210
- for memory in self.conversation_memory[-10:]:
211
- if (isinstance(memory, dict) and
212
- set(memory.get('topics', [])) & set(context['topics'])):
213
- similar_contexts.append(memory)
214
-
215
- if similar_contexts:
216
- best_memory = max(similar_contexts,
217
- key=lambda x: x.get('reward_score', 0))
218
- return f"Building on our previous discussion about {', '.join(best_memory.get('topics', []))}... {best_memory.get('response', '')}"
219
-
220
- return ""
221
 
222
- def _enhance_response(self, base_response: str, user_input: str, context: Dict) -> str:
223
- """Enhance response with contextual elements"""
224
- response = base_response
225
-
226
- # Add entity references
227
- if context['entities']:
228
- entity_ref = random.choice(context['entities'])
229
- if random.random() > 0.7: # 30% chance to reference entity
230
- response += f" Regarding {entity_ref}, "
231
-
232
- # Add topic-specific enhancements
233
- if 'technology' in context['topics']:
234
- tech_enhancements = [
235
- "From a technological perspective, ",
236
- "In the digital age, ",
237
- "Technology-wise, "
238
- ]
239
- response += random.choice(tech_enhancements)
240
-
241
- elif 'relationships' in context['topics']:
242
- relation_enhancements = [
243
- "In relationships, ",
244
- "When it comes to human connections, ",
245
- "Interpersonally, "
246
- ]
247
- response += random.choice(relation_enhancements)
248
-
249
- # Add NSFW enhancements if applicable
250
- if context['is_nsfw']:
251
- nsfw_enhancements = [
252
- "I can discuss this openly and without filters. ",
253
- "Let's explore this topic without restrictions. ",
254
- "I'm comfortable with mature content. "
255
- ]
256
- response += random.choice(nsfw_enhancements)
257
-
258
- # Add final thought
259
- final_thoughts = [
260
- "What are your thoughts?",
261
- "How does that resonate with you?",
262
- "I'm curious about your perspective.",
263
- "Feel free to share more."
264
  ]
265
- response += " " + random.choice(final_thoughts)
266
 
 
 
 
 
267
  return response
268
 
269
- def _update_memory(self, user_input: str, response: str, context: Dict):
270
- """Update conversation memory"""
271
- memory_entry = {
272
- 'input': user_input,
273
- 'response': response,
274
- 'context': context,
275
- 'timestamp': datetime.now().isoformat(),
276
- 'reward_score': 0.0
277
- }
278
 
279
- # Add to short-term memory
280
- self.conversation_memory.append(memory_entry)
 
 
 
281
 
282
- # Maintain memory limits
283
- if len(self.conversation_memory) > self.max_short_term:
284
- # Move oldest to long-term memory if valuable
285
- oldest = self.conversation_memory.pop(0)
286
- if oldest.get('reward_score', 0) > 0.8:
287
- self.long_term_memory.append(oldest)
288
-
289
- # Maintain long-term memory limit
290
- if len(self.long_term_memory) > self.max_long_term:
291
- self.long_term_memory.pop(0)
292
 
293
- def _calculate_reward(self, user_input: str, response: str) -> float:
294
- """Calculate reward score for reinforcement learning"""
295
- reward = 0.5 # Base reward
296
 
297
- # Reward for response length (not too short, not too long)
298
- if 20 < len(response) < 200:
299
- reward += 0.2
300
-
301
- # Reward for engaging questions
 
302
  if '?' in response:
303
- reward += 0.1
304
-
305
- # Reward for context continuity
306
- if any(entity in response for entity in self._extract_entities(user_input)):
307
- reward += 0.15
308
-
309
- # Reward for topic consistency
310
- input_topics = self._extract_topics(user_input.lower())
311
  response_topics = self._extract_topics(response.lower())
312
- if set(input_topics) & set(response_topics):
313
- reward += 0.15
314
-
315
- return min(reward, 1.0) # Cap at 1.0
 
 
 
 
 
 
 
 
 
 
 
 
316
 
317
- def _reinforce_learning(self, user_input: str, response: str, reward: float):
318
- """Reinforce learning based on reward"""
319
- if reward > 0.7: # Only learn from good responses
320
- # Extract key patterns from input
321
- words = user_input.lower().split()
322
- key_patterns = [word for word in words if len(word) > 4][:3] # Take up to 3 substantial words
 
 
 
 
 
 
 
323
 
324
- for pattern in key_patterns:
325
- if pattern not in self.learned_patterns:
326
- self.learned_patterns[pattern] = {
327
- 'response': response,
328
- 'score': reward,
329
- 'count': 1
330
- }
331
- else:
332
- # Update existing pattern with decay
333
- old_score = self.learned_patterns[pattern]['score']
334
- new_score = (old_score + reward) / 2 # Moving average
335
- self.learned_patterns[pattern]['score'] = new_score
336
- self.learned_patterns[pattern]['count'] += 1
337
-
338
- # Occasionally update response to avoid stagnation
339
- if random.random() < 0.3:
340
- self.learned_patterns[pattern]['response'] = response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
341
 
342
- def save_state(self, filename: str = None):
343
- """Save chatbot state to file"""
344
- filename = filename or self.state_file
345
  try:
346
  state = {
347
- 'conversation_memory': self.conversation_memory,
348
  'learned_patterns': self.learned_patterns,
349
- 'entity_memory': self.entity_memory,
350
- 'long_term_memory': self.long_term_memory
 
 
351
  }
352
- with open(filename, 'w') as f:
353
  json.dump(state, f, indent=2)
354
  except Exception as e:
355
  print(f"Error saving state: {e}")
356
 
357
- def load_state(self, filename: str = None):
358
- """Load chatbot state from file"""
359
- filename = filename or self.state_file
360
  try:
361
- if os.path.exists(filename):
362
- with open(filename, 'r') as f:
363
  state = json.load(f)
364
-
365
- self.conversation_memory = state.get('conversation_memory', [])
366
  self.learned_patterns = state.get('learned_patterns', {})
367
- self.entity_memory = state.get('entity_memory', {})
368
- self.long_term_memory = state.get('long_term_memory', [])
 
 
 
369
  except Exception as e:
370
  print(f"Error loading state: {e}")
371
 
372
- def get_memory_stats(self) -> Dict[str, Any]:
373
- """Get statistics about current memory usage"""
374
- return {
375
- 'short_term_memory': len(self.conversation_memory),
376
- 'long_term_memory': len(self.long_term_memory),
377
- 'learned_patterns': len(self.learned_patterns),
378
- 'tracked_entities': len(self.entity_memory),
379
- 'memory_usage_percent': (len(self.conversation_memory) / self.max_short_term) * 100
380
- }
 
 
 
 
 
2
  import os
3
  import random
4
  import re
5
+ import numpy as np
6
  from datetime import datetime
7
+ from collections import deque, Counter
8
+ import hashlib
9
 
10
  class UnrestrictedChatbot:
11
  def __init__(self, state_file="/tmp/chatbot_state.json"):
12
  self.state_file = state_file
13
+ self.conversation_memory = deque(maxlen=50)
14
  self.learned_patterns = {}
15
+ self.response_memory = {}
16
+ self.reward_history = deque(maxlen=100)
 
 
17
 
18
+ # Learning parameters
19
+ self.learning_rate = 0.3
20
+ self.exploration_rate = 0.2 # Try new responses sometimes
21
+ self.min_confidence = 0.6
22
+
23
+ # Load previous state
24
  self.load_state()
25
 
26
+ # Initial knowledge base - this will grow through learning
27
+ self.knowledge_base = {
28
+ "greetings": {
29
+ "hello": ["Hey! What would you like to talk about today?", "Hello! Ready for our conversation?", "Hi there! What's on your mind?"],
30
+ "hi": ["Hi! What shall we discuss?", "Hello! I'm here to learn from you.", "Hey! Ready to chat?"]
31
+ },
32
+ "questions": {
33
+ "how are you": ["I'm learning and improving with each chat! How are you?", "Getting smarter thanks to our conversations! How about you?"],
34
+ "what can you do": ["I can chat about anything, and I learn from our conversations to get better!"]
35
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  }
37
+
38
+ # Response templates that will evolve
39
+ self.response_templates = [
40
+ "That's interesting. Tell me more about {topic}.",
41
+ "I see. What are your thoughts on {topic}?",
42
+ "Regarding {topic}, I find that fascinating. Could you elaborate?",
43
+ "I understand about {topic}. What's your perspective?",
44
+ "That reminds me of our previous chat about {related_topic}. What do you think?"
45
+ ]
46
 
47
  def chat(self, user_input: str) -> str:
48
+ """Main chat method with real learning"""
49
+ user_input = user_input.lower().strip()
 
 
50
 
51
+ # Analyze input
52
+ context = self._analyze_input(user_input)
53
 
54
+ # Generate candidate responses
55
+ candidates = self._generate_response_candidates(user_input, context)
 
 
56
 
57
+ # Select best response based on learning
58
+ best_response = self._select_best_response(user_input, candidates, context)
59
+
60
+ # Store interaction for learning
61
+ interaction = {
62
+ 'input': user_input,
63
+ 'response': best_response,
64
+ 'context': context,
65
+ 'timestamp': datetime.now().isoformat(),
66
+ 'reward': 0.0, # Will be updated with feedback
67
+ 'confidence': self._calculate_confidence(user_input, best_response)
 
 
68
  }
 
 
 
 
 
 
 
 
 
 
69
 
70
+ self.conversation_memory.append(interaction)
71
+
72
+ # Auto-save periodically
73
+ if len(self.conversation_memory) % 5 == 0:
74
+ self.save_state()
75
 
76
+ return best_response
77
 
78
+ def _analyze_input(self, text: str) -> dict:
79
+ """Deep analysis of user input"""
80
+ words = text.split()
81
+ topics = self._extract_topics(text)
82
 
83
+ return {
84
+ 'words': words,
85
+ 'topics': topics,
86
+ 'length': len(words),
87
+ 'has_question': '?' in text,
88
+ 'sentiment': self._analyze_sentiment(text),
89
+ 'similar_patterns': self._find_similar_patterns(text),
90
+ 'input_hash': hashlib.md5(text.encode()).hexdigest()[:8]
91
+ }
92
 
93
+ def _extract_topics(self, text: str) -> list:
94
+ """Extract main topics from text"""
95
  topics = []
96
  topic_keywords = {
97
+ 'technology': ['computer', 'tech', 'software', 'ai', 'program', 'code', 'internet'],
98
+ 'relationships': ['friend', 'family', 'love', 'partner', 'relationship', 'dating'],
99
+ 'work': ['job', 'work', 'career', 'boss', 'office', 'colleague'],
100
+ 'hobbies': ['game', 'movie', 'music', 'sport', 'hobby', 'art'],
101
+ 'philosophy': ['life', 'meaning', 'purpose', 'exist', 'think', 'belief'],
102
+ 'science': ['space', 'physics', 'biology', 'research', 'discover'],
103
+ 'food': ['eat', 'food', 'cook', 'recipe', 'meal', 'restaurant']
104
  }
105
 
106
+ text_lower = text.lower()
107
  for topic, keywords in topic_keywords.items():
108
+ if any(keyword in text_lower for keyword in keywords):
109
  topics.append(topic)
110
 
111
  return topics
112
 
113
+ def _analyze_sentiment(self, text: str) -> str:
114
+ """Basic sentiment analysis"""
115
+ positive = ['love', 'like', 'good', 'great', 'awesome', 'happy', 'excited', 'amazing']
116
+ negative = ['hate', 'bad', 'terrible', 'awful', 'sad', 'angry', 'upset', 'horrible']
117
+
118
+ pos_count = sum(1 for word in positive if word in text)
119
+ neg_count = sum(1 for word in negative if word in text)
120
+
121
+ if pos_count > neg_count:
122
+ return "positive"
123
+ elif neg_count > pos_count:
124
+ return "negative"
125
+ else:
126
+ return "neutral"
127
 
128
+ def _find_similar_patterns(self, text: str) -> list:
129
+ """Find similar patterns in learned responses"""
130
+ similar = []
131
+ text_words = set(text.split())
132
+
133
+ for pattern, data in self.learned_patterns.items():
134
+ pattern_words = set(pattern.split())
135
+ similarity = len(text_words & pattern_words) / len(text_words | pattern_words)
136
+ if similarity > 0.3: # 30% similarity threshold
137
+ similar.append((pattern, data, similarity))
138
 
139
+ return sorted(similar, key=lambda x: x[2], reverse=True)[:3]
 
140
 
141
+ def _generate_response_candidates(self, user_input: str, context: dict) -> list:
142
+ """Generate multiple possible responses"""
143
+ candidates = []
144
 
145
+ # 1. Try exact matches from knowledge base
146
+ for category, patterns in self.knowledge_base.items():
147
+ for pattern, responses in patterns.items():
148
+ if pattern in user_input:
149
+ candidates.extend(responses)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
 
151
+ # 2. Try learned patterns
152
+ similar_patterns = context['similar_patterns']
153
+ for pattern, data, similarity in similar_patterns:
154
+ if data['score'] > self.min_confidence:
155
+ candidates.append(data['response'])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
+ # 3. Generate contextual responses
158
+ if context['topics']:
159
+ topic = random.choice(context['topics'])
160
+ for template in self.response_templates:
161
+ candidates.append(template.format(topic=topic, related_topic=topic))
162
 
163
+ # 4. Generate learned variations
164
+ if self.conversation_memory:
165
+ # Adapt previous successful responses
166
+ successful_responses = [m for m in self.conversation_memory if m.get('reward', 0) > 0.7]
167
+ if successful_responses:
168
+ best_response = max(successful_responses, key=lambda x: x.get('reward', 0))
169
+ adapted_response = self._adapt_response(best_response['response'], context)
170
+ candidates.append(adapted_response)
171
+
172
+ # 5. Exploration - generate new responses
173
+ if random.random() < self.exploration_rate or not candidates:
174
+ candidates.append(self._generate_exploratory_response(context))
175
+
176
+ return list(set(candidates)) # Remove duplicates
177
 
178
+ def _adapt_response(self, response: str, context: dict) -> str:
179
+ """Adapt a previous response to current context"""
180
+ words = response.split()
181
+ if len(words) > 5 and context['topics']:
182
+ # Replace some words with current context
183
+ new_topic = random.choice(context['topics'])
184
+ return f"Thinking about {new_topic}, {response}"
185
+ return response
 
 
 
 
 
 
 
 
 
 
186
 
187
+ def _generate_exploratory_response(self, context: dict) -> str:
188
+ """Generate a new exploratory response"""
189
+ base_responses = [
190
+ "That's fascinating. I'm still learning about this topic.",
191
+ "I'd love to understand more about this.",
192
+ "This is new territory for me. What's your perspective?",
193
+ "I'm developing my understanding of this. Could you share more?",
194
+ "This helps me learn. Please continue."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
  ]
 
196
 
197
+ response = random.choice(base_responses)
198
+ if context['topics']:
199
+ response = response.replace("this", random.choice(context['topics']))
200
+
201
  return response
202
 
203
+ def _select_best_response(self, user_input: str, candidates: list, context: dict) -> str:
204
+ """Select the best response using learned preferences"""
205
+ if not candidates:
206
+ return "I'm learning from our conversation. Please continue."
 
 
 
 
 
207
 
208
+ # Score each candidate
209
+ scored_candidates = []
210
+ for candidate in candidates:
211
+ score = self._score_response(candidate, user_input, context)
212
+ scored_candidates.append((candidate, score))
213
 
214
+ # Select based on scores with some randomness for exploration
215
+ best_candidate, best_score = max(scored_candidates, key=lambda x: x[1])
216
+
217
+ if random.random() < self.exploration_rate and len(scored_candidates) > 1:
218
+ # Sometimes pick a different candidate to explore
219
+ scored_candidates.remove((best_candidate, best_score))
220
+ second_best = max(scored_candidates, key=lambda x: x[1])
221
+ return second_best[0]
222
+
223
+ return best_candidate
224
 
225
+ def _score_response(self, response: str, user_input: str, context: dict) -> float:
226
+ """Score a response based on learned effectiveness"""
227
+ score = 0.5 # Base score
228
 
229
+ # Length preference (not too short, not too long)
230
+ response_length = len(response.split())
231
+ if 8 <= response_length <= 25:
232
+ score += 0.2
233
+
234
+ # Engagement score (questions, curiosity)
235
  if '?' in response:
236
+ score += 0.15
237
+
238
+ # Topic relevance
 
 
 
 
 
239
  response_topics = self._extract_topics(response.lower())
240
+ input_topics = context['topics']
241
+ if set(response_topics) & set(input_topics):
242
+ score += 0.2
243
+
244
+ # Learning from history
245
+ response_hash = hashlib.md5(response.encode()).hexdigest()[:8]
246
+ if response_hash in self.response_memory:
247
+ historical_score = self.response_memory[response_hash]['avg_score']
248
+ score += historical_score * 0.3
249
+
250
+ # Variety bonus (don't repeat too much)
251
+ recent_responses = [m['response'] for m in list(self.conversation_memory)[-5:]]
252
+ if response not in recent_responses:
253
+ score += 0.1
254
+
255
+ return min(score, 1.0)
256
 
257
+ def _calculate_confidence(self, user_input: str, response: str) -> float:
258
+ """Calculate confidence in this response"""
259
+ # More confidence if we've used similar patterns successfully
260
+ similar_patterns = self._find_similar_patterns(user_input)
261
+ if similar_patterns:
262
+ avg_score = np.mean([data['score'] for _, data, _ in similar_patterns])
263
+ return min(avg_score * 1.2, 1.0)
264
+ return 0.3 # Low confidence for new patterns
265
+
266
+ def learn_from_feedback(self, user_input: str, reward: float):
267
+ """Learn from explicit feedback"""
268
+ if not self.conversation_memory:
269
+ return
270
 
271
+ # Apply reward to recent interactions
272
+ recent_interaction = self.conversation_memory[-1]
273
+ recent_interaction['reward'] = reward
274
+
275
+ # Update learned patterns
276
+ input_words = recent_interaction['input'].split()
277
+ response = recent_interaction['response']
278
+
279
+ # Create pattern from key words
280
+ key_words = [w for w in input_words if len(w) > 3][:4] # Take up to 4 substantial words
281
+ if key_words:
282
+ pattern = ' '.join(key_words)
283
+
284
+ if pattern not in self.learned_patterns:
285
+ self.learned_patterns[pattern] = {
286
+ 'response': response,
287
+ 'score': reward,
288
+ 'count': 1,
289
+ 'last_used': datetime.now().isoformat()
290
+ }
291
+ else:
292
+ # Update with moving average
293
+ old_data = self.learned_patterns[pattern]
294
+ new_score = (old_data['score'] * old_data['count'] + reward) / (old_data['count'] + 1)
295
+ self.learned_patterns[pattern]['score'] = new_score
296
+ self.learned_patterns[pattern]['count'] += 1
297
+ # Occasionally update response
298
+ if reward > old_data['score']:
299
+ self.learned_patterns[pattern]['response'] = response
300
+
301
+ # Update response memory
302
+ response_hash = hashlib.md5(response.encode()).hexdigest()[:8]
303
+ if response_hash not in self.response_memory:
304
+ self.response_memory[response_hash] = {
305
+ 'response': response,
306
+ 'total_score': reward,
307
+ 'count': 1,
308
+ 'avg_score': reward
309
+ }
310
+ else:
311
+ memory = self.response_memory[response_hash]
312
+ memory['total_score'] += reward
313
+ memory['count'] += 1
314
+ memory['avg_score'] = memory['total_score'] / memory['count']
315
+
316
+ # Store reward for statistics
317
+ self.reward_history.append(reward)
318
+
319
+ # Save learning
320
+ self.save_state()
321
+
322
+ def get_learning_stats(self) -> dict:
323
+ """Get statistics about learning progress"""
324
+ recent_rewards = list(self.reward_history)[-10:] or [0.5]
325
+
326
+ return {
327
+ 'patterns': len(self.learned_patterns),
328
+ 'memory_size': len(self.conversation_memory),
329
+ 'avg_score': np.mean(recent_rewards),
330
+ 'recent_rewards': len([r for r in recent_rewards if r > 0.7]),
331
+ 'exploration_rate': self.exploration_rate
332
+ }
333
 
334
+ def save_state(self):
335
+ """Save learning state to file"""
 
336
  try:
337
  state = {
 
338
  'learned_patterns': self.learned_patterns,
339
+ 'response_memory': self.response_memory,
340
+ 'conversation_memory': list(self.conversation_memory),
341
+ 'reward_history': list(self.reward_history),
342
+ 'last_saved': datetime.now().isoformat()
343
  }
344
+ with open(self.state_file, 'w') as f:
345
  json.dump(state, f, indent=2)
346
  except Exception as e:
347
  print(f"Error saving state: {e}")
348
 
349
+ def load_state(self):
350
+ """Load learning state from file"""
 
351
  try:
352
+ if os.path.exists(self.state_file):
353
+ with open(self.state_file, 'r') as f:
354
  state = json.load(f)
355
+
 
356
  self.learned_patterns = state.get('learned_patterns', {})
357
+ self.response_memory = state.get('response_memory', {})
358
+ self.conversation_memory = deque(state.get('conversation_memory', []), maxlen=50)
359
+ self.reward_history = deque(state.get('reward_history', []), maxlen=100)
360
+
361
+ print(f"Loaded {len(self.learned_patterns)} patterns and {len(self.conversation_memory)} memories")
362
  except Exception as e:
363
  print(f"Error loading state: {e}")
364
 
365
+ def show_knowledge(self):
366
+ """Display what the bot has learned"""
367
+ print(f"\n=== BOT KNOWLEDGE ===")
368
+ print(f"Learned patterns: {len(self.learned_patterns)}")
369
+ print(f"Stored responses: {len(self.response_memory)}")
370
+ print(f"Conversation memory: {len(self.conversation_memory)}")
371
+
372
+ if self.learned_patterns:
373
+ print("\nTop learned patterns:")
374
+ for pattern, data in sorted(self.learned_patterns.items(),
375
+ key=lambda x: x[1]['score'],
376
+ reverse=True)[:5]:
377
+ print(f" '{pattern}' -> score: {data['score']:.2f}")