Travel Concierge¶
Full-lifecycle travel assistant with 6 specialist sub-agent groups orchestrated by a root coordinator: inspiration, planning, booking, pre-trip, in-trip, and post-trip. This is the largest ADK sample with 20+ agents across deeply nested directories.
Architecture¶
Root coordinator routes to 6 sub-agent groups, each containing their own specialist sub-agents:
root_agent (gemini-2.0-flash-001)
|-- inspiration_agent (agent_tool: place_agent, poi_agent; uses map_tool)
|-- planning_agent (agent_tool: flight_search, flight_seat, hotel_search, hotel_room, itinerary; uses memorize)
|-- booking_agent (agent_tool: create_reservation, payment_choice, process_payment)
|-- pre_trip_agent (agent_tool: what_to_pack_agent; uses google_search_grounding AgentTool)
|-- in_trip_agent (sub_agents: trip_monitor_agent; agent_tool: day_of_agent; uses memorize)
|-- post_trip_agent (uses memorize)
Native ADK¶
Original uses 30+ files across 15+ directories:
travel_concierge/
├── __init__.py
├── agent.py
├── prompt.py
├── tracing.py
├── profiles/
│ ├── itinerary_empty_default.json
│ └── itinerary_seattle_example.json
├── shared_libraries/
│ ├── __init__.py
│ ├── constants.py
│ └── types.py
├── tools/
│ ├── __init__.py
│ ├── memory.py
│ ├── places.py
│ └── search.py
└── sub_agents/
├── __init__.py
├── inspiration/
│ ├── __init__.py
│ ├── agent.py
│ └── prompt.py
├── planning/
│ ├── __init__.py
│ ├── agent.py
│ └── prompt.py
├── booking/
│ ├── __init__.py
│ ├── agent.py
│ └── prompt.py
├── pre_trip/
│ ├── __init__.py
│ ├── agent.py
│ └── prompt.py
├── in_trip/
│ ├── __init__.py
│ ├── agent.py
│ ├── prompt.py
│ └── tools.py
└── post_trip/
├── __init__.py
├── agent.py
└── prompt.py
sub_agents/inspiration/agent.py (click to expand)
from google.adk.agents import Agent
from google.adk.tools.agent_tool import AgentTool
from travel_concierge.shared_libraries.types import (
DestinationIdeas, POISuggestions, json_response_config,
)
from travel_concierge.sub_agents.inspiration import prompt
from travel_concierge.tools.places import map_tool
place_agent = Agent(
model="gemini-2.5-flash",
name="place_agent",
instruction=prompt.PLACE_AGENT_INSTR,
description="This agent suggests a few destination given some user preferences",
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_schema=DestinationIdeas,
output_key="place",
generate_content_config=json_response_config,
)
poi_agent = Agent(
model="gemini-2.5-flash",
name="poi_agent",
description="This agent suggests a few activities and points of interests given a destination",
instruction=prompt.POI_AGENT_INSTR,
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_schema=POISuggestions,
output_key="poi",
generate_content_config=json_response_config,
)
inspiration_agent = Agent(
model="gemini-2.5-flash",
name="inspiration_agent",
description="A travel inspiration agent...",
instruction=prompt.INSPIRATION_AGENT_INSTR,
tools=[AgentTool(agent=place_agent), AgentTool(agent=poi_agent), map_tool],
)
sub_agents/planning/agent.py (click to expand)
from google.adk.agents import Agent
from google.adk.tools.agent_tool import AgentTool
from google.genai.types import GenerateContentConfig
from travel_concierge.shared_libraries import types
from travel_concierge.sub_agents.planning import prompt
from travel_concierge.tools.memory import memorize
itinerary_agent = Agent(
model="gemini-2.5-flash",
name="itinerary_agent",
description="Create and persist a structured JSON representation of the itinerary",
instruction=prompt.ITINERARY_AGENT_INSTR,
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_schema=types.Itinerary,
output_key="itinerary",
generate_content_config=types.json_response_config,
)
hotel_room_selection_agent = Agent(
model="gemini-2.5-flash",
name="hotel_room_selection_agent",
description="Help users with the room choices for a hotel",
instruction=prompt.HOTEL_ROOM_SELECTION_INSTR,
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_schema=types.RoomsSelection,
output_key="room",
generate_content_config=types.json_response_config,
)
hotel_search_agent = Agent(
model="gemini-2.5-flash",
name="hotel_search_agent",
description="Help users find hotel around a specific geographic area",
instruction=prompt.HOTEL_SEARCH_INSTR,
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_schema=types.HotelsSelection,
output_key="hotel",
generate_content_config=types.json_response_config,
)
flight_seat_selection_agent = Agent(
model="gemini-2.5-flash",
name="flight_seat_selection_agent",
description="Help users with the seat choices",
instruction=prompt.FLIGHT_SEAT_SELECTION_INSTR,
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_schema=types.SeatsSelection,
output_key="seat",
generate_content_config=types.json_response_config,
)
flight_search_agent = Agent(
model="gemini-2.5-flash",
name="flight_search_agent",
description="Help users find best flight deals",
instruction=prompt.FLIGHT_SEARCH_INSTR,
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_schema=types.FlightsSelection,
output_key="flight",
generate_content_config=types.json_response_config,
)
planning_agent = Agent(
model="gemini-2.5-flash",
description="Helps users with travel planning...",
name="planning_agent",
instruction=prompt.PLANNING_AGENT_INSTR,
tools=[
AgentTool(agent=flight_search_agent),
AgentTool(agent=flight_seat_selection_agent),
AgentTool(agent=hotel_search_agent),
AgentTool(agent=hotel_room_selection_agent),
AgentTool(agent=itinerary_agent),
memorize,
],
generate_content_config=GenerateContentConfig(temperature=0.1, top_p=0.5),
)
sub_agents/booking/agent.py (click to expand)
from google.adk.agents import Agent
from google.adk.tools.agent_tool import AgentTool
from google.genai.types import GenerateContentConfig
from travel_concierge.sub_agents.booking import prompt
create_reservation = Agent(
model="gemini-2.5-flash",
name="create_reservation",
description="Create a reservation for the selected item.",
instruction=prompt.CONFIRM_RESERVATION_INSTR,
)
payment_choice = Agent(
model="gemini-2.5-flash",
name="payment_choice",
description="Show the users available payment choices.",
instruction=prompt.PAYMENT_CHOICE_INSTR,
)
process_payment = Agent(
model="gemini-2.5-flash",
name="process_payment",
description="Given a selected payment choice, processes the payment.",
instruction=prompt.PROCESS_PAYMENT_INSTR,
)
booking_agent = Agent(
model="gemini-2.5-flash",
name="booking_agent",
description="Given an itinerary, complete the bookings...",
instruction=prompt.BOOKING_AGENT_INSTR,
tools=[
AgentTool(agent=create_reservation),
AgentTool(agent=payment_choice),
AgentTool(agent=process_payment),
],
generate_content_config=GenerateContentConfig(temperature=0.0, top_p=0.5),
)
sub_agents/pre_trip/agent.py (click to expand)
from google.adk.agents import Agent
from google.adk.tools.agent_tool import AgentTool
from travel_concierge.shared_libraries import types
from travel_concierge.sub_agents.pre_trip import prompt
from travel_concierge.tools.search import google_search_grounding
what_to_pack_agent = Agent(
model="gemini-2.5-flash",
name="what_to_pack_agent",
description="Make suggestion on what to bring for the trip",
instruction=prompt.WHATTOPACK_INSTR,
disallow_transfer_to_parent=True,
disallow_transfer_to_peers=True,
output_key="what_to_pack",
output_schema=types.PackingList,
)
pre_trip_agent = Agent(
model="gemini-2.5-flash",
name="pre_trip_agent",
description="Given an itinerary, this agent keeps up to date...",
instruction=prompt.PRETRIP_AGENT_INSTR,
tools=[google_search_grounding, AgentTool(agent=what_to_pack_agent)],
)
sub_agents/in_trip/agent.py (click to expand)
from google.adk.agents import Agent
from google.adk.tools.agent_tool import AgentTool
from travel_concierge.sub_agents.in_trip import prompt
from travel_concierge.sub_agents.in_trip.tools import (
event_booking_check, flight_status_check,
transit_coordination, weather_impact_check,
)
from travel_concierge.tools.memory import memorize
day_of_agent = Agent(
model="gemini-2.5-flash",
name="day_of_agent",
description="Day_of agent is the agent handling the travel logistics.",
instruction=transit_coordination,
)
trip_monitor_agent = Agent(
model="gemini-2.5-flash",
name="trip_monitor_agent",
description="Monitor aspects of an itinerary...",
instruction=prompt.TRIP_MONITOR_INSTR,
tools=[flight_status_check, event_booking_check, weather_impact_check],
output_key="daily_checks",
)
in_trip_agent = Agent(
model="gemini-2.5-flash",
name="in_trip_agent",
description="Provide information about what the users need...",
instruction=prompt.INTRIP_INSTR,
sub_agents=[trip_monitor_agent],
tools=[AgentTool(agent=day_of_agent), memorize],
)
sub_agents/post_trip/agent.py (click to expand)
from google.adk.agents import Agent
from travel_concierge.sub_agents.post_trip import prompt
from travel_concierge.tools.memory import memorize
post_trip_agent = Agent(
model="gemini-2.5-flash",
name="post_trip_agent",
description="A follow up agent to learn from user's experience...",
instruction=prompt.POSTTRIP_INSTR,
tools=[memorize],
)
tools/search.py -- google_search_grounding wrapper (click to expand)
from google.adk.agents import Agent
from google.adk.tools.agent_tool import AgentTool
from google.adk.tools.google_search_tool import google_search
_search_agent = Agent(
model="gemini-2.5-flash",
name="google_search_grounding",
description="An agent providing Google-search grounding capability",
instruction="""Answer the user's question directly using google_search grounding tool...""",
tools=[google_search],
)
google_search_grounding = AgentTool(agent=_search_agent)
tools/memory.py -- memorize / forget / state init (click to expand)
import json, os
from datetime import datetime
from google.adk.agents.callback_context import CallbackContext
from google.adk.tools import ToolContext
from travel_concierge.shared_libraries import constants
SAMPLE_SCENARIO_PATH = os.getenv(
"TRAVEL_CONCIERGE_SCENARIO",
"travel_concierge/profiles/itinerary_empty_default.json",
)
def memorize(key: str, value: str, tool_context: ToolContext):
"""Memorize pieces of information, one key-value pair at a time."""
tool_context.state[key] = value
return {"status": f'Stored "{key}": "{value}"'}
def _load_precreated_itinerary(callback_context: CallbackContext):
"""Sets up the initial state from a JSON file."""
data = {}
with open(SAMPLE_SCENARIO_PATH) as file:
data = json.load(file)
_set_initial_states(data["state"], callback_context.state)
tools/places.py -- Google Places API wrapper (click to expand)
import os, requests
from google.adk.tools import ToolContext
class PlacesService:
def find_place_from_text(self, query: str) -> dict[str, str]:
# ... Google Places API call ...
pass
places_service = PlacesService()
def map_tool(key: str, tool_context: ToolContext):
"""Inspect POIs under the key in state and verify lat/lon via Map API."""
# ... iterates state[key]["places"], calls Places API ...
pass
shared_libraries/types.py -- 20+ Pydantic models (click to expand)
from google.genai import types
from pydantic import BaseModel, Field
json_response_config = types.GenerateContentConfig(
response_mime_type="application/json"
)
class Room(BaseModel):
is_available: bool
price_in_usd: int
room_type: str
class RoomsSelection(BaseModel):
rooms: list[Room]
class Hotel(BaseModel):
name: str
address: str
check_in_time: str
check_out_time: str
thumbnail: str
price: int
class HotelsSelection(BaseModel):
hotels: list[Hotel]
# ... Seat, SeatsSelection, Flight, FlightsSelection,
# Destination, DestinationIdeas, POI, POISuggestions,
# AttractionEvent, FlightEvent, HotelEvent, ItineraryDay,
# Itinerary, UserProfile, PackingList ...
sub_agents/in_trip/tools.py -- transit coordination logic (click to expand)
from datetime import datetime
from google.adk.agents.readonly_context import ReadonlyContext
from travel_concierge.shared_libraries import constants
from travel_concierge.sub_agents.in_trip import prompt
def flight_status_check(flight_number, flight_date, checkin_time, departure_time):
return {"status": f"Flight {flight_number} checked"}
def event_booking_check(event_name, event_date, event_location):
if event_name.startswith("Space Needle"):
return {"status": f"{event_name} is closed."}
return {"status": f"{event_name} checked"}
def weather_impact_check(activity_name, activity_date, activity_location):
return {"status": f"{activity_name} checked"}
def transit_coordination(readonly_context: ReadonlyContext):
"""Dynamically generates an instruction for the day_of agent."""
# ... 100+ lines of segment-finding logic ...
pass
# travel_concierge/agent.py (root)
from google.adk.agents import Agent
from travel_concierge import prompt
from travel_concierge.sub_agents.booking.agent import booking_agent
from travel_concierge.sub_agents.in_trip.agent import in_trip_agent
from travel_concierge.sub_agents.inspiration.agent import inspiration_agent
from travel_concierge.sub_agents.planning.agent import planning_agent
from travel_concierge.sub_agents.post_trip.agent import post_trip_agent
from travel_concierge.sub_agents.pre_trip.agent import pre_trip_agent
from travel_concierge.tools.memory import _load_precreated_itinerary
root_agent = Agent(
model="gemini-2.0-flash-001",
name="root_agent",
description="A Travel Concierge using the services of multiple sub-agents",
instruction=prompt.ROOT_AGENT_INSTR,
sub_agents=[
inspiration_agent,
planning_agent,
booking_agent,
pre_trip_agent,
in_trip_agent,
post_trip_agent,
],
before_agent_callback=_load_precreated_itinerary,
)
Fluent API¶
3 files, flat directory:
travel_concierge/
├── __init__.py
├── agent.py
└── prompt.py
# agent.py
from adk_fluent import Agent
from dotenv import load_dotenv
from google.adk.agents.callback_context import CallbackContext
from google.adk.agents.readonly_context import ReadonlyContext
from google.adk.tools import ToolContext, google_search
from google.adk.tools.agent_tool import AgentTool
from google.genai.types import GenerateContentConfig
from pydantic import BaseModel, Field
from .prompt import (
BOOKING_AGENT_PROMPT, CONFIRM_RESERVATION_PROMPT,
FLIGHT_SEARCH_PROMPT, FLIGHT_SEAT_SELECTION_PROMPT,
HOTEL_ROOM_SELECTION_PROMPT, HOTEL_SEARCH_PROMPT,
INSPIRATION_AGENT_PROMPT, INTRIP_AGENT_PROMPT,
ITINERARY_AGENT_PROMPT, LOGISTIC_PROMPT_TEMPLATE,
NEED_ITINERARY_PROMPT, PAYMENT_CHOICE_PROMPT,
PLACE_AGENT_PROMPT, PLANNING_AGENT_PROMPT,
POI_AGENT_PROMPT, POSTTRIP_AGENT_PROMPT,
PRETRIP_AGENT_PROMPT, PROCESS_PAYMENT_PROMPT,
ROOT_AGENT_PROMPT, SEARCH_GROUNDING_PROMPT,
TRIP_MONITOR_PROMPT, WHATTOPACK_PROMPT,
)
load_dotenv()
MODEL = "gemini-2.5-flash"
# Pydantic schemas, tools, and callbacks are inline in agent.py
# (see full source for details)
# --- Inspiration group ---
place_agent = (
Agent("place_agent", MODEL)
.describe("Suggests destination ideas given user preferences")
.instruct(PLACE_AGENT_PROMPT)
.disallow_transfer_to_parent(True)
.disallow_transfer_to_peers(True)
.returns(DestinationIdeas)
.writes("place")
.generate_content_config(json_response_config)
)
poi_agent = (
Agent("poi_agent", MODEL)
.describe("Suggests activities and points of interest for a destination")
.instruct(POI_AGENT_PROMPT)
.disallow_transfer_to_parent(True)
.disallow_transfer_to_peers(True)
.returns(POISuggestions)
.writes("poi")
.generate_content_config(json_response_config)
)
inspiration_agent = (
Agent("inspiration_agent", MODEL)
.describe("A travel inspiration agent...")
.instruct(INSPIRATION_AGENT_PROMPT)
.agent_tool(place_agent)
.agent_tool(poi_agent)
.tool(map_tool)
)
# --- Planning group ---
flight_search_agent = (
Agent("flight_search_agent", MODEL)
.describe("Help users find best flight deals")
.instruct(FLIGHT_SEARCH_PROMPT)
.disallow_transfer_to_parent(True)
.disallow_transfer_to_peers(True)
.returns(FlightsSelection)
.writes("flight")
.generate_content_config(json_response_config)
)
# ... (flight_seat, hotel_search, hotel_room, itinerary agents similar) ...
planning_agent = (
Agent("planning_agent", MODEL)
.describe("Helps users with travel planning...")
.instruct(PLANNING_AGENT_PROMPT)
.agent_tool(flight_search_agent)
.agent_tool(flight_seat_selection_agent)
.agent_tool(hotel_search_agent)
.agent_tool(hotel_room_selection_agent)
.agent_tool(itinerary_agent)
.tool(memorize)
.generate_content_config(GenerateContentConfig(temperature=0.1, top_p=0.5))
)
# --- Booking group ---
booking_agent = (
Agent("booking_agent", MODEL)
.describe("Complete the bookings by handling payment choices and processing.")
.instruct(BOOKING_AGENT_PROMPT)
.agent_tool(create_reservation)
.agent_tool(payment_choice)
.agent_tool(process_payment)
.generate_content_config(GenerateContentConfig(temperature=0.0, top_p=0.5))
)
# --- Pre-trip, In-trip, Post-trip groups ---
pre_trip_agent = (
Agent("pre_trip_agent", MODEL)
.describe("Provides relevant travel information before the trip.")
.instruct(PRETRIP_AGENT_PROMPT)
.tool(AgentTool(agent=google_search_grounding.build()))
.agent_tool(what_to_pack_agent)
)
in_trip_agent = (
Agent("in_trip_agent", MODEL)
.describe("Provide information during the tour.")
.instruct(INTRIP_AGENT_PROMPT)
.sub_agents([trip_monitor_agent.build()])
.agent_tool(day_of_agent)
.tool(memorize)
)
post_trip_agent = (
Agent("post_trip_agent", MODEL)
.describe("Learn from user's experience for future improvements.")
.instruct(POSTTRIP_AGENT_PROMPT)
.tool(memorize)
)
# --- Root coordinator ---
root_agent = (
Agent("root_agent", "gemini-2.0-flash-001")
.describe("A Travel Concierge using the services of multiple sub-agents")
.instruct(ROOT_AGENT_PROMPT)
.sub_agents([
inspiration_agent.build(),
planning_agent.build(),
booking_agent.build(),
pre_trip_agent.build(),
in_trip_agent.build(),
post_trip_agent.build(),
])
.before_agent(_load_precreated_itinerary)
.build()
)
What Changed¶
14x
AgentTool(agent=...)wrapping calls replaced by.agent_tool()output_key=replaced by.writes()instruction=replaced by.instruct()description=replaced by.describe()30+ files across 15+ directories collapsed to 3 files in 1 directory
No
__init__.pyre-export chain needed (eliminated 8 init files)No separate sub-agent packages for each of the 6 groups
shared_libraries/types.py,shared_libraries/constants.pyinlinedtools/memory.py,tools/places.py,tools/search.pyconsolidated into agent.pyAll 7 separate
prompt.pyfiles merged into a singleprompt.pyCross-package imports (
from travel_concierge.sub_agents.booking.agent import ...) eliminatedState constants, Pydantic schemas, and tool functions colocated with agent definitions
Metrics¶
Metric |
Native |
Fluent |
Reduction |
|---|---|---|---|
Agent definition files |
7 |
1 |
86% |
Prompt files |
7 |
1 |
86% |
Total Python files |
22 |
3 |
86% |
Directories |
15 |
1 |
93% |
|
9 |
1 |
89% |
|
50+ |
22 |
56% |
|
14 |
0 |
100% |