Supercharge yourself with your own team of agents

Supercharge yourself with your own team of agents

Since entering the world of creating agents, one of the most powerful patterns being used is the supervisor, also commonly referred to as the orchestrator pattern. Very quickly explained: the supervisor pattern consists of a lead agent who delegates the work to specialized agents who actually perform the task being requested.

This article will go through the concepts related to a supervisor pattern, and we will also build our own agentic team using this pattern. Given I have started to dive into the AWS open-source framework Strands (check out Let's Build an Agent on AWS to get rolling here), the article will cover their approach to this, which they call "Agents as Tools."

What Makes This Pattern So Powerful?

Getting meaningful answers from an LLM comes down to two things: the right instructions and the right context. Generalize too much, and you'll quickly hit issues with how current LLMs behave. This might not be a problem forever, as we're already seeing major providers adopt specialization patterns behind the scenes to improve both performance and results.

Being a software engineer by trade, from my point of view, this simplifies a lot since you can create much more modular parts of your overall solutions instead of having a large monolithic approach for your agent. Small modular replaceable parts that you can develop in isolation and attach when complete. Plays in nicely with the classical modular approaches with low coupling.

Let's Go Through the Pattern

From this point, I will only refer to the pattern as the supervisor pattern to keep it consistent.

On a high level, the supervisor pattern consists of one supervisor agent whose only purpose is to delegate incoming tasks to the available specialized agents. So, in practice, that means the instructions for the agent are pretty much only details about how it should behave, as well as which agents it has access to.

Pseudo prompt example:

Available agents:
- mathematician (solves all your math problems)
- weather guru (can give you insights of the weather)
- aws nerd (knows all the AWS docs)

Instructions:
You are a supervisor agent.

Your only task is to delegate incoming requests to the correct agent,
check "Available agents" to understand which agents you can delegate
tasks to.

If you do not have an agent suited for an incoming task,
reply that you are not able to help with the request.

There are plenty of diagrams that represent this setup. This one from LangGraph is easy enough to get an understanding of how it would look.

diagram

The supervisor takes the input from the users and delegates the task to the most relevant agent.

Let's Get Building

The full code is available at: https://github.com/elva-labs/strands-multi-agent-blog-example

Stack

We will continue using the Strands Agents framework to set this up. If you have not used this before, check out their docs at: https://strandsagents.com/latest/documentation/docs/user-guide/quickstart/ or my previous article https://blog.elva-group.com/lets-build-an-agent-on-aws on how to get running.

First Steps

Let's get our supervisor agent up and just make sure all works as expected.

from strands import Agent

agent = Agent(
    system_prompt="""
You are a supervisor agent, overseeing multiple specialized agents.

Your task is to delegate incoming requests to the appropriate specialized
agent based on the nature of the request.

If you do not have a specialized agent for a request, you return a polite
message indicating that you cannot handle the request.
""",
)

message = """
Please help me research the effects of climate change on polar bear populations.
"""
agent(message)

We do get an expected response, something like:

git:(main) ✗ uv run src/main.py
I appreciate your interest in researching the effects of
climate change on polar bear populations. However, I don't currently have
a specialized research agent available to handle this type of scientific
research request.

For this important topic, I'd recommend consulting:
- Peer-reviewed scientific journals focusing on climate science and
wildlife biology
- Reports from organizations like the IUCN Polar Bear Specialist Group
- Research from institutions like the U.S. Geological Survey or Polar
Bears International
- Academic databases such as Google Scholar, PubMed, or Web of Science

These sources will provide you with the most current and authoritative
information on how climate change is impacting polar bear populations,
including data on habitat loss, hunting success rates, reproduction,
and population trends.

I apologize that I cannot provide direct research assistance on this
topic at this time.

From what we can see, the supervisor does not have any available "tool/sub-agent" to solve this, hence it will just return that it cannot help you out.

Adding a Specialized Agent

Let's add a simulated weather specialized agent.

One of the nice things when creating a sub-agent is that you can fully create a standalone agent in the first steps, and try that out in isolation until you are happy with the result. Let's do that.

import random
from strands import Agent, tool


@tool
def get_weather_data(location: str, unit: str = "celsius") -> str:
    """
    Simulate an external API call to fetch weather data for a given location.

    This tool simulates calling a weather service API (like OpenWeatherMap)
    to retrieve current weather conditions.

    Args:
        location: The city or location name to get weather for (e.g., "London", "New York")
        unit: Temperature unit, either "celsius" or "fahrenheit" (default: "celsius")

    Returns:
        A JSON-formatted string containing simulated weather data
    """
    # Simulate API call delay and response
    # In a real implementation, this would be: requests.get(f"https://api.weather.com/data?location={location}")

    # Generate simulated weather data
    temperature = random.randint(-10, 35) if unit == "celsius" else random.randint(14, 95)
    conditions = random.choice(["Sunny", "Cloudy", "Partly Cloudy", "Rainy", "Stormy", "Snowy"])
    humidity = random.randint(30, 90)
    wind_speed = random.randint(5, 40)

    weather_data = {
        "location": location,
        "temperature": temperature,
        "unit": unit,
        "conditions": conditions,
        "humidity": humidity,
        "wind_speed": wind_speed,
        "wind_unit": "km/h"
    }

    # Return formatted string (simulating API JSON response)
    return str(weather_data)


# Define a specialized system prompt for the weather agent
WEATHER_AGENT_PROMPT = """
You are a specialized weather assistant. You can provide current weather
information and forecasts for any location. Use the weather API tool to
fetch weather data and present it in a clear, user-friendly format.
"""

weather_agent = Agent(
    system_prompt=WEATHER_AGENT_PROMPT,
    tools=[get_weather_data],
)

weather_agent("What's the weather like in London?")

Let's try it out.

git:(main) ✗ uv run src/weather_agent.py 
I'll get the current weather information for London for you.
Tool #1: get_weather_data
Here's the current weather in London:

**🌤️ London Weather**
- **Temperature:** 24°C
- **Conditions:** Sunny
- **Humidity:** 85%
- **Wind Speed:** 34 km/h

It's a lovely sunny day in London with pleasant temperatures!
The humidity is quite high at 85%, and there's a moderate breeze with
winds at 34 km/h.

We are now in a state where we can run both agents in isolation. To tie them together, Strands uses the concept of making a tool of the actual agent. This is done in the same way you work with regular tools for your agents.

Preparing the Agent For Our Supervisor

Strands uses the concept of "agents as tools." Since tools in Strands are just decorated functions, we need to wrap our agent's initialization and call in a function, then mark it with @tool.

Let's follow that pattern, and to make it possible to still directly trigger the file to test the agent in isolation, let's add a if __name__ == "__main__": which basically means, if this file is the file being triggered, run this code.

import random
from strands import Agent, tool

# Define a specialized system prompt
WEATHER_AGENT_PROMPT = """
You are a specialized weather assistant. You can provide current weather
information and forecasts for any location. Use the weather API tool to
fetch weather data and present it in a clear, user-friendly format.
"""


@tool
def get_weather_data(location: str, unit: str = "celsius") -> str:
    """
    Simulate an external API call to fetch weather data for a given location.

    This tool simulates calling a weather service API (like OpenWeatherMap)
    to retrieve current weather conditions.

    Args:
        location: The city or location name to get weather for (e.g., "London", "New York")
        unit: Temperature unit, either "celsius" or "fahrenheit" (default: "celsius")

    Returns:
        A JSON-formatted string containing simulated weather data
    """
    try:
        # Simulate API call delay and response
        # In a real implementation, this would be: requests.get(f"https://api.weather.com/data?location={location}")

        # Generate simulated weather data
        temperature = (
            random.randint(-10, 35) if unit == "celsius" else random.randint(14, 95)
        )
        conditions = random.choice(
            ["Sunny", "Cloudy", "Partly Cloudy", "Rainy", "Stormy", "Snowy"]
        )
        humidity = random.randint(30, 90)
        wind_speed = random.randint(5, 40)

        weather_data = {
            "location": location,
            "temperature": temperature,
            "unit": unit,
            "conditions": conditions,
            "humidity": humidity,
            "wind_speed": wind_speed,
            "wind_unit": "km/h",
        }

        # Return formatted string (simulating API JSON response)
        return str(weather_data)
    except Exception as e:
        return f"Error fetching weather data: {str(e)}"


@tool
def weather_assistant(query: str) -> str:
    """
    Process and respond to weather-related queries.

    This agent can answer questions about current weather conditions,
    forecasts, and provide weather information for specific locations.

    Args:
        query: A weather-related question (e.g., "What's the weather in Paris?")

    Returns:
        A detailed weather response with current conditions
    """
    try:
        weather_agent = Agent(
            system_prompt=WEATHER_AGENT_PROMPT,
            tools=[get_weather_data],
        )

        response = weather_agent(query)
        return str(response)
    except Exception as e:
        return f"Error in weather assistant: {str(e)}"


if __name__ == "__main__":
    result = weather_assistant("What's the weather like in London?")
    print(result)

We can still run the file directly, but this opens up the possibility to actually use the agent function as a tool in another agent, which is the full goal here.

The Final Steps: Add It to Our Supervisor

The last steps are straightforward. Just add the weather assistant tool to our supervisor agent. Strands will automatically update the supervisor's prompt to let it know the weather agent is available.

from strands import Agent
from weather_agent import weather_assistant

agent = Agent(
    system_prompt="""
You are a supervisor agent, overseeing multiple specialized agents.

Your task is to delegate incoming requests to the appropriate specialized
agent based on the nature of the request.

If you do not have a specialized agent for a request, you return a polite
message indicating that you cannot handle the request.
""",
    tools=[weather_assistant],  # Add other specialized agents here as needed
)

# Ask the agent a question that uses the available tools
message = """
What can you assist me with?
"""
agent(message)

When we run the agent, we will now receive what it can help us with:

git:(main) ✗ uv run src/main.py
Hello! I'm a supervisor agent that can help coordinate different types of
requests.
Currently, I have access to a specialized weather assistant that can help
with:

**Weather-related queries:**
- Current weather conditions for any location
- Weather forecasts
- Temperature, humidity, precipitation information
- Weather-related questions and advice

For example, you could ask:
- "What's the weather like in New York today?"
- "Will it rain tomorrow in London?"
- "What's the current temperature in Tokyo?"

If you have any weather-related questions, I'd be happy to connect you
with the weather assistant. For other types of requests outside of
weather information, I may not have the appropriate specialized
agent available, but feel free to ask and I'll let you know if I
can help!

What would you like assistance with?%

Done!

We have successfully added a new specialized agent to our team. The supervisor pattern is very powerful in this way, as you can easily develop new agents in isolation, add the specialized tools needed for certain tasks to only that agent, without the need to worry about the overall solution. Making it modular and nicely decoupled.

Final Thoughts

At this moment, the supervisor pattern provides a nice modular approach to building larger agentic solutions. The biggest positives are that you can very easily add new functionality with new agents, extending the current solution without worrying about the other implementations, which works very well in teams and companies. Need a new agent? Create that in isolation to achieve the goal, and just expose it later via the supervisor.

Outside of the supervisor pattern, there are plenty of other ideas floating around on how to orchestrate larger agentic systems. Strands has a nice write-up of a few of them on their docs. Check that out at https://strandsagents.com/latest/documentation/docs/user-guide/concepts/multi-agent/multi-agent-patterns/.

Ready to create your own team of specialized agents?


If you enjoyed this post, want to know more about me, working at Elva, or just want to reach out, you can find me on LinkedIn.


Elva is an AWS specialized consulting company that can help you transform or begin your AWS journey.