A2a Workshop-Guide
A2a Workshop-Guide
Inter-Agent Systems with Strands, Agents, Amazon Bedrock, MCP, and A2A
Prologue
Access Your Environment
Access CloudWatch Logs
Understanding the MCP Gateway
2
Chapter 6: Build a Hotel Recommender Agent
Prompt and Implement
Understanding the Filtering Logic
Deploy the Hotel Agent
Test the Pipeline
3
Intro
Workshop Overview
Level: Intermediate
4
System Architecture
High-level Architecture
Key Technologies
• Amazon Bedrock: Claude Sonnet 4 for LLM intelligence
• Strands: Agent framework with Bedrock model integration
• A2A Protocol: Agent-to-Agent communication standard
• MCP Protocol: Model Context Protocol for Claude Code integration
• AWS CloudMap: HTTP-based service discovery
• Amazon Elastic Container Service (ECS): Containerized agent deployment
• Docker: Container packaging and distribution
Learning Path
This workshop is organized into focused modules that build upon each other:
5
Module Topic
Prologue Environment setup, CloudWatch access, and understanding the MCP Gateway
Chapter 2 Testing and Understanding Your System (agent testing, context window constraints)
Chapter 7 Adding Intelligence with LLM Reasoning (Budget Agent with Claude)
Estimated Cost
$2-5 USD for the 4-hour workshop duration. All resources can be cleaned up at the end to avoid
ongoing charges.
6
Getting Started
Ready to begin? Let's start with the prologue to set up your environment and verify everything is
working correctly.
7
Prologue
The Challenge
Imagine you're the newly appointed People Operations Coordinator at AnyCompany Inc., a 250-
person SaaS company. The CEO just approved the company's first-ever all-hands offsite, bringing
together 50 employees from three offices (Seattle HQ, Austin, and Boston) for a 2-day strategic
planning session. The request is difficult but doable - a $15,000 budget, tight timeline, and high
visibility. The CEO wants "authentic local experience, not generic corporate conference rooms."
8
Complete System Architecture
Key Components
The system consists of several intelligent components working together:
9
Component Function
Translates between Claude Code (IDE assistant) and A2A agents, enabling natural
MCP Gateway
language interaction with the system.
Universal Coordinates agent interactions. Uses Claude Sonnet 4 to interpret queries, select
Orchestrator relevant agents, create a workflow, and synthesize their responses.
Specialist Agents Domain experts that work together through intelligent coordination
Service discovery system where agents register. The orchestrator discovers agents
AWS CloudMap
dynamically with no hardcoded endpoints.
Provides the inference for our LLM intelligence (Claude Sonnet 4) that powers
Amazon Bedrock
semantic understanding, agent selection, and response synthesis.
AWS ECS
Runs containerized agents with automatic scaling and health monitoring.
Fargate
Pre-Deployed Infrastructure
The following infrastructure has already been deployed to the AWS account. There's no need to
create any of this—it's ready to use.
10
Base Infrastructure
Service Purpose
AWS CloudMap HTTP namespace for agent registration and service discovery
Agent Infrastructure
Service Purpose
11
Workshop PDF Guide (Optional)
A comprehensive PDF version of all workshop content is available for download. Perfect for
offline reference or if you don't complete all chapters during the workshop session.
What's included:
12
Output
Security Note
Your code-server instance is secured with a unique password. The environment is isolated to
your workshop session and will be automatically cleaned up after the event.
Logging In
1. Click the a2a-workshop-main-code-server-url link to open your environment
2. Paste the password when prompted
3. You'll be taken directly to VS Code in your browser
13
Login
Download [Link]
:::
:::tab{label="Self-Hosting"}
One-Click Deployment
Choose your preferred region and click the launch button:
14
Deployment Steps
1. Click the Launch Stack button for your preferred region
2. On the CloudFormation console: - Review the stack name (default: a2a-workshop ) - Review the
parameters (defaults are fine for the workshop) - Check the box acknowledging that
CloudFormation will create IAM resources - Click Create stack
3. Wait for the stack creation to complete (approximately 10-15 minutes)
Security Note
Your code-server instance is secured with a unique password. The environment is isolated to
your AWS account.
Logging In
1. Click the CodeServerURL link to open your environment
2. Paste the password when prompted
3. You'll be taken directly to VS Code in your browser
15
Login
Cost Management
Remember to delete your CloudFormation stack after completing the workshop to avoid
ongoing charges. Be sure to delete all of the agents and then delete the `a2a-workshop` stack
from the CloudFormation console. More instructions can be found in the Epilogue.
::: ::::
16
Code Server
What's Included
• Workshop Repository: Complete codebase with all agents, gateway, and infrastructure code
• Terminal Access: Run deployment scripts, test commands, and AWS CLI operations
• Claude Code Integration: Pre-installed and ready to connect to your MCP gateway
• Python Environment: Python 3.12 with all required dependencies
• AWS Credentials: Pre-configured access to your workshop AWS account
You're Ready!
Your environment is fully operational. In the next step, we'll explore how the MCP Gateway
works and connects Claude Code to your agent network.
17
WORKSHOP/
├── .claude/ # Claude Code configuration
├── agents/ # Specialized agent implementations
├── config/ # Configuration files
├── data/ # Mock data for agents
├── gateway/ # MCP Gateway code
├── lib/ # Shared libraries and base classes
├── orchestrator/ # Universal Orchestrator implementation
├── scripts/ # Deployment and utility scripts
├── services/ # Dockerfiles and service configurations
├── static/ # CloudFormation templates
├── .[Link] # MCP server configuration
├── [Link] # Project documentation
└── [Link] # Python dependencies
You'll work with these directories throughout the workshop as you deploy, test, and enhance the
multi-agent system.
18
Access AWS Console
Navigate to CloudWatch
19
2. Click Logs Insights
You'll see the Logs Insights query interface with pre-configured saved queries for each workshop
chapter.
Each query includes the log groups for all agents deployed up to that chapter:
20
Query Log Groups Included
Chapter 2 + location-loader
Chapter 3 + geography-agent
Chapter 4 + restaurant-agent
Chapter 5 + weather-agent
Chapter 6 + hotel-agent
Chapter 7 + budget-agent
Chapter 8 + itinerary-agent
Progressive Queries
As you deploy more agents throughout the workshop, select the appropriate chapter query to
see logs from all active components. The queries automatically include all relevant log groups
for that stage of the workshop.
Running a Query
1. Select a saved query from the Saved queries panel (e.g., "Chapter 1 - CWLI")
2. Choose your time range (start with 5m)
3. Click the orange Run query button
4. View results in the Logs tab below
21
fields @timestamp, @message
| sort @timestamp asc
/ecs/a2a-workshop/{service-name}
22
Log Group Component Deployed In
Correlate across components: When debugging, look at orchestrator logs first to understand
workflow generation, then check individual agent logs to see task execution.
• MCP (Model Context Protocol): Used by Claude Code in VS Code to interact with tools and
services
23
• A2A (Agent to Agent Protocol): Used by our specialized agents (geography, restaurants,
events, weather) to communicate with each other
These protocols speak different languages - like trying to connect a USB device to an HDMI port.
The MCP Gateway is the adapter that makes them work together.
How It Works
Here's the flow when you make a request from Claude Code:
Mermaid Diagram
task = Task(
id=task_id, # Unique identifier
context_id=context_id, # Conversation context
status=TaskStatus(
state=[Link], # Initial state
timestamp=[Link](UTC).isoformat() + "Z",
),
history=[message], # Conversation history
input={"query": user_message}, # Your natural language query
artifacts=[], # Will hold agent responses
kind="task",
)
24
The gateway handles all the complexity of protocol translation, UUID management, and response
formatting so you can focus on natural language interactions with the agent network.
Gateway Infrastructure
The MCP Gateway is deployed as an ECS service with the following components: -
**Container**: Python 3.12 with FastAPI + MCP SDK - **Load Balancer**: Internal Application
Load Balancer for VPC access - **Storage**: ECR repository for the gateway Docker image -
**Monitoring**: CloudWatch Logs for request tracing The gateway is available at the URL
provided in your CloudFormation stack outputs, ready for Claude Code to connect.
25
Chapter 1: Building the Foundation
The Foundation
The MCP Gateway is deployed, which means Claude Code can now talk to your agent network.
There's just one problem: you don't actually have any agents yet. The gateway is like a telephone
exchange with no phones connected.
In this chapter, you'll build the foundation of your multi-agent system by exploring two critical
components: the pre-deployed Universal Orchestrator and deploying your first specialized agent.
Along the way, you'll learn how agents communicate, how they discover each other, and why
coordination matters when working with multiple specialized intelligences.
To build a system that can help with this, you'll need a coordinator (the orchestrator) and at least one
source of data (your first agent). This chapter gets both of those pieces in place.
You'll deploy the Location Loader agent. This agent has a single job: load raw data about Seattle's
restaurants, events, and venues. It might seem simple, but this simplicity is intentional - it
demonstrates an important principle about how to design agents that work well together.
26
Ready to Start
Let's begin by understanding how agents communicate with each other, then we'll explore your
orchestrator and deploy your first agent. Once your foundation is built, Chapter 2 will show you
how it performs.
Message: A single communication within a Task with a role (user/assistant/agent) and content.
Messages build the conversation history so agents see full context.
Artifact: Contains an agent's work results - text summaries, structured data (JSON), or both. Agents
package their output into Artifacts with metadata about what was done and what the results mean.
Mermaid Diagram
The Task travels through this flow, accumulating artifacts and messages. By the time it returns, it
contains a complete record of what happened.
27
Understanding the Universal Orchestrator
The Universal Orchestrator is the brain of your multi-agent system. While individual agents provide
specialized capabilities, the orchestrator is what makes them work together intelligently. It discovers
what agents are available, figures out the best way to coordinate their work, and synthesizes their
responses into coherent answers. This component has been pre-deployed as part of your workshop
infrastructure. Let's explore what makes it so important.
That's what the orchestrator does. It sits above your specialized agents and coordinates their work.
When a complex question comes in, the orchestrator breaks it down, figures out which agents can
help, determines the best order to call them, and synthesizes their responses into a useful answer.
Without an orchestrator, you'd need to manually figure out which agents to call, in what sequence,
with what inputs. You'd need to take the output from one agent and format it correctly as input for the
next agent. You'd need to handle errors, retries, and edge cases. The orchestrator handles all of this
automatically.
28
The discovery process is automatic. Each agent, when it starts up, registers itself in a service
registry. The orchestrator queries that registry whenever it needs to know what's available. This
makes the system flexible - agents can come and go, and the orchestrator adapts.
This is where the intelligence really shines. The orchestrator doesn't just blindly call every agent - it
analyzes your question and determines the optimal strategy. Some workflows run agents in
sequence, where one agent's output feeds into the next. Others run agents in parallel, combining
their results at the end. Some workflows include conditional logic, choosing different paths based on
what the data shows.
All of this happens dynamically for each unique request. The orchestrator isn't following pre-
programmed scripts - it's generating the best workflow on the fly based on your specific question and
the agents currently available.
This synthesis step is what makes the multi-agent system feel like a single, intelligent assistant
rather than a collection of separate tools.
When you deploy a new agent, that agent publishes a card describing its capabilities, what inputs it
expects, and what outputs it provides. The orchestrator reads these cards and incorporates that
knowledge into its workflow generation. This means you can add new agents to your system without
modifying the orchestrator's code.
29
The orchestrator is also model-powered, which means it can handle natural language queries that
don't neatly map to specific agent capabilities. It can interpret intent, handle ambiguity, and make
intelligent decisions about how to fulfill requests even when they're phrased in unexpected ways.
claude
ClaudeSetup
When logging into Claude Code for the first time, you have to answer some initial setup questions
Accept all the defaults and proceed.
30
• Confirm you understand the Security Notes
• Use the recommended setting for the terminal
• Allow Claude Code to trust the files in /home/participant/workshop directory. You'll see the
Claude Code interface:
ClaudeCode
You can verify that your MCP server is up and connected within Claude Code using the /mcp
command:
/mcp
31
Make Your Request
Now ask Claude Code to help with the Seattle planning challenge. Notice that you're explicitly
mentioning the MCP connection - this tells Claude Code to use the workshop tools you've deployed
rather than just answering from its general knowledge:
let's use our mcp to plan a corporate offsite in Seattle for 50 people from AnyCompany. Can you sugge
● I'll help you plan a corporate offsite in Seattle for 50 people from AnyCompany using
our MCP workshop tools. Let me ask the orchestrator to coordinate the planning with
appropriate specialist agents.
This error is exactly what we want to see. The MCP Gateway successfully connected to the
orchestrator, the orchestrator attempted to fulfill the request, but it discovered that no specialized
agents are available yet.
32
● It looks like we don't have any agents available in our MCP workshop yet. Let me create a specialis
Seattle recommendations.
● I'll help you create an event planning specialist agent for your Seattle corporate offsite! Since t
coordinating multiple services, I'll create a reasoning agent that can handle the sophisticated pla
● Let me start by reading the budget agent as our template. I'm going to read an existing agent as a
patterns we need to follow. For reasoning agents like event planning, I'll use the budget agent as
planning capabilities.
● Read(agents/budget_agent/[Link])
⎿ Read 796 lines
● It looks like the MCP workshop tool isn't currently available. Let me help you plan
this Seattle corporate offsite based on my knowledge of the city!
In Scenario 1, Claude Code is trying to help debug the system, but you already know what's missing
- you haven't deployed specialized agents yet.
33
In Scenario 2, Claude Code is answering from its training data about Seattle, not from your agent
system. While the information might be accurate, it's not using the workshop infrastructure you've
built. You want to see your MCP gateway, orchestrator, and agents working together.
To see what actually happened during that request, follow the Access CloudWatch Logs guide and
use the "Chapter 1 - CWLI" saved query.
34
These logs tell the complete story. The MCP Gateway received your request, converted it to A2A
format, and sent it to the orchestrator. The orchestrator started its workflow and immediately went to
discover available agents via CloudMap. CloudMap returned an empty list (no agents registered yet),
so the orchestrator logged "❌ No agents discovered" and completed gracefully.
These logs confirm the system is working exactly as designed. The orchestrator discovered no
agents and responded appropriately. This isn't a bug - it's the expected behavior when you haven't
deployed any specialized agents yet.
That's about to change. In the next section, you'll deploy the Location Loader agent, and you'll
immediately see how the orchestrator discovers it and incorporates it into workflows.
1. Open a second terminal: Click the + icon in the terminal panel or use `Ctrl+Shift+`` (backtick)
2. Organize your terminals: - Terminal 1 (Bash): For deployment scripts and system commands -
Terminal 2 (Claude Code): For interactive Claude Code sessions
Tips:
• Keep deployments running in the bash terminal while working in Claude Code
35
• You can enable multiple agents simultaneously: ./scripts/[Link] geography-agent
restaurant-agent
./scripts/[Link] location-loader
The enable process will update the CloudFormation stack to set `DesiredCount=1`, which tells
ECS to start a task from the pre-built Docker image. The task will register in CloudMap once it's
healthy. Once complete, the orchestrator will immediately discover this agent and can include it
in workflows.
This might seem overly simplistic. Why build an agent that just dumps all the data without any
intelligence? The answer reveals an important principle about designing multi-agent systems.
36
When you call the Location Loader with city: "Seattle" and category: "restaurants" , it returns
all Seattle restaurants from the mock data - typically several thousand entries. It doesn't filter them by
distance, cuisine, price, or rating. It doesn't limit the results to a manageable number. It doesn't try to
figure out which ones best match your needs. It just loads and returns the complete dataset.
This design has several advantages. First, it keeps the agent simple and maintainable. Loading data
is straightforward - there's no complex logic about filtering criteria or ranking algorithms. Second, it
makes the agent reusable. Different queries might need different filtering approaches, and by
returning the complete dataset, the Location Loader serves all of them equally well.
Third, and most importantly, it enables composition. By separating data loading from data filtering
and refinement, you create the foundation for a pipeline of specialized agents. The Location Loader
provides raw materials. Other agents (which you'll deploy in later chapters) can transform those raw
materials in different ways for different purposes.
The data volumes are substantial: nearly 2,000 restaurants, over 2,500 events, almost 1,500 outdoor
activities, plus hundreds of entries for accommodations, entertainment venues, and shopping
locations. This gives you realistic data volumes to see how the system behaves at scale.
The data is "mock" in the sense that it was generated for this workshop, but it's based on real Seattle
locations and follows realistic patterns. This makes it perfect for learning how multi-agent systems
work with actual data volumes and structures.
The orchestrator doesn't need to be told about this new agent. The next time it receives a request, it
will query CloudMap for available services, find the location-loader in the results, and fetch its agent
card to understand its capabilities.
37
The agent card is a standardized JSON document that each agent publishes at a well-known URL. It
describes what the agent can do, what inputs it expects, and what outputs it provides. For the
Location Loader, the card says: "I can load all locations for a city and category. Give me a city name
and a category, and I'll return the complete dataset."
This self-description is key to making the system extensible. The orchestrator doesn't have
hardcoded knowledge about the Location Loader. Instead, it reads the agent card and incorporates
that capability into its workflow generation. This means you can deploy new agents with new
capabilities, and the orchestrator will automatically learn about them and start using them.
The Location Loader ECS service starts and registers with CloudMap. The orchestrator doesn't
immediately know about this - it only discovers agents when processing requests. When you make a
request in the next section, the orchestrator will query CloudMap, discover the location-loader, fetch
its agent card, and generate a workflow that uses it.
This lazy discovery approach means the orchestrator doesn't waste time polling for changes. It only
needs to know what's available when it's actually trying to fulfill a request.
The service runs continuously, ready to handle requests. When the orchestrator calls it with a city
and category, it loads the appropriate data and returns it in an A2A Artifact.
Foundation Complete
Once deployment completes, you'll have your first specialized agent running. The orchestrator now
has something to coordinate. Your foundation is built:
The Universal Orchestrator can discover agents via CloudMap, generate workflows dynamically
based on your queries, and synthesize results from multiple agents into coherent responses.
38
The Location Loader provides complete datasets for Seattle's restaurants, events, and activities. It
follows the single responsibility principle: load data completely and let other components handle
filtering and refinement.
The MCP Gateway connects Claude Code to your agent network, translating between MCP and A2A
protocols seamlessly.
Together, these components form a complete multi-agent system foundation. In Chapter 2, you'll test
this system and discover important lessons about how it performs when handling real-world data
volumes.
Chapter 1 Complete
Your foundation is deployed and ready. Chapter 2 will show you how it performs and what you'll
learn from testing it with the Seattle planning query.
39
Chapter 2: Testing and Understanding
Your System
This chapter is about testing and learning. You'll make the same Seattle planning request that failed
in Chapter 1, and this time you'll get real recommendations. The orchestrator will discover your
Location Loader agent, generate a workflow, execute it, and synthesize the results into useful
suggestions.
But success reveals complexity. While the system provides good recommendations, something
important happens behind the scenes that affects how those recommendations are created. The
orchestrator has to make difficult choices about which data to keep and which to discard when
dealing with large datasets.
First, you'll see how the orchestrator synthesizes raw data into intelligent recommendations. The
Location Loader provides nearly 2,000 restaurants and over 2,500 events - complete, unfiltered
datasets. The orchestrator doesn't just dump this data on you. It uses its synthesis capabilities to
analyze the data, identify good options, and present organized recommendations with context and
reasoning.
Second, you'll understand the constraints that affect this synthesis. Large language models have
context window limits - maximum amounts of information they can process in a single request. When
your data exceeds these limits, the system must use truncation strategies to fit within the available
budget. This truncation works, but it's not optimal. Understanding how and why it happens will show
you the value of the specialized filtering agents you'll deploy in later chapters.
40
Ready to Test
Your foundation is deployed and ready. Let's see how it performs and what it teaches you about
building effective multi-agent systems.
/clear
let's use our mcp to plan a corporate offsite in Seattle for 50 people from AnyCompany.
Can you suggest some authentic local restaurants that could host our group,
and recommend some team-building activities we could do?
41
● I'll help you plan a corporate offsite in Seattle for 50 people! Let me use the MCP
workshop to get suggestions for authentic local restaurants and team-building activities.
● Great! I've gathered comprehensive recommendations for your corporate offsite in Seattle.
Top Recommendations:
- The Walrus and the Carpenter (Ballard) - Famous oyster culture, wheelchair accessible
- Anthony's Pier 66 - Upscale waterfront seafood with corporate group capabilities
- Salty's (West Seattle) - Waterfront dining with Pacific Northwest seafood
- Palace Kitchen (Belltown) - Tom Douglas restaurant with group accommodation
Alternative Options:
- Serious Pie (Ballard) - Artisanal pizza with flexible group arrangements
- Maximilien (Pike Place Market area) - Highly rated international cuisine
- The Melting Pot - Interactive fondue perfect for team bonding
Team-Building Activities
Pro Tips:
- Many restaurants are clustered in Ballard, Capitol Hill, and Belltown
- Several venues offer wheelchair accessibility and outdoor seating
- Book reservations first, then plan activities in the same neighborhoods
42
What's Working Well
The orchestrator discovered your new agent automatically without any reconfiguration. You deployed
the location-loader, and the orchestrator immediately incorporated it into its workflows. The
orchestrator then analyzed your natural language question, determined which agent could help,
generated an appropriate workflow, and executed it successfully. The location-loader agent
successfully loaded the data and presented it to our Orchestrator.
43
To view these logs, refer to the Access CloudWatch Logs guide and use the "Chapter 2 - CWLI"
saved query to see all relevant component logs for this stage of the workshop.
Alternatively, you can view the specific log group at /ecs/a2a-workshop/orchestrator by navigating
to CloudWatch > Log groups.
Here's what those logs showed during the synthesis phase of your request:
But there's a problem. The model the orchestrator uses for synthesis (Claude Sonnet 4) has a
context window limit of 200,000 tokens. That's the maximum amount of information the model can
process in a single request, including your question, all the agent data, the system instructions, and
room for the response.
44
Mermaid Diagram
While the truncation is not completely random - it keeps some of the first and last items in the list - it
is not intelligent about what is removed. And we can be losing a lot of data in this process. A better
way to do this would be to intelligently filter the data before we attempt to process it with our LLM.
Geographic filtering is a perfect example of how to do this. To determine if a restaurant is within 5
miles of your venue, you need to:
This is a straightforward algorithmic task. It takes milliseconds to run, uses zero tokens, and
produces deterministic results. Any restaurant at 4.8 miles qualifies; any restaurant at 5.2 miles
doesn't. If you rely on truncation, the LLM never sees most restaurants. It can't calculate distances to
restaurants that were discarded before synthesis even began. We'll see how to do that in the next
chatper.
45
Chapter 3: Adding Specialized Filtering
In Chapter 2, you saw your multi-agent system work successfully - the orchestrator discovered the
Location Loader, generated a workflow, and synthesized useful restaurant recommendations. But
you also saw a significant limitation: the system had to truncate 43% of the data to fit within the
model's context window. That's nearly half the restaurants discarded based on their position in the
list, not their relevance to your query.
The truncation worked, but it was inefficient. The system used 168,000 tokens to process randomly-
sampled data, and the best restaurant for your needs might have been in the discarded 43%. More
importantly, the truncation was arbitrary - it didn't understand that you needed restaurants "near Pike
Place Market." It just counted tokens and removed data until it fit.
This chapter solves that problem by deploying your first enhancement service: the Geography Agent.
This agent does one thing exceptionally well - it filters locations by proximity using coordinate-based
calculations. When you ask for restaurants "near Pike Place Market," it mathematically determines
which restaurants are actually close by, returning only the relevant subset. No LLM needed, no token
budget consumed, no guesswork involved.
You'll also see multi-agent coordination in action. The orchestrator doesn't just discover two agents; it
understands their relationships and creates an optimal workflow automatically. It knows the Location
Loader provides raw data, the Geography Agent filters that data by location, and the synthesis step
should only see the filtered results. This understanding comes from metadata in the agent cards -
fields like service_type and provides_for that tell the orchestrator how agents relate to each
other.
By the end of this chapter, you'll have deployed the Geography Agent and seen the dramatic
performance difference. Instead of truncating 43% of 1,823 restaurants, the system will intelligently
filter down to around 465 nearby restaurants. Instead of consuming 168,000 tokens on arbitrary data,
46
it will use around 103,000 tokens on geographically relevant data - and no truncation will be needed.
And instead of arbitrary sampling, every restaurant in the results will actually be near your specified
location.
./scripts/[Link] geography-agent
The enable process will start the pre-deployed Geography Agent ECS task. Once the task is
healthy, it will register in CloudMap and the orchestrator will discover it alongside the location-
loader.
The distance calculation uses the haversine formula, which computes great-circle distances between
two points on a sphere given their longitudes and latitudes. This is standard geographic math - the
same calculation your phone uses when showing you nearby restaurants. It's fast, accurate, and
deterministic. A restaurant 4.8 kilometers from Pike Place Market will always be 4.8 kilometers away,
regardless of how many times you calculate it.
The Geography Agent doesn't make judgments about whether a location is "good" or "appropriate." It
doesn't evaluate cuisine, atmosphere, or reviews. It just does the math and returns locations within
the specified distance. This focused responsibility makes it reliable, testable, and easily composable
with other agents.
47
How the Orchestrator Knows This Agent Exists
When you deploy the Geography Agent, several things happen that enable the orchestrator to
discover and use it:
The agent registers itself in AWS CloudMap under the service name "geography-agent." This
registration includes its IP address and port. The orchestrator queries CloudMap whenever it needs
to know what agents are available, so this registration makes the Geography Agent discoverable
immediately.
The agent hosts an agent card at a well-known URL ( /.well-known/[Link] ). This standardized
location means the orchestrator knows exactly where to fetch capability information for any service it
discovers in CloudMap. The agent card contains structured metadata that describes what the agent
does and how it relates to other agents.
{
"service_type": "enhancement",
"provides_for": ["restaurant-agent", "events-agent", "hotel-agent"]
}
service_type: "enhancement" tells the orchestrator this is not a primary data provider and not a
domain specialist. It's a supporting service that transforms data from other agents. Enhancement
services typically sit between data loaders and domain specialists in workflow pipelines.
provides_for declares which agents benefit from this enhancement service's filtering. The
Geography Agent can filter restaurants, events, or accommodations - any location-based data. The
orchestrator uses this information when generating workflows. If the query involves restaurants and
geography, the orchestrator knows the Geography Agent should process the Location Loader's
restaurant data before passing it to any restaurant-specific agents.
These metadata fields enable the orchestrator to make intelligent decisions about agent ordering and
data flow. It doesn't need hardcoded rules about the Geography Agent. It reads the agent card and
understands the agent's role in the system.
48
Dynamic Landmark Resolution
One interesting capability the Geography Agent has is dynamic landmark resolution. When you ask
for restaurants "near Pike Place Market," you don't need to provide coordinates. The Geography
Agent searches through all available data files (events, restaurants, accommodations) to find a
location matching that name, extracts its coordinates, and uses those as the center point for filtering.
This means you can use natural landmarks in your queries - "Space Needle," "University of
Washington," "Pioneer Square" - and the Geography Agent will find the appropriate coordinates
automatically. This makes the system more user-friendly while keeping the filtering objective and
deterministic.
In the next section, you'll test this coordination by making a request that requires both agents. You'll
see the orchestrator discover both agents, analyze their capabilities, generate a workflow that uses
them in the right order, and produce results without any truncation.
/clear
let's use our mcp to find restaurants near Pike Place Market in Seattle.
49
This query is more specific than the one you tried in Chapter 2. You're not just asking for restaurants
in Seattle - you're asking for restaurants near a particular landmark. This specificity will let you see
how the orchestrator coordinates the Location Loader and Geography Agent.
Agent Description
Much of the instructions being provided to the Orchestrator from the Agent are contained in the
description of the Agent. For example, our Geography Agent gives very clear instructions to the
Orchestrator what it does and when it should be used. The Orchestrator will read this description and
include it when deciding what workflow to create.
50
Spatial filtering agent. I filter locations by proximity to landmarks.
WORKFLOW:
EFFICIENCY NOTE:
Filter 2,000 locations → 20 relevant ones (me, objective/fast),
then select best 10 (LLM, subjective/thoughtful).
This is more efficient than LLM reasoning about all 2,000.
51
Coordination Between Multiple Agents
To view these logs, refer to the Access CloudWatch Logs guide and use the "Chapter 3 - CWLI"
saved query to see all relevant component logs for this stage of the workshop.
Alternatively, you can view the specific log group at /ecs/a2a-workshop/orchestrator by navigating
to CloudWatch > Log groups.
Here's what those logs showed during the workflow generation phase:
52
I need to analyze this query about finding restaurants near
Pike Place Market and create a workflow with the available agents.
**Query Analysis:**
- Location: Seattle
- Category: Restaurants
- Geographic constraint: Near Pike Place Market
- This is a classic load → filter pattern
**Available Agent Analysis:**
- `location-loader`: Can load all Seattle restaurants
- `geography-agent`: Can filter by proximity to Pike Place Market
- Perfect match for the agent-declared workflow pattern: "Load → Filter by geography"
**Workflow Pattern:**
Sequential execution is required because geography-agent needs the locations array from location-lo
Tool #3: generate_workflow
I've created an optimal workflow configuration for finding restaurants near Pike Place Market. This
**Workflow Strategy:**
- **Pattern**: Sequential (Load → Filter)
- **Stage 1**: Load all Seattle restaurants (complete dataset)
- **Stage 2**: Filter by proximity to Pike Place Market
**How It Works:**
The Results
Let's look at what the Geography Agent accomplished. When you asked for restaurants near Pike
Place Market, the system processed your request like this:
Location Loader returned 1,819 restaurants in Seattle - the complete, unfiltered dataset.
53
Geography Agent calculated the distance from Pike Place Market to each of those 1,819
restaurants using the haversine formula. It found that 465 restaurants are within reasonable proximity
of the landmark. It returned only those 465 restaurants to the next stage.
Synthesis received a dataset of 465 restaurants, all verified to be near Pike Place Market. These
465 restaurants consumed 103,297 tokens - well within the 180,000 token budget. No truncation was
needed.
The filtering happened algorithmically, before synthesis. The synthesis model never saw the 1,354
restaurants that are farther away because the Geography Agent excluded them mathematically. This
is fundamentally different from the truncation you saw in Chapter 2.
Mermaid Diagram
The performance improvement is dramatic. In Chapter 2, when you asked for Seattle restaurants
without geographic filtering:
In Chapter 3, when you asked for restaurants near Pike Place Market:
Location Loader provides complete, unfiltered datasets. It's a primary agent that gathers all
available information without interpretation.
Geography Agent applies objective, algorithmic filtering based on coordinates and distance. It's an
enhancement service that transforms data before synthesis.
54
Orchestrator discovers these agents via CloudMap, reads their capabilities from agent cards,
generates workflows that coordinate them optimally, and synthesizes final results.
The orchestrator didn't need explicit programming to coordinate these agents. It read their metadata
( service_type: "enhancement" , provides_for ), analyzed your query's requirements, and
generated a workflow that uses them in the right sequence with proper data flow.
Looking Ahead
You've solved the truncation problem for geographic queries. But your Seattle offsite planning task
has other requirements: group capacity, cuisine preferences, budget constraints, weather
considerations. Each of these could benefit from specialized filtering or domain expertise.
55
Chapter 4: Adding Domain Expertise
But proximity alone doesn't guarantee relevance. Those 465 nearby restaurants include
steakhouses, seafood restaurants, Italian trattorias, Thai noodle shops, burger joints, and pizza
places. If you're planning a corporate offsite and need vegan options for team members with dietary
restrictions, knowing a restaurant is "nearby" doesn't tell you if it serves food they can eat.
Restaurant Agent (domain specialist) understands restaurants as a concept. It knows about cuisine
types, dietary restrictions, price ranges, ratings, and how these factors relate to each other. It applies
business logic: "This restaurant has [Link]: true in its metadata, so it can serve vegan
customers." It works specifically with restaurant data, using restaurant-specific knowledge.
The key difference is deterministic, metadata-driven filtering. The Geography Agent uses
mathematics (haversine formula). The Restaurant Agent uses structured data fields
( [Link] , cuisine , price , rating ). Neither one is guessing or interpreting. Both are
applying clear, deterministic rules to filter data precisely.
56
The Domain Specialist Pattern
The enable process will start both pre-deployed agent ECS tasks. Once the tasks are healthy,
they will register in CloudMap. The orchestrator will discover these agents alongside location-
loader and geography-agent when processing your next request. Let's keep reading while they
start.
But geographic proximity doesn't address preference. Those 465 restaurants near Pike Place Market
represent dozens of different cuisine types, price points, and dietary accommodations. If you're
planning a corporate offsite and several team members follow vegan diets, knowing which
restaurants are nearby doesn't tell you which ones can actually serve food they'll eat.
This isn't a weakness of the Geography Agent. It's doing exactly what it should do: filtering by
location. Dietary preferences are a different dimension entirely, requiring different knowledge and
different filtering logic.
57
• Pizza Pi Vegan Pizzeria - the name tells you everything
But many restaurants with excellent vegan options don't advertise it in their names:
• Basil Viet Kitchen - serves Thai and Vietnamese food, happens to have extensive vegan
offerings
• Teapot Vegetarian House - vegetarian doesn't necessarily mean vegan, but this restaurant
does offer vegan dishes
• Luna Park Cafe - an American diner that serves vegan options alongside traditional fare
• Emmy's Vege House - despite the informal name, this is actually a fully vegan restaurant
Even worse, some names are misleading. A restaurant called "Green Garden" might sound plant-
based, but it could be a seafood restaurant with a nature-themed name. "The Garden Room" could
serve steaks. "Fresh Start" might be a juice bar with no actual food.
You need structured, explicit data about dietary accommodations. Guessing from names produces
false positives and false negatives. This is exactly the kind of problem that requires domain-specific
knowledge.
Location Loader provides the raw data: all 1,819 Seattle restaurants with their complete metadata
including coordinates and dietary information.
Geography Agent applies the first filter: calculate distances from Pike Place Market, keep only
restaurants within the specified radius. This reduces 1,819 restaurants to 465 nearby options.
Restaurant Agent applies the second filter: check the [Link] field for each of the 465
nearby restaurants, keep only those where the value is true . This might reduce 465 restaurants to
perhaps 5-7 vegan options.
The synthesis model receives 5-7 highly-relevant restaurants. Every single one meets both criteria:
it's near Pike Place Market (geographic filter) AND it serves vegan food (dietary filter). The results
are precise, relevant, and deterministically verified.
58
Deterministic Quality
The filtering is deterministic and metadata-driven. When you request vegan restaurants, the agent
checks each restaurant's [Link] field. If the value is true , the restaurant can serve vegan
food and gets included. If it's false or absent, the restaurant can't serve vegan food and gets
excluded. No inference, no guessing, just checking explicitly-set metadata fields.
Cuisine type - Filter by specific cuisine types like "italian", "thai", "vietnamese", "mexican". The
cuisine field can contain multiple values (e.g., "thai;vietnamese"), and the agent matches if any of
them align with your request.
Price range - Filter by price level ($, $$, $$$, $$$$) to match budget constraints.
Minimum rating - Exclude restaurants below a certain rating threshold to ensure quality.
The Restaurant Agent doesn't make subjective judgments. It doesn't interpret menus or guess about
ingredients. It checks structured data fields that explicitly declare each restaurant's characteristics.
This deterministic approach produces reliable, consistent results.
59
What Happens Next
Once deployment completes, your system will have three specialized agents: the Location Loader
(primary data provider), the Geography Agent (enhancement service for geographic filtering), and the
Restaurant Agent (domain specialist for restaurant-specific refinement).
The orchestrator can now create sophisticated workflows that coordinate all three agents. When you
ask for vegan restaurants near a landmark, the orchestrator will discover these three agents, analyze
their capabilities and relationships, and generate a three-stage workflow: load data, filter by
geography, refine by dietary preferences.
In the next section, you'll test this three-agent coordination by making exactly that request. You'll see
how the orchestrator chains the agents together and produces highly-relevant results where every
restaurant meets multiple criteria deterministically.
/clear
let's use our mcp to find vegan restaurants near Pike Place Market in Seattle.
This query requires two distinct types of filtering. Geographic filtering (which restaurants are near
Pike Place Market) and dietary filtering (which of those nearby restaurants serve vegan food). The
orchestrator will need to coordinate all three agents to fulfill this request.
60
What You'll See
Claude Code will discover three agents, generate a three-stage workflow, and return highly-relevant
vegan restaurant recommendations. Here's what the output looks like:
Closest option:
• El Borracho (0.05 miles away) - 4.2★ Mexican restaurant offering
creative vegan options including plant-based tacos, burritos, and
specialty cocktails. Casual atmosphere perfect for team gatherings.
Discovery found three agents: When the orchestrator queried CloudMap, it found the location-
loader, geography-agent, and restaurant-agent. It fetched all three agent cards and analyzed their
capabilities and relationships to determine how they could work together.
61
Three-stage workflow generated: The orchestrator analyzed your query ("vegan restaurants near
Pike Place Market"), identified that you need both geographic filtering (proximity) and dietary filtering
(vegan), and generated a three-stage workflow: load all Seattle restaurants, filter by proximity to Pike
Place Market, then refine by vegan dietary accommodations.
Deterministic filtering at each stage: The Geography Agent used the haversine formula to
mathematically verify proximity. The Restaurant Agent checked the [Link] metadata field to
deterministically verify vegan accommodations. No guessing, no inference - just checking structured
data fields.
Highly-relevant results: Every restaurant in the response meets both criteria with certainty. Each
one is geographically near Pike Place Market (verified by distance calculation) AND serves vegan
food (verified by metadata check).
Stage 1 - Location Loader: Loaded all 1,819 Seattle restaurants from the data store, returning
complete metadata including coordinates, cuisine types, dietary accommodations, and ratings.
Stage 2 - Geography Agent: Received the 1,819 restaurants and Pike Place Market as the center
point. Calculated distances using haversine formula. Kept only restaurants within reasonable
proximity (465 restaurants).
Stage 3 - Restaurant Agent: Received the 465 nearby restaurants. Checked the [Link]
field for each one. Kept only restaurants where the value is explicitly true (14 vegan restaurants).
Synthesis: Received 14 highly-relevant restaurants, all verified to be both nearby and vegan.
Generated natural language recommendations with context about each option, including the closest
match (El Borracho at just 0.05 miles away).
The data volume decreased at each stage through deterministic filtering: 1,819 → 465 → 14. The
synthesis model worked with a small, precise dataset where every entry met your exact criteria.
Mermaid Diagram
62
The Geography Agent's metadata declares provides_for: ["restaurant-agent"] , indicating it
enhances restaurant data.
Based on these metadata declarations, the orchestrator reasoned: "Geography Agent should run
before Restaurant Agent. Geography provides location filtering that Restaurant can use. This creates
an efficient pipeline."
The workflow emerged from agent metadata and query analysis, not from hardcoded rules. You
could deploy additional agents with different relationship declarations, and the orchestrator would
incorporate them into workflows automatically.
Chapter 2 (Location Loader only): All 1,819 restaurants sent to synthesis, requiring three truncation
iterations to fit within token budget. Random sampling based on list position. Results included
restaurants all over Seattle, with no dietary filtering.
Chapter 3 (Location Loader + Geography Agent): 465 nearby restaurants sent to synthesis, no
truncation needed. Results all near Pike Place Market, but mixed dietary options - some vegan,
some not.
Each agent added a new filtering dimension. The results became increasingly precise, increasingly
relevant, and increasingly useful.
63
To view these logs, refer to the Access CloudWatch Logs guide and use the "Chapter 4 - CWLI"
saved query to see all relevant component logs for this stage of the workshop.
Alternatively, you can view the specific log group at /ecs/a2a-workshop/orchestrator by navigating
to CloudWatch > Log groups.
Here's what those logs showed during the workflow generation phase:
I've created an optimal workflow configuration for finding vegan restaurants near Pike Place Market.
**Workflow Strategy:**
**How It Works:**
Mermaid Diagram
64
The generated workflow chains agents together through input_mapping. Here's a simplified view of
what the workflow structure looks like:
{
"nodes": [
{
"id": "load_restaurants",
"type": "agent_call",
"agent_id": "location-loader",
"input_mapping": {
"location": "Seattle",
"category": "restaurants"
}
},
{
"id": "filter_by_proximity",
"type": "agent_call",
"agent_id": "geography-agent",
"input_mapping": {
"locations": "$.load_restaurants.locations",
"center": "Pike Place Market",
"radius_km": 1.0
}
},
{
"id": "refine_by_dietary",
"type": "agent_call",
"agent_id": "restaurant-agent",
"input_mapping": {
"locations": "$.filter_by_proximity.filtered_locations",
"dietary_restrictions": ["vegan"]
}
}
]
}
Notice how each stage's input_mapping references the output from the previous stage:
65
The restaurant-agent's locations input comes from $.filter_by_proximity.filtered_locations
- the output of the geography-agent.
This chaining creates a data pipeline where each agent transforms the data and passes it to the next
stage. The workflow is like an assembly line: each station (agent) performs its specialized operation
and hands the work product to the next station.
The addition of the Restaurant Agent creates another filtering stage, further refining the data before
synthesis. Each stage applies deterministic filtering:
The synthesis model benefits from this multi-stage refinement. Instead of receiving 465 restaurants
and trying to identify vegan options through inference, it receives 14 restaurants that are already
verified to be vegan. The synthesis can focus on subjective recommendations (which vegan
restaurant is best for your group) rather than objective filtering (which restaurants are vegan).
let's use our mcp to find some fun activities in Seattle for the next few days
Remember this response. In Chapter 5, you'll run the same query and see what has changed.
66
Chapter 5: Parallel Workflows
But not all agents have data dependencies. When planning a corporate outing, weather matters - but
weather forecasts don't depend on restaurant data. The weather agent only needs to know the
location name, which comes directly from your query. This independence enables a different
execution pattern: parallel workflows.
They require only query parameters, not agent outputs. The weather agent needs a location name
from your query, nothing more.
They return lightweight decision-making data, not bulk datasets. Weather forecasts contain perhaps
10-20 data points, not thousands of items.
They must reach synthesis unchanged. The synthesis model needs weather context to explain
recommendations: "It's rainy, so I recommend indoor activities."
67
Parallel Execution
When the orchestrator analyzes your query "Find team-building activities in Seattle considering
weather," it discovers that weather and activities can be fetched simultaneously. There's no
dependency forcing sequential execution. The weather agent doesn't need activity data, and the
activity agents don't need weather data.
The orchestrator generates a parallel workflow: fetch weather and activities in the same stage, then
synthesize recommendations using both datasets. This is faster (parallel execution reduces total
latency) and more natural (weather and activities are genuinely independent concerns).
./scripts/[Link] weather-agent
The enable process will start the pre-deployed Weather Agent ECS task. Once the task is
healthy, it will register in CloudMap. The orchestrator will discover this agent alongside your
existing agents when processing your next request. In the meantime, let's keep reading.
The Restaurant Agent must run after the Geography Agent because it works better with pre-filtered
data. While it could load all restaurants itself, receiving 465 nearby restaurants is more efficient than
processing 1,819. This is a logical dependency - the agent can work standalone but benefits from
running second.
68
The Weather Agent has no dependencies. It doesn't need restaurant lists, event lists, or filtered
results. It needs the location name - "Seattle" - which comes directly from your query. There's no
reason it must wait for other agents to complete.
Forecast array - Multi-day weather predictions with temperature, precipitation, wind, and conditions
for each day.
Location confirmation - Verified location name to ensure the forecast matches your query.
Indoor recommendation - A boolean flag indicating whether conditions favor indoor activities. This
is calculated based on precipitation probability, wind speed, and temperature extremes.
Average precipitation - The mean precipitation probability across forecast days, useful for
understanding overall wetness.
This data is lightweight - perhaps 500-1000 tokens - but valuable for decision-making. When
synthesis receives both weather context and activity options, it can filter or prioritize
recommendations based on conditions: "It's rainy this weekend, so I recommend these indoor
venues."
Stage history pruning checks: does any remaining stage reference this stage's output? If not, remove
it to save tokens.
This creates a problem for context agents. The weather agent returns forecast data. No other agent
needs forecasts - the Restaurant Agent doesn't check weather, the Events Agent doesn't check
weather. Without special handling, the pruning logic would remove weather data after Stage 1
completes, before synthesis occurs.
69
The solution is the wait_for field. Setting wait_for: true on the weather agent tells the
orchestrator: "Keep this agent's output for synthesis. Don't prune it even though no other agent
references it." This ensures weather context survives until the synthesis model can use it to make
weather-aware recommendations.
/clear
Run the same query you tried before deploying the Weather Agent:
let's use our mcp to find some fun activities in Seattle for the next few days
In Chapter 4, this returned a mix of indoor and outdoor activities with no weather context. Now that
the Weather Agent is deployed, the orchestrator will fetch weather data in parallel and use it to inform
recommendations.
70
Based on the weather forecast analysis from both agents, I recommend focusing
on indoor team-building activities for this weekend in Seattle. The weather
data shows heavy rain on Saturday (95% precipitation chance) and continued
rain on Sunday (80% chance), making indoor venues the clear choice for
successful team bonding.
Practical Considerations:
• Accessibility: Several venues offer wheelchair accessibility (Dimitriou's
Jazz Alley, Benaroya Hall)
• Budget Flexibility: Options range from $15 (cinemas) to $50 (theaters)
Weekend Strategy:
Plan indoor activities for Saturday and Sunday due to heavy rain. However,
Monday shows partly cloudy conditions (30% precipitation) - if your schedule
allows, outdoor options could work then.
The orchestrator discovered four agents, generated a map-reduce workflow with parallel execution
and aggregation, and completed successfully.
71
Discovery found four agents: When the orchestrator queried CloudMap, it found location-loader,
geography-agent, restaurant-agent, and weather-agent in 0.3 seconds. It fetched all four agent cards
and analyzed their capabilities, input schemas, and relationships.
Map-reduce workflow generated: The orchestrator analyzed your query and generated a map-
reduce workflow that fetches weather and activity data in parallel, then aggregates results for
synthesis.
Parallel execution with aggregation: Stage 1 executed four agents in parallel: weather-agent,
location-loader (entertainment category), location-loader (events category), and location-loader
(outdoor category). Stage 2 used a Joiner node to merge all results into a single aggregate output,
which the orchestrator then synthesized into natural language recommendations.
Weather-aware recommendations: The synthesis model received merged data containing weather
forecasts (95% precipitation Saturday, 80% Sunday) and 118+ venue options across categories. It
prioritized indoor activities and explained why: "Heavy rain makes indoor venues the clear choice for
successful team bonding."
• Weather Agent: Fetches 3-day forecast for Seattle (Heavy Rain 95% Sat, Rain 80% Sun, Partly
Cloudy 30% Mon)
• Location Loader (entertainment): Loads 118 entertainment venues (theaters, karaoke, comedy
clubs)
• Location Loader (events): Loads event venues
• Location Loader (outdoor): Loads 1,470 outdoor venues (parks, trails)
All four agents execute simultaneously. The orchestrator recognizes they have no dependencies on
each other - all need only the location name "Seattle" from the query. Total Stage 1 time is dominated
by the slowest loader, not the sum of all four.
72
• Prunes individual agent outputs to save tokens (no longer needed after merge)
• Orchestrator's GenericSynthesizer receives the aggregate
• Generates weather-aware recommendations in natural language (~14,000 tokens input)
Here's why pruning would happen: the Restaurant Agent doesn't reference weather data in its
input_mapping . The Events Agent doesn't reference weather. No Stage 2 agent consumes weather
output. The pruning logic concludes: "Weather data isn't needed by remaining stages, remove it to
save tokens."
With wait_for: true , the orchestrator treats weather as synthesis-bound. The data isn't for
intermediate agents - it's for the final synthesis step. This prevents pruning and ensures synthesis
receives weather context alongside venue options.
Comparing to Chapter 4
The evolution from Chapter 4 to Chapter 5 introduces conditional logic and branching:
Chapter 5 (Conditional with parallel gathering): (Weather || Entertainment Loader || Outdoor Loader)
→ Conditional Evaluation → Branch Synthesis. Parallel data gathering (118 + 1,470 venues),
weather-dependent routing, 1,087 tokens to final synthesis, intelligent branching.
The sophistication increased dramatically. Chapter 4 filtered data through a linear pipeline. Chapter 5
gathered data in parallel, evaluated environmental conditions, and routed to appropriate synthesis
strategies. The orchestrator didn't just run agents in parallel - it created branching logic based on
weather forecasts.
73
How Parallel Workflows Work
To view these logs, refer to the Access CloudWatch Logs guide and use the "Chapter 5 - CWLI"
saved query to see all relevant component logs for this stage of the workshop.
Alternatively, you can view the specific log group at /ecs/a2a-workshop/orchestrator by navigating
to CloudWatch > Log groups.
Here's what those logs showed during the workflow generation phase:
74
Agent Discovery Phase (0.3 seconds):
Discovered 4 agents: ['location-loader', 'weather-agent', 'geography-agent', 'restaurant-agent']
Retrieved agent cards from /.well-known/[Link] endpoints
Node 0 - weather_check:
type: agent_call
agent_id: weather-agent
input_mapping: {
days: "3",
location: "Seattle"
}
Node 1 - load_entertainment:
type: agent_call
agent_id: location-loader
input_mapping: {
category: "entertainment",
location: "Seattle"
}
Node 2 - load_events:
type: agent_call
agent_id: location-loader
input_mapping: {
category: "events",
location: "Seattle"
}
Node 3 - load_outdoor:
type: agent_call
agent_id: location-loader
input_mapping: {
category: "outdoor",
location: "Seattle"
}
75
Node 4 - aggregate:
type: joiner
strategy: synthesize
wait_for: ["weather_check", "load_entertainment", "load_events", "load_outdoor"]
Execution Results:
Stage 1: 4 agents in parallel → weather (3-day forecast), entertainment (118 venues), events (2,597
Stage 2: Joiner aggregates all outputs → synthesis (134,503 tokens input) → final response
Map Phase (Stage 1) - Multiple agents execute in parallel to gather data. Each agent produces
independent output that will be aggregated.
Reduce Phase (Stage 2) - A Joiner node collects all parallel outputs, merges them into a single
aggregate structure, then sends to synthesis for the final response.
Mermaid Diagram
{
"pattern": "sequential",
"flow": [
["load_restaurants"], // Stage 1
["filter_proximity"], // Stage 2 - waits for Stage 1
["synthesize"] // Stage 3 - waits for Stage 2
]
}
76
Each stage is a single-element array containing one agent ID. Execution is strictly linear: Stage 1
completes, then Stage 2 starts, then Stage 3 starts.
In Chapter 5's map-reduce workflow, the flow array shows parallel gathering and aggregation:
{
"pattern": "map-reduce",
"flow": [
["weather_check", "load_entertainment", "load_events", "load_outdoor"], // Stage 1 - parallel gat
["aggregate"] // Stage 2 - aggregation & synthesis (reduce)
]
}
Stage 1 is a four-element array. All four agents execute in parallel. The orchestrator makes parallel
API calls and waits for all four responses.
Stage 2 contains the Joiner node that aggregates all parallel outputs into a single structure for
synthesis.
{
"id": "aggregate",
"type": "joiner",
"strategy": "synthesize",
"wait_for": [
"weather_check",
"load_entertainment",
"load_events",
"load_outdoor"
]
}
This tells the orchestrator: "The aggregate joiner needs all four parallel outputs before proceeding."
The Joiner waits for all agents in the wait_for list to complete, then merges their outputs into a single
data structure.
The merge process creates an aggregate object containing all agent outputs:
77
{
"weather_check": {
"location": "Seattle",
"forecast": [...],
"average_precipitation": 68.3,
"indoor_recommended": true
},
"load_entertainment": {
"locations": [...118 venues...],
"total_count": 118
},
"load_events": {
"locations": [...2597 venues...],
"total_count": 2597
},
"load_outdoor": {
"locations": [...1470 venues...],
"total_count": 1470
}
}
This aggregate structure (134,503 tokens) is then sent to synthesis, which generates the final
weather-aware response.
The orchestrator:
78
8. Sent the merged aggregate (134,503 tokens) to synthesis for weather-aware recommendations
All of this emerged from query analysis, agent metadata, and the orchestrator's reasoning
capabilities. This is LLM-powered orchestration: intelligent, dynamic coordination that creates parallel
execution patterns and aggregates diverse data sources into coherent responses, without explicit
workflow programming.
79
Chapter 6: Build a Hotel Recommender
Agent
This is similar to the restaurant agent's filtering, but with hotel-specific criteria. You'll adapt the
restaurant agent pattern for a new domain - learning how to reuse proven patterns while handling
domain-specific differences.
By the end of this chapter, your travel planning system will be complete with agents for dining,
weather, and accommodations - all coordinated by the orchestrator.
80
Ready to Build
You've seen how domain specialists work with restaurants. Now let's build one for hotels and
enhance your travel planning system.
What you see in Claude Code depends on your prompts and what Claude discovers. Keep your
prompts to Claude Code clear and direct. Generally, the more information you provide to Claude
Code, the better your results will be. Claude can make mistakes - that's normal. You can use
Claude Code to help you fix issues as they arise.
/workshop:create-agent I want to create a hotel agent using the restaurant agent as a template. It sh
81
4. May create deployment files (or you'll need to ask)
Read(agents/restaurant_agent/[Link])
Read(data/mock/accommodations/[Link])
Update(lib/[Link])
Write(static/cloudformation/parameters/[Link])
Because we are mostly copying and modifying an existing template, Claude should get most of this
correct. Claude may ask you for permission to edit a file. It is often correct to do this, but be sure you
understand what Claude is trying to do. If it looks like it is making a mistake, interrupt it by pressing
esc or rejecting a request. Then tell the correct process.
Claude may not get everything right on the first try. Don't panic. Ask Claude to help you with
whatever problem you're having. Effective AI-assisted development is iterative. Be specific about
what's wrong and Claude will help you fix it.
82
# ANY-OF: Match if ANY restriction is satisfied
for restriction in dietary_restrictions:
restaurants = [r for r in restaurants
if [Link]("dietary", {}).get(restriction) is True]
Result: A hotel with gym=True and pool=False does not match the query.
Dining - "vegetarian or vegan" means either works (ANY-OF) Hotels - "gym and pool" means both
required (ALL-OF)
This is deterministic, metadata-driven filtering - no LLM calls, just boolean logic on structured data.
Deployment Options
Claude may deploy the agent for you automatically. If not, you can deploy it manually using the
deployment script. Or have Claude do it using the scripts!
Manual Deployment
If Claude hasn't deployed the agent, run:
83
./scripts/[Link] hotel-agent
Unlike the pre-deployed agents you've been enabling, this is a **custom agent** you're building
from scratch. Custom agents require the full deployment process using `[Link]` which
builds the Docker image and creates the CloudFormation stack. Once deployed, you can use
`[Link]` and `[Link]` to control this agent just like the pre-deployed ones.
This script:
Verify Deployment
Check the agent is running:
Expected:
{
"Status": "ACTIVE",
"Running": 1
}
Or have Claude check! Claude is capable of running these AWS CLI commands.
84
Deployment Complete
The hotel agent is running in ECS and registered with CloudMap. The orchestrator can now
discover and use it in multi-agent workflows.
please recommend hotels within walking distance of Pike Place Market with a gym
85
Geography Agent ( /ecs/a2a-workshop/geography-agent ):
Your hotel agent successfully coordinates with location-loader and geography-agent. The 3-
agent pipeline processed 132 → 52 → 37 hotels, delivering 20 top-rated results.
86
● Based on my search, here are the best hotel recommendations within walking distance of Pike Place M
- Mayflower Park Hotel Seattle - 0.25 miles away, 4.4 rating, pet-friendly
- Hotel Seattle - 0.37 miles away, 4.5 rating
Additional Options:
All of these hotels are consistently priced at $150 per night and include standard amenities like W
parking along with their fitness facilities. The average rating across all options is 4.3/5, so yo
these properties.
For the shortest walk to Pike Place Market, I'd recommend The Palladian Hotel or Four Seasons Seatt
on foot and have excellent gym facilities.
Mermaid Diagram
You should recognize this as a sequential workflow, just like we had when we were filtering
restaurants for vegetarian options.
87
Chapter 7: Adding Intelligence with LLM
Reasoning (Optional)
But deterministic filtering has limits. Your agents can answer questions like "Which restaurants are
vegan?" (check [Link] == true ) or "Which hotels have gyms?" (check [Link] ==
true ). These are factual checks with clear yes/no answers.
What happens when the question requires judgment? Consider this planning scenario: "Find dinner
and entertainment for 25 people near Pike Place Market with a $2,000 budget and vegetarian
options." Your existing agents can find vegetarian restaurants, locate entertainment venues, and
calculate whether combinations fit the budget. But which combination is the best use of the budget?
Should you prioritize a high-quality dining experience and modest entertainment? Or good dining
with upgraded entertainment? What trade-offs make sense?
These aren't boolean checks. They're strategic decisions that require understanding value,
evaluating trade-offs, considering experience quality, and making recommendations based on
subjective preferences. This is where deterministic agents reach their limit and reasoning agents
become essential.
88
Restaurant Agent (deterministic) checks [Link] field → true/false → filter. It's precise, fast
(200ms), and consistent. It applies rules written by engineers.
The key difference is decision-making capability. The Restaurant Agent determines if a restaurant
qualifies based on attributes. The Budget Agent determines which qualified option is optimal based
on context, constraints, and goals. One filters facts. The other makes recommendations.
This is the first time you'll deploy an agent powered by Claude (or any LLM). Previous agents used
the BaseAgent framework with deterministic Python functions. The Budget Agent uses
AgenticBaseAgent with the Strands framework, which enables LLM-powered conversation, tool use,
and multi-turn reasoning.
Filtering vs Reasoning
The agents you've deployed so far are filters. They apply deterministic rules to structured data:
Restaurant Agent: [Link] == true AND rating >= 4.0 → boolean → filter
These agents are fast (200ms), consistent, and precise. They check facts. They don't interpret,
judge, or recommend. When you ask "Which restaurants are vegan?", there's a definitive answer
based on metadata.
Budget planning is different. When you ask "How should I allocate $2,000 for 25 people?", there's no
single correct answer. There are better and worse strategies based on:
89
This requires reasoning, not filtering. The agent needs to:
The Budget Agent doesn't just return filtered JSON like {"restaurants": [...]} . It returns strategic
advice like:
"I recommend allocating $1,000-1,200 ($40-48/person) for the meal at Masala of India, which has
extensive vegetarian options and $$ pricing. This leaves $800-1,000 for logistics: gratuity (18-20%
for large groups typically $200-240), potential private dining room fees, and appetizers or
beverages beyond the base meal."
Notice the difference: The Restaurant Agent tells you which restaurants are vegetarian. The Budget
Agent tells you how to spend your budget wisely given those vegetarian options.
./scripts/[Link] budget-agent
90
The enable process will start the pre-deployed Budget Agent ECS task. Once the task is healthy, it
will register in CloudMap. Let's keep reading while it starts.
Unlike your deterministic agents that filter data, the Budget Agent reasons. It doesn't determine
which options fit the budget (that's arithmetic). It determines how to spend the budget wisely (that's
strategy). When you have multiple valid options, the Budget Agent evaluates trade-offs like:
The Budget Agent uses the Strands Agent framework, which integrates Claude (via Bedrock) to
enable LLM-powered reasoning. It makes tool calls to analyze data, evaluates options using natural
language understanding, and generates conversational recommendations rather than filtered JSON.
class RestaurantAgent(BaseAgent):
def _process_task_impl(self, task, user_input):
# Deterministic filtering with Python logic
filtered_restaurants = [r for r in restaurants if r['dietary']['vegan']]
return filtered_restaurants
91
Budget Agent:
class BudgetPlanningAgent(AgenticBaseAgent):
def _define_agent_tools(self):
# Define tools the LLM can use
@tool(name="calculate_budget_allocation")
def calculate_budget_allocation(total_budget: float, num_people: int):
return {"per_person": total_budget / num_people}
return [calculate_budget_allocation]
def _get_agent_system_prompt(self):
# Prompt that guides LLM reasoning
return "You are a budget planning specialist..."
Your deterministic agents respond in 100-300ms. The Budget Agent takes 10-12 seconds because
it's actually reasoning through the problem using Claude.
92
{
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Natural language query about budget planning"
},
"total_budget": {
"type": "string",
"description": "Total budget amount (e.g., '2000')"
},
"num_people": {
"type": "string",
"description": "Number of people in the group"
},
"num_days": {
"type": "string",
"description": "Number of days for the trip",
"default": "1"
},
"geography": {
"type": "string",
"description": "Location/city name"
},
"preferences": {
"type": "string",
"description": "Additional preferences or context"
}
},
"required": ["query", "total_budget", "num_people"]
}
Notice the difference from previous agents. The Budget Agent doesn't require pre-filtered data arrays
in its schema (like {"locations": [...]} or {"restaurants": [...]} ). Instead, it receives:
93
The orchestrator will pass filtered restaurant/entertainment results to the Budget Agent through the
execution context, but they're not declared as required schema fields. This allows the Budget Agent
to receive results from whatever agents the orchestrator determines are relevant.
The Budget Agent's capability metadata declares it as a reasoning agent that operates on results
from domain specialists:
{
"name": "validate-budget",
"type": "reasoning",
"description": "Provides strategic budget allocation recommendations...",
"wait_for": true
}
{
"budget_allocation": {
"total_budget": "2000",
"num_people": "25",
"num_days": "1"
},
"recommendations": "I recommend allocating $1,000-1,200 ($40-48/person) for the meal...",
"reasoning_provided": true
}
The recommendations field contains natural language generated by Claude. It might be 500-2000
characters of conversational advice, not just filtered data. This output gets incorporated into the final
synthesis, where the synthesis model can present the Budget Agent's strategic recommendations to
you.
Unlike deterministic agents that return compact JSON (maybe 200-500 bytes), the Budget Agent
returns 1,500-2,000 bytes because it's providing explanatory reasoning, not just facts.
94
Discover six agents: location-loader, geography-agent, restaurant-agent, weather-agent, hotel-
agent, and budget-agent.
Generate a workflow where deterministic agents run first (loading, filtering, refining), then the
budget-agent receives their results and generates strategic recommendations.
Set wait_for: true on the budget-agent node to ensure its reasoning output (which might be
1,500-2,000 bytes of natural language) reaches synthesis and gets incorporated into the final
response.
This happens automatically through workflow generation. The orchestrator's planning LLM
recognizes "budget planning" requirements in your query, discovers the budget-agent capability, and
includes it in the generated workflow.
/clear
i need to plan a dinner for 25 people in seattle near pike place. we need to support vegetarian optio
This query requires multiple capabilities: geographic filtering (near Pike Place), dietary filtering
(vegetarian options), and strategic budget allocation (how to spend $2000 for 25 people). The
orchestrator will coordinate multiple agents, culminating with the Budget Agent providing strategic
recommendations.
95
What You'll See
Claude Code will discover six agents, generate a four-stage workflow with a reasoning agent, and
return strategic budget allocation recommendations. Here's what the output looks like:
96
● I'll help you plan a dinner for 25 people in Seattle near Pike Place with
vegetarian options and a $2000 budget. Let me use the MCP workshop
orchestrator to coordinate this planning task.
● Perfect! I've got a comprehensive dinner plan for your group. Here's what
the MCP workshop orchestrator recommends:
Backup Options
Logistics Recommendations
97
- Location: All close to Pike Place Market for easy access
Next Steps
The orchestrator discovered six agents, generated a multi-stage workflow that included budget
reasoning, and returned strategic recommendations with specific dollar amounts, venue suggestions,
and logistics advice. The entire process took about 30-35 seconds, with the Budget Agent
contributing 10-12 seconds for LLM-powered strategic reasoning.
Discovery found six agents: When the orchestrator queried CloudMap, it found location-loader,
geography-agent, restaurant-agent, weather-agent, hotel-agent, and budget-agent. It analyzed their
capabilities to understand which agents could contribute to budget-constrained dining planning.
Orchestrator reasoning about agent selection: The orchestrator's planning LLM analyzed the
query and made intelligent choices about which agents to use:
98
**Critical Agent Declaration Analysis:**
- budget-agent declares: "Use me AFTER restaurant-agent and hotel-agent have filtered
options. I provide the best recommendations when I can see the filtered results from
other agents."
- This means budget-agent should receive filtered restaurants, not work independently
**Pattern Choice:**
Sequential pattern - budget-agent explicitly declares it should receive filtered results
from other agents, creating a clear dependency chain.
Four-stage sequential workflow generated: The orchestrator generated a workflow that processes
data through filtering stages before applying reasoning: (1) data loading, (2) geographic filtering near
Pike Place, (3) restaurant refinement for vegetarian options, and (4) budget strategy analysis on
filtered results.
Deterministic filtering before reasoning: The Geography Agent filtered restaurants to those near
Pike Place Market. The Restaurant Agent refined to vegetarian options. These deterministic steps
created a manageable dataset. The Budget Agent then received these filtered results and reasoned
about optimal allocation strategy.
Strategic recommendations with natural language: The Budget Agent didn't just return numbers.
It generated conversational advice: specific restaurant names (Masala of India), dollar amount
ranges ($40-48/person for meal), logistics considerations (gratuity for large groups, timing advice),
and backup options. This is LLM-generated strategic thinking, not filtered data.
Stage 1 - Location Loader: Fetched complete Seattle restaurant dataset, returning restaurants with
full metadata (cuisine, dietary options, ratings, price tiers).
99
Stage 2 - Geography Agent: Received restaurant data, filtered to restaurants near Pike Place Market
using haversine distance calculations.
Stage 3 - Restaurant Agent: Received nearby restaurants, filtered to those with vegetarian options
by checking [Link] and [Link] metadata fields.
Stage 4 - Budget Agent: Received filtered vegetarian restaurant results from previous stages.
Constructed prompt: "Plan a travel budget allocation for 25 people visiting Seattle for 1 days with a
total budget of $2000." Invoked Claude via Bedrock. Made tool calls to
calculate_budget_allocation and get_restaurants_by_price_tier . Generated strategic
reasoning about optimal budget allocation. Returned recommendations with specific dollar amounts
and logistics advice.
Synthesis: Combined filtered restaurant options and budget strategy recommendations from all four
stages into the comprehensive dinner plan you saw, with specific venues, pricing breakdown, and
actionable next steps.
Budget Agent (Reasoning): "How should I spend $2,000 for 25 people at these 14 vegetarian
restaurants?" - Answer: Strategic allocation ($1,000-1,200 for meal, $200-240 for gratuity, $560-800
for extras), specific venue recommendation (Masala of India), logistics advice (timing, reservations,
group considerations). Slower (11s), thoughtful, strategic.
The Restaurant Agent filtered facts. The Budget Agent made strategic recommendations based on
those facts. Neither agent could replace the other:
• Without deterministic filtering, the Budget Agent would receive 1,819 restaurants and struggle to
evaluate all options efficiently.
• Without budget reasoning, you'd have 14 vegetarian restaurant options but no guidance on how
to allocate your budget optimally among them.
100
What Makes This Different
The Budget Agent's contribution showcases reasoning capabilities that deterministic agents can't
provide:
Subjective Value Assessment: "Extensive vegetarian menu" and "accommodates groups" aren't
boolean fields in metadata. The agent interpreted restaurant characteristics and made a judgment
about suitability.
Trade-off Evaluation: Deciding to allocate $1,000-1,200 for the meal vs other allocation strategies
involves evaluating the trade-off between dining quality and budget buffer. This is strategic thinking.
Natural Language Explanation: The output reads like advice from an event planning professional:
"Why it's perfect," "Backup Options," "Logistics Recommendations." This conversational framing
makes recommendations actionable.
To view these logs, refer to the Access CloudWatch Logs guide and use the "Chapter 7 - CWLI"
saved query to see all relevant component logs for this stage of the workshop.
Alternatively, you can view the specific log group at /ecs/a2a-workshop/budget-agent by navigating
to CloudWatch > Log groups. Use Live Tail for real-time monitoring of LLM reasoning.
Here's what those logs showed during the Budget Agent's processing:
101
📥 BUDGET INPUT - task_id=5db58271-2c4a-463d-a8fb-58f3aee0760c
query: ""
geography: "Seattle"
total_budget: "2000"
num_people: "25"
num_days: "1"
preferences: "group dining for 25 people near Pike Place Market, vegetarian options required"
🤖 Sending query to LLM: Plan a travel budget allocation for 25 people visiting Seattle for 1 days wi
Given the group size of 25 people and the requirement for vegetarian
options near Pike Place Market, I recommend the following approach:
102
📤 BUDGET OUTPUT - Completed successfully
✓ task_id=5db58271-2c4a-463d-a8fb-58f3aee0760c
✓ duration=11.0s
✓ status=completed
✓ response_size=1838 bytes
The Budget Agent doesn't just execute code - it reasons. The LLM analyzes the problem, decides
which tools to call, interprets their results, evaluates trade-offs, and generates conversational
recommendations.
103
Seconds 0-1: Prompt Construction
System Prompt: "You are a budget planning specialist for group travel...
You have access to tools: calculate_budget_allocation,
get_restaurants_by_price_tier, analyze_spending_options..."
Claude analyzes the query and determines: - Need to understand per-person budget: 25 people,
$2000 total = $80/person - Should call calculate_budget_allocation to get baseline
recommendations - Need to find vegetarian restaurant options in appropriate price tier
Returns:
{
"per_person_per_day": 80.0,
"recommended_allocation": {
"meals": 40,
"entertainment": 24,
"attractions": 16
}
}
Claude interprets: "Baseline suggests $40/person for meals. For 25 people, that's $1,000 for dining."
Claude decides: "Need to check what $$ tier restaurants are available for vegetarian groups."
104
Claude reasons through options: - Masala of India: Extensive vegetarian menu, $ pricing, good for
groups - Ha!: 4.5 stars, international cuisine, open late (flexibility) - Mashiko: Sushi with vegan
options, unique experience
Evaluates budget allocation: - $1,000-1,200 for meal ($40-48/person) provides quality dining -
$200-240 for gratuity (18-20% for large groups is standard) - $560-800 remaining for extras
(appetizers, beverages, private room fee)
Makes strategic decision: Prioritize dining experience (Masala of India) because vegetarian
requirements + large group size means finding a restaurant that accommodates both is critical.
Budget Agent extracts the LLM response text and structures it into A2A protocol format with
metadata.
105
@tool(
name="calculate_budget_allocation",
description="Calculate recommended budget allocation for group travel"
)
def calculate_budget_allocation(
total_budget: float,
num_people: int,
num_days: int
) -> dict:
per_person = total_budget / num_people / num_days
return {
"per_person_per_day": per_person,
"recommended_allocation": {
"meals": per_person * 0.50,
"entertainment": per_person * 0.30,
"attractions": per_person * 0.20
}
}
When Claude sees this tool definition, it understands: - Name: What the tool does - Description:
When to use it - Parameters: What inputs it needs (with types) - Return type: What output it
provides
Claude decides: "I have a budget question. There's a tool called calculate_budget_allocation that
takes total_budget, num_people, and num_days. I should call this first to get baseline numbers."
This is intelligent tool use, not hardcoded function calls. Different queries might result in different tool
sequences: - Query A: "What's a good budget?" → calls calculate_budget_allocation only -
Query B: "Find cheap restaurants" → calls get_restaurants_by_price_tier("$") only - Query C:
"Plan dinner for 25 people with $2000" → calls both tools in sequence
106
from strands import Agent as StrandsAgent
from [Link] import BedrockModel
response = self.strands_agent(query)
response_text = [Link] if hasattr(response, "text") else str(response)
Strands handles: - Bedrock API authentication and invocation - Tool definition formatting for Claude -
Conversation state management (multi-turn if needed) - Response parsing and extraction - Error
handling for failed LLM calls
107
Performance Breakdown: Where the 11 Seconds Go
Compare this to deterministic agents: - Restaurant Agent: 0.2 seconds (Python filtering) - Geography
Agent: 0.3 seconds (haversine calculations) - Budget Agent: 11 seconds (LLM reasoning)
The latency comes from actual thinking. Claude is analyzing your constraints, evaluating options,
considering trade-offs, and generating strategic advice - not just checking boolean fields.
108
Cost Analysis: LLM Reasoning vs Deterministic Filtering
This cost difference is expected and acceptable: - Deterministic agents check facts (cheap, fast,
simple) - Reasoning agents make strategic decisions (more expensive, slower, complex) - Use
deterministic agents for filtering, reasoning agents for recommendations - The value of strategic
advice often justifies the cost premium
1. Contextual Understanding: Interpreted "group dining for 25 people" to mean need for large-
group accommodations, group pricing considerations, and reservation logistics.
2. Trade-off Evaluation: Evaluated $1,000 for premium dining vs $800 for good dining + $400 better
entertainment. Chose the former based on vegetarian requirements making restaurant selection
more constrained.
4. Explanation Generation: Didn't just return "Masala of India" - explained WHY (extensive
vegetarian menu), provided alternatives (Ha!, Mashiko), and gave actionable next steps (call ahead,
confirm options, ask about group pricing).
109
These capabilities come from Claude's training, not from programmer-defined rules. The agent can
adapt to different scenarios, handle novel requests, and provide human-like strategic thinking.
What's Next
You've seen inside the Budget Agent's reasoning process - from LLM prompt construction through
tool use, strategic evaluation, and response generation. You understand how the Strands framework
enables LLM-powered agents and how reasoning differs fundamentally from deterministic filtering.
In the next section, you'll learn about the reasoning agent pattern more broadly. You'll understand
when to use reasoning agents vs deterministic agents, how AgenticBaseAgent differs from
BaseAgent, and how the Budget Agent serves as a foundation for future reasoning agents (just as
Restaurant Agent served as a foundation for Hotel Agent).
The Budget Agent's 11-second latency isn't overhead - it's Claude actually thinking through your
budget constraints, evaluating options, and generating strategic recommendations. This is
intelligent reasoning, not rule execution.
The Results
Let's examine what the Budget Agent accomplished in your dinner planning request. When you
asked for dinner planning with budget constraints, the system coordinated multiple agents with
distinct capabilities:
Restaurant Agent (deterministic) filtered to 14 vegetarian restaurants near Pike Place Market by
checking [Link] and [Link] metadata fields - precise, fast (200ms), factual.
Budget Agent (reasoning) received those 14 options and generated strategic recommendations:
allocate $1,000-1,200 for dining ($40-48/person), reserve $200-240 for gratuity (18-20% for large
groups), maintain $560-800 buffer for extras, prioritize Masala of India for extensive vegetarian menu
- thoughtful, slower (11s), strategic.
110
The deterministic agents filtered facts. The reasoning agent made strategic recommendations based
on those facts. This demonstrates the complementary nature of deterministic and reasoning patterns:
one provides precision, the other provides judgment.
This tells you which restaurants fit the budget. It's precise, fast, and reliable. But it doesn't answer: -
Is this a good value for the price? - Should I prioritize dining quality or entertainment variety? - What
about gratuity, reservations, group logistics? - Which specific restaurant should I choose and why?
"I recommend allocating $1,000-1,200 ($40-48/person) for the meal at Masala of India, which has
extensive vegetarian options and $$ pricing. This leaves $800-1,000 for logistics: gratuity (18-20%
for large groups typically $200-240), potential private dining room fees, and appetizers or
beverages beyond the base meal."
This isn't just calculation - it's strategic planning: - Value assessment: "Extensive vegetarian
options" evaluates quality, not just price - Experience optimization: Prioritizes dining quality for
constrained vegetarian requirements - Practical considerations: Factors in gratuity norms, private
room fees, group logistics - Actionable advice: Specific dollar ranges, venue recommendation, next
steps
You can't achieve this with boolean checks or arithmetic. This requires understanding context,
evaluating trade-offs, and making strategic decisions - capabilities that come from LLM reasoning.
111
Decision
Pattern Framework Speed Use Case Example
Method
Geography:
Objective
Enhancement BaseAgent Mathematical 100-300ms distance <
filtering
2km
Restaurant:
Domain Metadata Boolean
BaseAgent 100-300ms vegan ==
Specialist checks criteria
true
Budget:
LLM Strategic
Reasoning AgenticBaseAgent 10-15s value trade-
judgment decisions
offs
The first three patterns use deterministic logic - rules defined by engineers. The reasoning pattern
uses LLM inference - judgment guided by prompts. Both are valuable:
Deterministic Agents (BaseAgent): - Filter facts based on clear criteria - Fast (milliseconds),
consistent, inexpensive - Perfect for objective decisions: "Which items qualify?" - Examples:
Geographic proximity, dietary restrictions, price ranges
Use Reasoning Agents When: - Questions require judgment, not just facts ("What's the best
allocation?" vs "What fits the budget?") - Trade-offs must be evaluated (dining quality vs
entertainment variety) - Context understanding is critical (group size affects logistics, weather affects
venue choice) - Natural language explanations add value (users want to understand "why"
recommendations make sense) - Adaptive responses are beneficial (same question with different
contexts needs different answers)
112
Use Deterministic Agents When: - Criteria are objective and well-defined (distance, price, ratings,
dietary restrictions) - Speed matters (filtering 1,000s of items in milliseconds) - Consistency is critical
(same input must produce same output) - Cost optimization is important (no LLM inference costs) -
Results must be verifiable (every decision has a checkable reason)
Real-World Example:
Query: "Find dinner for 25 people near Pike Place, vegetarian, $2000 budget"
Deterministic filtering (fast, precise): - Geography Agent: Filter to restaurants within 2km of Pike
Place Market - Restaurant Agent: Filter to restaurants with [Link] == true - Result:
14 vegetarian restaurants nearby (200ms total)
Reasoning (strategic, thoughtful): - Budget Agent: "Given these 14 options and your $2000 budget, I
recommend Masala of India ($40-48/person for meal, $200-240 gratuity, $560-800 buffer). Here's
why..." - Result: Strategic allocation with rationale (11s)
Together: Fast filtering reduces search space, then strategic reasoning optimizes the choice within
that space.
AgenticBaseAgent vs BaseAgent
The Budget Agent uses a different base class than previous agents. This architectural difference
enables LLM capabilities:
class RestaurantAgent(BaseAgent):
def _process_task_impl(self, task, user_input):
# Deterministic Python logic
restaurants = user_input.get('locations', [])
dietary = user_input.get('dietary_restrictions', [])
This is pure Python logic. No LLM calls, no tools, no reasoning - just filtering based on rules.
AgenticBaseAgent (budget-agent):
113
from [Link].agentic_base import AgenticBaseAgent
from strands import Agent as StrandsAgent
from [Link] import tool
class BudgetPlanningAgent(AgenticBaseAgent):
def _define_agent_tools(self):
@tool(name="calculate_budget_allocation")
def calculate_budget_allocation(total_budget: float, ...):
return {...} # Tool implementation
return [calculate_budget_allocation]
def _get_agent_system_prompt(self):
return "You are a budget planning specialist..."
This integrates Strands Agent library for LLM capabilities. The agent: - Defines tools the LLM can call
- Provides system prompt to guide reasoning - Invokes Claude via Bedrock - Processes LLM
responses with tool use - Generates conversational recommendations
Architecture Implications
Adding reasoning agents changes system architecture in important ways:
Latency: - Deterministic workflow: 15-20 seconds total (10-12s workflow generation, 3-4s synthesis,
0.5s agent processing) - With reasoning agent: 30-35 seconds total (adds 11s for LLM inference) -
Impact: Users notice longer response time but accept it for strategic advice
Cost: - Deterministic agents: $0.00001 per request (compute only) - Reasoning agents: $0.00065
per request (compute + LLM tokens) - Impact: 65x higher cost, but still pennies per request
114
Token Usage: - Deterministic agents return: 200-1,000 bytes (compact JSON) - Reasoning agents
return: 1,500-2,000 bytes (conversational text) - Impact: Synthesis receives more data, but it's
strategic context rather than bulk filtering results
Consistency: - Deterministic agents: Identical outputs for identical inputs - Reasoning agents:
Adaptive outputs based on LLM reasoning (may vary slightly) - Impact: Less predictable, but more
contextually appropriate
These trade-offs are expected and acceptable. Reasoning agents cost more and take longer
because they're doing something fundamentally different: strategic thinking rather than rule
execution.
Chapter 1 - Single orchestrator with no agents: - Capability: Basic A2A protocol workflow - Result:
Framework established, no specialized agents
Chapter 2 - Single data agent (Location Loader): - Capability: Bulk data loading - Result: All 1,819
restaurants, truncation required
Chapter 5 - Context provider added (Weather): - Capability: External context (parallel data fetching) -
Result: Weather-aware recommendations
Chapter 6 - Second domain specialist (Hotel): - Capability: Amenity filtering (reusing patterns) -
Result: Complete travel planning system
Each chapter added a new capability dimension. The system didn't just get larger - it got smarter. It
progressed from data retrieval → filtering → refinement → context awareness → strategic reasoning.
115
Foundation for Future Agents
The Budget Agent serves as a foundation for future reasoning agents, just as the Restaurant Agent
served as a foundation for the Hotel Agent:
Restaurant Agent → Hotel Agent (deterministic pattern reuse): - Both filter by metadata (dietary
restrictions vs amenities) - Both use dual-mode operation (pre-filtered or self-load) - Both apply
deterministic checks (boolean fields) - Lesson: Domain specialist pattern is reusable across domains
Budget Agent → Itinerary Agent (reasoning pattern reuse): - Both use LLM-powered decision-
making - Both evaluate trade-offs and make strategic recommendations - Both use
AgenticBaseAgent + Strands - Both generate conversational explanations - Lesson: Reasoning
agent pattern is reusable for strategic planning tasks
In a future workshop extension, you might build an Itinerary Agent that: - Receives hotel, restaurant,
and activity options from domain specialists - Uses LLM reasoning to create day-by-day travel
itineraries - Evaluates trade-offs: travel time vs variety, relaxation vs activity, cost vs experience -
Generates natural language itineraries with timing, logistics, and reasoning - Uses the same
AgenticBaseAgent framework and Strands integration
The Budget Agent demonstrates the pattern. The Itinerary Agent would adapt it for a new domain.
✅ Enhancement agents for objective filtering (Geography) ✅ Domain specialists for metadata-
driven refinement (Restaurant, Hotel) ✅ Context providers for independent information (Weather)
✅ Reasoning agents for strategic decision-making (Budget)
Your system can now: - Load bulk data efficiently - Filter by objective criteria (proximity, attributes,
metadata) - Provide environmental context (weather, conditions) - Make strategic recommendations
(budget allocation, trade-offs) - Coordinate all capabilities automatically through orchestrator
workflow generation
The orchestrator discovers all six agents, analyzes their capabilities, generates appropriate
workflows (sequential, parallel, or hybrid), and synthesizes results into comprehensive
recommendations that are both factually accurate (from deterministic filtering) and strategically
sound (from LLM reasoning).
116
Looking Forward
The multi-agent patterns you've learned extend beyond this workshop:
Additional Reasoning Agents could provide: - Itinerary planning (day-by-day schedules with trade-
off analysis) - Experience optimization (balancing variety, cost, convenience) - Risk assessment
(weather impacts, capacity constraints, timing conflicts) - Personalization (adapting
recommendations to group preferences)
Hybrid Agents could combine patterns: - Start with deterministic filtering for efficiency - Apply LLM
reasoning for strategic selection - Use tools for calculations and data fetching - Generate both
structured data and natural language explanations
Production Enhancements could add: - Caching for repeated LLM calls (same query patterns) -
Streaming for real-time reasoning visibility - Multi-turn conversations for iterative planning - User
preference learning for personalized recommendations
The patterns are established. The framework is proven. The orchestrator handles complexity
automatically. You can extend this system in any direction that adds value to your users.
Your multi-agent system now combines deterministic precision with LLM-powered strategic
thinking, providing both factually accurate filtering and intelligent recommendations for complex
planning scenarios.
117
Chapter 8: Build an Itinerary Planning
Agent (Optional)
This is similar to the budget agent's strategic reasoning, but focused on scheduling instead of
finances. You'll adapt the budget agent pattern for itinerary planning.
The agent uses the AgenticBaseAgent framework with Strands, just like the budget agent. It
receives filtered hotels, restaurants, activities, and weather from other agents, then applies strategic
scheduling logic to create comprehensive itineraries.
118
By the end of this chapter, you'll have your second LLM-powered reasoning agent, and you'll see
how reasoning patterns transfer across domains.
Ready to Build
You've seen how the budget agent makes strategic allocation recommendations. Now let's build
an itinerary agent that makes strategic scheduling recommendations.
What you see in Claude Code depends on your prompts and what Claude discovers. Keep your
prompts to Claude Code clear and direct. Generally, the more information you provide to Claude
Code, the better your results will be. Claude can make mistakes - that's normal. You can use
Claude Code to help you fix issues as they arise.
/workshop:create-agent let's create an itinerary agent based on our budget agent. we want this agent
119
3. Replace budget tools with itinerary-specific tools
4. Update the system prompt for scheduling strategy
5. Create deployment files (Dockerfile, parameters)
6. Ask before deploying
Read(agents/budget_agent/[Link])
Read(services/budget-agent/Dockerfile)
Read(static/cloudformation/parameters/[Link])
Write(agents/itinerary_agent/[Link])
Write(services/itinerary-agent/Dockerfile)
Write(static/cloudformation/parameters/[Link])
Because we are copying and modifying an existing reasoning agent template, Claude should get
most of this correct. Claude may ask you for permission to deploy. This is correct - confirm when
ready.
Working Iteratively
Claude may not get everything right on the first try. Common issues:
"you still have BudgetPlanningAgent in the get_agent() function signature. please change all
references to ItineraryPlanningAgent"
Deployment errors?
"the deployment failed with: [paste error]. can you fix it?"
"the agent returned an error instead of an itinerary. check the CloudWatch logs at /ecs/a2a-
workshop/itinerary-agent"
120
Key Mindset
Effective AI-assisted development is iterative. Be specific about what's wrong, and Claude will
help you fix it. This is normal - you're learning to work collaboratively with AI assistants.
In the next section, you'll learn more about what makes reasoning agents different from simple
agents.
Building on Chapter 7
In Chapter 7, you learned how reasoning agents work. The Budget Agent uses AgenticBaseAgent
with LLM reasoning to make strategic financial decisions. Now you're applying that same pattern to a
different domain: scheduling.
121
Aspect Budget Agent Itinerary Agent
Strategic
"How should we allocate money?" "How should we schedule time?"
Question
calculate_travel_time,
Tools calculate_budget_allocation
optimize_schedule
"I recommend allocating $1,000-1,200 for dining at Masala of India (extensive vegetarian menu, $
$ pricing). This prioritizes dining quality given your vegetarian requirement, leaving $800-1,000 for
gratuity and logistics."
122
"I recommend scheduling Pike Place Market in the morning when it's less crowded, followed by
lunch at El Borracho (0.3 miles away). This minimizes travel time and positions you for afternoon
indoor activities when rain is forecast."
Both make strategic recommendations with rationale. Both evaluate trade-offs. Both explain their
reasoning. The pattern is the same.
The AgenticBaseAgent + Strands framework handles the rest: LLM invocation, tool use, conversation
management, and response formatting.
Key Insight
Once you understand one reasoning agent, you can build others by adapting the domain-
specific components. The framework and reasoning patterns transfer.
What's Next
Now that you understand how itinerary agents adapt the budget agent pattern, you'll deploy your
itinerary agent and test the complete multi-agent system.
Deployment Options
Claude may deploy the agent for you automatically. If not, you can deploy it manually using the
deployment script. Or have Claude do it using the scripts!
123
Manual Deployment
If Claude hasn't deployed the agent, run:
./scripts/[Link] itinerary-agent
Unlike the pre-deployed agents you've been enabling, this is a **custom agent** you're building
from scratch. Custom agents require the full deployment process using `[Link]` which
builds the Docker image and creates the CloudFormation stack. Once deployed, you can use
`[Link]` and `[Link]` to control this agent just like the pre-deployed ones.
This script:
Verify Deployment
Check the agent is running:
Expected:
{
"Status": "ACTIVE",
"Running": 1
}
Or have Claude check! Claude is capable of running these AWS CLI commands.
124
Deployment Complete
The itinerary agent is running in ECS and registered with CloudMap. The orchestrator can now
discover and use it in multi-agent workflows.
create a 2-day Seattle itinerary for 8 people staying near Pike Place Market. We need vegetarian rest
This query requires coordination across multiple agents: - Weather Agent: Checks forecast for 2
days (determines indoor/outdoor balance) - Location Loader: Loads hotels, restaurants, activities -
Geography Agent: Filters by proximity to Pike Place Market - Restaurant Agent: Filters for
vegetarian options - Hotel Agent: Filters for gym amenity - Budget Agent: Validates $3000 budget
allocation strategy - Itinerary Agent: Creates day-by-day schedule with timing and reasoning
125
Based on the comprehensive data analysis, here's your perfect 2-day Seattle itinerary for 8 people wi
Morning:
- Pike Place Market (covered, perfect for rain)
- Lunch at El Borracho (Mexican with vegetarian options)
Afternoon:
- Market Theater ($50/person)
- Coffee break in Pike Place area
Evening:
- Dinner at Limoncello Belltown (Italian with vegetarian dishes)
- SIFF Cinema Downtown ($15/person)
Morning:
- Boeing IMAX Theater ($15/person)
- Lunch at Food Junction (Indian street food, vegetarian options)
Afternoon:
- Benaroya Hall - Seattle Symphony ($50/person)
- Explore Seattle Center covered areas
Evening:
- Dinner at Menya Musashi Niten Ichiryu (ramen with vegan/vegetarian options)
- Dimitriou's Jazz Alley ($50/person)
The agents considered the weather forecast (heavy rain both days) and focused on indoor activities an
126
Understanding the Coordination
This demonstrates sophisticated multi-agent coordination:
Deterministic Filtering (fast, precise): - Geography: Proximity to Pike Place Market - Restaurant:
Vegetarian metadata checks - Hotel: Gym amenity verification
The itinerary agent receives filtered results from multiple agents and generates strategic scheduling
recommendations that optimize timing, minimize travel, adapt to weather, and stay within budget.
Your system coordinates deterministic filtering, context provision, and strategic reasoning
across multiple agents to generate comprehensive, optimized itineraries.
127
What You've Learned
By completing this chapter, you've:
The itinerary agent demonstrates how reasoning patterns transfer across domains. The same
AgenticBaseAgent + Strands framework that powers budget planning also powers itinerary planning -
only the tools, system prompt, and strategic logic change.
128
Epilogue: Your Path Forward
You've moved from single-agent limitations to multi-agent coordination with both deterministic filtering
and LLM-powered reasoning.
129
Chapter Agents Pattern Data Flow Result Quality
Strategic
Chapter 6 Reasoning agent Budget calculations with
recommendations
7 agents (LLM) reasoning
Budget analysis
130
• Filter by budget constraints
• Rank options by various criteria (budget fit, proximity, ratings)
• Provide context-aware, multi-criteria recommendations
Your system evolved from "here are some restaurants" to "here's a complete 2-day itinerary with
budget-appropriate restaurant and entertainment combinations for your group size, filtered by dietary
needs and location, adapted for weather conditions, with strategic scheduling and timing
recommendations."
Journey Complete
Cleanup
If you're doing this as part of an AWS event, no need to worry about this. We'll clean up everything
for you. If you're doing this on your own, be sure to clean up any stacks to avoid costs.
./scripts/[Link]
Workshop Cleanup
The `[Link]` script only deletes learner-created agents (hotel-agent and itinerary-
agent). The pre-deployed agents (location-loader, geography-agent, restaurant-agent, weather-
agent, budget-agent, events-agent) are part of the main CloudFormation stack and will be
deleted when you delete the entire workshop infrastructure. To delete everything, remove the
main `a2a-workshop` CloudFormation stack from the AWS Console (see below).
131
This deletes only the custom agents you deployed: - hotel-agent (Chapter 6) - itinerary-agent
(Chapter 8)
The pre-deployed agents, workshop infrastructure (Code Server, MCP Gateway, Orchestrator, VPC),
and all other resources remain active.
This deletes all workshop infrastructure: Code Server, MCP Gateway, Orchestrator, VPC, ECS
cluster, Application Load Balancer, and all nested stacks.
Don't forget to download the comprehensive PDF guide! It includes all chapters, diagrams, code
examples, and design patterns in a single offline-friendly document. **Download:** [workshop-
[Link]]([Link]
c079209e-3f27-4614-9b4f-48eae3921e57/[Link]) (~5MB, 100+ pages) This is
perfect for: - Reviewing concepts after the workshop - Sharing with colleagues - Reference
during your own multi-agent implementations - Offline access to all workshop content
Final Thoughts
From a single agent with truncation problems to a sophisticated multi-agent system with reasoning
capabilities—you've come a long way.
132
Workshop Complete
You've completed the Inter-Agent Systems workshop. You've learned A2A protocol
fundamentals, deployed production agents, built custom logic, and mastered multi-agent
coordination patterns. The foundation is yours. Where you go from here is up to you. Thank you
for your time and effort. Build something amazing.
133
The Strands framework enhances the Budget Agent by integrating LLM capabilities, allowing the agent to think through trade-offs, consider various constraints, and generate nuanced natural language outputs that include rationale and strategic planning. Unlike deterministic agents that provide binary or arithmetic results, the Budget Agent offers value assessments, experience optimization, and practical advice such as specific budget allocations and group logistics. This approach enables richer, context-aware recommendations that account for qualitative aspects of user needs.
The Budget Agent offers strategic capabilities like context interpretation, trade-off evaluation, and experience-based knowledge. It can assess the value of different options, prioritize aspects such as dining quality over entertainment variety, and provide recommendations with actionable advice like specific dollar allocations and logistics guidance. These capabilities are essential for group event planning as they offer adaptation to varying needs, strategic allocation of resources, and thoughtful guidance beyond mere arithmetic calculations, all of which cannot be achieved by deterministic agents.
Reasoning agents outperform deterministic agents in scenarios requiring judgment and strategic decision-making. Key use cases include budget allocation, itinerary planning, and experience optimization, where it's important to evaluate trade-offs, understand context, and provide explanatory recommendations. These cases are significant because they involve complex, subjective decisions that require adaptability to different situations and detailed guidance, which deterministic agents, operating on fixed rules or criteria, cannot efficiently provide.
The orchestrator processes the request by first querying the CloudMap and discovering both the Location Loader and Geography Agent. It fetches the agent cards, analyzes their capabilities, and generates a workflow with them in the correct sequence. The orchestrator uses the Location Loader to load the complete dataset of Seattle restaurants. The Geography Agent then filters this data, using the haversine formula to calculate the distance from Pike Place Market to each restaurant and retains only those within a reasonable proximity. This results in a dataset of nearby recommendations, ensuring no truncation occurs during synthesis.
The transition illustrates an evolutionary enhancement from objective data filtering, where deterministic agents provide precise, factual information, to strategic reasoning, where the Budget Agent adds layers of understanding and context-driven decision-making. This progression reflects the capacity to handle more complex, real-world applications like group budget allocation, which requires evaluating multiple variables, making informed judgments about value and practicality, and delivering nuanced, actionable insights that deterministic methods alone cannot offer.
Metadata declarations within agent cards describe the services and enhancements each agent provides, informing the orchestrator about how the agents can collaborate. For example, the Geography Agent's metadata might include provides_for attributes to indicate it enhances restaurant data. The orchestrator uses these declarations to decide the sequence of operations, ensuring that agents are utilized effectively within workflows. The process relies on understanding these relationships without hard coding, enabling dynamic and adaptive workflow generation.
Deterministic agents use predefined rules and criteria to filter data, offering precision and speed. For example, they verify geographic proximity or dietary criteria quickly and reliably. In contrast, LLM-powered agents, like the Budget Agent, reason through complex scenarios, evaluate trade-offs, and generate strategic recommendations, which are slower but consider context, quality, and adaptability. This combination in a multi-agent system enables both objective filtering of data and nuanced decision-making, leading to well-rounded, relevant, and context-sensitive outcomes.
The orchestrator determines the optimal sequence by analyzing the metadata and declared capabilities of each agent involved. It identifies that the Geography Agent enhances restaurant data and should run before the Restaurant Agent, as it performs location filtering which the Restaurant Agent can subsequently use to apply dietary criteria. This understanding is driven by the agents' provides_for metadata declarations and ensures that the workflow is efficiently structured for the task at hand.
The inclusion of a Restaurant Agent refines the results by adding an additional filtering dimension focused on dietary accommodations. After the Geography Agent filters restaurants by proximity to Pike Place Market, the Restaurant Agent further filters these by checking the dietary.vegan' field, retaining only the vegan options. This process turns a set of 465 nearby restaurants into 14 highly-relevant ones that meet both geographic and dietary criteria, thus increasing the precision and utility of the final recommendations.
The orchestrator avoids truncation by employing the Geography Agent to filter the list of restaurants before synthesis. By calculating distances and only passing data on restaurants near the query location, the system ensures that only relevant, proximity-based data enters the synthesis phase. This algorithmic filtering method ensures that the synthesis process utilizes the full token budget effectively, eliminating the need to discard excess data through arbitrary truncation and thereby retaining higher relevance and completeness of information.