BeatDebate / Design /context_aware_planner_fix.md
SulmanK's picture
Add autopep8 as a dependency for code formatting - Updated pyproject.toml to include autopep8 version 2.3.2 for improved code style consistency. Modified uv.lock to reflect the new dependency and its requirements. This update aims to enhance code maintainability and adherence to style guidelines.
f7943d1
|
Raw
History Blame Contribute Delete
14.1 kB
# Context-Aware Planner Agent Fix
## Problem Statement
Follow-up queries like "More tracks" are being incorrectly classified as discovery queries instead of respecting conversation context, resulting in irrelevant recommendations.
**Root Cause**: The planner agent ignores context overrides and always runs fresh query understanding on raw queries, even when the conversation context service has correctly identified them as follow-ups with preserved entities and intent.
**Impact**:
- Users get random discovery tracks (mostly Radiohead) instead of more Michael Jackson R&B tracks
- Broken conversation flow where "More tracks" doesn't build on previous queries
- Poor user experience with conversational music discovery
**Success Criteria**:
- "More tracks" after "Songs by Michael Jackson that are R&B" returns more Michael Jackson R&B tracks
- Fresh queries still work normally through query understanding
- No regression in existing functionality
## Current Architecture Analysis
### Current Flow (Broken)
1. **Context Analyzer** ✅ correctly identifies "More tracks" as follow-up, creates context override
2. **Planner Agent** ❌ ignores context override, runs fresh query understanding on "More tracks"
3. **Query Understanding** ❌ returns `intent: 'discovery'`, `entities: {artists: [], genres: []}`
4. **Discovery Agent** ✅ receives context override, tries to patch entities after wrong planning
### Log Evidence
```
Line 1363: 🔧 LLM RAW RESPONSE: {'intent': 'discovery', 'musical_entities': {'artists': [], 'genres': [], 'tracks': [], 'moods': []}
Line 1376: Converted QueryUnderstanding to entities [artists_count=0, genres_count=0, intent=discovery]
Line 1393: 🔧 DEBUG: context_override data: {'intent_override': 'hybrid_artist_genre', 'target_entity': 'Michael Jackson', 'required_genres': ['R&B']}
```
## Solution Design
### New Flow (Fixed)
1. **Context Analyzer** ✅ correctly identifies follow-up, creates context override
2. **Planner Agent** ✅ checks for context override before query understanding
3. **Context Override Path** ✅ uses preserved entities and intent from context
4. **Fresh Query Path** ✅ runs normal query understanding for new queries
### Architecture Changes
#### 1. Context-Aware Planner Agent
```python
class PlannerAgent(BaseAgent):
async def process(self, state: MusicRecommenderState) -> MusicRecommenderState:
"""Process user query with context-awareness."""
try:
self.logger.info("Starting planner agent processing")
# 🚀 NEW: Check for context override before query understanding
if hasattr(state, 'context_override') and state.context_override:
context_override = state.context_override
if self._is_followup_with_preserved_context(context_override):
self.logger.info("Context override detected, using preserved entities and intent")
# Use preserved context instead of fresh query understanding
query_understanding = self._create_understanding_from_context(
state.user_query, context_override
)
entities = self._create_entities_from_context(context_override)
state.query_understanding = query_understanding
state.entities = entities
# Mark that we used context override
self._context_override_applied = True
else:
# Context override exists but not a preserved context case
query_understanding = await self._understand_user_query(state.user_query)
entities = self._convert_understanding_to_entities(query_understanding)
state.query_understanding = query_understanding
state.entities = entities
else:
# No context override - fresh query understanding
query_understanding = await self._understand_user_query(state.user_query)
entities = self._convert_understanding_to_entities(query_understanding)
state.query_understanding = query_understanding
state.entities = entities
# Continue with rest of planner logic (task analysis, strategy creation, etc.)
# ... existing code ...
except Exception as e:
# ... existing error handling ...
```
#### 2. Context Override Detection
```python
def _is_followup_with_preserved_context(self, context_override: Dict) -> bool:
"""
Check if context override contains preserved entities that should skip query understanding.
Returns True for follow-ups with preserved entities like:
- Artist deep dives with preserved genres
- Style continuations with preserved context
- Hybrid queries with preserved filters
"""
if not isinstance(context_override, dict):
return False
# Check for follow-up indicators
is_followup = context_override.get('is_followup', False)
has_preserved_entities = 'preserved_entities' in context_override
has_intent_override = 'intent_override' in context_override
# Specific follow-up types that need preserved context
followup_types_with_context = [
'hybrid_artist_genre', # "More tracks" after "Songs by X that are Y"
'artist_style_refinement', # "More jazzy tracks" after artist query
'style_continuation' # Continue with same style/mood
]
intent_override = context_override.get('intent_override')
return (is_followup and
has_preserved_entities and
has_intent_override and
intent_override in followup_types_with_context)
```
#### 3. Context-to-Entities Conversion
```python
def _create_understanding_from_context(self, user_query: str, context_override: Dict) -> QueryUnderstanding:
"""Create QueryUnderstanding from preserved context override."""
preserved_entities = context_override.get('preserved_entities', {})
intent_override = context_override.get('intent_override', 'discovery')
confidence = context_override.get('confidence', 0.9)
# Extract preserved entities
artists = self._extract_entity_names(preserved_entities.get('artists', {}).get('primary', []))
genres = self._extract_entity_names(preserved_entities.get('genres', {}).get('primary', []))
moods = self._extract_entity_names(preserved_entities.get('moods', {}).get('primary', []))
# Map intent override to QueryIntent enum
intent_mapping = {
'hybrid_artist_genre': QueryIntent.HYBRID,
'artist_style_refinement': QueryIntent.HYBRID,
'style_continuation': QueryIntent.GENRE_MOOD,
'artist_deep_dive': QueryIntent.ARTIST_SIMILARITY
}
intent = intent_mapping.get(intent_override, QueryIntent.DISCOVERY)
self.logger.info(
f"🎯 Created understanding from context: intent={intent.value}, "
f"artists={artists}, genres={genres}, confidence={confidence}"
)
return QueryUnderstanding(
intent=intent,
confidence=confidence,
artists=artists,
genres=genres,
moods=moods,
activities=[],
original_query=user_query,
normalized_query=user_query.lower(),
reasoning=f"Context override: {intent_override} follow-up with preserved entities"
)
def _create_entities_from_context(self, context_override: Dict) -> Dict[str, Any]:
"""Create entities structure from context override."""
preserved_entities = context_override.get('preserved_entities', {})
# Extract preserved entity data
artists_data = preserved_entities.get('artists', {})
genres_data = preserved_entities.get('genres', {})
moods_data = preserved_entities.get('moods', {})
# Convert to proper entities structure
entities = {
"musical_entities": {
"artists": {
"primary": self._extract_entity_names(artists_data.get('primary', [])),
"similar_to": []
},
"genres": {
"primary": self._extract_entity_names(genres_data.get('primary', [])),
"secondary": []
},
"tracks": {
"primary": [],
"referenced": []
},
"moods": {
"primary": self._extract_entity_names(moods_data.get('primary', [])),
"energy": [],
"emotion": []
}
},
"contextual_entities": {
"activities": {
"physical": [],
"mental": [],
"social": []
},
"temporal": {
"decades": [],
"periods": []
}
},
"confidence_scores": {
"overall": context_override.get('confidence', 0.9)
},
"extraction_method": "context_override_preserved",
"intent_analysis": {
"intent": context_override.get('intent_override', 'hybrid'),
"confidence": context_override.get('confidence', 0.9),
"context_override_applied": True
}
}
self.logger.info(
f"🎯 Created entities from context: "
f"artists={len(entities['musical_entities']['artists']['primary'])}, "
f"genres={len(entities['musical_entities']['genres']['primary'])}"
)
return entities
def _extract_entity_names(self, entity_list: List) -> List[str]:
"""Extract names from entity list that may contain dicts or strings."""
names = []
for item in entity_list:
if isinstance(item, dict):
names.append(item.get('name', str(item)))
elif isinstance(item, str):
names.append(item)
else:
names.append(str(item))
return names
```
## Implementation Plan
### Phase 1: Core Context Detection
1. Add context override detection to planner agent
2. Implement preserved entity extraction
3. Add logging for debugging context usage
### Phase 2: Context-to-Entities Conversion
1. Implement understanding creation from context
2. Implement entities creation from context
3. Handle intent mapping from context override
### Phase 3: Integration & Testing
1. Test with "More tracks" scenario
2. Verify backward compatibility with fresh queries
3. Test edge cases (malformed context overrides)
### Phase 4: Monitoring & Validation
1. Add metrics for context override usage
2. Monitor recommendation quality
3. Validate conversation flow improvements
## Testing Strategy
### Test Cases
#### 1. Follow-up Query Success
```
Query 1: "Songs by Michael Jackson that are R&B"
Expected: Michael Jackson R&B tracks returned
Query 2: "More tracks"
Expected: More Michael Jackson R&B tracks (not random discovery)
```
#### 2. Fresh Query Compatibility
```
Query: "Electronic music for studying"
Expected: Normal query understanding, genre/mood-based recommendations
```
#### 3. Edge Cases
```
- Context override with missing preserved_entities
- Malformed intent_override values
- Context override with empty entities
```
### Success Metrics
- Follow-up queries maintain conversation context (95%+ success rate)
- Fresh queries unaffected (no regression)
- User satisfaction with "More tracks" functionality
## Risk Mitigation
### Backward Compatibility
- Fresh queries (no context override) use existing query understanding path
- Malformed context overrides fall back to normal query understanding
- All existing planner functionality preserved
### Error Handling
- Invalid context overrides log warnings but don't break workflow
- Missing entities in context override fall back to empty entities
- Intent mapping failures default to DISCOVERY intent
### Performance
- Context override path is faster (skips LLM query understanding)
- No additional API calls or LLM overhead for follow-ups
- Minimal code complexity increase
## Metrics & Monitoring
### Key Metrics
1. **Context Override Usage Rate**: % of queries using context override
2. **Follow-up Success Rate**: % of follow-ups maintaining conversation context
3. **Query Understanding Accuracy**: Fresh vs context-override query accuracy
4. **User Engagement**: Session length and follow-up query frequency
### Logging Enhancements
- Log when context override is detected and used
- Log preserved entities and intent mappings
- Log fallback cases for debugging
## Dependencies
### No External Dependencies
- Uses existing context override structure
- Uses existing QueryUnderstanding and entities formats
- No new API calls or services required
### Internal Dependencies
- Enhanced recommendation service context override creation
- Existing conversation context service functionality
- Existing planner agent query understanding flow
## Deployment Strategy
### Rollout Plan
1. **Dev Testing**: Implement and test in development environment
2. **A/B Testing**: Split traffic to compare context-aware vs original planner
3. **Gradual Rollout**: Start with 10% of follow-up queries, monitor metrics
4. **Full Deployment**: Roll out to 100% after validation
### Rollback Plan
- Feature flag to disable context override detection
- Fallback to original query understanding for all queries
- No database or API changes required for rollback
---
## Technical Implementation Notes
### Code Structure
- New methods added to existing `PlannerAgent` class
- No breaking changes to existing interfaces
- Context override detection is additive functionality
### Testing Requirements
- Unit tests for context override detection
- Integration tests for end-to-end follow-up flow
- Performance tests to ensure no regression
### Documentation Updates
- Update planner agent documentation with context-aware behavior
- Add troubleshooting guide for context override issues
- Update system architecture diagrams