By Sagar Shankaran, Founder of CallSphere
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.
Key takeaways
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.
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
# 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()]
The builder packs activities into days based on available hours and user preferences.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
INPUT(["User intent"])
PARSE["Parse plus<br/>classify"]
PLAN["Plan and tool<br/>selection"]
AGENT["Agent loop<br/>LLM plus tools"]
GUARD{"Guardrails<br/>and policy"}
EXEC["Execute and<br/>verify result"]
OBS[("Trace and metrics")]
OUT(["Outcome plus<br/>next action"])
INPUT --> PARSE --> PLAN --> AGENT --> GUARD
GUARD -->|Pass| EXEC --> OUT
GUARD -->|Fail| AGENT
AGENT --> OBS
style AGENT fill:#4f46e5,stroke:#4338ca,color:#fff
style GUARD fill:#f59e0b,stroke:#d97706,color:#1f2937
style OBS fill:#ede9fe,stroke:#7c3aed,color:#1e1b4b
style OUT fill:#059669,stroke:#047857,color:#fff
# 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)
# 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.
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.
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.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
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.
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.
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

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
How to build a safety eval pipeline that runs known jailbreak corpora, prompt-injection attacks, and tool-misuse scenarios on every release — and gates merges on it.
OpenAI's parallel function calling can cut latency in half — or burn money on dependent calls. The architecture, code, and an eval that proves the win.
Honest principal-engineer comparison of the OpenAI Agents SDK and the legacy Assistants API, with a migration checklist and eval-parity strategy so you don't ship regressions.
Langgraph multi-agent supervisor handoffs docs: the supervisor pattern in LangGraph for coordinating specialist agents, with full code, an eval pipeline that scores routing accuracy, and the failure modes to watch for.
How to stream tokens, tool-call deltas, and intermediate steps from an agent — with code for both the OpenAI Agents SDK and LangChain — and the gotchas that bite in production.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco