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.
- Multiple async tool types: All functions use proper async patterns for true parallelism
- Thread safety testing: Tools modify shared state to verify thread-safe operations
- Performance demonstration: Clear time differences between parallel and sequential execution
- GIL-aware design: Uses
await asyncio.sleep()instead oftime.sleep()to avoid blocking
- get_weather(city) - Async function, 2-second delay
- get_currency_rate(from_currency, to_currency) - Async function, 1.5-second delay
- calculate_distance(city1, city2) - Async function, 1-second delay
- get_population(cities) - Async function, 0.5 seconds per city
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.
Get the weather for New York, London, and Tokyo
Expected: 3 parallel get_weather calls (~2 seconds total instead of ~6 seconds sequential)
Get the weather in Paris, the USD to EUR exchange rate, and the distance between New York and London
Expected: 3 parallel async calls with different functions (~2 seconds total)
Compare New York and London by getting weather, population, and distance between them
Expected: Multiple parallel calls combining different data types
You can test the timing difference by asking for the same information in different ways:
Sequential-style request:
First get the weather in New York, then get the weather in London, then get the weather in Tokyo
Expected time: ~6 seconds (2s + 2s + 2s)
Parallel-style request:
Get the weather in New York, London, and Tokyo
Expected time: ~2 seconds (max of parallel 2s delays)
The parallel version should be 3x faster due to concurrent execution.
All tools modify the agent's state (tool_context.state) with request logs including timestamps. This helps verify that:
- Multiple tools can safely modify state concurrently
- No race conditions occur during parallel execution
- State modifications are preserved correctly
# Start the agent in interactive mode
adk run contributing/samples/parallel_functions
# Or use the web interface
adk web- "Get weather for New York, London, Tokyo, and Paris" (4 parallel calls, ~2s total)
- "What's the USD to EUR rate and GBP to USD rate?" (2 parallel calls, ~1.5s total)
- "Compare New York and San Francisco: weather, population, and distance" (3 parallel calls, ~2s total)
- "Get population data for Tokyo, London, Paris, and Sydney" (1 call with 4 cities, ~2s total)
- "What's the weather in Paris and the distance from Paris to London?" (2 parallel calls, ~2s total)
Root Cause: Using blocking operations like time.sleep() in function implementations.
Solution: Always use async patterns:
# ❌ Wrong - blocks the GIL, forces sequential execution
def my_tool():
time.sleep(2) # Blocks entire event loop
# ✅ Correct - allows true parallelism
async def my_tool():
await asyncio.sleep(2) # Non-blocking, parallel-friendly- Parallel execution: ~2 seconds for 3 weather calls
- Sequential execution: ~6 seconds for 3 weather calls
- If you see 6+ seconds, your functions are blocking the GIL