Spaces:
Build error
Build error
File size: 14,885 Bytes
f7943d1 | 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 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 | # LangGraph Workflow State Management Fix - Design Document
**Date**: January 2025
**Author**: BeatDebate Team
**Status**: Draft
**Review Status**: Pending
---
## 1. Problem Statement
**Objective**: Fix the LangGraph workflow state management issue preventing discovery agent candidate scaling for follow-up queries, ensuring `recently_shown_track_ids` is available to the discovery agent when needed.
**Current State**: Follow-up queries like "More tracks by Kendrick Lamar" only return 2 tracks instead of the expected 8-10 because the discovery agent processes before `recently_shown_track_ids` is populated in the workflow state, preventing sophisticated candidate scaling logic from triggering.
**Root Cause Identified**:
- `recently_shown_track_ids` gets populated correctly by `EnhancedRecommendationService`
- Discovery agent processes before this data is available in the workflow state
- Discovery agent sees empty `state.recently_shown_track_ids`, doesn't trigger scaling
- Logs show "π― NEW QUERY: Using base 100 candidates" instead of expected "π― CANDIDATE SCALING"
**Value Proposition**:
- **Restore Expected Behavior**: Follow-up queries return 8-10 tracks as designed
- **Activate Existing Logic**: Enable sophisticated candidate scaling (100 β 300+ candidates for artist follow-ups)
- **Improve User Experience**: Users get substantial results for "more" requests
- **Maintain System Integrity**: Fix without breaking existing functionality
---
## 2. Goals & Non-Goals
### β
In Scope
- **State Management Fix**: Ensure `recently_shown_track_ids` is available to discovery agent during workflow execution
- **Workflow Timing Analysis**: Understand and fix the timing issue in LangGraph state preservation
- **Candidate Scaling Activation**: Verify existing scaling logic triggers properly for follow-up queries
- **Comprehensive Testing**: End-to-end testing of follow-up query scenarios
- **Monitoring & Logging**: Enhanced logging to track state management and scaling decisions
- **Backward Compatibility**: Ensure fix doesn't break existing functionality
### β Out of Scope (This Fix)
- **Scaling Logic Changes**: The sophisticated scaling logic already exists and works correctly
- **Duplicate Detection Changes**: The filtering logic already works correctly
- **New Features**: Focus only on fixing the state management issue
- **Performance Optimization**: Beyond fixing the core issue
- **UI/UX Changes**: This is a backend workflow fix
---
## 3. Architecture Overview
### Current Problematic Flow
```
User Query: "More tracks by Kendrick Lamar"
β
EnhancedRecommendationService.get_recommendations()
ββ populate_recently_shown_track_ids() β [10 track IDs] β
ββ LangGraph Workflow Start
β ββ Planner Agent β planning_strategy β
β ββ Discovery Agent β sees empty recently_shown_track_ids β
β β ββ Uses base 100 candidates (should be 300+) β
β ββ Judge Agent β filters duplicates correctly β
ββ Result: Only 2 tracks (should be 8-10) β
```
### Fixed Flow (Target)
```
User Query: "More tracks by Kendrick Lamar"
β
EnhancedRecommendationService.get_recommendations()
ββ populate_recently_shown_track_ids() β [10 track IDs] β
ββ Initialize State with recently_shown_track_ids β
ββ LangGraph Workflow Start
β ββ Planner Agent β planning_strategy β
β ββ Discovery Agent β sees populated recently_shown_track_ids β
β β ββ Triggers scaling: 300+ candidates for artist queries β
β ββ Judge Agent β filters duplicates, returns 8-10 tracks β
ββ Result: 8-10 new tracks β
```
---
## 4. Technical Design
### 4.1 Root Cause Analysis
#### Current Workflow State Initialization
**Location**: `src/services/enhanced_recommendation_service.py`
**Problem**: State is initialized before `recently_shown_track_ids` is populated:
```python
# Current problematic sequence
state = MusicRecommenderState(
user_query=query,
session_id=session_id,
# recently_shown_track_ids is empty here
)
# Later...
self.populate_recently_shown_track_ids(state, session_id) # Too late!
# Workflow starts with empty recently_shown_track_ids
workflow_result = await workflow.ainvoke(state)
```
#### Discovery Agent Scaling Logic Check
**Location**: `src/agents/discovery/agent.py` (lines 295-330)
**Current Behavior**:
```python
if state.recently_shown_track_ids:
# This never triggers because list is empty
scaled_candidates = self._calculate_scaled_candidates(...)
else:
# Always uses this path
candidates = 100 # Base amount
```
### 4.2 Solution Strategy
#### Option A: Fix State Initialization Order (Recommended)
**Approach**: Populate `recently_shown_track_ids` BEFORE workflow initialization
**Implementation**:
```python
class EnhancedRecommendationService:
async def get_recommendations(self, query: str, session_id: str) -> Dict:
# 1. First populate recently shown tracks
recently_shown = await self._get_recent_tracks_for_session(session_id)
# 2. Initialize state with populated data
state = MusicRecommenderState(
user_query=query,
session_id=session_id,
recently_shown_track_ids=recently_shown, # Pre-populated!
timestamp=time.time()
)
# 3. Start workflow with complete state
workflow_result = await workflow.ainvoke(state)
return workflow_result
```
#### Option B: State Update Before Discovery Agent
**Approach**: Update state in workflow before discovery agent executes
**Implementation**: Add a pre-discovery node to populate state:
```python
def populate_context_node(state):
"""Populate recently_shown_track_ids before discovery agent"""
if not state.recently_shown_track_ids:
state.recently_shown_track_ids = get_recent_tracks(state.session_id)
return state
# Add to workflow graph
workflow.add_node("populate_context", populate_context_node)
workflow.add_edge("planner", "populate_context")
workflow.add_edge("populate_context", "discovery")
```
### 4.3 Recommended Solution: Option A
**Rationale**:
- **Simpler**: Fewer workflow changes
- **More Efficient**: No additional workflow node
- **Clearer Logic**: Context populated upfront
- **Better Separation**: Service layer handles state preparation
### 4.4 Implementation Details
#### 4.4.1 Enhanced Recommendation Service Changes
**File**: `src/services/enhanced_recommendation_service.py`
**Key Changes**:
1. Move `populate_recently_shown_track_ids` before state initialization
2. Extract session tracking logic into separate method
3. Add state validation before workflow execution
```python
async def get_recommendations(self, query: str, session_id: str) -> Dict:
"""Get music recommendations with proper state management"""
try:
# 1. Prepare session context FIRST
recently_shown = await self._prepare_session_context(session_id)
# 2. Initialize state with complete context
state = MusicRecommenderState(
user_query=query,
session_id=session_id,
recently_shown_track_ids=recently_shown,
conversation_context=await self._get_conversation_context(session_id),
timestamp=time.time()
)
# 3. Validate state before workflow
self._validate_state_for_workflow(state)
# 4. Execute workflow with complete state
workflow_result = await self.workflow.ainvoke(state)
# 5. Update session after successful execution
await self._update_session_after_recommendation(session_id, workflow_result)
return workflow_result
except Exception as e:
logger.error(f"Recommendation service error: {e}")
raise
```
#### 4.4.2 State Validation
```python
def _validate_state_for_workflow(self, state: MusicRecommenderState) -> None:
"""Validate state has all required data for workflow execution"""
if not state.user_query:
raise ValueError("user_query is required")
if not state.session_id:
raise ValueError("session_id is required")
# Log state preparation for debugging
logger.info(f"π― STATE PREPARED: recently_shown={len(state.recently_shown_track_ids)} tracks")
if state.recently_shown_track_ids:
logger.info(f"π― FOLLOW-UP DETECTED: Will trigger candidate scaling")
```
#### 4.4.3 Discovery Agent Logging Enhancement
**File**: `src/agents/discovery/agent.py`
**Enhanced Logging**:
```python
async def execute_strategy(self, state: MusicRecommenderState) -> Dict:
"""Execute discovery strategy with enhanced state logging"""
# Log state received
logger.info(f"π― DISCOVERY AGENT STATE: recently_shown={len(state.recently_shown_track_ids)}")
if state.recently_shown_track_ids:
logger.info(f"π― CANDIDATE SCALING: Detected {len(state.recently_shown_track_ids)} previous tracks")
scaled_candidates = self._calculate_scaled_candidates(state)
logger.info(f"π― CANDIDATE SCALING: Scaling to {scaled_candidates} candidates")
else:
logger.info(f"π― NEW QUERY: Using base 100 candidates")
scaled_candidates = 100
# Continue with existing logic...
```
---
## 5. Implementation Plan
### Phase 1: Core Fix (Week 1)
1. **State Management Fix**
- Modify `EnhancedRecommendationService.get_recommendations()`
- Move context population before state initialization
- Add state validation
2. **Enhanced Logging**
- Add state preparation logging
- Enhance discovery agent scaling logs
- Add workflow state tracking
3. **Basic Testing**
- Unit tests for state preparation
- Integration tests for follow-up queries
- Manual testing with "More tracks by X" scenarios
### Phase 2: Validation & Monitoring (Week 2)
1. **Comprehensive Testing**
- End-to-end follow-up query tests
- Edge case testing (empty sessions, invalid data)
- Performance impact assessment
2. **Monitoring Enhancement**
- Add metrics for candidate scaling triggers
- Track follow-up query success rates
- Monitor state management performance
3. **Documentation Updates**
- Update API documentation
- Add troubleshooting guide
- Document state management patterns
---
## 6. Testing Strategy
### 6.1 Unit Tests
```python
class TestStateManagement:
async def test_recently_shown_populated_before_workflow(self):
"""Test that recently_shown_track_ids is populated before workflow starts"""
async def test_discovery_agent_receives_populated_state(self):
"""Test discovery agent sees populated recently_shown_track_ids"""
async def test_candidate_scaling_triggers_correctly(self):
"""Test candidate scaling logic triggers for follow-up queries"""
```
### 6.2 Integration Tests
```python
async def test_followup_query_full_workflow():
"""Test complete follow-up query workflow"""
# 1. First query
result1 = await service.get_recommendations("Music by Kendrick Lamar", session_id)
assert len(result1['tracks']) == 10
# 2. Follow-up query
result2 = await service.get_recommendations("More tracks by Kendrick Lamar", session_id)
assert len(result2['tracks']) >= 8 # Should get substantial results
# 3. Verify no duplicates
track_ids_1 = {track['id'] for track in result1['tracks']}
track_ids_2 = {track['id'] for track in result2['tracks']}
assert track_ids_1.isdisjoint(track_ids_2)
```
### 6.3 Manual Testing Scenarios
1. **Basic Follow-up**: "Music by X" β "More tracks by X"
2. **Cross-Artist**: "Music by X" β "More tracks by Y"
3. **Session Boundary**: Test session expiry and reset
4. **Error Handling**: Invalid session IDs, network failures
---
## 7. Success Metrics
### 7.1 Primary Metrics
- **Follow-up Query Success Rate**: >95% return 8+ tracks
- **Candidate Scaling Trigger Rate**: >90% of follow-up queries trigger scaling
- **Duplicate Rate**: <5% duplicates in follow-up queries
- **Response Time Impact**: <10% increase in processing time
### 7.2 Quality Metrics
- **User Satisfaction**: Follow-up queries feel substantial and relevant
- **System Reliability**: No workflow failures due to state issues
- **Log Clarity**: Clear visibility into scaling decisions
- **Maintainability**: Code remains clean and testable
---
## 8. Risk Assessment
### 8.1 Technical Risks
**Risk**: State initialization order changes break existing functionality
**Mitigation**: Comprehensive testing of all query types, not just follow-ups
**Risk**: Performance impact from additional state preparation
**Mitigation**: Profile state preparation performance, optimize if needed
**Risk**: Complex edge cases with session management
**Mitigation**: Extensive edge case testing, graceful error handling
### 8.2 Implementation Risks
**Risk**: Breaking changes during active development
**Mitigation**: Feature branch development, thorough testing before merge
**Risk**: Incomplete understanding of LangGraph state management
**Mitigation**: Deep dive into LangGraph documentation, incremental changes
---
## 9. Future Considerations
### 9.1 State Management Patterns
- **Standardized State Preparation**: Create reusable patterns for state initialization
- **State Validation Framework**: Comprehensive validation for all workflow states
- **Context Management**: Enhanced conversation context handling
### 9.2 Monitoring & Analytics
- **State Management Metrics**: Track state preparation success rates
- **Workflow Performance**: Monitor impact of state management changes
- **User Experience**: Track follow-up query satisfaction
### 9.3 Extensibility
- **Multi-Context Support**: Support for multiple context types
- **Dynamic State Updates**: Runtime state updates during workflow execution
- **Context Persistence**: Enhanced session context management
---
## 10. Conclusion
This design addresses the core issue preventing follow-up queries from returning substantial results. By fixing the state management timing issue, we can activate the sophisticated candidate scaling logic that already exists in the system.
The solution is focused, low-risk, and maintains backward compatibility while significantly improving the user experience for follow-up queries. The enhanced logging and monitoring will provide clear visibility into the fix's effectiveness.
**Expected Outcome**: Follow-up queries like "More tracks by Kendrick Lamar" will consistently return 8-10 new tracks instead of just 2, providing users with the substantial results they expect from the BeatDebate system.
---
**Document Version**: 1.0
**Created**: January 2025
**Status**: Design Phase
**Next Phase**: Implementation Planning |