File size: 10,934 Bytes
b6ae7b8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
"""
Learner System - Extracts improvements from experience.
Analyzes success/failure patterns and generates actionable insights.
"""

import json
from datetime import datetime, timedelta
from typing import List, Dict, Optional, Any
from collections import defaultdict
from pathlib import Path
import os

from .memory import get_memory
from .observer import get_observer


class ExperienceLearner:
    """Analyzes experiences and extracts learning insights."""
    
    def __init__(self, memory=None, observer=None):
        self.memory = memory or get_memory()
        self.observer = observer or get_observer()
        
        self.min_success_for_pattern = 3
        self.min_failure_for_pattern = 2
    
    def analyze_task_outcome(self, task_id: str, task_type: str,
                            success: bool, steps: List[Dict],
                            decisions: List[Dict]) -> Dict:
        """Analyze a task's outcome and extract learnings."""
        learnings = []
        
        # Analyze decision patterns
        good_decisions = [d for d in decisions if d.get('rationale')]
        if success and good_decisions:
            # Document what worked
            for decision in good_decisions:
                self.memory.store_memory(
                    content=f"Task {task_type} succeeded when: {decision.get('choice')} - {decision.get('rationale')}",
                    category='success_pattern',
                    metadata={'task_type': task_type, 'decision': decision.get('choice')}
                )
                learnings.append({
                    'type': 'success_pattern',
                    'content': f"Using {decision.get('choice')} worked well for {task_type}"
                })
        
        if not success:
            # Document failure patterns
            for decision in decisions:
                self.memory.store_memory(
                    content=f"Task {task_type} failed when: {decision.get('choice')}",
                    category='failure_pattern',
                    metadata={'task_type': task_type, 'decision': decision.get('choice')}
                )
                learnings.append({
                    'type': 'failure_pattern',
                    'content': f"Avoid {decision.get('choice')} for {task_type}"
                })
        
        # Generate improvement suggestions based on failures
        if not success:
            suggestions = self._generate_improvements(task_type, steps, decisions)
            for suggestion in suggestions:
                self.memory.add_improvement(
                    suggestion=suggestion['suggestion'],
                    category=suggestion['category'],
                    priority=suggestion['priority']
                )
                learnings.append({
                    'type': 'improvement_suggestion',
                    'content': suggestion['suggestion']
                })
        
        # Update lesson statistics
        lesson_title = f"{task_type} task {'success' if success else 'failure'}"
        lesson_desc = f"Learned from {task_type} task: {len(steps)} steps, {len(decisions)} decisions"
        
        # Find or create lesson
        lessons = self.memory.get_lessons()
        existing = [l for l in lessons if task_type in l.get('title', '')]
        
        if existing:
            self.memory.update_lesson_stats(existing[0]['id'], success)
        else:
            self.memory.add_lesson(
                title=lesson_title,
                description=lesson_desc,
                pattern=task_type
            )
        
        return {
            'learnings': learnings,
            'improvement_suggestions': len([l for l in learnings if l['type'] == 'improvement_suggestion'])
        }
    
    def _generate_improvements(self, task_type: str, steps: List[Dict],
                               decisions: List[Dict]) -> List[Dict]:
        """Generate improvement suggestions based on failure analysis."""
        suggestions = []
        
        # Analyze step patterns
        step_types = defaultdict(int)
        for step in steps:
            step_types[step.get('type', 'unknown')] += 1
        
        # Common failure-based suggestions
        if step_types.get('error', 0) > 2:
            suggestions.append({
                'suggestion': f"Consider error handling improvements for {task_type} tasks",
                'category': 'error_handling',
                'priority': 8
            })
        
        if len(steps) > 10:
            suggestions.append({
                'suggestion': f"Break down {task_type} tasks into smaller steps",
                'category': 'task_decomposition',
                'priority': 7
            })
        
        # Decision-based suggestions
        if not decisions:
            suggestions.append({
                'suggestion': f"Add more decision checkpoints for {task_type} tasks",
                'category': 'decision_making',
                'priority': 6
            })
        
        return suggestions
    
    def extract_patterns(self, lookback_days: int = 7) -> Dict:
        """Extract patterns from recent history."""
        patterns = {
            'successful_approaches': [],
            'failure_patterns': [],
            'recommended_improvements': []
        }
        
        # Get all memories
        memories = self.memory.get_all_memories()
        
        # Analyze success patterns
        success_memories = [m for m in memories 
                          if m.get('success_rate', 0) > 0.7 and m.get('use_count', 0) > 2]
        
        for mem in success_memories[:10]:
            patterns['successful_approaches'].append({
                'content': mem['content'],
                'success_rate': mem['success_rate'],
                'uses': mem['use_count']
            })
        
        # Analyze failure patterns
        failure_memories = [m for m in memories 
                          if m.get('success_rate', 0) < 0.4]
        
        for mem in failure_memories[:10]:
            patterns['failure_patterns'].append({
                'content': mem['content'],
                'success_rate': mem['success_rate']
            })
        
        # Get pending improvements
        improvements = self.memory.get_pending_improvements()
        patterns['recommended_improvements'] = [
            {'suggestion': i['suggestion'], 'priority': i['priority']}
            for i in improvements[:10]
        ]
        
        return patterns
    
    def analyze_session_improvements(self, session_id: str) -> Dict:
        """Analyze a specific session and recommend improvements."""
        observer_data = self.observer.analyze_reasoning_patterns(session_id)
        
        # Get session from observer
        session_file = self.observer.log_dir / f"session_{session_id}.json"
        if not session_file.exists():
            return {'error': 'Session not found'}
        
        with open(session_file) as f:
            session = json.load(f)
        
        recommendations = []
        
        # Analyze task types
        task_success = defaultdict(lambda: {'success': 0, 'failed': 0})
        for task in session.get('tasks', []):
            task_type = task.get('task_type', 'unknown')
            if task.get('success'):
                task_success[task_type]['success'] += 1
            else:
                task_success[task_type]['failed'] += 1
        
        # Generate recommendations
        for task_type, stats in task_success.items():
            if stats['failed'] > stats['success']:
                recommendations.append({
                    'category': 'task_improvement',
                    'task_type': task_type,
                    'suggestion': f"Focus on improving {task_type} task handling",
                    'priority': min(10, 5 + stats['failed'])
                })
        
        return {
            'session_id': session_id,
            'task_stats': dict(task_success),
            'recommendations': recommendations
        }
    
    def generate_learning_report(self) -> Dict:
        """Generate a comprehensive learning report."""
        stats = self.memory.get_stats()
        patterns = self.extract_patterns()
        
        # Calculate improvement metrics
        total_tasks = stats.get('total_tasks_completed', 0) + stats.get('total_tasks_failed', 0)
        if total_tasks > 0:
            improvement_trend = stats.get('overall_success_rate', 0)
        else:
            improvement_trend = 0
        
        return {
            'generated_at': datetime.utcnow().isoformat(),
            'statistics': stats,
            'patterns': patterns,
            'improvement_trend': improvement_trend,
            'top_recommendations': patterns['recommended_improvements'][:5]
        }
    
    def continuous_learning_cycle(self):
        """Run a continuous learning cycle - analyze and improve."""
        # Get all lessons
        lessons = self.memory.get_lessons()
        
        # Find lessons that need verification
        unverified_lessons = [l for l in lessons if not l.get('verified')]
        
        # Mark high-success lessons as verified
        for lesson in lessons:
            total = lesson.get('success_count', 0) + lesson.get('failure_count', 0)
            if total >= self.min_success_for_pattern:
                success_rate = lesson.get('success_count', 0) / total
                if success_rate > 0.7:
                    # Could mark as verified (would need update method)
                    pass
        
        # Generate new improvement suggestions based on patterns
        patterns = self.extract_patterns()
        
        new_suggestions = []
        
        # Check for repetitive failures
        for failure in patterns.get('failure_patterns', [])[:3]:
            # Generate specific improvement
            new_suggestions.append({
                'suggestion': f"Address pattern: {failure.get('content', '')[:100]}",
                'category': 'pattern_improvement',
                'priority': 6
            })
        
        # Add new suggestions
        for suggestion in new_suggestions:
            self.memory.add_improvement(
                suggestion=suggestion['suggestion'],
                category=suggestion['category'],
                priority=suggestion['priority']
            )
        
        return {
            'verified_lessons': len([l for l in lessons if l.get('verified')]),
            'new_suggestions': len(new_suggestions),
            'pending_improvements': len(patterns.get('recommended_improvements', []))
        }


# Global instance
_learner_instance = None


def get_learner() -> ExperienceLearner:
    """Get or create the global learner instance."""
    global _learner_instance
    if _learner_instance is None:
        _learner_instance = ExperienceLearner()
    return _learner_instance