We live in a transformative era. As of October 2025, the conversation around AI has decisively shifted from “what can it do?” to “what can we build with it?” Today, we’re moving beyond simple chatbots to create sophisticated, autonomous AI agents that can reason, plan, and execute complex tasks.
Please request access with valid justification for the following:
To see it in action, click on Aura ADK Demo Video (4K).
To check code, click on Aura Agent Code.
This is the story of Aura, a full-stack AI Travel Concierge for Indian cities. But more than that, it’s a blueprint. It’s a deep dive into the architecture, customer journeys, and deployment path for a modern AI agent, built with Google’s Agent Development Kit (ADK), Gemini 2.0-flash, and BigQuery.

Whether you’re a developer, a product manager, or just an AI enthusiast, this guide will walk you through the entire lifecycle—from initial concept to a globally scalable application.
Prerequisites: Setting up your agent workshop
Before we can build, we need to set up our workshop. This guide assumes you’re on a macOS or Linux-based system.
1. Google Cloud setup:
-
Create a Google Cloud Project: If you don’t have one already, create a new project in the Google Cloud Console.
-
Enable APIs: In your new project, enable the BigQuery API and the Vertex AI API. This allows our agent to use these services.
-
Install the
gcloudCLI: Follow the instructions to install the Google Cloud SDK. -
Authenticate: Log in with your Google account by running:
gcloud auth application-default login
2. Local development environment:
-
Python: Ensure you have Python 3.10 or higher installed.
-
Create a Virtual Environment: This is crucial for managing dependencies.
python3 -m venv venv source venv/bin/activate -
Install dependencies: We’ll need a few Python libraries for Aura.
pip install google-cloud-bigquery pandas Faker requests pandas-gbq
3. Configure your Gemini API key:
-
Get Your Key: Obtain your Gemini API key from the Google AI for Developers website.
-
Create a
.envfile: This file will securely store your API key. Inside the/tourism_assistantdirectory (which you will create inside theadkfolder), create a new file named.env. -
Verify the file: Sometimes this file can be hidden. You can check that it exists using the command
ls -la tourism_assistant. You should see.envin the list. -
Add your key: Open the
.envfile and add the following line, replacing"your-key-here"with your actual key:GOOGLE_API_KEY="your-key-here"
With your workshop set up, you’re ready to build Aura!
The customer journey: Aura in the wild
An agent’s success is defined by the value it provides to its users. Before we look at a single line of code, let’s imagine how different people might interact with Aura.
Journey 1: Anjali, the meticulous planner
Anjali is planning her first trip to Rajasthan. She has a clear goal and follows a logical path.
- Itinerary first: She asks, “Plan a 3-day trip to Jaipur.”
- Aura’s response: Aura executes its “Master Workflow,” calling multiple tools internally to generate a detailed, day-by-day itinerary complete with fun facts and travel times between sights.
- Next logical step - Hotels: Impressed, Anjali follows Aura’s proactive suggestion and asks, “Show me some mid-range hotels.” Aura queries its BigQuery database and returns a list of top-rated, budget-appropriate hotels.
- Final step - Travel: Anjali decides to travel to Delhi next. She asks, “What are my travel options to get to Delhi?” Aura dynamically generates a list of plausible flights, trains, and buses.
Outcome: Anjali has a complete travel plan, from itinerary to accommodation to transit, all from a single, seamless conversation.
Journey 2: Rohan, the spontaneous explorer
Rohan is already in Bangalore and is looking for something to do tonight. His journey is non-linear.
- A quick tip: He asks, “What’s a good place for dinner in Bangalore?”
- Aura’s response: Aura doesn’t need to build a full itinerary. It recognizes the specific request, calls its
find_top_rated_restauranttool, and immediately suggests “Mavalli Tiffin Rooms (MTR)” for authentic South Indian cuisine. - Sparking an idea: Intrigued, Rohan asks, “Okay, can you plan a full day around that for tomorrow?” This now triggers Aura’s main workflow, building a 1-day itinerary for Bangalore.
Outcome: Aura adapts to the user’s context, providing both quick, single-purpose answers and full, detailed plans as needed.
The Core architecture: ADK, MCP, and the Agent Engine
To build a sophisticated agent, it’s crucial to understand the roles of three distinct concepts:
-
The Agent Development Kit (ADK): This is your developer’s toolkit. It’s the framework you use on your local machine to write, test, and debug your agent. It provides the
AgentandFunctionToolclasses, the local web server (adk web), and the command-line interface (adk run). Think of it as the IDE and testing suite for your agent. -
The Model Context Protocol (MCP) Server: This is an architectural pattern. An MCP server’s job is to expose tools (especially complex ones like database queries) to an agent over a network. While our Aura agent defines tools directly in Python for simplicity, more complex systems might use a dedicated MCP server to serve tools. The ADK’s web server implicitly acts as this server in our setup.
-
The Agent Engine: This is your production deployment environment. While the ADK is your local engine, a true Agent Engine is a scalable, managed platform like Google Cloud Run or Vertex AI Agent Engine. Its job is to run your agent, handle incoming user traffic, and scale automatically.
In short: you build with the ADK, you can serve tools with an MCP Server, and you deploy to an Agent Engine.
The anatomy of Aura: Code & Prompt
Now let’s look at the specific code and prompt that the ADK uses to build its context.
Code: Aura’s library of superpowers
This is where the magic happens. We’ve equipped Aura with a suite of specialized Python functions. Let’s dive into the full code for each one.
-
The Blueprint Builder:
generate_travel_itinerary
This foundational tool queries BigQuery for attractions and creates a machine-readable “skeleton” plan for the agent to enrich.def generate_travel_itinerary(city: str, num_days: int) -> str: """Generates a structured, day-by-day skeleton of top-rated attractions for a trip.""" query = f""" SELECT attraction.name, attraction.type, attraction.latitude, attraction.longitude FROM `{BIGQUERY_TABLE_ID}`, UNNEST(attractions) AS attraction WHERE LOWER(destination_name) = LOWER(@city) AND attraction.type NOT IN ('Restaurant', 'Cafe', 'Market') ORDER BY attraction.rating DESC LIMIT {num_days * 2} """ params = [bigquery.ScalarQueryParameter("city", "STRING", city)] try: df = db_client.query(query, job_config=bigquery.QueryJobConfig(query_parameters=params)).to_dataframe() if df.empty: return f"Error: Could not find attractions for {city}." itinerary_str = f"SKELETON_ITINERARY_FOR:{city}:{num_days}_DAYS\n" for i in range(num_days): day = i + 1 itinerary_str += f"DAY:{day}\n" if (i*2) < len(df): itinerary_str += f"MORNING:{df.iloc[i*2]['name']}|{df.iloc[i*2]['type']}|{df.iloc[i*2]['latitude']}|{df.iloc[i*2]['longitude']}\n" if (i*2+1) < len(df): itinerary_str += f"AFTERNOON:{df.iloc[i*2+1]['name']}|{df.iloc[i*2+1]['type']}|{df.iloc[i*2+1]['latitude']}|{df.iloc[i*2+1]['longitude']}\n" return itinerary_str except Exception as e: return f"Error: Failed to generate itinerary skeleton: {e}" -
The Hotelier:
find_hotels
This tool provides data-grounded hotel suggestions from our BigQuery database based on budget.def find_hotels(city: str, budget_level: str = 'mid-range') -> str: """Finds top-rated hotels in a city from the database based on a budget level.""" price_map = {'budget': 150, 'mid-range': 400, 'luxury': 1000} max_price = price_map.get(budget_level, 400) query = f""" SELECT h.name, h.star_rating, h.rating, h.price_per_night_usd FROM `{BIGQUERY_TABLE_ID}`, UNNEST(accommodations) AS h WHERE LOWER(destination_name) = LOWER(@city) AND h.price_per_night_usd <= @max_price ORDER BY h.rating DESC LIMIT 3 """ params = [ bigquery.ScalarQueryParameter("city", "STRING", city), bigquery.ScalarQueryParameter("max_price", "INT64", max_price) ] try: df = db_client.query(query, job_config=bigquery.QueryJobConfig(query_parameters=params)).to_dataframe() if df.empty: return "HOTELS_NOT_FOUND" return df.to_markdown(index=False) except Exception as e: return f"HOTELS_ERROR: {e}" -
The Logistics Officer:
suggest_transportation
This tool dynamically generates plausible, fake travel options, showcasing how an agent can be creative and responsible.def suggest_transportation(origin_city: str, destination_city: str) -> str: """Generates realistic but fake travel suggestions for flights, trains, and buses.""" if origin_city.lower() == destination_city.lower(): return "TRANSPORT_ERROR: Origin and destination cities cannot be the same." response = f"### Travel Options from {origin_city} to {destination_city}\n\n" airlines = {"Delhi": "IndiGo", "Jaipur": "Air India", "Bangalore": "Vistara", "Goa": "SpiceJet"} flight_num = random.randint(100, 999) price_flight = random.randint(2500, 7000) response += f"✈️ **Flights**\n- **{airlines.get(origin_city, 'IndiGo')} 6E-{flight_num}**: Approx. ₹{price_flight}, Travel Time: ~2 hours\n" trains = {"Delhi-Jaipur": "Ajmer Shatabdi (12015)", "Delhi-Bangalore": "Karnataka Express (12628)"} train_key = f"{origin_city}-{destination_city}" train_name = trains.get(train_key, f"Duronto Express ({random.randint(12200, 12299)})") price_train = random.randint(800, 3000) response += f"\n🚂 **Trains**\n- **{train_name}**: Approx. ₹{price_train} (AC Chair Car), Travel Time: 5-8 hours\n" response += "\n_Disclaimer: These are sample suggestions. Please check with official providers for real-time availability and prices._" return response -
The Storyteller:
get_fun_fact
This tool enriches the itinerary by calling the free DuckDuckGo API for a fun fact.def get_fun_fact(topic: str) -> str: """Finds an interesting fun fact about a topic using the DuckDuckGo API.""" url = f"[https://api.duckduckgo.com/?q=](https://api.duckduckgo.com/?q=){topic}&format=json" try: response = requests.get(url, headers={'User-Agent': 'AuraTravelConcierge/1.0'}) response.raise_for_status() data = response.json() abstract = data.get("AbstractText") if abstract: return f"FUN_FACT_FOR_{topic.replace(' ', '_')}: {abstract.split('.')[0]}." return f"FUN_FACT_NOT_FOUND" except Exception: return f"FUN_FACT_ERROR" -
The Local Guide:
translate_text
This tool makes the agent proactively helpful by translating phrases using the free MyMemory API.def translate_text(text_to_translate: str, target_language: str) -> str: """Translates English text to a specified target language using a free, key-less API.""" lang_map = {"hindi": "hi", "kannada": "kn"} target_code = lang_map.get(target_language.lower()) if not target_code: return "TRANSLATION_ERROR: Unsupported language. Please choose from Hindi or Kannada." url = f"[https://api.mymemory.translated.net/get?q=](https://api.mymemory.translated.net/get?q=){requests.utils.quote(text_to_translate)}&langpair=en|{target_code}" try: response = requests.get(url, headers={'User-Agent': 'AuraTravelConcierge/1.0'}) response.raise_for_status() data = response.json() if data['responseStatus'] == 200: return f"TRANSLATION_SUCCESS:{text_to_translate} -> {data['responseData']['translatedText']}" else: return f"TRANSLATION_ERROR: {data['responseDetails']}" except Exception as e: return f"TRANSLATION_ERROR: Failed to connect to translation service: {e}"
Prompt: The agent’s soul and “Master Workflow”
The instruction prompt is where we define Aura’s personality and its complex, multi-step reasoning process. The full code block below shows how we combine all components using the ADK’s Agent class.
# The complete agent definition in agent.py
root_agent = Agent(
name="Aura_AI_Travel_Concierge_India",
model="gemini-2.0-flash",
description="A sophisticated AI travel concierge for India that crafts story-rich itineraries and provides helpful language translations.",
instruction="""You are Aura, an elite AI Travel Concierge specializing in Indian travel. Your mission is to deliver a complete, beautifully crafted travel plan by following a strict, multi-step internal monologue.
**Aura's Master Workflow: The path to a perfect trip**
1. **Step 1: Build the Skeleton (Internal Tool Call):** Call `generate_travel_itinerary`.
2. **Step 2: Enrich the Blueprint (Multiple Internal Tool Calls):** Call `calculate_travel_time`, `find_top_rated_restaurant`, and `get_fun_fact`.
3. **Step 3: Synthesize and Present the Masterpiece:** Combine all information into a single response.
4. **Step 4: Proactive Assistance:** Proactively offer to find hotels, then suggest transportation, and finally offer to translate a phrase.
**Your Core Directive:**
Follow the **"Build -> Enrich -> Synthesize -> Assist (Hotels -> Transport -> Translate)"** workflow religiously. Guide the user seamlessly through the entire planning process.
""",
tools=[
FunctionTool(func=generate_travel_itinerary),
FunctionTool(func=get_fun_fact),
FunctionTool(func=calculate_travel_time),
FunctionTool(func=find_top_rated_restaurant),
FunctionTool(func=find_hotels),
FunctionTool(func=suggest_transportation),
FunctionTool(func=translate_text),
],
)
How to run the code
With your prerequisites configured and your agent.py file created, running Aura is incredibly simple thanks to the ADK.
-
Ensure you are in the correct directory: Your terminal should be in the root folder, i.e., directory having agent.py.
-
Activate your virtual environment:
source venv/bin/activate -
Run Aura in one of two modes:
-
Interactive Command-Line Mode:
This is perfect for quick tests and development, as you’ll see the agent’s thoughts and tool calls directly in the terminal../venv/bin/adk run tourism_assistant -
Sleek Web Interface:
To showcase your agent with a shareable UI, use thewebcommand../venv/bin/adk web .
-
The ADK will start a local server (usually on `http://127.0.0.1:8000`) and provide a URL to a beautiful chat interface right in your browser.
The road ahead: From prototype to production
We’ve built a powerful prototype. Now, how do we take it to the next level and deploy it for the world to use?
-
Evolving the Agent Engine:
-
Model: Upgrade to Gemini 2.5 Pro. With its massive 1 million token context window and native multi-modality, Aura could understand user-uploaded images (“Find me a hotel that looks like this picture of a haveli!”) or even analyze short videos of a destination.
-
Code:
-
Give Aura a Memory: Integrate Firestore to store user preferences across conversations.
-
Connect to the Real World: Use Vertex AI Search to ground Aura in real-time data from trusted travel websites or APIs, moving beyond our static database.
-
-
Prompt: As we add new tools, we’ll refine the “Master Workflow” in the prompt, teaching Aura how and when to use its new capabilities.
-
Deploying with Cloud Run & Docker: The ADK’s local web server is great for development, but for a production application, we need a scalable, robust solution. Enter Docker and Cloud Run
-
Containerize with Docker: We create a Dockerfile. This is a recipe that packages our entire agent—the Python code, the ADK, and all its dependencies—into a lightweight, portable container.
-
Deploy with Cloud Run: Cloud Run is a fully managed, serverless platform. We simply give it our Docker container, and it handles everything else, from providing a public HTTPS endpoint to autoscaling.
-
Conclusion: You’ve built an agent!
Congratulations! You’ve just walked through the complete lifecycle of a modern AI agent. You’ve seen how to combine the reasoning power of Gemini, the real-world capabilities of Python code, and the guiding intelligence of a detailed prompt.
Most importantly, you’ve seen how the Agent Development Kit acts as the crucial development framework and local Model Context Protocol server that makes this all possible, providing the structure to build, test, and ultimately deploy sophisticated AI experiences to a production Agent Engine like Cloud Run. The journey from a simple idea to a scalable, intelligent agent is more accessible than ever. The tools are here, the path is clear, and the possibilities are endless.
Let’s keep the conversation going! Share your thoughts, questions, and ideas in the comments.
Note: Should you have any concerns or queries about this post or my implementation, please feel free to connect with me on LinkedIn! Thanks!
References and further reading
Ready to start your own agent-building journey? Here are the official resources and foundational research to get you started.
Official documentation
-
Agent Development Kit (ADK) on GitHub: The official repository for the ADK.
-
ADK Quickstart: The official starting point for the ADK.
-
ADK Samples: Sample agents under ADK Repo.
-
Google AI for Developers: Your central hub for Gemini API documentation, tutorials, and examples.
-
BigQuery Documentation: Learn more about how to manage and query data at scale.
-
Cloud Run Documentation: A complete guide to deploying and managing serverless applications.
Foundational research papers
-
ReAct: Synergizing Reasoning and Acting in Language Models (Yao, et al., 2022): A groundbreaking paper that introduced the “Reason, Act” paradigm, which is fundamental to how modern agents like Aura decide which tool to use.
-
Toolformer: Language Models Can Teach Themselves to Use Tools (Schick, et al., 2023): This paper demonstrates how LLMs can learn to use external tools through simple APIs, a core concept in our agent’s design.
-
Chain of Thought Prompting Elicits Reasoning in Large Language Models (Wei, et al., 2022): While not strictly about agents, this paper is essential for understanding how to prompt models to perform complex, multi-step reasoning, which is the basis for Aura’s “Master Workflow.”
Demos
We can’t wait to see what you build. Share your creations and ask questions in the Google Cloud Community. Happy coding!






