A common first attempt at building a travel-planning agent looks like the classic prototype: one large model, a few tools, and a long system prompt. It works — until real-world complexity arrives. Flights come from a clean API, hotels hide behind a shifting web UI, and the model starts mixing instructions, forgetting context, and inventing steps. At that point the single agent is not the solution; it is the obstacle.
This walkthrough, adapted from a tutorial on the AWS Machine Learning Blog, shows how agent-to-agent collaboration on Amazon Bedrock works in practice — using Amazon Nova 2 Lite for planning and Amazon Nova Act for browser interaction — to turn a fragile single-agent setup into a predictable multi-agent system.
Why One Agent Collapses Under Load
The more responsibilities a single agent carries, the worse it performs: it slows down, loses context, makes inconsistent choices, and eventually buckles under its own weight. The failure is architectural, and no amount of prompt tuning fixes an architectural problem.
The fix is dividing the work among three agents, each with a single responsibility. The travel agent owns user intent and planning. The flight agent talks to a structured flight API. The hotel agent uses browser automation to navigate real hotel sites. Each does one thing well, and they coordinate through a simple message-passing layer — so from the outside, the workflow still feels like one seamless assistant.
Solution Overview
The three agents communicate through a direct agent-to-agent (A2A) message-passing pattern: short, structured messages that exchange goals, share status, and trigger behavior. Because the messages are structured, the workflow stays predictable even when each agent runs in a different execution environment. In a multi-agent system, coordination matters as much as expertise — agents need a reliable way to hand work to each other, especially on a composite task like trip planning.
Travel Agent Implementation (Amazon Nova 2 Lite)
The travel agent is the orchestrator. It receives the user’s request, interprets intent using Amazon Nova 2 Lite, and decides which agent to call next. Nova 2 Lite does the heavy reasoning: breaking input into steps, identifying when to trigger the flight or hotel agent, and tracking the overall plan. Because the travel agent never touches external systems directly, its only job is to think clearly and route messages over A2A.
The following code example shows a simplified version of initializing the travel agent:
# Initialize A2A client tools
provider = A2AClientToolProvider(known_agent_urls=(
"http://localhost:9000", # Hotel Booking Expert (NovaAct)
"http://localhost:9001" # Flight Booking Expert
))
bedrock_model = BedrockModel(
model_id="global.amazon.nova-2-lite-v1:0",
region_name="us-east-1",
)
# Create client agent with A2A tools
client_agent = Agent(
name="Travel Client",
model=bedrock_model,
description="Client agent that coordinates travel planning using specialized A2A agents",
tools=provider.tools,
system_prompt="""You are an autonomous travel planning agent. You MUST take action immediately without asking for confirmation.In practice the travel agent receives natural-language requests, decomposes them, and delegates — keeping planning logic entirely separate from execution details.
Flight Agent Implementation (Amazon Nova 2 Lite + API)
The flight agent handles the fully structured side of the problem. It uses Nova 2 Lite for lightweight logic — validating input, formatting the search, and deciding whether to call the live flights API or fall back to mock data when credentials are unavailable — then returns a clean, predictable JSON response to the travel agent via A2A.
Here is a condensed version of the flight search tool; the full implementation, including OAuth, fallback logic, and airport-code handling, is available in the accompanying GitHub repository:
@tool
def search_flights(origin: str, destination: str, departure_date: str, return_date: Optional(str) = None) -> str:
# Nova 2 Lite handles the reasoning around which path to take
if amadeus_configured():
return _search_amadeus_flights(
origin=origin,
destination=destination,
departure_date=departure_date,
return_date=return_date
)
else:
# Local development fallback
return _search_flights_web(origin, destination, departure_date, return_date)
{
"flights": (
{ "flight_number": "DL456", "price": 520, "duration": "14h 30m" },
{ "flight_number": "JL701", "price": 545, "duration": "13h 50m" }
),
"source": "Amadeus API"
}Because this agent deals with clean, structured data, the reasoning load is light: Nova 2 Lite mostly chooses the right execution path and normalizes output. That keeps the pipeline predictable and keeps API-specific logic out of the travel agent.
Hotel Agent Implementation (Amazon Nova Act)
Hotels are nothing like flights. There is no clean API to call, and most booking sites load content dynamically behind JavaScript. This is where Amazon Nova Act comes in: the hotel agent describes what it needs, and Nova Act handles the browsing and returns structured data.
A condensed version of the tool:
@tool
def search_hotels(location: str, checkin_date: str, nights: int = 2) -> str:
with NovaAct() as nova:
result = nova.act(
f"Search for hotels in {location} from {checkin_date} for {nights} nights. "
f"Return the top 3 listings with name, price, and rating.",
schema=HotelSearchResults.model_json_schema()
)
return json.dumps(result)And a simplified example of the response — the full code, including scrolling, cookie banners, and other details, is in the GitHub repository:
{
"hotels": (
{ "name": "Shinjuku Grand", "price": "$180", "rating": 4.3 },
{ "name": "Park Tower Tokyo", "price": "$210", "rating": 4.6 },
{ "name": "Hotel Blossom", "price": "$155", "rating": 4.0 }
),
"source": "Anycompany.com via Nova Act"
}Using Nova Act spares the hotel agent from breaking every time a site layout changes, and removes the need to write custom scraping or DOM-parsing logic.
A2A Message Flow: How the Agents Talk
With responsibilities settled, the agents need a protocol. Before sending real work, the travel agent calls each peer’s A2A endpoint to confirm it is ready and loads the list of capabilities each agent exposes, so Nova 2 Lite knows what is available. From there the flow is direct: the travel agent sends the flight agent a message containing the required fields, waits for the structured reply, and does the same for hotels.
The Full Workflow, End to End
A single user request drives the whole system:
Please arrange travel for one person from NYC to Paris on December 6, 2025, including a two night stay in Paris.The sequence unfolds in three hops. First, the travel agent extracts the flight portion of the request and sends it to the flight agent, which returns three direct, inexpensive flights from JFK to Paris with airline, time, price, and duration. Next, the travel agent forwards the hotel portion to the hotel agent, which uses Nova Act to check Paris hotels and returns the top three options with names, prices, and brief notes. Finally, the travel agent merges both responses into one clear summary — recommended flight, recommended hotels, dates, total cost, and a question asking what to book.
Why This Pattern Generalizes
The travel planner is a teaching example, but the division of labor applies to most production agent systems: one agent that reasons and routes, specialist agents that own one integration each, and a structured protocol between them. Splitting responsibilities makes systems easier to test (each agent can be validated alone), easier to evolve (swapping a data source touches one agent), and easier to explain — qualities that matter more than raw capability once a system reaches production. Similar orchestration thinking is appearing across the industry, including in managed form, as covered in this related piece on the Databricks Agent Bricks Supervisor Agent.
Limitations and What to Watch
The pattern is not free. Multi-agent systems add coordination overhead: more moving parts, more latency per hop, and new failure modes when one agent is down or returns malformed data — the readiness checks and structured messages exist precisely because of this. Browser automation remains the most fragile link; tools like Nova Act reduce breakage from layout changes but cannot eliminate it, and automated interaction with third-party websites should respect those sites’ terms of service. Costs also multiply, since each hop is a model invocation. Finally, the example code is simplified for teaching — production use requires the error handling, authentication, and fallback logic in the full repository, plus monitoring to detect when an agent quietly degrades. Teams should start with the smallest number of agents that cleanly separates their integrations — often two — and add more only when a single responsibility grows unwieldy.
Conclusion
Built as three small agents, the travel planner is far easier to manage than one large agent. Each agent focuses on a single task, Amazon Nova 2 Lite supplies the reasoning where it is needed, Nova Act absorbs the messiness of the web, and short structured messages hold the system together — making it easier to change, and easier to explain. The full code and examples are in the Agent to Agent with Amazon Nova GitHub repository linked from the original post.
About the original authors
This article is adapted from a walkthrough written by three AWS Solutions Architects.

Yoav Fishman is an AWS Solutions Architect with 12 years of cloud and engineering experience, specializing in generative AI, agentic AI, and cybersecurity.

Elior Farajpur is a Solutions Architect at AWS with seven years of experience in cloud technologies.

Dan Kolodny is an AWS Solutions Architect specializing in big data and analytics.