[Tutorial] Building a Multi-Agent Travel Planner with Google ADK

Most travel apps optimize for popularity and convenience, often directing users toward overcrowded, carbon-intensive destinations.

In this tutorial, we’ll learn how to build a multi-agent travel planner with Google ADK that instead recommends highly relevant, more sustainable alternatives as inviting possibilities rather than prescriptive choices.

The implementation is based on TRACE, our research prototype accepted at SIGIR 2026 (Banerjee et al.), bridging research with a production-ready engineering workflow.

TRACE architecture overview

The backend is built using Google’s Agent Development Kit (ADK) and powered by Gemini 2.5 Flash, with Chainlit used for the frontend.

In this article, we focus exclusively on the backend architecture, as it is the most interesting component.
Below, we break down the core multi-agent design and provide a lightweight, runnable prototype to illustrate the concept.

Note: The original GCP demo of the paper has moved to Hugging Face Spaces due to a Chainlit security update. See the end of this post for details.

The Architecture

To influence user choices without restricting autonomy, TRACE generates two parallel recommendations and evaluates the trade-offs:

  1. A Baseline Recommendation: Optimizes solely for user relevance.
  2. A Sustainability-Aware Recommendation: Balanced for carbon footprint and crowd density.

A Counterfactual Explanation (CFE) Agent then compares the two options. It highlights the sustainable choice as the primary recommendation while keeping the popular baseline visible as an alternative. This transparent comparison serves as a gentle cognitive nudge.

                  ┌──▶ Baseline RecSys ──┐
│ (Relevance Only)
│ │
User Query ───────┤ ├──▶ CFE Agent (Compare & Nudge)
│ │
└──▶ Context RecSys ───┘
(Sustainable)

The Production Pipeline

This orchestration is handled efficiently in the TRACE backend via parallel agent execution. Here is a simplified view of the pipeline orchestration (backend/adk/assembly/pipeline.py):

import asyncio
from google.adk.agents import ParallelAgent, LlmAgent, SequentialAgent

async def create_pipeline():
# Load agents in parallel
ic_agent, rec_baseline_agent, ca_recsys_agent = await asyncio.gather(
get_ic_agent(),
get_recsys_agent(has_context=False), # Baseline: Relevance only
get_recsys_agent(has_context=True), # Context-aware: Sustainable
)

# Sequence intent classification followed by context-aware recommendation
sequential = SequentialAgent(
name="SequentialPipeline",
sub_agents=[ic_agent, ca_recsys_agent]
)

# Run the sustainable recommendation path and the baseline path concurrently
parallel = ParallelAgent(
name="ParallelRecAgents",
sub_agents=[sequential, rec_baseline_agent]
)

# Consolidate both recommendations into a single counterfactual output
cfe = LlmAgent(
name="CFEAgent",
model="gemini-2.5-flash",
instruction=ENV.get_template("cfe_combination.jinja2").render(),
output_schema=CFEOutput
)

return SequentialAgent(name="CRSPipeline", sub_agents=[parallel, cfe])

The design relies entirely on three foundational ADK concepts: SequentialAgent, ParallelAgent, and structured data validation via output_schema (powered by Pydantic to ensure strict JSON schemas).

Building a Lightweight Prototype

To demonstrate the structural logic of TRACE, we can build a minimal, local version that runs with a standard Gemini API key (without requiring external databases such as Firestore).

Project Setup

Acquire a free API key from Google AI Studio, then initialize your local environment:

mkdir trace-mini && cd trace-mini
python -m venv .venv && source .venv/bin/activate # On Windows: .venvScriptsactivate
pip install google-adk
mkdir trace_agent
echo "from . import agent" > trace_agent/__init__.py
printf "GOOGLE_GENAI_USE_VERTEXAI=FALSEnGOOGLE_API_KEY=your_key_heren" > .env

The Agent Implementation

Save the following agent configuration as trace_agent/agent.py:

from __future__ import annotations
from typing import Optional
from pydantic import BaseModel, Field
from google.adk.agents import Agent, ParallelAgent, SequentialAgent

MODEL: str = "gemini-2.5-flash"

class Recommendation(BaseModel):
recommendation: str = Field(..., description="List of cities")
explanation: str = Field(..., description="A single-sentence justification for the choice.")

class CFEOutput(BaseModel):
recommendation_shown: str
is_recommendation_sustainable: bool
explanation_shown: str
alternative_recommendation: Optional[str] = None
# Agent 1: Relevance-focused baseline
baseline = Agent(
model=MODEL,
name="baseline_recsys",
output_schema=Recommendation,
output_key="baseline_rec",
description="Recommends a city strictly matching user preferences.",
instruction=f"Recommend the best city for the user's request, ignoring sustainability. Available options: {CITIES}",
) # this is a sample instruction which can be updated on a need basis

# Agent 2: Sustainability-focused recommender
green = Agent(
model=MODEL,
name="context_recsys",
output_schema=Recommendation,
output_key="green_rec",
description="Recommends a city balancing relevance and environmental/social impact.",
instruction=f"Recommend the best city that fits the user's intent but is historically less crowded and lower-carbon. Available options: {CITIES}",
) # this is a sample instruction which can be updated on a need basis

# Agent 3: The CFE synthesis agent
cfe = Agent(
model=MODEL,
name="CFEAgent",
output_schema=CFEOutput,
description="Compares both recommendations and formulates a gentle behavioral nudge.",
instruction=(
"Relevance-only recommendation: {baseline_rec}nSustainable recommendation: {green_rec}nn"
"Present the sustainable option (recommendation_shown) if it meets the user's core intent. "
"Explain it warmly as a comparative alternative: 'what if you chose X instead of Y'-highlighting "
"a similar cultural experience with fewer crowds and lower carbon impact. "
"Populate alternative_recommendation with the baseline city. Do not lecture the user."
),
)# this is a sample instruction which can be updated on a need basis; for exact prompts used in the paper, check resources

# Root pipeline orchestration
root_agent = SequentialAgent(
name="CRSPipeline",
sub_agents=[
ParallelAgent(name="ParallelRecAgents", sub_agents=[baseline, green]),
cfe
],
)

Running the Agent

Launch the native ADK web playground to interact with your agents:

adk web

In the UI, select trace_agent and try entering a query like:

“A lively canal city with rich art, historical architecture, and great food — I am thinking Amsterdam.”

You will see both recommenders execute concurrently. The CFE agent will evaluate both and gently suggest Ghent or Bruges as the greener “what-if” alternative, keeping Amsterdam transparently available as the baseline option you originally weighed it against.

Why This Paradigm Succeeds

Forcing sustainable choices upon users by omitting popular options entirely is a paternalistic approach that often prompts users to look elsewhere. TRACE succeeds by validating the user’s initial preference and offering a direct, side-by-side comparison. By reducing the friction of finding greener alternatives, behavioral science and AI agents work together to make the sustainable choice the easy choice.

📢 Live Sandbox Migration Note

The original GCP-hosted demo referenced in the paper is currently offline due to a vulnerability in the Chainlit UI framework. To maintain zero-trust security, the entire system has been migrated to Hugging Face Spaces.

Out of the box, the space utilizes Gemma-3-27b to serve zero-shot, lightweight agent decisions. To experience the full, production-grade ADK parallel agent pipeline as detailed in the paper, you can securely input your own Gemini API Key directly into the UI. Read the full Hugging Face README for deeper implementation details.

Resources

If you like the article, please subscribe to my latest ones.
To get in touch, contact me on
LinkedIn or via ashmibanerjee.com.

GenAI usage disclosure: GenAI models were used to check for grammar inconsistencies in the blog and to refine the text for greater clarity. The authors accept full responsibility for the content presented in this blog.


[Tutorial] Building a Multi-Agent Travel Planner with Google ADK was originally published in Google Developer Experts on Medium, where people are continuing the conversation by highlighting and responding to this story.

Total
0
Shares
Leave a Reply

Your email address will not be published. Required fields are marked *

Previous Post

網紅程式設計師回應炒股一年虧130萬:當流量主體走進散戶敘事,虧損也成了內容

Next Post

Customizing D365 Sales — For Our Own Sales Team (Customer Zero) (3) The “Missed Tasks” Problem

Related Posts