|
| 1 | +# Parallel Function Test Agent |
| 2 | + |
| 3 | +This agent demonstrates parallel function calling functionality in ADK. It includes multiple tools with different processing times to showcase how parallel execution improves performance compared to sequential execution. |
| 4 | + |
| 5 | +## Features |
| 6 | + |
| 7 | +- **Multiple async tool types**: All functions use proper async patterns for true parallelism |
| 8 | +- **Thread safety testing**: Tools modify shared state to verify thread-safe operations |
| 9 | +- **Performance demonstration**: Clear time differences between parallel and sequential execution |
| 10 | +- **GIL-aware design**: Uses `await asyncio.sleep()` instead of `time.sleep()` to avoid blocking |
| 11 | + |
| 12 | +## Tools |
| 13 | + |
| 14 | +1. **get_weather(city)** - Async function, 2-second delay |
| 15 | +2. **get_currency_rate(from_currency, to_currency)** - Async function, 1.5-second delay |
| 16 | +3. **calculate_distance(city1, city2)** - Async function, 1-second delay |
| 17 | +4. **get_population(cities)** - Async function, 0.5 seconds per city |
| 18 | + |
| 19 | +**Important**: All functions use `await asyncio.sleep()` instead of `time.sleep()` to ensure true parallel execution. Using `time.sleep()` would block Python's GIL and force sequential execution despite asyncio parallelism. |
| 20 | + |
| 21 | +## Testing Parallel Function Calling |
| 22 | + |
| 23 | +### Basic Parallel Test |
| 24 | +``` |
| 25 | +Get the weather for New York, London, and Tokyo |
| 26 | +``` |
| 27 | +Expected: 3 parallel get_weather calls (~2 seconds total instead of ~6 seconds sequential) |
| 28 | + |
| 29 | +### Mixed Function Types Test |
| 30 | +``` |
| 31 | +Get the weather in Paris, the USD to EUR exchange rate, and the distance between New York and London |
| 32 | +``` |
| 33 | +Expected: 3 parallel async calls with different functions (~2 seconds total) |
| 34 | + |
| 35 | +### Complex Parallel Test |
| 36 | +``` |
| 37 | +Compare New York and London by getting weather, population, and distance between them |
| 38 | +``` |
| 39 | +Expected: Multiple parallel calls combining different data types |
| 40 | + |
| 41 | +### Performance Comparison Test |
| 42 | +You can test the timing difference by asking for the same information in different ways: |
| 43 | + |
| 44 | +**Sequential-style request:** |
| 45 | +``` |
| 46 | +First get the weather in New York, then get the weather in London, then get the weather in Tokyo |
| 47 | +``` |
| 48 | +*Expected time: ~6 seconds (2s + 2s + 2s)* |
| 49 | + |
| 50 | +**Parallel-style request:** |
| 51 | +``` |
| 52 | +Get the weather in New York, London, and Tokyo |
| 53 | +``` |
| 54 | +*Expected time: ~2 seconds (max of parallel 2s delays)* |
| 55 | + |
| 56 | +The parallel version should be **3x faster** due to concurrent execution. |
| 57 | + |
| 58 | +## Thread Safety Testing |
| 59 | + |
| 60 | +All tools modify the agent's state (`tool_context.state`) with request logs including timestamps. This helps verify that: |
| 61 | +- Multiple tools can safely modify state concurrently |
| 62 | +- No race conditions occur during parallel execution |
| 63 | +- State modifications are preserved correctly |
| 64 | + |
| 65 | +## Running the Agent |
| 66 | + |
| 67 | +```bash |
| 68 | +# Start the agent in interactive mode |
| 69 | +adk run contributing/samples/parallel_functions |
| 70 | + |
| 71 | +# Or use the web interface |
| 72 | +adk web |
| 73 | +``` |
| 74 | + |
| 75 | +## Example Queries |
| 76 | + |
| 77 | +- "Get weather for New York, London, Tokyo, and Paris" *(4 parallel calls, ~2s total)* |
| 78 | +- "What's the USD to EUR rate and GBP to USD rate?" *(2 parallel calls, ~1.5s total)* |
| 79 | +- "Compare New York and San Francisco: weather, population, and distance" *(3 parallel calls, ~2s total)* |
| 80 | +- "Get population data for Tokyo, London, Paris, and Sydney" *(1 call with 4 cities, ~2s total)* |
| 81 | +- "What's the weather in Paris and the distance from Paris to London?" *(2 parallel calls, ~2s total)* |
| 82 | + |
| 83 | +## Common Issues and Solutions |
| 84 | + |
| 85 | +### ❌ Problem: Functions still execute sequentially (6+ seconds for 3 weather calls) |
| 86 | + |
| 87 | +**Root Cause**: Using blocking operations like `time.sleep()` in function implementations. |
| 88 | + |
| 89 | +**Solution**: Always use async patterns: |
| 90 | +```python |
| 91 | +# ❌ Wrong - blocks the GIL, forces sequential execution |
| 92 | +def my_tool(): |
| 93 | + time.sleep(2) # Blocks entire event loop |
| 94 | + |
| 95 | +# ✅ Correct - allows true parallelism |
| 96 | +async def my_tool(): |
| 97 | + await asyncio.sleep(2) # Non-blocking, parallel-friendly |
| 98 | +``` |
| 99 | + |
| 100 | +### ✅ Verification: Check execution timing |
| 101 | +- Parallel execution: ~2 seconds for 3 weather calls |
| 102 | +- Sequential execution: ~6 seconds for 3 weather calls |
| 103 | +- If you see 6+ seconds, your functions are blocking the GIL |
0 commit comments