Introduction & Context

Welcome back! In the previous lessons, you built a fully async agent system that can handle multiple conversations concurrently and execute tools in parallel. Now we're ready to take this parallelization to the next level by building an orchestrator agent that can delegate work to specialized agents.

In this lesson, you'll discover how to wrap agents as asynchronous tools, allowing one agent to call another agent just as it would call any other tool. You'll learn how to make your Agent class smart enough to detect whether a tool is async or sync and handle each type appropriately, enabling efficient parallel orchestration across multiple specialized agents.

Understanding Agent Orchestration

Agent orchestration is a pattern in which one coordinator agent manages and delegates work to multiple specialized agents. Think of it as a project manager who receives a complex task and breaks it down into smaller pieces, assigning each piece to a team member with the right expertise. The orchestrator doesn't need to know how to do every task itself; it just needs to understand the problem well enough to delegate effectively.

In our case, we'll build an orchestrator that handles complex math problems by delegating calculations to specialized calculator agents. When you ask the orchestrator to calculate the total cost of purchases from three different stores, it recognizes that each store's calculation is independent and can happen in parallel. Instead of solving all three calculations sequentially, the orchestrator delegates each one to a separate calculator agent call. These agent calls execute concurrently, just like the parallel tool execution you implemented in the previous lesson. The beauty of this pattern is that it combines the strengths of specialization and parallelization, creating a scalable system in which adding more specialized agents or handling more complex problems doesn't require rewriting your core logic.

Building the Calculator Agent

Before we can create the orchestrator, we need to build the specialized agent that will handle the actual calculations. This calculator agent will be equipped with all the math tools we've been using throughout the course, making it an expert at solving mathematical problems:

The calculator assistant is a straightforward agent with a focused purpose. Its system prompt clearly defines its role as a specialist in mathematical calculations and equations. We provide it with all six math tools, which give the calculator everything it needs to handle a wide range of mathematical problems.

Notice that we're loading the tool schemas from the schemas.json file. The Gemini Agent class automatically handles schema mapping internally: it can accept schemas in either Claude format (with input_schema) or Gemini format (with parameters) and passes standard lowercase JSON Schema types to the Google Gen AI SDK. This means you can use the same schema files across different implementations without modification. The calculator assistant doesn't need to know anything about orchestration or delegation; it just needs to be good at solving the specific math problems it receives, which makes the system easier to understand and maintain.

Creating Asynchronous Agent Wrappers

To use an agent as a tool, we need to wrap it in a function that can be called like any other tool. With Gemini, we create an asynchronous wrapper that allows the agent to run directly on the event loop alongside other async operations. Here's what an async agent wrapper looks like:

Notice the key characteristics: we define agent_tool_function with async def and use await agent.run() to call the agent asynchronously. This creates a coroutine function that runs directly on the event loop. The schema uses Gemini's parameters format instead of input_schema, defining a simple interface that accepts a message string to send to the agent.

This async wrapper approach is efficient: all agent calls run concurrently on the same event loop, sharing the same asynchronous runtime. No extra threads, no nested event loops—just clean, efficient async execution. When the orchestrator makes multiple agent tool calls, they'll all execute in parallel on the event loop, just like the parallel tool execution you implemented in the previous lesson.

Detecting Async vs Sync Tools with inspect

To support both sync tools (like our math functions) and async tools (like our agent wrapper), we need the _call_tool() method to check whether a tool is async and execute it appropriately. Python's inspect module provides the perfect tool for this: inspect.iscoroutinefunction().

Here's how inspect.iscoroutinefunction() works:

The function returns True for async functions (defined with async def) and False for regular functions. We can use this to implement intelligent tool execution in our Agent class, allowing it to handle both regular tools and agent-as-tool wrappers seamlessly.

Implementing Intelligent Tool Execution

Now let's look at how the _call_tool() method in our Gemini Agent class handles both async and sync tools intelligently:

The key addition is the if inspect.iscoroutinefunction(fn) check. For async tools (like our agent wrapper), we call them directly with await fn(**args), which runs them on the event loop. For sync tools (like our math functions), we use asyncio.to_thread(fn, **args) to run them in worker threads. This gives us the best of both worlds: efficient event loop execution for async tools and safe thread-based execution for blocking sync tools.

Notice that the method returns results using _function_response_part(), which formats the response in Gemini's expected function response format. This helper method wraps the result in the proper structure that Gemini's API expects when receiving tool execution results.

Creating the Orchestrator Agent

Let's create our orchestrator using the async agent wrapper to enable efficient parallel delegation:

We use create_agent_tool() to create an async wrapper for the calculator agent. The orchestrator's system prompt instructs it to break down complex problems and make multiple tool calls in parallel. When the orchestrator makes multiple calculator_assistant_agent calls, they'll all execute concurrently on the event loop—this is the most efficient way to coordinate multiple agents.

The orchestrator receives both the tool function (which it will call) and the tool schema (which tells Gemini's API what the tool does and what parameters it accepts). This separation allows Gemini to understand when to call the tool while our code handles the actual execution.

Running the Orchestrator

Let's see the orchestrator in action with a complex problem that requires multiple independent calculations:

We create a message asking for the total cost across three stores, each with different items, prices, and shipping costs. The orchestrator will recognize that each store's calculation is independent and delegate them to separate calculator agents running in parallel. Because we used the async wrapper approach, all three calculator agents will run concurrently on the event loop. Let's examine the output to see this efficient parallel execution in action.

Observing Parallel Agent Delegation

When we run the orchestrator, the output shows parallel delegation in action:

Notice how the orchestrator makes three calculator_assistant_agent tool calls right at the start. Because we used async wrappers, all three execute concurrently on the event loop—no thread overhead! Each calculator agent then uses its math tools (which are sync functions) that execute in worker threads via asyncio.to_thread(). You can see the interleaved tool calls from all three calculator agents: they're all calling multiply_numbers and sum_numbers at roughly the same time, demonstrating true parallel execution at both the orchestrator and calculator levels. The "Agent finished working" messages appear together, confirming that all three calculator agents completed their work concurrently.

This is the power of combining async agent wrappers with intelligent sync/async tool detection: the orchestrator and calculator agents run on the event loop, while the math functions safely execute in worker threads, all happening in parallel for maximum efficiency.

Examining the Final Response

After the three calculator agents return their results, the orchestrator synthesizes them into a clear, complete answer:

The orchestrator successfully broke down the problem, delegated the independent calculations to run in parallel, and then combined the results into a well-formatted final answer. This demonstrates the power of the orchestrator pattern: complex problems get solved efficiently through parallel delegation to specialized agents, while maintaining a clean separation of concerns between coordination and execution.

Summary & Final Challenges

You've just completed the final lesson of this course, and what an incredible journey it's been! You started by building your first Gemini agent, then you learned to give it tools, made it fully asynchronous, enabled parallel tool execution, and now you've built a complete orchestrator system that can coordinate multiple specialized agents running in parallel.

In this lesson, you learned how to wrap agents as asynchronous tools, allowing one agent to call another agent just like any other tool. You updated your understanding of how the Agent class intelligently detects and handles both sync and async tools using inspect.iscoroutinefunction(). You now understand how async agent wrappers run directly on the event loop for maximum efficiency, while sync tools safely execute in worker threads, all coordinated seamlessly by the intelligent tool execution logic.

This orchestrator pattern you just implemented represents the cutting edge of agentic system design. You now understand how to break down complex problems, delegate work to specialized agents, handle both sync and async tools intelligently, and leverage Python's async capabilities to execute multiple agent calls concurrently. These are the exact skills that top AI engineering teams use to build production systems that scale.

All that's left now is to complete the practice exercises and claim your certificate! The exercises will challenge you to apply everything you've learned—from basic agent setup to advanced parallel orchestration with intelligent async/sync tool handling. Once you finish them, you'll have demonstrated mastery of building sophisticated Gemini agent systems in Python, and you'll have your certificate to prove it. Let's finish strong—you're almost there! 🚀

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal