Advanced patterns for composing and orchestrating complex agent workflows
Learn how to combine multiple workflow patterns, create nested workflows, and implement advanced coordination patterns for sophisticated agent systems.
Pattern Composition Overview
Workflow pattern composition allows you to build complex agent systems by combining simpler, well-tested patterns. This approach provides:
Choose workflow patterns based on runtime conditions:
@app.workflowclass AdaptiveAnalysisWorkflow(Workflow[dict]): """Dynamically selects analysis patterns based on input characteristics.""" @app.workflow_run async def run(self, content: dict) -> WorkflowResult[dict]: # Analyze input to determine optimal patterns content_analysis = await self.analyze_content_characteristics(content) # Select appropriate patterns based on characteristics selected_patterns = await self.select_patterns(content_analysis) # Execute selected patterns dynamically pattern_results = {} for pattern_name in selected_patterns: result = await self.execute_pattern(pattern_name, content) pattern_results[pattern_name] = result # Synthesize results final_result = await self.synthesize_results(pattern_results, content_analysis) return WorkflowResult(value=final_result) async def analyze_content_characteristics(self, content: dict) -> dict: """Analyze input to determine its characteristics.""" analyzer_agent = Agent( name="content_analyzer", instruction="Analyze content characteristics to guide processing strategy.", server_names=["analysis_service"] ) async with analyzer_agent: llm = await analyzer_agent.attach_llm(OpenAIAugmentedLLM) characteristics = await llm.generate_str(f""" Analyze these content characteristics: 1. Content type and format 2. Length and complexity 3. Language and domain 4. Required processing depth 5. Time sensitivity Content: {content} """) return {"characteristics": characteristics, "content_type": content.get("type")} async def select_patterns(self, content_analysis: dict) -> list[str]: """Select optimal patterns based on content analysis.""" selector_agent = Agent( name="pattern_selector", instruction="Select optimal processing patterns based on content analysis.", server_names=["decision_engine"] ) async with selector_agent: llm = await selector_agent.attach_llm(OpenAIAugmentedLLM) pattern_selection = await llm.generate_str(f""" Based on this content analysis, select the most appropriate processing patterns: Available patterns: - detailed_analysis: Deep, comprehensive analysis (slow, thorough) - rapid_analysis: Quick insights extraction (fast, basic) - multilingual_analysis: Language-specific processing - technical_analysis: Domain-specific technical processing - sentiment_analysis: Emotion and opinion analysis - factual_analysis: Fact-checking and verification - comparative_analysis: Comparison with reference materials Analysis: {content_analysis} Return comma-separated list of selected patterns. """) # Parse selected patterns selected = [p.strip() for p in pattern_selection.split(",")] return selected async def execute_pattern(self, pattern_name: str, content: dict) -> dict: """Execute a specific analysis pattern.""" pattern_executors = { "detailed_analysis": self.detailed_analysis_pattern, "rapid_analysis": self.rapid_analysis_pattern, "multilingual_analysis": self.multilingual_analysis_pattern, "technical_analysis": self.technical_analysis_pattern, "sentiment_analysis": self.sentiment_analysis_pattern, "factual_analysis": self.factual_analysis_pattern, "comparative_analysis": self.comparative_analysis_pattern } executor = pattern_executors.get(pattern_name) if executor: return await executor(content) else: return {"error": f"Unknown pattern: {pattern_name}"} async def detailed_analysis_pattern(self, content: dict) -> dict: """Comprehensive analysis pattern.""" detailed_agent = Agent( name="detailed_analyzer", instruction="Perform thorough, comprehensive analysis with deep insights.", server_names=["deep_analysis", "knowledge_base", "ml_service"] ) async with detailed_agent: llm = await detailed_agent.attach_llm(OpenAIAugmentedLLM) # Multi-stage deep analysis structural_analysis = await llm.generate_str(f"Deep structural analysis: {content}") contextual_analysis = await llm.generate_str(f"Contextual analysis: {structural_analysis}") implications = await llm.generate_str(f"Derive implications: {contextual_analysis}") return { "pattern": "detailed_analysis", "structural": structural_analysis, "contextual": contextual_analysis, "implications": implications, "depth": "comprehensive" } async def rapid_analysis_pattern(self, content: dict) -> dict: """Quick analysis pattern for time-sensitive processing.""" rapid_agent = Agent( name="rapid_analyzer", instruction="Provide quick, essential insights with time efficiency.", server_names=["fast_analysis"] ) async with rapid_agent: llm = await rapid_agent.attach_llm(OpenAIAugmentedLLM) quick_insights = await llm.generate_str(f"Quick key insights: {content}") return { "pattern": "rapid_analysis", "insights": quick_insights, "depth": "surface" }
State Sharing Between Workflows
Shared State Management
Implement state sharing across workflow patterns:
from typing import Dict, Anyimport json@app.workflowclass StatefulOrchestrator(Workflow[dict]): """Orchestrator that maintains shared state across patterns.""" def __init__(self): self.shared_state: Dict[str, Any] = { "global_context": {}, "pattern_results": {}, "workflow_metadata": {}, "communication_log": [] } @app.workflow_run async def run(self, initial_data: dict) -> WorkflowResult[dict]: # Initialize shared state self.shared_state["global_context"] = initial_data self.shared_state["workflow_metadata"] = { "start_time": datetime.utcnow().isoformat(), "workflow_id": workflow.info().workflow_id, "run_id": workflow.info().run_id } # Execute patterns with shared state await self.execute_data_collection_pattern() await self.execute_processing_patterns() await self.execute_synthesis_pattern() return WorkflowResult(value={ "final_state": self.shared_state, "execution_summary": await self.generate_execution_summary() }) async def execute_data_collection_pattern(self): """Data collection pattern that updates shared state.""" collector_agent = Agent( name="data_collector", instruction="Collect data and update shared context.", server_names=["data_sources"] ) async with collector_agent: llm = await collector_agent.attach_llm(OpenAIAugmentedLLM) # Collect data based on current context collected_data = await llm.generate_str( f"Collect relevant data based on context: {self.shared_state['global_context']}" ) # Update shared state self.shared_state["pattern_results"]["data_collection"] = { "collected_data": collected_data, "timestamp": datetime.utcnow().isoformat(), "status": "completed" } # Update global context with new data self.shared_state["global_context"]["collected_data"] = collected_data # Log communication self.shared_state["communication_log"].append({ "pattern": "data_collection", "action": "state_update", "timestamp": datetime.utcnow().isoformat(), "data_keys": list(self.shared_state["pattern_results"]["data_collection"].keys()) }) async def execute_processing_patterns(self): """Execute multiple processing patterns that share state.""" # Pattern 1: Analysis await self.execute_analysis_pattern() # Pattern 2: Validation (uses analysis results) await self.execute_validation_pattern() # Pattern 3: Enhancement (uses both previous results) await self.execute_enhancement_pattern() async def execute_analysis_pattern(self): """Analysis pattern that reads and updates shared state.""" analysis_agent = Agent( name="analyzer", instruction="Analyze data using shared context and state.", server_names=["analysis_service"] ) async with analysis_agent: llm = await analysis_agent.attach_llm(OpenAIAugmentedLLM) # Use shared state for analysis current_context = self.shared_state["global_context"] previous_results = self.shared_state.get("pattern_results", {}) analysis_result = await llm.generate_str(f""" Perform analysis using shared context: Context: {current_context} Previous Results: {previous_results} """) # Update shared state with analysis self.shared_state["pattern_results"]["analysis"] = { "result": analysis_result, "timestamp": datetime.utcnow().isoformat(), "input_context": current_context } # Update global context self.shared_state["global_context"]["analysis_insights"] = analysis_result async def execute_validation_pattern(self): """Validation pattern that uses analysis results from shared state.""" validator_agent = Agent( name="validator", instruction="Validate analysis results using shared state.", server_names=["validation_service"] ) async with validator_agent: llm = await validator_agent.attach_llm(OpenAIAugmentedLLM) # Access analysis results from shared state analysis_result = self.shared_state["pattern_results"]["analysis"]["result"] validation_result = await llm.generate_str(f""" Validate analysis result: Analysis to validate: {analysis_result} Global context: {self.shared_state['global_context']} """) # Update shared state self.shared_state["pattern_results"]["validation"] = { "validation_result": validation_result, "validated_analysis": analysis_result, "timestamp": datetime.utcnow().isoformat() } # Update global context based on validation is_valid = "valid" in validation_result.lower() self.shared_state["global_context"]["validation_status"] = is_valid async def execute_enhancement_pattern(self): """Enhancement pattern that uses all previous results.""" enhancer_agent = Agent( name="enhancer", instruction="Enhance results using all available shared state.", server_names=["enhancement_service"] ) async with enhancer_agent: llm = await enhancer_agent.attach_llm(OpenAIAugmentedLLM) # Use all shared state for enhancement all_results = self.shared_state["pattern_results"] global_context = self.shared_state["global_context"] enhancement_result = await llm.generate_str(f""" Enhance results using all available information: All Pattern Results: {all_results} Global Context: {global_context} """) # Final state update self.shared_state["pattern_results"]["enhancement"] = { "enhanced_result": enhancement_result, "used_results": list(all_results.keys()), "timestamp": datetime.utcnow().isoformat() } async def execute_synthesis_pattern(self): """Final synthesis pattern that creates comprehensive output.""" synthesizer_agent = Agent( name="synthesizer", instruction="Synthesize all shared state into final comprehensive result.", server_names=["synthesis_engine"] ) async with synthesizer_agent: llm = await synthesizer_agent.attach_llm(OpenAIAugmentedLLM) synthesis = await llm.generate_str(f""" Synthesize comprehensive final result from all shared state: Complete State: {self.shared_state} """) self.shared_state["pattern_results"]["synthesis"] = { "final_synthesis": synthesis, "synthesized_patterns": list(self.shared_state["pattern_results"].keys()), "timestamp": datetime.utcnow().isoformat() } async def generate_execution_summary(self) -> dict: """Generate summary of workflow execution.""" return { "executed_patterns": list(self.shared_state["pattern_results"].keys()), "execution_duration": "calculated_duration", "state_updates": len(self.shared_state["communication_log"]), "final_context_keys": list(self.shared_state["global_context"].keys()) }
Advanced Coordination Patterns
Event-Driven Coordination
Implement event-driven coordination between patterns:
from dataclasses import dataclassfrom typing import Listfrom enum import Enumclass EventType(Enum): PATTERN_STARTED = "pattern_started" PATTERN_COMPLETED = "pattern_completed" DATA_UPDATED = "data_updated" ERROR_OCCURRED = "error_occurred" THRESHOLD_REACHED = "threshold_reached"@dataclassclass WorkflowEvent: event_type: EventType source_pattern: str data: dict timestamp: str@app.workflowclass EventDrivenCoordinator(Workflow[dict]): """Event-driven coordination between workflow patterns.""" def __init__(self): self.event_queue: List[WorkflowEvent] = [] self.pattern_states: Dict[str, str] = {} self.event_handlers: Dict[EventType, callable] = { EventType.PATTERN_COMPLETED: self.handle_pattern_completion, EventType.DATA_UPDATED: self.handle_data_update, EventType.ERROR_OCCURRED: self.handle_error, EventType.THRESHOLD_REACHED: self.handle_threshold } @app.workflow_run async def run(self, config: dict) -> WorkflowResult[dict]: # Initialize event-driven execution await self.initialize_patterns(config) # Event processing loop while not self.all_patterns_complete(): # Process queued events await self.process_events() # Check for new triggers await self.check_triggers() # Wait a bit before next iteration await asyncio.sleep(1) return WorkflowResult(value={ "execution_results": self.pattern_states, "processed_events": len(self.event_queue), "completion_time": datetime.utcnow().isoformat() }) async def initialize_patterns(self, config: dict): """Initialize patterns based on configuration.""" patterns_to_start = config.get("initial_patterns", ["data_ingestion"]) for pattern_name in patterns_to_start: await self.start_pattern(pattern_name, config) async def start_pattern(self, pattern_name: str, config: dict): """Start a pattern and emit start event.""" self.pattern_states[pattern_name] = "running" # Emit pattern started event event = WorkflowEvent( event_type=EventType.PATTERN_STARTED, source_pattern=pattern_name, data={"config": config}, timestamp=datetime.utcnow().isoformat() ) self.event_queue.append(event) # Execute pattern asynchronously asyncio.create_task(self.execute_pattern_async(pattern_name, config)) async def execute_pattern_async(self, pattern_name: str, config: dict): """Execute pattern and emit completion event.""" try: # Pattern execution logic pattern_agent = Agent( name=f"{pattern_name}_executor", instruction=f"Execute {pattern_name} pattern according to configuration.", server_names=config.get("required_services", ["general"]) ) async with pattern_agent: llm = await pattern_agent.attach_llm(OpenAIAugmentedLLM) result = await llm.generate_str(f"Execute {pattern_name}: {config}") # Update pattern state self.pattern_states[pattern_name] = "completed" # Emit completion event completion_event = WorkflowEvent( event_type=EventType.PATTERN_COMPLETED, source_pattern=pattern_name, data={"result": result, "status": "success"}, timestamp=datetime.utcnow().isoformat() ) self.event_queue.append(completion_event) except Exception as e: # Update state and emit error event self.pattern_states[pattern_name] = "failed" error_event = WorkflowEvent( event_type=EventType.ERROR_OCCURRED, source_pattern=pattern_name, data={"error": str(e), "status": "failed"}, timestamp=datetime.utcnow().isoformat() ) self.event_queue.append(error_event) async def process_events(self): """Process all queued events.""" events_to_process = self.event_queue.copy() self.event_queue.clear() for event in events_to_process: handler = self.event_handlers.get(event.event_type) if handler: await handler(event) async def handle_pattern_completion(self, event: WorkflowEvent): """Handle pattern completion event.""" completed_pattern = event.source_pattern # Determine next patterns to start based on completion next_patterns = self.get_next_patterns(completed_pattern) for next_pattern in next_patterns: if self.pattern_states.get(next_pattern) != "running": await self.start_pattern(next_pattern, event.data) async def handle_data_update(self, event: WorkflowEvent): """Handle data update event.""" # Check if update triggers new patterns or threshold events data_size = len(str(event.data)) if data_size > 10000: # Large data threshold threshold_event = WorkflowEvent( event_type=EventType.THRESHOLD_REACHED, source_pattern=event.source_pattern, data={"threshold": "large_data", "size": data_size}, timestamp=datetime.utcnow().isoformat() ) self.event_queue.append(threshold_event) async def handle_error(self, event: WorkflowEvent): """Handle error event.""" failed_pattern = event.source_pattern # Implement error recovery logic recovery_patterns = self.get_recovery_patterns(failed_pattern) for recovery_pattern in recovery_patterns: await self.start_pattern(recovery_pattern, { "recovery_mode": True, "failed_pattern": failed_pattern, "error_details": event.data }) async def handle_threshold(self, event: WorkflowEvent): """Handle threshold reached event.""" threshold_type = event.data.get("threshold") if threshold_type == "large_data": # Start parallel processing pattern for large data await self.start_pattern("parallel_processing", event.data) def get_next_patterns(self, completed_pattern: str) -> List[str]: """Get patterns that should start after completion.""" pattern_dependencies = { "data_ingestion": ["data_validation", "initial_analysis"], "data_validation": ["data_processing"], "initial_analysis": ["detailed_analysis"], "data_processing": ["result_synthesis"], "detailed_analysis": ["result_synthesis"], "parallel_processing": ["result_aggregation"], "result_synthesis": ["final_reporting"], "result_aggregation": ["final_reporting"] } return pattern_dependencies.get(completed_pattern, []) def get_recovery_patterns(self, failed_pattern: str) -> List[str]: """Get recovery patterns for failed patterns.""" recovery_map = { "data_ingestion": ["data_ingestion_retry"], "data_processing": ["alternative_processing"], "detailed_analysis": ["fallback_analysis"] } return recovery_map.get(failed_pattern, []) def all_patterns_complete(self) -> bool: """Check if all patterns are complete.""" active_states = ["running", "pending"] return not any(state in active_states for state in self.pattern_states.values()) async def check_triggers(self): """Check for external triggers that might start new patterns.""" # This could check external systems, databases, APIs, etc. # For now, it's a placeholder for trigger logic pass
Best Practices for Pattern Composition
Design for Composability
Keep patterns focused on single responsibilities
Use well-defined interfaces between patterns
Make patterns stateless when possible
Document pattern dependencies clearly
# Good: Single responsibility pattern@app.workflowclass DataValidationPattern(Workflow[dict]): """Focuses solely on data validation.""" pass# Avoid: Pattern that tries to do everything@app.workflowclass DataEverythingPattern(Workflow[dict]): """Validates, processes, analyzes, and reports data.""" pass