Section 4: Agents that work together
Agentic AI on AWS, hands-on labs by Shivank Agrawal

Agents that
work together

Section 4. One agent with tools goes a long way. When a job is too big, too varied or too slow for one agent, you build a team: a supervisor with workers, agents used as tools, swarms that hand work around, and graphs that route it along fixed paths.

Folder section-04-multi-agent-patterns Notebooks 01_supervisor_worker 02_agent_as_tool 03_graph_pattern 04_swarm_pattern Model: Claude Sonnet 4.6 on Amazon Bedrock
Swipe sideways to see the full diagram
one agent with tools the right team for the job, or none 1Why ateam 2Supervisorlab 1 3Agent as toollab 2 4How agentstalk 5Swarmlab 4 6Graphlab 3 7Or justone agent
The journey in this section. We start with why teams help, build four team shapes in four notebooks, and finish with the most important skill: knowing when you don't need a team at all.
4.0

From one agent to a team

When a software team takes on a hard problem, nobody does everything alone. Architects, developers and testers each do what they're best at. A hospital works the same way, with doctors, nurses, specialists and administrators. The same idea applies to AI agents.

In Sections 1 to 3 you built single agents with tools and memory. For most use cases, one well-equipped agent is enough. But what if the problem is too big or too varied for one agent to handle well?

In this section you move from one agent to several. You'll see why one agent isn't always enough, then build the main ways of coordinating a team: a supervisor delegating to workers, agents reused as tools, swarms that hand work to each other, and graphs that route work along fixed paths. Along the way you'll learn when each pattern makes sense, and when it doesn't.

✦Think of it like this

A wedding in India. One person can't book the hall, manage the caterer, handle the decorators, coordinate the mehendi artist and greet 500 guests. So there's a wedding planner (the coordinator) and specialists who each own one job. The planner doesn't cook; the caterer doesn't decorate. Multi-agent systems are built on exactly this division of labour.

What you'll learn here

  1. When one agent isn't enough
  2. What multi-agent patterns are
  3. How agents communicate: sequential, parallel and collaborative
  4. Common orchestration patterns: supervisor–worker, agent as tool, swarm and graph
  5. When multi-agent is not the answer

Before you start

  1. 1

    A working Strands setup from Sections 1 to 3

    Virtual environment, AWS credentials with Amazon Bedrock access, and check_setup.py passing.

  2. 2

    Basic Python functions and classes

  3. 3

    An AWS account with Bedrock permissions

    Every notebook calls Claude Sonnet 4.6 on Bedrock. Nothing else is created in your account, so there's nothing to clean up.

  4. 4

    REST APIs and JSON (helpful, not required)

  5. 5

    An IDE

    VS Code, JupyterLab or Kiro, whichever you've been using.

  6. 6

    A free Tavily API key for notebook 1

    The news and sentiment agents search the web with Tavily. An Alpha Vantage key is optional; without it, financial data comes from Yahoo Finance, which needs no key.

NotebookPatternExampleOn this page
01_supervisor_worker.ipynbSupervisor–workerMarket research: news, financial and sentiment workers coordinated by a supervisor4.3
02_agent_as_tool.ipynbAgent as toolTutoring: a teacher delegates to math, writing and science tutors4.4
03_graph_pattern.ipynbGraphE-commerce orders routed through risk, fraud, inventory, shipping and refund steps4.7
04_swarm_pattern.ipynbSwarmGame level design: four specialists hand work to each other4.7
terminal
pip install -r requirements.txt
export TAVILY_API_KEY="tvly-..."          # notebook 1 (free key at tavily.com)
export ALPHA_VANTAGE_API_KEY="..."        # optional
jupyter lab
iWhat's in requirements.txt

strands-agents and strands-agents-tools (the agents, plus the ready-made Tavily search tool), yfinance (free Yahoo Finance data), alpha_vantage (optional market data), pandas and boto3.

4.1

When one agent isn't enough

Before the patterns, the why: when should several agents work together instead of one super-agent doing everything?

The problem with single agents

Imagine asking one agent to research the latest AI trends, analyse competitors' pricing, write a technical specification, generate the code, review that code for security issues, and build a presentation for executives. That's a lot to ask. These are the warning signs that one agent is carrying too much:

Context overload

It tries to track too many different tasks at once and loses focus, the you met in Section 3.

Jack of all trades, master of none

Results are mediocre across every task instead of excellent and accurate in specific areas.

No parallelisation

Everything happens one step after another, even when tasks could run at the same time.

Hard to debug

When something goes wrong, it's hard to pinpoint which part of the process failed.

The multi-agent solution

Picture a busy restaurant. One person could take orders, cook, serve and wash up, but it would be slow. Instead there's a host, servers, chefs and dishwashers, each focused on what they do best. Together they deliver a smooth experience.

Multi-agent systems work the same way. Instead of one very complex agent, you build several specialised agents that cooperate. Under the hood, a coordinator agent receives the request, breaks it into subtasks, and delegates each one to the right specialist. The specialists do their work and report back, and the coordinator stitches the results into one response.

Swipe sideways to see the full diagram
One agent Agentdoes everything Research Writing Analysis Coding overloaded, sequential, hard to debug A team Coordinatorsplits and combines Researchagent Writingagent Analysisagent Codingagent focused, parallel where possible, easy to debug
One generalist vs a team of specialists. The coordinator breaks the request into subtasks and delegates; each specialist does one job well and reports back.

Benefits of the multi-agent approach

  1. 1

    Specialisation

    Each agent becomes an expert in its domain, with a focused prompt and the right tools.

  2. 2

    Parallel processing

    Independent tasks run at the same time, saving time.

  3. 3

    Better debugging

    You can isolate and fix problems in one specific agent.

  4. 4

    Scalability

    Add new agents as the system grows, without rewriting everything.

Try it

The same six jobs, one agent vs a team

Total time

Context the busiest agent carries

4.2

What are multi-agent patterns?

A is a structured way of organising several AI agents to work towards a common goal.

Think of an orchestra: strings, brass, woodwind and percussion each play their part, together, under a conductor. And just as music can be organised in different ways (a small chamber group, a large symphony, a jazz band that improvises), there are different patterns for organising agents. Each solves a particular kind of problem, and choosing the right one makes your system faster and easier to build.

Every multi-agent design answers four questions:

Swipe sideways to see the full diagram
1 Orchestration How does information flow between agents? central conductor agents talk directly 2 Specialisation Each agent gets a focused role, expertise and tools. A research agent shouldn't also do data visualisation. Let specialists specialise. 3 Collaboration How do agents share and build on each other's work? research agent output analysis agent becomes input 4 Coordination Who decides the order of work? • A workflow you define (a classical score) • Agents decide as they go (jazz improvisation)
Four questions behind every pattern. Orchestration, specialisation, collaboration and coordination. Each pattern in this section is a different set of answers to them.
AspectThe questionTwo ends of the spectrum
OrchestrationHow will information flow between agents?A central coordinator (a conductor) ↔ agents talking directly (a jazz group)
SpecialisationWhat is each agent's focused role and toolset?One narrow job per agent ↔ broader generalists
CollaborationHow do agents share information and build on each other's work?Output of one becomes input of the next ↔ ongoing discussion
CoordinationWho decides the order of work?A workflow you define (a classical score) ↔ agents figure it out (improvisation)

These four aspects underpin every pattern ahead. We start with the simplest and most common one.

4.3

Lab: the supervisor–worker pattern

Your first multi-agent pattern, and the most common one in production. One agent coordinates; specialist agents do the work.

In the pattern, one agent acts as the coordinator. It receives the request, decides which specialists to call, delegates the work, and combines their results into one response. The workers focus on their tasks and report back to the supervisor.

A story in three versions

You're building a market-research assistant. An investor asks: "Should I invest in electric vehicle companies?"

  1. 1

    Version 1: one agent

    It scans recent news, analyses stock performance, checks social media sentiment, reads analyst reports and compares competitors. About five minutes per question. Fine for a few questions a day. Then your manager says it must handle hundreds of questions an hour.

  2. 2

    Version 2: three agents in parallel

    A news agent scans EV news, a financial agent analyses stock performance, a sentiment agent checks social media. The tasks are independent, so they run at the same time: ideally about three times faster. It works! But the investor now gets three separate answers:

News agent

"Several large automakers announced new battery plants. One EV start-up reported production delays."

Financial agent

"EV stocks are up this quarter on average. Valuations of pure-play EV makers are high; one is burning cash fast."

Sentiment agent

"Social media sentiment is mixed. Retail investors are bullish; charging infrastructure is a common worry."

The investor stares at this wall of unconnected facts and asks: "So… should I invest or not?" Each agent did its job perfectly, but nobody synthesised the information into a recommendation.

  1. 3

    Version 3: add a supervisor

    The supervisor receives the question and thinks: "This person needs news context, financial analysis and market sentiment. I have three workers for exactly those." It calls all three at once, rephrasing the question for each, then weaves their answers into one recommendation.

Swipe sideways to see the full diagram
User question Supervisor agentplans, delegates, synthesises Response News agent"What's happening in theEV industry this month?" Financial agent"How financially healthy arethe major EV companies?" Sentiment agent"What's the marketsentiment around EVs?" delegatereport back ✕✕
All communication goes through the supervisor. Workers do specialised tasks and report back; they never talk to each other (the red ✕s). Each worker gets a question rephrased for its speciality.

All three workers run at the same time. Two minutes later the supervisor has every perspective and answers with one recommendation, something like: "The EV sector shows strong growth, but valuations are high and some companies face production problems. Established makers look steadier than newer entrants. With a high risk tolerance the sector looks promising; if you prefer stability, wait for valuations to settle." One coherent answer, and the investor can decide.

Workers stay independent

The news agent doesn't care what the financial agent is doing. Each focuses on its expertise and reports back.

Debugging is straightforward

Wrong financial data? Check the financial agent. Old news? Check the news agent. The supervisor handles synthesis, not domain logic, so bugs stay isolated.

New skills are easy to add

Need regulatory analysis? Create a regulatory agent and tell the supervisor about it. Existing workers don't change.

Try it

Workers on their own vs with a supervisor

    Run the notebook: 01_supervisor_worker.ipynb

    The notebook builds exactly this assistant with real data: Tavily web search for news and sentiment, and Yahoo Finance (through yfinance) for prices and fundamentals. Cell numbers on this page count code cells only, so they skip the notebook's text cells.

    cell 1
    %pip install -q -r requirements.txt
    cell 2: model
    import os
    from strands.models.bedrock import BedrockModel
    
    MODEL_ID = os.getenv("BEDROCK_MODEL_ID", "global.anthropic.claude-sonnet-4-6")
    REGION = os.getenv("AWS_REGION", os.getenv("AWS_DEFAULT_REGION", "us-east-1"))
    os.environ.setdefault("AWS_REGION", REGION)
    
    model = BedrockModel(model_id=MODEL_ID, region_name=REGION)
    print(f"Using {MODEL_ID} in {REGION}")

    One BedrockModel object, shared by every agent in the notebook. All four notebooks start with this same cell.

    Configure API keys

    Set them in your shell before starting Jupyter (recommended), or uncomment the two lines in this cell:

    cell 3
    # os.environ["TAVILY_API_KEY"] = "tvly-..."
    # os.environ["ALPHA_VANTAGE_API_KEY"] = "..."
    
    if not os.getenv("TAVILY_API_KEY"):
        print("⚠️  TAVILY_API_KEY not set — the news and sentiment agents cannot search the web.")
    else:
        print("✓ TAVILY_API_KEY is configured")
    
    if not os.getenv("ALPHA_VANTAGE_API_KEY"):
        print("ℹ️  ALPHA_VANTAGE_API_KEY not set — financial data comes from yfinance (free, no key).")
    else:
        print("✓ ALPHA_VANTAGE_API_KEY is configured")
    !Keep keys out of notebooks you share

    An API key pasted into a cell travels with the notebook file. Prefer export in your shell, so the key lives only in your environment.

    cell 4: imports
    import yfinance as yf
    from strands import Agent, tool
    from strands_tools.tavily import tavily_search

    tavily_search is a ready-made tool from strands-agents-tools. It reads TAVILY_API_KEY from the environment and supports options such as time_range ("day", "week", "month", "year") and max_results.

    Custom financial tools (Yahoo Finance)

    cell 5
    def _num(value, money=False, pct=False):
        """Format numbers safely — Yahoo sometimes leaves fields empty."""
        if not isinstance(value, (int, float)):
            return "N/A"
        if pct:
            return f"{value * 100:.2f}%"
        return f"${value:,.2f}" if money else f"{value:,.2f}"
    
    
    @tool
    def get_stock_price(symbol: str) -> str:
        """Get the current stock price and key metrics using Yahoo Finance.
    
        Args:
            symbol: Stock ticker symbol (e.g., 'AAPL', 'TSLA', 'MSFT')
        """
        try:
            info = yf.Ticker(symbol).info
            return (
                f"Stock: {symbol}\n"
                f"Current Price: {_num(info.get('currentPrice') or info.get('regularMarketPrice'), money=True)}\n"
                f"Market Cap: {_num(info.get('marketCap'), money=True)}\n"
                f"P/E Ratio: {_num(info.get('trailingPE'))}\n"
                f"52 Week High: {_num(info.get('fiftyTwoWeekHigh'), money=True)}\n"
                f"52 Week Low: {_num(info.get('fiftyTwoWeekLow'), money=True)}\n"
                f"Average Volume: {_num(info.get('averageVolume'))}\n"
                f"Sector: {info.get('sector', 'N/A')}\n"
                f"Industry: {info.get('industry', 'N/A')}"
            )
        except Exception as e:
            return f"Error fetching stock data for {symbol}: {e}"
    
    
    @tool
    def get_stock_history(symbol: str, period: str = "1mo") -> str:
        """Get historical stock price data.
    
        Args:
            symbol: Stock ticker symbol (e.g., 'AAPL', 'TSLA')
            period: One of 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd, max
        """
        try:
            hist = yf.Ticker(symbol).history(period=period)
            if hist.empty:
                return f"No historical data available for {symbol}"
            start_price = hist["Close"].iloc[0]
            end_price = hist["Close"].iloc[-1]
            change_pct = (end_price - start_price) / start_price * 100
            return (
                f"Historical Data for {symbol} ({period}):\n"
                f"Starting Price: ${start_price:.2f}\n"
                f"Ending Price: ${end_price:.2f}\n"
                f"Change: {change_pct:+.2f}%\n"
                f"Highest: ${hist['High'].max():.2f}\n"
                f"Lowest: ${hist['Low'].min():.2f}\n"
                f"Average Volume: {hist['Volume'].mean():,.0f}\n\n"
                f"Recent 5-day trend:\n{hist[['Close', 'Volume']].tail().to_string()}"
            )
        except Exception as e:
            return f"Error fetching historical data for {symbol}: {e}"
    
    
    @tool
    def get_company_financials(symbol: str) -> str:
        """Get company financial metrics: revenue, margins, growth and leverage.
    
        Args:
            symbol: Stock ticker symbol (e.g., 'AAPL', 'TSLA')
        """
        try:
            info = yf.Ticker(symbol).info
            return (
                f"Financial Metrics for {symbol}:\n"
                f"Revenue: {_num(info.get('totalRevenue'), money=True)}\n"
                f"Revenue Growth: {_num(info.get('revenueGrowth'), pct=True)}\n"
                f"Gross Profit: {_num(info.get('grossProfits'), money=True)}\n"
                f"Profit Margin: {_num(info.get('profitMargins'), pct=True)}\n"
                f"Operating Margin: {_num(info.get('operatingMargins'), pct=True)}\n"
                f"EBITDA: {_num(info.get('ebitda'), money=True)}\n"
                f"Earnings Growth: {_num(info.get('earningsGrowth'), pct=True)}\n"
                f"Return on Equity: {_num(info.get('returnOnEquity'), pct=True)}\n"
                f"Debt to Equity: {_num(info.get('debtToEquity'))}\n"
                f"Free Cash Flow: {_num(info.get('freeCashflow'), money=True)}"
            )
        except Exception as e:
            return f"Error fetching financial data for {symbol}: {e}"
    
    
    print(get_stock_price(symbol="AAPL"))  # tools are still plain functions you can call
    1. a

      _num(...): a defensive formatter

      Yahoo Finance sometimes leaves fields empty. Instead of crashing on None, the tool prints "N/A". The leading underscore marks it as a private helper, not a tool.

    2. b

      Three focused @tool functions

      Current price and key metrics, price history over a period, and company fundamentals. Each returns a plain-text summary the model can read, and each catches errors and returns them as text so one failed lookup doesn't break the whole run.

    3. c

      Test before you give it to an agent

      The last line calls a tool directly. A decorated tool is still a normal Python function, so you can check its output first.

    outputillustrative numbers
    Stock: AAPL
    Current Price: $…
    Market Cap: $…
    P/E Ratio: …
    52 Week High: $…
    52 Week Low: $…
    Average Volume: …
    Sector: Technology
    Industry: Consumer Electronics

    Optional: an Alpha Vantage tool

    cell 6
    from alpha_vantage.timeseries import TimeSeries
    
    
    @tool
    def get_alpha_vantage_quote(symbol: str) -> str:
        """Get the latest stock quote from the Alpha Vantage API.
    
        Args:
            symbol: Stock ticker symbol (e.g., 'AAPL')
        """
        key = os.getenv("ALPHA_VANTAGE_API_KEY")
        if not key:
            return "ALPHA_VANTAGE_API_KEY is not set."
        ts = TimeSeries(key=key, output_format="pandas")
        data, _ = ts.get_quote_endpoint(symbol=symbol)
        return data.to_string()
    
    
    financial_tools = [get_stock_price, get_stock_history, get_company_financials, tavily_search]
    if os.getenv("ALPHA_VANTAGE_API_KEY"):
        financial_tools.append(get_alpha_vantage_quote)

    Use this instead of, or alongside, Yahoo Finance if you have a key. Other data providers worth knowing include Polygon.io, Finnhub and Financial Modeling Prep. The financial agent's toolbox is built conditionally: the Alpha Vantage tool is only added when the key exists, so the agent never sees a tool that can't work.

    The worker agents

    cell 7
    news_agent = Agent(
        name="news_agent",
        description="Researches recent news and developments about companies and industries.",
        model=model,
        system_prompt="""You are a news research specialist. Search for recent news and developments about companies and industries.
    
    When searching:
    - Focus on recent news (use time_range='week' or 'month')
    - Prefer reliable sources such as major news outlets
    - Summarize key developments and trends
    - Include specific facts, dates, and figures when available""",
        tools=[tavily_search],
        callback_handler=None,
    )
    
    financial_agent = Agent(
        name="financial_agent",
        description="Analyzes stock prices, price history, and company financial metrics.",
        model=model,
        system_prompt="""You are a financial analysis specialist. Analyze financial metrics, stock performance, and company valuations.
    
    When analyzing:
    - Use get_stock_price for current prices and key metrics
    - Use get_stock_history for price trends
    - Use get_company_financials for revenue, earnings, and growth
    - Compare companies within the same sector and give specific numbers""",
        tools=financial_tools,
        callback_handler=None,
    )
    
    sentiment_agent = Agent(
        name="sentiment_agent",
        description="Analyzes market and investor sentiment from forums, social media, and commentary.",
        model=model,
        system_prompt="""You are a market sentiment analyst. Analyze market sentiment from social media, forums, and investor discussions.
    
    When analyzing sentiment:
    - Look for both bullish and bearish opinions
    - Identify common concerns and excitement points
    - Distinguish retail from institutional sentiment
    - Summarize the overall market mood""",
        tools=[tavily_search],
        callback_handler=None,
    )
    print("✓ Worker agents created")
    ArgumentWhat it does here
    nameBecomes the tool name the supervisor sees: letters, digits, underscores or hyphens, like news_agent.
    descriptionBecomes the tool description: how the supervisor knows when to call this worker.
    system_promptThe worker's job description, with concrete instructions about which tools to use and how.
    toolsEach worker gets only the tools for its speciality. News and sentiment share Tavily search; finance gets the Yahoo Finance tools.
    callback_handler=NoneSilences the worker's own streaming output, so you only see the supervisor's answer instead of four voices talking at once.

    The supervisor agent

    cell 8
    supervisor = Agent(
        name="research_supervisor",
        model=model,
        system_prompt="""You are a market research coordinator. When asked about investment opportunities:
    
    1. Work out what information is needed
    2. Delegate to the specialist agents:
       - news_agent: recent developments and industry news
       - financial_agent: financial metrics and performance data
       - sentiment_agent: market sentiment and investor opinions
    3. Synthesize their responses into one balanced recommendation
    
    This is educational analysis, not financial advice — say so briefly at the end.""",
        tools=[news_agent.as_tool(), financial_agent.as_tool(), sentiment_agent.as_tool()],
    )
    print("✓ Supervisor created")

    agent.as_tool() wraps a worker so the supervisor can call it like any other tool. (Passing the agents directly in tools=[...] does the same thing; you'll see that in the next lab.) By default each call starts the worker from a clean slate, so one question's research doesn't leak into the next. The supervisor's prompt tells it how to delegate and how to synthesise, and it ends with a guardrail: this is education, not financial advice.

    Run the multi-agent system

    cell 9
    question = "Should I invest in electric vehicle companies?"
    print(f"Question: {question}\n")
    
    response = supervisor(question)
    outputillustrative, shortened
    Question: Should I invest in electric vehicle companies?
    
    I'll gather recent news, financial data and market sentiment, then combine them.
    Tool #1: news_agent
    Tool #2: financial_agent
    Tool #3: sentiment_agent
    EV investment: a balanced view

    News: large automakers keep expanding EV and battery capacity; some newer EV makers face production delays.

    Financials: sector revenue is growing, but valuations of pure-play EV makers are high and several are not yet profitable.

    Sentiment: retail investors are mostly bullish; institutional investors are cautious about valuations.

    Recommendation: established manufacturers look steadier than newer entrants. With a high risk tolerance the sector looks promising; if you prefer stability, wait for valuations to normalise.

    This is educational analysis, not financial advice.

    Notice the three tool calls. Claude usually asks for all three workers in a single turn, and Strands runs tool calls from the same turn by default, so the three workers really do research in parallel. Your run will quote real, current figures from the live searches.

    Try different questions

    cell 10
    questions = [
        "What's the outlook for renewable energy stocks?",
        "Is the semiconductor industry a good investment right now?",
    ]
    
    for q in questions:
        print(f"\n{'=' * 80}\nQuestion: {q}\n{'=' * 80}")
        supervisor(q)

    Same supervisor, same workers, different sectors. Nothing about the team is specific to EVs; the supervisor works out what each question needs.

    Specialisation

    Each worker has one job and the right tools for it.

    Coordination

    The supervisor decides whom to ask and combines the answers.

    Modularity

    Add a new worker without touching the others.

    </>Making it production-ready

    Add retries and caching for API calls, respect rate limits, run asynchronously, and add tracing so you can see every call (Section 7).

    The trade-offs of supervisor–worker

    Swipe sideways to see the full diagram
    1 Bottleneck supervisor every decision goes through one agent Fixes • more supervisor copies (scale out)• cache repeat answers, prompt caching• load balancing, async processing 2 Single point of failure supervisor ✕ workers idle if it crashes, the whole system stops Fixes • health checks, automatic restarts• checkpoints to resume from last state• redundant supervisors with failover 3 No direct worker talk sentiment financial via supervisor When it matters • fine for simple workflows• negotiations need collaborative communication (4.6)
    Three costs of having one boss. Each has well-known fixes, which is why the pattern still dominates in production.
    1. 1

      The supervisor is a

      Every decision flows through one agent. In production you address this with (run several supervisor instances), caching (repeated questions return instantly) and . For high volume, add load balancing and asynchronous processing.

    2. 2

      It's a

      If the supervisor crashes, the system stops. You need health checks, automatic restarts, and checkpoint management so the supervisor resumes from its last known state. For critical systems, run redundant supervisors with a mechanism.

    3. 3

      Workers can't collaborate directly

      If the sentiment agent discovers something the financial agent should know, it has to go through the supervisor. That's fine for simple workflows; for real negotiation you need collaborative communication, covered in 4.6.

    iWhy it's still the favourite

    Most multi-agent systems don't need complex negotiation. They need clear accountability, simple communication and easy debugging. That's why supervisor–worker is the most common pattern in production.

    4.4

    Lab: make agents reusable with "agent as tool"

    Build a specialist once, give it a clear description, and any other agent can discover and call it like an ordinary tool.

    Supervisor–worker works well, but if every specialist is built from scratch for one particular supervisor, you rebuild it again and again. The pattern fixes that: you wrap an agent with a description of what it does, and other agents can call it just like a regular tool. Treating components as modular, reusable tools lets a system grow without rigid dependencies between its parts.

    When an agent receives a task, it reads the available tool descriptions and picks the right one, exactly as you saw with tools in Section 2. The only difference is that these tools are agents.

    ✦Think of it like this

    A hospital's specialist doctors. The cardiologist isn't hired by one GP; any doctor in the hospital can refer a patient to them. The referral desk only needs to know "cardiologist: heart problems". Build the specialist once, and every department can use them.

    Swipe sideways to see the full diagram
    Student ∫ 2x dx, from 1 to 3 8 Main agentreads tooldescriptions available tools Mathagent Writingagent Scienceagent call 8
    The tools are agents. The main agent doesn't know calculus; it knows that "math agent: solves equations" fits the question. Writing and science agents sit unused until a question needs them.

    The example: a tutoring assistant

    Students ask about different subjects, and you have specialist tutors for math, writing and science. Instead of writing routing logic like "if the question contains 'equation', call the math tutor", you wrap each specialist as a tool and let the teacher agent decide which one to use.

    Run the notebook: 02_agent_as_tool.ipynb

    Cells 1 and 2 are the same install and model cells as notebook 1. Then:

    cell 3
    from strands import Agent

    Specialist agents

    Each specialist gets a name and a description. When an agent is used as a tool, the name becomes the tool name and the description tells the teacher when to call it.

    cell 4
    math_tutor = Agent(
        name="math_tutor",
        description="Solves mathematical equations and explains math concepts step by step",
        model=model,
        system_prompt="You are a math tutor. Solve math problems and explain your solutions clearly, step by step.",
        callback_handler=None,
    )
    
    writing_coach = Agent(
        name="writing_coach",
        description="Helps with writing, grammar, and essay structure",
        model=model,
        system_prompt="You are a writing coach. Help with writing, grammar, and essay structure. Provide constructive feedback.",
        callback_handler=None,
    )
    
    science_helper = Agent(
        name="science_helper",
        description="Explains scientific concepts and principles in simple terms",
        model=model,
        system_prompt="You are a science tutor. Explain scientific concepts clearly using simple language and real-world examples.",
        callback_handler=None,
    )

    The teacher ()

    Pass the specialist agents straight into tools=[...]; Strands wraps each one as a callable tool for you.

    cell 5
    teacher = Agent(
        model=model,
        system_prompt="""You are a teacher who helps students by delegating to specialist tutors.
    Never solve problems yourself. Always delegate to the appropriate specialist:
    - math_tutor: for math problems and equations
    - writing_coach: for writing, grammar, and essays
    - science_helper: for science questions
    
    After receiving the specialist's response, present it to the student in a friendly way.""",
        tools=[math_tutor, writing_coach, science_helper],
    )
    ?Two ways to say the same thing

    Notebook 1 wrote tools=[news_agent.as_tool(), …]. Here it's tools=[math_tutor, …]. When Strands finds an agent in the tools list, it calls .as_tool() for you with the default settings. Use .as_tool(name=..., description=...) explicitly when you want to override the name or description.

    Test with different questions

    cell 6
    # Math → math_tutor
    response = teacher("Can you help me solve this quadratic equation: x^2 + 5x + 6 = 0?")
    outputillustrative
    I'll help you with that quadratic equation! Let me connect you with our math specialist.
    Tool #1: math_tutor
    
    Great! Here's what our math tutor explained:
    
    To solve x² + 5x + 6 = 0, we can use factoring.
    
    Step 1: Look for two numbers that multiply to 6 and add to 5.
            Those numbers are 2 and 3 (since 2 × 3 = 6 and 2 + 3 = 5).
    
    Step 2: Factor the equation: (x + 2)(x + 3) = 0
    
    Step 3: Solve each factor:
            If x + 2 = 0, then x = −2
            If x + 3 = 0, then x = −3
    
    Solution: x = −2 or x = −3
    
    You can check both answers by substituting them back into the original equation; both make it equal zero!
    Would you like help with another problem?

    The teacher has no hard-coded rule like "if math question, call the math tutor". It saw three tools, read their descriptions, and chose math_tutor because the question matched "solves mathematical equations". Then, as its prompt asks, it presented the tutor's answer in a friendly way.

    cell 7
    # Writing → writing_coach
    response = teacher("How do I write a strong thesis statement for my essay about climate change?")
    cell 8
    # Science → science_helper
    response = teacher("Why does ice float on water?")

    Expect Tool #1: writing_coach for the thesis question and Tool #1: science_helper for the ice question (ice is less dense than liquid water because its molecules lock into an open crystal structure).

    Try it

    Which tutor does the teacher call?

    Pick a question.

    Why this is so flexible

    Want a history tutor? Create a history_tutor agent and add it to the teacher's tools. The teacher automatically knows it exists. No routing logic to update, no new if statements.

    And it goes deeper. Specialists can have their own tools, including other agents, creating layers of expertise. Your math_tutor might use a calculator_agent and a graph_plotter_agent. And the same math_tutor can be reused by a teacher agent, a tutoring-assistant agent and a homework-grader agent:

    Swipe sideways to see the full diagram
    Teacher agent Tutoring assistant Homework grader uses as tooluses as tooluses as tool Math tutor agentbuilt once, reused three times uses as tooluses as tool Calculator agent Graph plotter agent
    Layers of expertise. One specialist serves many callers, and can itself call other specialists. Each layer only needs to know the descriptions of the layer below.
    iSupervisor–worker vs agent as tool

    They look alike, and in Strands both use agents as tools. The difference is intent. Supervisor–worker is about coordination: fan out, then synthesise one answer. Agent as tool is about reuse: a self-contained specialist that any orchestrator can pick up, while the orchestrator stays in control of the conversation.

    4.5

    How data flows through a team of agents

    You've built two patterns. Now zoom out and follow one request as it moves through a multi-agent system, step by step.

    The request: "What's the best laptop for video editing under ₹1,80,000?"

    Swipe sideways to see the full diagram
    Userinput Routersplits, rephrases Specs agentneeds for 4K editing Price agentcurrent retailer prices Review agentcustomer feedback "can we stretch the budget?" Aggregatorcombines Answer
    One question, many hands. A router fans the request out, specialists work in parallel (and occasionally consult each other), and an aggregator merges everything into one reply.
    Step through

    Follow the laptop question

    Pick step 1.
    1. 1

      The request arrives

      The question enters the system as raw text. Nothing has happened yet.

    2. 2

      The router makes decisions

      It reads the question and thinks: "This person needs product recommendations, price comparisons and technical specs. I know which agents can help." It sends work to the specs agent (knows what video editing needs), the price agent (searches current prices across retailers) and the review agent (analyses customer feedback). It doesn't forward the question blindly; it rephrases it for each. The specs agent gets "What CPU and GPU are needed for 4K video editing?"; the price agent gets "Find laptops under ₹1,80,000 with these specs."

    3. 3

      The agents do their thing

      All three work at the same time: one works out requirements, one searches retailer listings, one reads thousands of reviews. Each agent can keep its own memory of the conversation (in Strands, wrap it with as_tool(preserve_context=True); the default starts each call fresh), so if you mentioned your editing software earlier, the specs agent remembers.

    4. 4

      Agents talk to each other (sometimes)

      The price agent finds a laptop at ₹1,65,000, but the specs agent realises it doesn't have enough RAM for 4K. The price agent asks: "If we stretch the budget to ₹1,90,000, can we get something that works?" Agents negotiate and refine based on what the others find. (In a strict supervisor–worker setup this exchange goes through the supervisor; in collaborative setups, covered next, agents talk directly.)

    5. 5

      Results are combined

      An aggregator (often the supervisor) collects: specs agent: "At least 32 GB RAM, a recent mid-range dedicated GPU and a fast multi-core CPU." Price agent: "Three laptops match: ₹1,72,000, ₹1,79,000 and ₹1,86,000." Review agent: "The ₹1,79,000 one is rated 4.5 stars, with praise for rendering speed."

    6. 6

      You get your answer

      "I recommend Laptop B at ₹1,79,000. It meets the specs for 4K editing, fits your budget, and users specifically praise its rendering performance." It feels like one agent answered, but three specialists collaborated behind the scenes.

    A single agent would have to know video-editing requirements, search several retailers, analyse reviews and synthesise everything itself: slower, more expensive and more likely to miss something. With several agents, each is an expert in its area, and they work in parallel. That raises the obvious next questions: how do agents share information, and how do you decide whether they work in sequence, in parallel or together?

    4.6

    How agents talk to each other

    Agents working in isolation aren't a system; they're just separate programs. Communication is what turns them into a team.

    Agents communicate in three main ways. Each solves a different problem and fits a different situation.

    Sequential communication

    Think of writing a research paper. You can't write the conclusion before the research, and you can't edit what hasn't been written. Some things must happen in order. In communication, one agent finishes and hands its results to the next.

    Swipe sideways to see the full diagram
    Researchagent findings Writingagent draft Editingagent Final output time research 10 min writing 15 min edit 5 10 + 15 + 5 = 30 minutes
    A relay race. Each agent waits for the previous one; total time is the sum of every step.

    The research agent might spend 10 minutes gathering information, the writing agent 15 minutes drafting, and the editing agent 5 minutes polishing: 30 minutes in total. The writing agent literally cannot start without the research, and the editor needs a complete draft.

    +Use it when

    There are real dependencies: step B needs step A's output. Bonus: only one agent calls the LLM at a time, which helps if your account has API .

    −The cost

    Time adds up with no overlap. And if the writing agent fails after 10 minutes of research, that time is wasted: everything downstream stops and the pipeline has to restart.

    Parallel communication

    Now picture planning a conference: one person books the venue, another arranges catering, a third handles marketing. None depends on the others, so they happen at the same time. In communication, several agents work simultaneously on different parts of a problem, and their results are combined when all have finished.

    Swipe sideways to see the full diagram
    User query runs simultaneously Pricing agent5 min Feedback agent5 min Trends agent5 min results collected Combined results 5 minutes, not 15
    Everyone at once. Total time is the time of the slowest agent, not the sum.

    Say you're analysing a market before a product launch and need three kinds of analysis. One agent scrapes a competitor's website and builds a pricing matrix (5 minutes). Another reads thousands of customer reviews of similar products, finding patterns in complaints and praise (5 minutes). A third analyses search trends, social media mentions and industry reports (5 minutes). Run them one after another and you wait 15 minutes; run them in parallel and you wait 5, the time of the slowest agent.

    Parallel works best when tasks are independent: agent 1 doesn't need agent 2's results. It's also useful for accuracy, with several agents checking the same thing from different angles.

    Try it

    Sequential vs parallel timing

    Sequential

    Parallel

    Parallel for accuracy: let agents vote

    Imagine a content-moderation system. Three agents independently check whether a post breaks community guidelines. Agent 1 says safe, agent 2 says safe, agent 3 says unsafe. Two of three vote safe, so the post is approved.

    Agents make mistakes, but if they're independent they don't all make the same mistakes. If two of three agree, you can trust the result more than any single opinion; if all three flag something, you can be very confident it needs review. The maths backs this up: if each agent is wrong 5% of the time, independently, a majority vote of five agents is wrong only about 0.12% of the time, because at least three would have to be wrong together.

    Swipe sideways to see the full diagram
    Post Agent 1safe Agent 2safe Agent 3unsafe Vote2 safe · 1 unsafe Approve
    Majority voting. One agent's mistake is outvoted by the other two, as long as their mistakes are independent.
    Voting ruleHow it decidesFits
    Simple majorityMore than half agreeContent moderation
    UnanimousAll agents must agreeHigh-stakes calls such as a medical-diagnosis assistant
    WeightedSome votes count moreLegal document review, where the specialist agent's vote weighs more
    !Voting only works with independent agents

    If every agent uses the same model, the same data source and the same reasoning, they'll make the same mistakes and voting adds nothing. You need diversity: different models, different data sources, different approaches.

    Try it

    How much does voting help?

    Number of voting agents

    One agent alone is wrong

    Majority vote is wrong

    Collaborative communication

    Think about designing a building. The architect proposes a design; the structural engineer says it won't bear the load; the architect adjusts; the interior designer suggests changes for better flow. They go back and forth until the design satisfies every constraint. In communication, agents talk to each other, share insights, negotiate trade-offs and refine their work together.

    Here three agents review code: a developer agent wrote a function that processes user data, a security agent checks for vulnerabilities, and a performance agent makes sure it's fast enough. Step through their negotiation:

    Swipe sideways to see the full diagram
    Shared coderevised each round Developer agent Security agent Performance agent negotiate trade-offs, round after round
    A real conversation. Unlike sequential (one direction) or parallel (no talking), collaborative agents push back on each other until every concern is met.
    Step through

    A code review negotiation

    Security: openPerformance: openMemory use: not checked
      Read the whole conversation
      1. Developer: I've written a function that processes user data quickly.
      2. Security: This is vulnerable to SQL injection. You need to sanitise inputs.
      3. Developer: If I add sanitisation, performance drops by 40%.
      4. Performance: 40% slower is unacceptable. We have strict latency requirements.
      5. Security: Then use parameterised queries. They're secure and fast.
      6. Developer: Good idea. Let me refactor.
      7. Performance: Wait, you're loading the entire dataset into memory. Stream it instead.
      8. Developer: Done. All three concerns addressed.
      9. All agents review the updated code.
      10. Security: Secure now. Performance: Meets latency requirements. Developer: Code is ready.

      This back-and-forth doesn't happen in sequential or parallel setups. Sequential passes information one way; parallel agents work without talking. Collaboration is a real conversation that continues until the solution satisfies every agent's requirements, here balancing security, performance and maintainability. And it does work better on hard problems: experiments with multi-agent discussion show that iterative debate catches errors and explores alternatives a single agent would miss.

      +Use it when

      Agents must balance trade-offs with no single right answer: security vs performance, cost vs quality, speed vs accuracy. Decisions where one agent's recommendation affects another's.

      −The cost

      Several rounds of discussion means several rounds of LLM calls. One agent might answer in 10 seconds; three collaborating might take 30 to 40. It's slower, costlier, and harder to troubleshoot: finding which round introduced a bad decision takes more work than debugging a simple pipeline.

      TechniqueInformation flowsTimeBest for
      SequentialOne direction, A → B → CSum of all stepsReal dependencies; tight rate limits
      ParallelOut to everyone, then combinedThe slowest stepIndependent tasks; accuracy through voting
      CollaborativeBack and forth, in roundsSeveral rounds of callsTrade-offs with no single right answer

      Sequential, parallel and collaborative are the building blocks. Next you'll see how whole systems are organised around them.

      4.7

      Common orchestration patterns

      Supervisor–worker is the pattern most organisations use, but it isn't the only one. Two more solve very different problems: swarms and graphs.

      Each uses the communication building blocks you just learned, arranged for a specific kind of problem. A swarm lets agents decide who goes next; a graph makes you decide, in code, ahead of time. We cover the swarm (notebook 04) before the graph (notebook 03) because the graph is easiest to understand by contrast; you can run the two notebooks in either order.

      Swarm: agents hand work to each other

      A builds a whole system out of collaborative communication. The hard part of collaboration is coordination: who talks to whom, when, and how do you stop agents talking in circles? A swarm answers with : agents explicitly pass control to each other.

      ✦Think of it like this

      A game-design team building a stealth mission in an abandoned space station. The level designer creates the floor plan and a rough 3D . In playtesting, the game designer finds players get lost, so it goes back to the level designer, who adds landmarks and lighting. Then the narrative designer objects: bright lighting contradicts the story of a creepy, abandoned station. The level designer switches to dim emergency lights and environmental storytelling. Now the difficulty balancer finds the level too easy with clearer navigation, so back to the level designer for tougher enemy placement, without removing the visual cues. The path loops and branches based on what each specialist finds. Nobody could have predicted it up front.

      In a swarm you'd have four agents, level_designer, game_designer, narrative_designer and difficulty_balancer, each with its own expertise. There's no supervisor and no fixed sequence. When an agent finishes its part, it decides who should go next and hands off the task with context about what's been done.

      Swipe sideways to see the full diagram
      Briefing for the next agent • the handoff message• the original task• who has worked on it so far• notes passed along in handoffs• the other agents, and what they do level_designerlayout, visuals narrative_designerstory, atmosphere game_designerplaytests, final review difficulty_balancerdifficulty, pacing handoff_to_agent(...) entry point →
      No boss, just handoffs. Any agent can pass control to any other. Whoever picks up the task receives a briefing built by the swarm: the handoff message, the original task, who has worked on it, and any notes passed along.

      Run the notebook: 04_swarm_pattern.ipynb

      After the usual install and model cells:

      cell 3: imports and handoff logging
      import logging
      
      from strands import Agent
      from strands.multiagent import Swarm
      
      # Show each handoff as it happens
      logging.getLogger("strands.multiagent").setLevel(logging.DEBUG)
      logging.basicConfig(format="%(levelname)s | %(name)s | %(message)s", handlers=[logging.StreamHandler()])

      The two logging lines switch on Strands' multi-agent debug messages, so you can watch every handoff live.

      cell 4: specialist agents
      level_designer = Agent(
          name="level_designer",
          model=model,
          callback_handler=None,
          system_prompt="""You are a Level Designer in a game design swarm.
      Your role is to create level layouts, environments, and visual elements.
      Focus ONLY on layout and visual design. Do NOT write narrative or balance difficulty.
      After completing your layout, hand off to narrative_designer for story elements
      and difficulty_balancer for gameplay tuning.
      When receiving feedback from other agents, revise your design accordingly.""",
      )
      
      game_designer = Agent(
          name="game_designer",
          model=model,
          callback_handler=None,
          system_prompt="""You are a Game Designer in a game design swarm.
      Your role is to playtest designs for fun, usability, and player confusion.
      Focus ONLY on gameplay feel and player experience. Do NOT design layouts or write narrative.
      When you find issues, hand off to the appropriate specialist to fix them.
      When the design is ready, give the final summary and do not hand off.""",
      )
      
      narrative_designer = Agent(
          name="narrative_designer",
          model=model,
          callback_handler=None,
          system_prompt="""You are a Narrative Designer in a game design swarm.
      Your role is to create story elements, character backstories, and atmospheric details.
      Focus ONLY on narrative. Do NOT redesign layouts or adjust difficulty.
      After adding narrative elements, hand off to difficulty_balancer for balance review
      or game_designer for playtesting feedback.""",
      )
      
      difficulty_balancer = Agent(
          name="difficulty_balancer",
          model=model,
          callback_handler=None,
          system_prompt="""You are a Difficulty Balancer in a game design swarm.
      Your role is to evaluate and tune difficulty progression, enemy placement, and pacing.
      Focus ONLY on balance. Do NOT redesign layouts or write narrative.
      When you find layout or narrative issues, hand off to the appropriate agent.
      After balancing, hand off to game_designer for a final playtest review.""",
      )

      Read the prompts closely: each one says what the agent does, what it must not do ("Do NOT write narrative"), and whom to hand off to. The game designer's prompt contains the exit: "When the design is ready, give the final summary and do not hand off." A swarm finishes when an agent completes its turn without handing off.

      Create the swarm

      The swarm gives every agent a handoff_to_agent tool and a shared view of the work so far. For example, the game designer might call:

      a handoff, as the model calls it
      handoff_to_agent(
          agent_name="difficulty_balancer",
          message="Patrol patterns too predictable - need more variation",
      )
      cell 5
      swarm = Swarm(
          [level_designer, game_designer, narrative_designer, difficulty_balancer],
          entry_point=level_designer,
          max_handoffs=20,
          max_iterations=20,
          execution_timeout=1200.0,
          node_timeout=300.0,
          repetitive_handoff_detection_window=8,
          repetitive_handoff_min_unique_agents=3,
      )
      print("✓ Swarm created")
      SettingValueWhat it protects against
      entry_pointlevel_designerWhich agent starts. A layout has to exist before anyone can review it.
      max_handoffs20Caps how many times control can be passed.
      max_iterations20Caps the total number of agent turns. Without these limits, agents could loop forever. (In the current Strands version both are counted as completed agent turns, so they behave alike; setting both is belt and braces.)
      execution_timeout1200.0 sWall-clock cap for the whole swarm (20 minutes).
      node_timeout300.0 sWall-clock cap for any single agent's turn (5 minutes).
      repetitive_handoff_detection_window8: two agents bouncing the task back and forth without progress. The swarm looks at the last 8 turns; if fewer than 3 different agents appear, it stops. So if the level and narrative designers trade the task 8 times without anyone else, the loop is detected and the swarm ends.
      repetitive_handoff_min_unique_agents3

      Run the swarm (takes a few minutes)

      cell 6
      result = swarm("""Design a stealth mission level in an abandoned space station.
      The level needs to support multiple playstyles (aggressive stealth, patient stealth, and ghost runs).
      Include at least 3 distinct zones with different difficulty levels,
      narrative elements that explain why the station was abandoned,
      and ensure the difficulty curve is appropriate for mid-game players.""")
      
      print("=" * 80)
      print("HANDOFF CHAIN:")
      for node in result.node_history:
          print(f"  → {node.node_id}")
      print(f"\nStatus: {result.status} | iterations: {result.execution_count} | time: {result.execution_time / 1000:.0f}s")
      outputillustrative; your path will differ
      DEBUG | strands.multiagent.swarm | current_node=<level_designer>, iteration=<1> | executing node
      DEBUG | strands.multiagent.swarm | from_node=<level_designer>, to_node=<narrative_designer> | handoff detected
      DEBUG | strands.multiagent.swarm | current_node=<narrative_designer>, iteration=<2> | executing node
      DEBUG | strands.multiagent.swarm | from_node=<narrative_designer>, to_node=<difficulty_balancer> | handoff detected
      DEBUG | strands.multiagent.swarm | current_node=<difficulty_balancer>, iteration=<3> | executing node
      DEBUG | strands.multiagent.swarm | from_node=<difficulty_balancer>, to_node=<game_designer> | handoff detected
      DEBUG | strands.multiagent.swarm | current_node=<game_designer>, iteration=<4> | executing node
      DEBUG | strands.multiagent.swarm | from_node=<game_designer>, to_node=<level_designer> | handoff detected
      ...
      DEBUG | strands.multiagent.swarm | node=<game_designer> | no handoff occurred, marking swarm as complete
      DEBUG | strands.multiagent.swarm | status=<Status.COMPLETED> | swarm execution completed
      DEBUG | strands.multiagent.swarm | node_history_length=<11>, time=<486.46>s | metrics
      ================================================================================
      HANDOFF CHAIN:
        → level_designer
        → narrative_designer
        → difficulty_balancer
        → game_designer
        → level_designer
        → game_designer
        → …
        → game_designer
      
      Status: Status.COMPLETED | iterations: 11 | time: 486s

      Read the chain as a story. The level designer drew the layout and handed off to the narrative designer for story elements, who added the backstory and passed it to the difficulty balancer for tuning, who handed off to the game designer for playtesting. The game designer found navigation problems in zone 2 and sent it back to the level designer. That back-and-forth continued for 11 turns until the game designer approved the final design. Nobody programmed this sequence. The agents worked it out from what needed to happen next.

      cell 7: show the final design
      from IPython.display import Markdown, display
      
      last_node = result.node_history[-1].node_id
      display(Markdown(str(result.results[last_node].result)))

      result.node_history lists every turn in order; the last entry is the agent that finished (usually the game designer with its final summary). result.results maps each agent's name to its last output, which this cell renders as Markdown.

      How a handoff works under the hood

      Handoffs happen through tool calls. When you create a Swarm, every agent automatically gets a handoff_to_agent tool, and the swarm keeps a shared record of the run. Say the game designer finishes playtesting and finds enemy patrols are too predictable. That's a difficulty problem, not a playtesting problem, so it needs someone who can tune the challenge. When its turn began, the swarm gave the game designer a formatted briefing that includes:

      1. 1

        The handoff message

        What the previous agent said it needs, for example "Add landmarks in zone 2".

      2. 2

        The original task

      3. 3

        The agents who have worked on it so far

        For example level_designer → game_designer.

      4. 4

        What previous agents contributed

        As "shared knowledge from previous agents": whatever notes an agent chose to pass in the handoff's optional context argument.

      5. 5

        The other available agents

        By name, plus each one's description if you gave it one. The game designer reads: the level designer creates layouts and visuals; the narrative designer creates story and atmosphere; the difficulty balancer tunes difficulty, enemy placement and pacing.

      The patrol problem matches the difficulty balancer, so the game designer calls handoff_to_agent(agent_name="difficulty_balancer", message="Patrol patterns too predictable - need more variation"). Now the difficulty balancer is in control and receives the same formatted context, including what the game designer found. The LLM makes each routing decision by matching the problem to what each agent does.

      !Write handoffs like a good shift-change note

      Each agent's own conversation is reset before its turn, so a returning level designer doesn't automatically see its earlier layout. Knowledge travels in the handoff message (and optional context). Prompting agents to summarise what they did and what's needed when they hand off makes swarms work much better.

      iTip: give swarm agents a description

      The lab's agents rely on their names and prompts. Adding description="Tunes difficulty, enemy placement and pacing" to each Agent(...) puts that text in every teammate's list of available agents, which makes handoff choices more reliable.

      Try it

      Run a swarm, one turn at a time

      Swarm log

        Briefing the next agent receives

        Last 8 turns: unique agents

        +Use a swarm for

        Creative and iterative work where you can't predict the path up front. Sometimes the layout passes every reviewer first time; sometimes it bounces around as new problems appear. You define the agents and their strengths; they work out who handles each issue.

        −Don't use one when

        You need a fixed sequence where certain checks always happen in a specific order. That's the graph pattern's job.

        Graph: you define the paths

        The is built on a : data flows along defined paths from one node to the next. There are two flavours:

        Swipe sideways to see the full diagram
        DAG: directed, acyclicarrows only point forward, never loop back risk check fraud check shipping Once an order clears fraud detection,it never goes back to the risk check. Cyclic graphdata can loop back to earlier nodes developer reviewer code issues found done Loops until the reviewer approves.
        Forward-only vs loops. Production workflows mostly use , so that's the focus here. (Strands also supports cyclic graphs, with safety limits such as a cap on total node executions and timeouts.)

        "Isn't that what a swarm does?" The key difference: in a swarm, agents decide the path; in a graph, you define the paths up front. Before the example, the building blocks:

        Nodes

        Anything that processes data. In Strands a node can be a single agent (like a fraud-detection agent), a swarm of agents, another graph (graphs can be nested), or custom business logic: your own deterministic code wrapped as a node.

        Edges

        The connections. An edge says "when node A finishes, pass its output to node B". Edges can carry , for example "only follow this edge if the order is over $1,000".

        Dependencies

        If node B depends on node A, A must finish before B starts. The graph manages this and runs nodes in the right order. When several arrows lead into one node (like inventory check below), it runs as soon as one incoming arrow fires.

        The example: processing e-commerce orders

        Orders arrive all day. Some are small purchases from regular customers; some are big orders from brand-new accounts; some ship somewhere that doesn't match the billing address. You can't treat them all the same way.

        You could use supervisor–worker: the supervisor reads each order and decides where to send it. It works, until your payment processor asks, "How do you guarantee high-value orders always get fraud review?" With a supervisor, the routing happens inside the LLM's reasoning. You can log it, but the decision logic isn't explicit in your code.

        A graph makes the routing explicit. The risk analyser runs first; high-value orders go to manual review; suspicious ones go to fraud detection; clean, low-risk orders go straight to the inventory check. The decision tree is visible in your code, and anyone can see exactly what happens to each kind of order.

        Swipe sideways to see the full diagram
        Order received Risk analyzerrisk_analysis HIGH_VALUE Manual reviewmanual_review SUSPICIOUS Advanced fraud checkfraud_check LOW_RISK No extra checkedge goes straight to inventory Inventory checkinventory_check IN_STOCK Shippinglabel, dispatch OUT_OF_STOCK Refundcustomer refunded
        Every path drawn in advance. Three ways in, one meeting point at inventory, two possible endings. The labels on the edges are the exact words the routing code looks for. The dashed box isn't a node: low-risk orders go straight from risk analysis to the inventory check.
        1. 1

          High-value orders go to manual review first

        2. 2

          Suspicious orders go to advanced fraud detection

          Red flags such as a billing and shipping mismatch, or a brand-new account placing a large order.

        3. 3

          Low-risk orders from trusted customers skip the checks

          They go straight to inventory.

        4. 4

          All three paths meet at the inventory check

          In stock: shipping generates a label and the package goes out. Out of stock: refund processing notifies the customer and returns the payment. Every order ends in one of these two states.

        Directed means arrows point one way: from the risk analyser to fraud check, never backwards. Acyclic means no loops: you can't go risk analyser → fraud check → inventory → back to risk analyser, which could cycle forever. That structure guarantees every order eventually reaches an end state, shipped or refunded.

        That's why graphs are great for compliance and auditability. When your payment processor asks about fraud prevention, you point at the graph. The logic lives in code, not hidden in LLM reasoning. A large order from a new customer always follows the same path; it doesn't vary with how an LLM reads the situation that day.

        ?So where does the LLM fit?

        The graph defines the flow; each node is still an agent that uses an LLM for its task. The risk analyser understands the order in natural language, the fraud agent reasons about suspicious patterns, the inventory agent interprets stock data. You define the routing; the LLMs do the reasoning at each step.

        Run the notebook: 03_graph_pattern.ipynb

        After the usual install and model cells:

        cell 3
        from strands import Agent
        from strands.multiagent import GraphBuilder

        The two "decision" agents end their answer with a fixed line (RISK_CATEGORY: ..., STOCK_STATUS: ...). The routing functions read only that line, so an explanation that merely mentions another category can't send the order down the wrong path.

        cell 4: specialised agents
        risk_analyzer = Agent(
            name="risk_analyzer",
            model=model,
            callback_handler=None,
            system_prompt="""Analyze the order's risk level and pick exactly ONE category:
        - HIGH_VALUE: orders over $1,000
        - SUSPICIOUS: mismatched billing/shipping country, brand-new account with a large order, or known fraud patterns
        - LOW_RISK: regular orders from trusted customers
        If more than one applies, SUSPICIOUS wins over HIGH_VALUE.
        Give a one-paragraph explanation, then finish with a final line in exactly this format:
        RISK_CATEGORY: <HIGH_VALUE|SUSPICIOUS|LOW_RISK>""",
        )
        
        manual_review_agent = Agent(
            name="manual_review_agent",
            model=model,
            callback_handler=None,
            system_prompt="Review high-value orders. Verify customer identity, check order history, and approve or flag for further review.",
        )
        
        fraud_detection_agent = Agent(
            name="fraud_detection_agent",
            model=model,
            callback_handler=None,
            system_prompt="Run fraud detection on suspicious orders: known fraud patterns, velocity checks, and device fingerprinting. Decide whether to proceed.",
        )
        
        inventory_agent = Agent(
            name="inventory_agent",
            model=model,
            callback_handler=None,
            system_prompt="""Check inventory for the order items. Assume laptops, books and phones are in stock and
        anything described as 'limited edition' or 'pre-order' is out of stock.
        Explain briefly, then finish with a final line in exactly this format:
        STOCK_STATUS: <IN_STOCK|OUT_OF_STOCK>""",
        )
        
        shipping_agent = Agent(
            name="shipping_agent",
            model=model,
            callback_handler=None,
            system_prompt="Generate a shipping label and process the shipment. Confirm a tracking number and estimated delivery date.",
        )
        
        refund_agent = Agent(
            name="refund_agent",
            model=model,
            callback_handler=None,
            system_prompt="Process a refund for out-of-stock items. Notify the customer and initiate the payment reversal.",
        )
        
        print("✓ All agents created")
        iTwo lab conveniences

        The risk rules use dollars ($1,000) to match the sample orders. And the inventory agent pretends to check stock using a simple rule (laptops, books and phones in stock; "limited edition" and "pre-order" out of stock). In a real system that node would call your inventory database as a tool.

        Routing conditions

        Each condition reads the previous node's output and returns True or False. The logic is plain Python, not hidden inside model reasoning.

        cell 5
        import re
        
        
        def decision(state, node_id: str, label: str) -> str:
            """Return the value of the final 'LABEL: VALUE' line written by a node."""
            text = str(state.results.get(node_id, ""))
            matches = re.findall(rf"{label}\s*:\s*([A-Z_]+)", text.upper())
            return matches[-1] if matches else ""
        
        
        def is_high_value(state):
            return decision(state, "risk_analysis", "RISK_CATEGORY") == "HIGH_VALUE"
        
        
        def is_suspicious(state):
            return decision(state, "risk_analysis", "RISK_CATEGORY") == "SUSPICIOUS"
        
        
        def is_low_risk(state):
            return decision(state, "risk_analysis", "RISK_CATEGORY") == "LOW_RISK"
        
        
        def is_in_stock(state):
            return decision(state, "inventory_check", "STOCK_STATUS") == "IN_STOCK"
        
        
        def is_out_of_stock(state):
            return decision(state, "inventory_check", "STOCK_STATUS") == "OUT_OF_STOCK"
        
        
        print("✓ Routing conditions defined")
        1. a

          state.results

          The graph's record of every node's output so far, keyed by node ID. str(...) turns a node's result into its text.

        2. b

          The

          RISK_CATEGORY\s*:\s*([A-Z_]+) finds "RISK_CATEGORY", optional spaces, a colon, optional spaces, then captures a word in capitals and underscores. .upper() makes it case-insensitive, and matches[-1] takes the last match, the required final line, so earlier mentions in the explanation are ignored.

        3. c

          Five tiny conditions

          One per edge that needs a decision. Each is a one-line, testable Python function.

        Build the graph

        cell 6
        builder = GraphBuilder()
        
        # Nodes
        builder.add_node(risk_analyzer, "risk_analysis")
        builder.add_node(manual_review_agent, "manual_review")
        builder.add_node(fraud_detection_agent, "fraud_check")
        builder.add_node(inventory_agent, "inventory_check")
        builder.add_node(shipping_agent, "shipping")
        builder.add_node(refund_agent, "refund")
        
        # Conditional edges out of risk analysis
        builder.add_edge("risk_analysis", "manual_review", condition=is_high_value)
        builder.add_edge("risk_analysis", "fraud_check", condition=is_suspicious)
        builder.add_edge("risk_analysis", "inventory_check", condition=is_low_risk)
        
        # Review / fraud check → inventory
        builder.add_edge("manual_review", "inventory_check")
        builder.add_edge("fraud_check", "inventory_check")
        
        # Inventory → ship or refund
        builder.add_edge("inventory_check", "shipping", condition=is_in_stock)
        builder.add_edge("inventory_check", "refund", condition=is_out_of_stock)
        
        builder.set_entry_point("risk_analysis")
        graph = builder.build()
        print("✓ Graph built")
        
        
        def show_path(result):
            path = " → ".join(node.node_id for node in result.execution_order)
            print(f"Status: {result.status}\nPath:   {path}")
            last = result.execution_order[-1].node_id
            print(f"\nFinal step ({last}):\n{result.results[last].result}")
        output
        UserWarning: … no max_node_executions or execution_timeout set …
        ✓ Graph built

        The warning is expected. Strands is reminding you that this graph has no overall execution limit; for a forward-only graph like this one that's fine, because every run ends at shipping or refund. For cyclic graphs, set limits with builder.set_max_node_executions(...) or an execution timeout.

        add_node(agent, "node_id") registers each agent under a short ID. add_edge(from, to, condition=...) draws an arrow; with a condition, the arrow is only followed when the function returns True. Edges without a condition are always followed. set_entry_point says where every run starts, and build() validates the setup (at least one node, a valid entry point) and returns a runnable graph. show_path prints the route an order took, using result.execution_order, and the last node's output.

        Process orders: each one takes a different path

        cell 7
        # High value → manual review → inventory → shipping
        result = graph("Order #12345: $1,500 laptop from an existing customer with 3 previous orders; billing and shipping address match")
        show_path(result)
        outputillustrative
        Status: Status.COMPLETED
        Path:   risk_analysis → manual_review → inventory_check → shipping
        
        Final step (shipping):
        Shipping label generated for Order #12345 (1 × laptop). Tracking number: …
        Estimated delivery: 3–5 business days.
        cell 8
        # Low risk → inventory → shipping
        result = graph("Order #67890: $45 book from a returning customer with 50+ previous orders")
        show_path(result)
        outputillustrative
        Status: Status.COMPLETED
        Path:   risk_analysis → inventory_check → shipping
        cell 9
        # Suspicious → fraud check → inventory → refund (limited edition is out of stock)
        result = graph(
            "Order #11111: $800 limited edition headphones, account created 5 minutes ago, "
            "shipping to a different country than the billing address"
        )
        show_path(result)
        outputillustrative
        Status: Status.COMPLETED
        Path:   risk_analysis → fraud_check → inventory_check → refund
        
        Final step (refund):
        Refund initiated for Order #11111: limited edition headphones are out of stock. The customer has been notified …

        Same graph, three orders, three different paths, and each path is fully explained by the edges you wrote. Run the same order again and it takes the same path.

        Try it

        Send an order through the graph

        Set up an order, then process it. The path lights up in the diagram above.

        Use graphs wherever you have an established procedure: e-commerce order processing, compliance workflows, approval chains, content moderation, or anywhere you must prove that certain checks always happen in a certain order. Don't use one when the path can't be predicted up front; that's a swarm's job.

        Choosing a pattern

        PatternWho decides the path?Best forTrade-off
        Supervisor–workerThe supervisor agentCentralised coordination with one synthesised answer; parallel researchSupervisor is a bottleneck and a single point of failure
        Agent as toolThe orchestrator picks from available tools, and stays in controlReusable agents across different systems; simple delegationAgents only communicate through the orchestrator; nested calls add latency
        SwarmThe agents, through handoffsCreative or iterative work where the path can't be predictedHarder to debug; the order of work varies between runs
        Graph (DAG)You, in code (conditional edges)Fixed, auditable procedures and compliance workflowsEvery possible path must be defined up front
        </>Patterns combine

        Most production systems mix them. A supervisor might coordinate workers that run in parallel; a graph node might itself be a swarm; a specialist used as a tool might be a small graph. Match the pattern to your constraints, one layer at a time.

        4.8

        When multi-agent is not the answer

        You now know supervisor–worker, swarms and graphs. Here's what doesn't get said often enough: most problems don't need multiple agents.

        It's a common story. Someone learns about multi-agent systems and immediately rebuilds a working single-agent app as five agents coordinating through a graph. The demo looks impressive. Then it goes live.

        Debugging becomes a nightmare. Which agent failed? Was it agent three's input or agent two's output? The logs show five execution paths and you're tracing which one ran. The single agent used to answer in 3 seconds; the multi-agent version takes 12 because of coordination overhead. And the error rate went up, because there are now five points of failure instead of one.

        Swipe sideways to see the full diagram
        One agent + good tools Agent tools Response time3 s Points of failure1 Paths to debug1 Five agents in a graph Response time12 s Points of failure5 Paths to debug5
        Complexity has a price. Coordination overhead, more ways to fail, and much harder debugging. Pay it only when you get something real in return.
        ✦Think of it like this

        You don't hire a committee to fix a leaking tap. A good plumber with the right toolkit is faster, cheaper and easier to hold accountable. You call in a team for building a house, not for every job.

        A single agent with good tools beats a poorly designed multi-agent system. Before you design something complex, try giving one agent the right tools. Can it call your APIs? Query your databases? Run code? Read your documentation? If you structure the tools well, most problems fit in one agent's context window.

        The patterns in this section solve real problems, but they aren't default architectures. You'll know you need multiple agents when you hit actual limits:

        1. 1

          The context window keeps filling up

          You're processing thousands of documents and the agent loses track.

        2. 2

          You need true parallelism

          Waiting for three sequential 30-second API calls is too slow for your users.

        3. 3

          The expertise genuinely doesn't fit in one prompt

          The roles need such different instructions and tools that one prompt becomes a muddle.

        iThe rule of thumb

        Start with one agent, give it tools, and test it. Only add agents when one agent clearly can't cope. Three agents that work reliably beat ten that don't.

        Try it

        Do you need a team? Which one?

        Answer the questions above.
        4.9

        Recap and self-check

        You can now build teams of agents, and, just as importantly, decide when not to.

        You started with the basic question of when several agents beat one. You built a supervisor that fans questions out to news, financial and sentiment workers and synthesises one answer, and a teacher that reuses specialist tutors as tools. You followed data through a multi-agent system and learned the three ways agents communicate: sequential for dependencies, parallel for speed (and voting for accuracy), and collaborative for negotiating trade-offs. Then you organised whole systems: a swarm that hands work around when the path can't be predicted, and a graph with explicit, auditable routes. Most production systems combine these patterns; the skill is matching the pattern to your constraints.

        You should now be able to explain

        IdeaIn one line
        Signs you need a teamContext overload, mediocre results everywhere, no parallelism, hard to debug
        Four aspectsOrchestration, specialisation, collaboration, coordination
        Supervisor–workerA coordinator fans out to specialists, who never talk to each other, then synthesises one answer
        agent.as_tool()Turns an agent into a tool; its name and description tell the caller when to use it
        Agent as toolBuild a specialist once, reuse it anywhere; the orchestrator stays in control
        Sequential / parallel / collaborativeSum of times / slowest time / several rounds of calls
        VotingIndependent agents outvote each other's mistakes; correlated agents don't
        SwarmAgents hand off with handoff_to_agent, share context, and stop when nobody hands off; limits prevent loops
        GraphNodes, edges with conditions, dependencies; routing lives in code, so it's auditable
        When not toOne agent with good tools first; add agents only at a real limit

        When something goes wrong

        What you seeWhat it usually means and how to fix it
        "TAVILY_API_KEY not set", or the news agent says it can't searchExport the key before starting Jupyter, or set it in the commented lines of the keys cell, then re-run the cells that create the agents.
        Financial numbers show "N/A"Yahoo Finance left that field empty for this ticker. _num handles it; the agent works with what's available.
        ThrottlingException from BedrockParallel workers and swarms make many calls quickly. Wait and retry, run fewer questions at once, or ask for a higher quota.
        The teacher answers the question itselfIts prompt says "Never solve problems yourself". Check the prompt ran unchanged and the tutors are in tools=[...].
        A graph run stops after risk_analysisNo condition matched: the risk analyser didn't end with a valid RISK_CATEGORY: line. Print result.results["risk_analysis"].result to see what it wrote.
        The swarm stops early (Status.FAILED) with a repetitive-handoff, max-handoffs or timeout reasonThe safety limits did their job. Make the prompts clearer about who to hand off to and when to finish, or raise the limits slightly.
        The swarm takes several minutesNormal: 10 or more full agent turns. Each costs model calls, so don't run it in a loop.
        iNothing to clean up

        These notebooks only call Bedrock (and the Tavily and Yahoo Finance APIs). They create no AWS resources, so there's nothing to delete. The cost is model usage, which is highest for the swarm.

        Self-check

        Ten quick questions. Pick an answer to see why it's right or wrong.

        Score: 0 of 10 answered

        →Up next: Section 5, MCP and A2A

        So far your agents have lived in one notebook. Next you'll connect them across processes with two open protocols: MCP, which connects an agent to tools and data served by other programs, and A2A, which lets independent agents discover and call each other. Folder: section-05-mcp-and-a2a.

        A–Z

        Glossary

        Every underlined word on this page, in one place.

        © 2026 Shivank Agrawal, Future with Shivank. Agentic AI on AWS, Section 4. Course code is provided under the MIT License.

        AWS service names, tool names and library versions change often. When something here differs from what you see, the current documentation wins.