feat(corrections): Add automatic COACHING integration for Correction Packs
- Add CorrectionPackIntegration class to bridge learning components - Modify TrainingDataCollector to auto-propagate corrections to packs - Modify FeedbackProcessor to capture corrections on INCORRECT/PARTIAL feedback - Add convenience functions: get_correction_pack_integration(), capture_coaching_correction() - Add 19 integration tests (all passing) Corrections made during COACHING mode are now automatically captured into a dedicated "auto_captured_corrections" pack for cross-workflow reuse. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
176
core/learning/feedback_processor.py
Normal file
176
core/learning/feedback_processor.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""Feedback Processor - Processes user feedback to improve workflows"""
|
||||
import logging
|
||||
from typing import Dict, List, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Lazy import for correction pack integration
|
||||
_correction_integration = None
|
||||
|
||||
class FeedbackType(str, Enum):
|
||||
"""Types of user feedback"""
|
||||
CORRECT = "correct"
|
||||
INCORRECT = "incorrect"
|
||||
PARTIAL = "partial"
|
||||
SKIP = "skip"
|
||||
|
||||
@dataclass
|
||||
class Feedback:
|
||||
"""User feedback on workflow execution"""
|
||||
workflow_id: str
|
||||
execution_id: str
|
||||
feedback_type: FeedbackType
|
||||
timestamp: datetime
|
||||
confidence_before: float
|
||||
user_comment: Optional[str] = None
|
||||
corrections: Optional[Dict] = None
|
||||
|
||||
class FeedbackProcessor:
|
||||
"""Processes user feedback to improve workflows"""
|
||||
|
||||
def __init__(self, auto_integrate_corrections: bool = True):
|
||||
"""
|
||||
Initialize the feedback processor.
|
||||
|
||||
Args:
|
||||
auto_integrate_corrections: Auto-propagate corrections to Correction Packs
|
||||
"""
|
||||
self.feedback_history: List[Feedback] = []
|
||||
self._auto_integrate = auto_integrate_corrections
|
||||
self._correction_integration = None
|
||||
logger.info("FeedbackProcessor initialized")
|
||||
|
||||
def process_feedback(
|
||||
self,
|
||||
workflow_id: str,
|
||||
execution_id: str,
|
||||
feedback_type: FeedbackType,
|
||||
confidence: float,
|
||||
comment: Optional[str] = None,
|
||||
corrections: Optional[Dict] = None,
|
||||
context: Optional[Dict[str, Any]] = None
|
||||
) -> Dict:
|
||||
"""Process user feedback and return improvement suggestions"""
|
||||
|
||||
feedback = Feedback(
|
||||
workflow_id=workflow_id,
|
||||
execution_id=execution_id,
|
||||
feedback_type=feedback_type,
|
||||
timestamp=datetime.now(),
|
||||
confidence_before=confidence,
|
||||
user_comment=comment,
|
||||
corrections=corrections
|
||||
)
|
||||
|
||||
self.feedback_history.append(feedback)
|
||||
|
||||
logger.info(
|
||||
f"Feedback processed: workflow={workflow_id}, "
|
||||
f"type={feedback_type.value}, confidence={confidence:.2f}"
|
||||
)
|
||||
|
||||
# Propagate corrections to Correction Packs if applicable
|
||||
correction_id = None
|
||||
if corrections and feedback_type in [FeedbackType.INCORRECT, FeedbackType.PARTIAL]:
|
||||
correction_id = self._propagate_corrections(
|
||||
workflow_id, execution_id, corrections, context
|
||||
)
|
||||
|
||||
# Generate improvement suggestions
|
||||
suggestions = self._generate_suggestions(feedback)
|
||||
|
||||
return {
|
||||
'feedback_recorded': True,
|
||||
'suggestions': suggestions,
|
||||
'should_update_workflow': feedback_type in [FeedbackType.INCORRECT, FeedbackType.PARTIAL],
|
||||
'correction_pack_id': correction_id
|
||||
}
|
||||
|
||||
def _propagate_corrections(
|
||||
self,
|
||||
workflow_id: str,
|
||||
execution_id: str,
|
||||
corrections: Dict,
|
||||
context: Optional[Dict[str, Any]] = None
|
||||
) -> Optional[str]:
|
||||
"""Propagate corrections to Correction Pack system."""
|
||||
if not self._auto_integrate:
|
||||
return None
|
||||
|
||||
try:
|
||||
if self._correction_integration is None:
|
||||
from core.corrections import get_correction_pack_integration
|
||||
self._correction_integration = get_correction_pack_integration()
|
||||
|
||||
return self._correction_integration.capture_feedback_correction(
|
||||
workflow_id=workflow_id,
|
||||
execution_id=execution_id,
|
||||
corrections=corrections,
|
||||
context=context
|
||||
)
|
||||
except ImportError:
|
||||
logger.debug("Correction pack integration not available")
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.warning(f"Error propagating corrections: {e}")
|
||||
return None
|
||||
|
||||
def _generate_suggestions(self, feedback: Feedback) -> List[str]:
|
||||
"""Generate improvement suggestions based on feedback"""
|
||||
suggestions = []
|
||||
|
||||
if feedback.feedback_type == FeedbackType.INCORRECT:
|
||||
suggestions.append("Review target resolution strategy")
|
||||
suggestions.append("Check if UI elements changed")
|
||||
suggestions.append("Verify action sequence")
|
||||
|
||||
if feedback.corrections:
|
||||
suggestions.append(f"Apply user corrections: {feedback.corrections}")
|
||||
|
||||
elif feedback.feedback_type == FeedbackType.PARTIAL:
|
||||
suggestions.append("Some steps succeeded - identify failing step")
|
||||
suggestions.append("Consider splitting workflow into smaller parts")
|
||||
|
||||
elif feedback.feedback_type == FeedbackType.CORRECT:
|
||||
suggestions.append("Workflow performing well - increase confidence")
|
||||
|
||||
return suggestions
|
||||
|
||||
def get_feedback_stats(self, workflow_id: str) -> Dict:
|
||||
"""Get feedback statistics for a workflow"""
|
||||
workflow_feedback = [f for f in self.feedback_history if f.workflow_id == workflow_id]
|
||||
|
||||
if not workflow_feedback:
|
||||
return {
|
||||
'total': 0,
|
||||
'correct': 0,
|
||||
'incorrect': 0,
|
||||
'partial': 0,
|
||||
'skip': 0,
|
||||
'accuracy': 0.0
|
||||
}
|
||||
|
||||
total = len(workflow_feedback)
|
||||
correct = sum(1 for f in workflow_feedback if f.feedback_type == FeedbackType.CORRECT)
|
||||
incorrect = sum(1 for f in workflow_feedback if f.feedback_type == FeedbackType.INCORRECT)
|
||||
partial = sum(1 for f in workflow_feedback if f.feedback_type == FeedbackType.PARTIAL)
|
||||
skip = sum(1 for f in workflow_feedback if f.feedback_type == FeedbackType.SKIP)
|
||||
|
||||
accuracy = correct / total if total > 0 else 0.0
|
||||
|
||||
return {
|
||||
'total': total,
|
||||
'correct': correct,
|
||||
'incorrect': incorrect,
|
||||
'partial': partial,
|
||||
'skip': skip,
|
||||
'accuracy': accuracy
|
||||
}
|
||||
|
||||
def get_recent_feedback(self, workflow_id: str, limit: int = 10) -> List[Feedback]:
|
||||
"""Get recent feedback for a workflow"""
|
||||
workflow_feedback = [f for f in self.feedback_history if f.workflow_id == workflow_id]
|
||||
return sorted(workflow_feedback, key=lambda f: f.timestamp, reverse=True)[:limit]
|
||||
Reference in New Issue
Block a user