Sequential chains work well when tasks have a clear order, but real-world problems often require more flexibility. Sometimes you need to route tasks to different specialists based on their content. Other times, multiple agents should work simultaneously on different aspects of a problem. In this post, I’ll cover two powerful patterns: routing for intelligent task dispatch and parallelization for concurrent processing.
The Routing Pattern
Think of routing like a sophisticated mail sorting facility. Instead of one person handling every type of mail, specialized systems ensure each piece reaches the right destination quickly.
flowchart TD
I[Input] --> R{Router}
R -->|Type A| A1[Specialist A]
R -->|Type B| A2[Specialist B]
R -->|Type C| A3[Specialist C]
A1 --> O[Output]
A2 --> O
A3 --> O
style R fill:#fff3e0
style A1 fill:#e3f2fd
style A2 fill:#e8f5e9
style A3 fill:#fce4ec
Why Route?
Routing offers four key benefits:
- Task Specialization: Agents optimized for specific tasks perform better than generalists
- Resource Optimization: Route simple tasks to faster/cheaper models, complex ones to powerful models
- Flexibility: Handle diverse request types through a single entry point
- Scalability: Add new specialists without restructuring the entire system
Two Stages of Routing
Every routing workflow has two core stages:
Stage 1: Classification
Analyze the incoming task to determine its type, category, or complexity.
Rule-based classification uses programmatic logic:
1 | def classify_by_rules(message: str) -> str: |
LLM-based classification handles nuance better:
1 | def classify_with_llm(message: str) -> str: |
LLM classification excels when:
- Categories have fuzzy boundaries
- Context matters for classification
- New categories emerge over time
Stage 2: Task Dispatch
Direct the classified input to the appropriate specialist:
1 | def dispatch_to_agent(category: str, message: str) -> str: |
Complete Routing Implementation
1 | from openai import OpenAI |
Advanced: Routing with Orchestration
Sometimes the router needs to gather information from multiple sources before final dispatch:
flowchart TD
Q[Query] --> R{Router}
R -->|Pricing Query| P[Product Research]
R -->|Pricing Query| C[Customer Analysis]
P --> S[Pricing Strategist]
C --> S
S --> O[Response]
style R fill:#fff3e0
style P fill:#e3f2fd
style C fill:#e8f5e9
style S fill:#fce4ec
The router identifies that pricing queries need context from both product and customer data before the pricing specialist can respond.
The Parallelization Pattern
When subtasks don’t depend on each other, run them simultaneously. Parallelization follows a scatter-gather pattern: distribute work to multiple agents, then consolidate results.
flowchart TD
I[Input] --> S[Scatter]
S --> A1[Agent 1]
S --> A2[Agent 2]
S --> A3[Agent 3]
A1 --> G[Gather]
A2 --> G
A3 --> G
G --> O[Output]
style S fill:#e3f2fd
style G fill:#e8f5e9
The Golden Rule: Independence
Parallelization only works when subtasks are independent:
- Agent A shouldn’t need to wait for Agent B’s output
- If there’s a dependency, use sequential chaining instead
Task Decomposition Strategies
Three ways to split work for parallel processing:
1. Sectioning (Sharding)
Split large inputs into chunks processed simultaneously:
1 | def parallel_summarize(long_document: str) -> str: |
Use case: Summarizing a 100-page report by processing each chapter simultaneously.
2. Aspect-Based Decomposition
Different agents analyze different facets of the same input:
flowchart LR
P[Product] --> T[Technical Specs]
P --> S[Sentiment Analysis]
P --> C[Competitive Pricing]
T --> R[Combined Report]
S --> R
C --> R
style T fill:#e3f2fd
style S fill:#fff3e0
style C fill:#e8f5e9
Use case: Product analysis where technical specs, customer sentiment, and pricing each require different expertise.
3. Diversity/Voting
Run the same task multiple times for reliability or creative variety:
1 | def diverse_generation(prompt: str, num_variations: int = 3) -> list: |
Aggregation Strategies
After parallel execution, combine results:
| Strategy | Description | Use Case |
|---|---|---|
| Concatenation | Join outputs together | Chapter summaries → document |
| Selection | Pick best output | Choose top creative option |
| Voting | Majority wins | Classification consensus |
| Synthesis | LLM combines intelligently | Multi-perspective report |
Implementation with Threading
1 | import threading |
Real-World Example: Contract Analysis
A 50-page enterprise contract needs review. Sequential review by one expert takes too long.
Parallel approach:
flowchart TD
C[Contract] --> L[Legal Terms
Checker]
C --> CO[Compliance
Validator]
C --> F[Financial Risk
Assessor]
L --> S[Summary
Agent]
CO --> S
F --> S
S --> R[Executive
Report]
style L fill:#e3f2fd
style CO fill:#fff3e0
style F fill:#fce4ec
style S fill:#e8f5e9
Three specialists work simultaneously:
- Legal Terms Checker: Identifies problematic clauses
- Compliance Validator: Checks regulatory requirements
- Financial Risk Assessor: Evaluates financial exposure
A synthesizer combines their findings into a comprehensive executive summary.
Combining Routing and Parallelization
These patterns work well together. A router can trigger parallel workflows for complex queries:
1 | def smart_router(query: str) -> str: |
Key Takeaways
Routing
- Classify then dispatch: Two-stage process for intelligent task direction
- LLM classification for nuance: Handles fuzzy boundaries better than rules
- Specialist agents perform better: Focused prompts outperform generalists
- Can orchestrate sub-workflows: Router may gather context before final dispatch
Parallelization
- Independence is required: Subtasks must not depend on each other
- Three decomposition strategies: Sectioning, aspect-based, diversity/voting
- Four aggregation strategies: Concatenation, selection, voting, synthesis
- Threading for true concurrency: Python threading enables simultaneous API calls
These patterns handle task variety (routing) and task complexity (parallelization). But what about tasks that require iterative improvement or dynamic planning? In the next post, I’ll explore evaluator-optimizer loops and orchestrator-worker patterns.
This is Part 6 of my series on building intelligent AI systems. Next: evaluator-optimizer and orchestrator-worker patterns.
Comments