Skip to content
Learn Agentic AI14 min read0 views

Build a Travel Planning Agent: Destination Research, Itinerary Building, and Booking Assistance

Create a complete travel planning AI agent that researches destinations, builds day-by-day itineraries, optimizes budgets, and provides booking links — your personal AI travel advisor built with Python.

Why Build a Travel Planning Agent

Planning a trip involves dozens of micro-decisions: choosing destinations, finding flights, booking hotels, scheduling activities, and managing budgets. Each step requires cross-referencing multiple websites and mentally juggling constraints like time, money, and personal preferences. A travel planning agent handles this complexity through a single conversational interface, producing structured itineraries with real cost estimates.

This tutorial builds an agent with destination research, day-by-day itinerary generation, budget optimization, and booking link generation.

Project Setup

mkdir travel-agent && cd travel-agent
python -m venv venv && source venv/bin/activate
pip install openai-agents pydantic
mkdir -p src
touch src/__init__.py src/destinations.py src/itinerary.py
touch src/budget.py src/agent.py

Step 1: Destination Database

# src/destinations.py
from pydantic import BaseModel

class Activity(BaseModel):
    name: str
    category: str  # culture, nature, food, adventure
    duration_hours: float
    cost_usd: float
    description: str

class Destination(BaseModel):
    city: str
    country: str
    best_months: list[str]
    avg_daily_cost: float  # food + transport
    avg_hotel_night: float
    activities: list[Activity]
    tips: list[str]

DESTINATIONS: dict[str, Destination] = {
    "tokyo": Destination(
        city="Tokyo", country="Japan",
        best_months=["March", "April", "October", "November"],
        avg_daily_cost=80.0, avg_hotel_night=120.0,
        activities=[
            Activity(name="Senso-ji Temple", category="culture",
                     duration_hours=2, cost_usd=0,
                     description="Ancient Buddhist temple in Asakusa"),
            Activity(name="Tsukiji Outer Market", category="food",
                     duration_hours=3, cost_usd=30,
                     description="Fresh sushi and street food"),
            Activity(name="Meiji Shrine", category="culture",
                     duration_hours=1.5, cost_usd=0,
                     description="Serene Shinto shrine in Harajuku"),
            Activity(name="Akihabara Tour", category="culture",
                     duration_hours=3, cost_usd=20,
                     description="Electronics and anime district"),
            Activity(name="Mount Takao Hike", category="nature",
                     duration_hours=5, cost_usd=10,
                     description="Scenic hike with city views"),
            Activity(name="TeamLab Borderless", category="culture",
                     duration_hours=2.5, cost_usd=35,
                     description="Immersive digital art museum"),
        ],
        tips=[
            "Get a Suica card for all public transit.",
            "Convenience stores have excellent cheap meals.",
            "Learn basic phrases: sumimasen, arigatou.",
        ],
    ),
    "paris": Destination(
        city="Paris", country="France",
        best_months=["April", "May", "September", "October"],
        avg_daily_cost=70.0, avg_hotel_night=150.0,
        activities=[
            Activity(name="Louvre Museum", category="culture",
                     duration_hours=4, cost_usd=20,
                     description="World's largest art museum"),
            Activity(name="Eiffel Tower", category="culture",
                     duration_hours=2, cost_usd=30,
                     description="Iconic landmark with city views"),
            Activity(name="Seine River Cruise", category="nature",
                     duration_hours=1.5, cost_usd=18,
                     description="Scenic boat ride through the city"),
            Activity(name="Montmartre Walk", category="culture",
                     duration_hours=3, cost_usd=0,
                     description="Artist quarter and Sacre-Coeur"),
            Activity(name="French Cooking Class", category="food",
                     duration_hours=3, cost_usd=85,
                     description="Learn to make classic French dishes"),
        ],
        tips=[
            "Buy museum passes for multi-day visits.",
            "Metro is fastest for getting around.",
            "Many restaurants close between lunch and dinner.",
        ],
    ),
}

def search_destination(query: str) -> Destination | None:
    return DESTINATIONS.get(query.lower().strip())

def list_destinations() -> list[str]:
    return [d.city for d in DESTINATIONS.values()]

Step 2: Itinerary Builder

The builder packs activities into days based on available hours and user preferences.

# src/itinerary.py
from src.destinations import Destination, Activity

class DayPlan:
    def __init__(self, day_num: int):
        self.day_num = day_num
        self.activities: list[Activity] = []
        self.hours_used: float = 0.0
        self.cost: float = 0.0

    def can_fit(self, activity: Activity, max_hours: float = 8) -> bool:
        return self.hours_used + activity.duration_hours <= max_hours

    def add(self, activity: Activity):
        self.activities.append(activity)
        self.hours_used += activity.duration_hours
        self.cost += activity.cost_usd

def build_itinerary(
    destination: Destination,
    days: int,
    preferred_categories: list[str] | None = None,
    max_hours_per_day: float = 8.0,
) -> list[DayPlan]:
    activities = list(destination.activities)
    if preferred_categories:
        activities.sort(
            key=lambda a: (
                0 if a.category in preferred_categories else 1
            )
        )

    day_plans = [DayPlan(i + 1) for i in range(days)]
    for activity in activities:
        for plan in day_plans:
            if plan.can_fit(activity, max_hours_per_day):
                plan.add(activity)
                break
    return day_plans

def format_itinerary(
    destination: Destination, plans: list[DayPlan],
) -> str:
    lines = [f"=== {destination.city} Itinerary ===\n"]
    total_cost = 0.0
    for plan in plans:
        lines.append(f"Day {plan.day_num} ({plan.hours_used}h):")
        for act in plan.activities:
            cost_str = "Free" if not act.cost_usd else f"{act.cost_usd:.0f} USD"
            lines.append(
                f"  - {act.name} ({act.duration_hours}h, {cost_str})"
            )
            lines.append(f"    {act.description}")
        lines.append(f"  Day cost: {plan.cost:.2f} USD\n")
        total_cost += plan.cost
    lines.append(f"Total activity cost: {total_cost:.2f} USD")
    hotel_total = destination.avg_hotel_night * len(plans)
    daily_total = destination.avg_daily_cost * len(plans)
    grand = total_cost + hotel_total + daily_total
    lines.append(f"Estimated hotel ({len(plans)} nights): {hotel_total:.2f} USD")
    lines.append(f"Estimated food/transport: {daily_total:.2f} USD")
    lines.append(f"Estimated trip total: {grand:.2f} USD")
    lines.append(f"\nTips:")
    for tip in destination.tips:
        lines.append(f"  - {tip}")
    return "\n".join(lines)

Step 3: Build the Agent

# src/agent.py
import asyncio
from agents import Agent, Runner, function_tool
from src.destinations import search_destination, list_destinations
from src.itinerary import build_itinerary, format_itinerary

@function_tool
def get_destination_info(city: str) -> str:
    """Research a travel destination."""
    dest = search_destination(city)
    if not dest:
        available = ", ".join(list_destinations())
        return f"Destination not found. Available: {available}"
    lines = [
        f"{dest.city}, {dest.country}",
        f"Best months: {', '.join(dest.best_months)}",
        f"Avg daily cost: ${dest.avg_daily_cost}",
        f"Avg hotel/night: ${dest.avg_hotel_night}",
        f"Activities: {len(dest.activities)} available",
    ]
    return "\n".join(lines)

@function_tool
def create_itinerary(
    city: str,
    days: int = 3,
    preferred_categories: str = "",
) -> str:
    """Build a day-by-day itinerary for a destination."""
    dest = search_destination(city)
    if not dest:
        return "Destination not found."
    prefs = (
        [c.strip() for c in preferred_categories.split(",")]
        if preferred_categories else None
    )
    plans = build_itinerary(dest, days, prefs)
    return format_itinerary(dest, plans)

travel_agent = Agent(
    name="Travel Planner",
    instructions="""You are an expert travel planning agent.
Help users research destinations and build itineraries.
Always include cost estimates and practical tips.
If the user has a budget, optimize the itinerary to fit.
Suggest the best travel months when relevant.""",
    tools=[get_destination_info, create_itinerary],
)

async def main():
    result = await Runner.run(
        travel_agent,
        "Plan a 3-day trip to Tokyo focused on food and culture. "
        "What will it cost approximately?",
    )
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

Run it with python -m src.agent and the agent will research Tokyo, build a three-day itinerary prioritizing food and culture activities, and provide a full cost breakdown.

Extending the System

Flight search. Add a tool that queries a flight API (or mock) with origin, destination, and dates. The agent can incorporate flight costs into the total budget estimate.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

Accommodation options. Expand the destination model with hotel tiers (budget, mid-range, luxury) and let the agent pick based on the user's stated budget.

Multi-city trips. Support itineraries spanning multiple cities by chaining destination lookups and inserting travel days between them.

FAQ

How do I connect this to real booking APIs?

Replace the static DESTINATIONS dictionary with calls to APIs like Amadeus (flights), Booking.com (hotels), or Google Places (activities). Each API returns structured data that maps to the existing Pydantic models. The itinerary builder and agent tools work unchanged because they depend on the model interfaces, not the data source.

Can the agent handle group travel with different preferences?

Yes. Extend the create_itinerary tool to accept multiple preference sets and implement a scoring algorithm that balances activities across all group members' interests. The agent can negotiate compromises by selecting activities that score well across multiple categories.

How would I add weather-aware recommendations?

Add a get_weather_forecast tool that queries a weather API for the user's travel dates. Pass the forecast to the itinerary builder so it can prioritize indoor activities on rainy days and outdoor activities on clear days. The agent can proactively adjust the itinerary based on weather conditions.


#TravelPlanning #AIAgent #Python #ItineraryBuilder #OpenAIAgentsSDK #AgenticAI #LearnAI #AIEngineering

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.