Academy
Updated on
Sep 3, 2025

Building an AI-Powered Trip Planner with Weather Integration: A Complete Guide

In this tutorial, learn to build trip planning system that combines the power of large language models, vector databases, and weather APIs

Building an AI-Powered Trip Planner with Weather Integration: A Complete Guide
Ready to ship your own agentic-AI solution in 30 days? Book a free strategy call now.

Planning the perfect trip involves juggling countless details, from destinations and budgets to weather forecasts and daily itineraries. What if you could streamline this entire process using AI? In this comprehensive guide, we'll walk you through building a sophisticated trip planning system that combines the power of large language models, vector databases, and weather APIs to create truly intelligent travel recommendations.

What We're Building

Our project combines two powerful components into a seamless travel planning experience:

  1. AI Trip Planner: Converts natural language requests into structured travel plans and stores them intelligently for future reference
  2. Weather Integration: Provides accurate weather forecasts and historical data for any destination and date range

The magic happens when these systems work together: you can plan your trip with AI and immediately get weather insights to make informed decisions about activities, packing, and timing.




The Architecture: How It All Fits Together

Before diving into the implementation, let's understand the key technologies powering our system:

  • Pydantic AI: Handles natural language processing and structured data extraction
  • Qdrant Vector Database: Stores trip plans as searchable vectors for intelligent retrieval
  • OpenAI GPT-4: Powers the conversational AI and planning logic
  • WeatherAPI: Provides real-time forecasts and historical weather data
  • Sentence Transformers: Creates semantic embeddings for trip storage and search

The workflow is elegantly simple: users describe their trip in natural language, AI structures this into detailed plans, everything gets saved to a vector database for future reference, and weather data enhances the planning with location-specific insights.




Part 1: The AI Trip Planner

Understanding the User Experience

The trip planner transforms casual requests into comprehensive travel plans. A user might simply type:

"I want a 5-day trip to Paris in October with 2 people, budget around $3000"

Within seconds, they receive a detailed itinerary, budget breakdown, and personalized tips, all generated by AI and saved for future reference.

How the Magic Happens

The system operates through a carefully orchestrated process:

Step 1: Capture User Intent: The AI first extracts structured information from natural language input, identifying key details like destination, dates, traveler count, and budget constraints.

Step 2: Generate Comprehensive Plans: Using the extracted details, the system creates detailed itineraries including day-by-day activities, budget allocations, and contextual recommendations.

Step 3: Vector Storage: Each trip plan is converted into vector embeddings and stored in vectorDB Qdrant, enabling semantic search and intelligent recommendations for future trips.

Step 4: Human-Readable Output: The structured data is transformed into beautifully formatted summaries that travelers can actually use.

Here's the complete implementation:

import os
import asyncio
import uuid
from qdrant_client.http import models
from dotenv import load_dotenv
from pydantic import BaseModel
from pydantic_ai import Agent
from qdrant_client import QdrantClient
from qdrant_client.models import VectorParams, Distance
from sentence_transformers import SentenceTransformer
import json

load_dotenv()

user_input_example = input("Enter your trip request: ")
client = QdrantClient(host="localhost", port=6333)
model = SentenceTransformer("all-MiniLM-L6-v2")

if not client.collection_exists("trips"):
    client.create_collection(
        collection_name="trips",
        vectors_config=models.VectorParams(size=384, distance=models.Distance.COSINE),
    )

def get_embedding(text: str):
    return model.encode(text).tolist()


class TripBooking(BaseModel):
    destination: str
    date: str
    travelers: int
    budget: str | None = None
    notes: str | None = None
    duration_days: int | None = None


class ItineraryDay(BaseModel):
    day: int
    title: str
    activities: list[str]


class TripPlan(BaseModel):
    destination: str
    date: dict  # {"start": str, "end": str}
    travelers: int
    budget: int |   None
    duration_days: int
    overview: str
    itinerary: list[ItineraryDay]
    budget_breakdown: dict  # {"flights": int, "accommodation": int, "attractions": int, "dining": int, "total": int}
    tips: list[str]


def save_trip(trip: TripBooking, plan: TripPlan):
    client.upsert(
        collection_name="trips",
        points=[
            models.PointStruct(
                id=str(uuid.uuid4()),
                vector=get_embedding(trip.destination),
                payload={
                    "destination": trip.destination,
                    "date": trip.date,
                    "travelers": trip.travelers,
                    "budget": trip.budget,
                    "notes": trip.notes,
                    "duration_days": trip.duration_days,
                    "plan": plan.model_dump()  #  Save clean structured plan
                }
            )
        ]
    )
    print(f"Trip for {trip.destination} saved in Qdrant")

booking_agent = Agent("openai:gpt-4o-mini")

user_input = user_input_example

async def main():
    # Step 1: Extract trip details
    structured_prompt = f"""
    Extract trip details from the following request and return **only valid JSON** 
    matching this schema:
    {{
      "destination": str,
      "date": str,
      "travelers": int,
      "budget": str,
      "notes": str,
      "duration_days": int
    }}

    Request: "{user_input}"
    """

    result = await booking_agent.run(structured_prompt)
    raw_text = result.output.strip()

    try:
        trip = TripBooking.model_validate_json(raw_text)
    except Exception:
        json_str = raw_text[raw_text.find("{"): raw_text.rfind("}")+1]
        trip = TripBooking.model_validate_json(json_str)

    # Step 2: Generate structured plan
    itinerary_prompt = f"""
    Create a structured JSON itinerary with this schema:
    {{
      "destination": str,
      "date": {{"start": str, "end": str}},
      "travelers": int,
      "budget": int,
      "duration_days": int,
      "overview": str,
      "itinerary": [{{"day": int, "title": str, "activities": [str]}}],
      "budget_breakdown": {{"flights": int, "accommodation": int, "attractions": int, "dining": int, "total": int}},
      "tips": [str]
    }}

    Details:
    Destination: {trip.destination}
    Date: {trip.date}
    Travelers: {trip.travelers}
    Budget: {trip.budget}
    Duration: {trip.duration_days or "N/A"} days
    Notes: {trip.notes or "N/A"}
    """

    response = await booking_agent.run(itinerary_prompt)
    raw_plan = response.output.strip()

    try:
        plan = TripPlan.model_validate_json(raw_plan)
    except Exception:
        json_str = raw_plan[raw_plan.find("{"): raw_plan.rfind("}")+1]
        plan = TripPlan.model_validate_json(json_str)
        
    save_trip(trip, plan)

    # print("\n Trip saved in Qdrant!")
    # print(json.dumps(plan.model_dump(), indent=2))

    def trip_plan_to_text(plan: TripPlan) -> str:
        summary = f"Trip to {plan.destination}\n"
        summary += f"Dates: {plan.date['start']} to {plan.date['end']}\n"
        summary += f"Travelers: {plan.travelers}\n"
        summary += f"Duration: {plan.duration_days} days\n"
        summary += f"Budget: {plan.budget or 'Not provided'}\n\n"
        summary += "Overview:\n"
        summary += f"{plan.overview}\n\n"

        summary += "Itinerary:\n"
        for day in plan.itinerary:
            summary += f"Day {day.day}: {day.title}\n"
            for act in day.activities:
                summary += f"  - {act}\n"
            summary += "\n"

        summary += "Budget Breakdown:\n"
        for key, value in plan.budget_breakdown.items():
            summary += f"  {key.capitalize()}: {value if value is not None else 'N/A'}\n"
        summary += "\n"

        summary += "Tips:\n"
        for tip in plan.tips:
            summary += f"  - {tip}\n"

        return summary

    text_summary = trip_plan_to_text(plan)
    print(text_summary)

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

The Power of Structured Data

One of the key innovations in this system is how it handles data transformation. By using Pydantic models, we ensure that every piece of information is properly validated and structured. This not only prevents errors but also makes the data highly portable and searchable.

The trip plan model captures everything a traveler needs: destinations, dates, budgets, detailed itineraries, and practical tips. When stored as vectors in Qdrant, these plans become part of an intelligent knowledge base that can suggest similar trips or help with future planning.




Part 2: Weather Intelligence Integration

Why Weather Matters in Trip Planning

Weather can make or break a travel experience. Our weather integration goes beyond simple forecasts—it provides historical context, helps with packing decisions, and can even suggest optimal activity timing based on conditions.

Smart Weather Queries

The weather system interprets natural language requests and converts them into precise API calls. Whether someone asks, "What will the weather be like in London tomorrow?" or, "Show me historical weather for Tokyo last December," the system understands and responds appropriately.

Here's the complete weather integration:

from pydantic_ai import Agent
from pydantic import BaseModel
from dotenv import load_dotenv
import requests, os, json
from typing import List

load_dotenv()

WEATHER_API_KEY = os.getenv("WEATER_API_KEY")

class WeatherQuery(BaseModel):
    location: str
    dates: List[str]

agent = Agent("openai:gpt-4o-mini")

class WeatherReport(BaseModel):
    location: str
    date: str  
    temperature_celsius: float
    condition: str
    humidity: int
    wind_kph: float

def fetch_weather(location: str, target_dates: List[str]) -> List[WeatherReport]:
    reports = []

    # Forecast endpoint
    forecast_url = "http://api.weatherapi.com/v1/forecast.json"
    forecast_params = {"key": WEATHER_API_KEY, "q": location, "days": 10}
    forecast_resp = requests.get(forecast_url, params=forecast_params).json()

    # Check if API returned error
    if "error" in forecast_resp:
        print("API error:", forecast_resp["error"]["message"])
        return []

    forecast_days = forecast_resp["forecast"]["forecastday"]
    loc_name = forecast_resp["location"]["name"]

    for date in target_dates:
        report_data = None

        # Check if date is in forecast
        for day in forecast_days:
            if day["date"] == date:
                report_data = day
                break

        # If date not in forecast, use history endpoint
        if not report_data:
            history_url = "http://api.weatherapi.com/v1/history.json"
            history_params = {"key": WEATHER_API_KEY, "q": location, "dt": date}
            history_resp = requests.get(history_url, params=history_params).json()

            if "error" in history_resp:
                print(f"API error for date {date}: {history_resp['error']['message']}")
                continue

            report_data = history_resp["forecast"]["forecastday"][0]

        # Build structured WeatherReport
        reports.append(
            WeatherReport(
                location=loc_name,
                date=report_data["date"],
                temperature_celsius=report_data["day"]["avgtemp_c"],
                condition=report_data["day"]["condition"]["text"],
                humidity=report_data["day"]["avghumidity"],
                wind_kph=report_data["day"]["maxwind_kph"],
            )
        )

    return reports


nlq = input("What do you know about weather: ")
strict_prompt = f"""
Extract the weather query from the user's request.
Return ONLY JSON, nothing else, no text, no markdown, not even a single work extra.
most importantly convert the date into YYYY-MM-DD format.
if the user provides a date as today or tomorrow or yesterday convert it to the actual date.
JSON schema:
{json.dumps(WeatherQuery.model_json_schema(), indent=2)}

User request: "{nlq}"
"""
result = agent.run_sync(strict_prompt)
parsed_json = result.output.strip()  
query = WeatherQuery.model_validate_json(parsed_json)
reports = fetch_weather(query.location, query.dates)

# for report in reports:
#     print(report.model_dump_json(indent=2))


def json_report_to_nlq(reports: List[WeatherReport]) -> str:
    if not reports:
        return "No weather data available."

    summary = f"Weather report for {reports[0].location}:\n"
    for report in reports:
        summary += (
            f"Date: {report.date}\n"
            f"Temperature: {report.temperature_celsius}°C\n"
            f"Condition: {report.condition}\n"
            f"Humidity: {report.humidity}%\n"
            f"Wind: {report.wind_kph} kph\n\n"
        )
    return summary
print(json_report_to_nlq(reports))

Intelligent Date Handling

One of the sophisticated features of our weather system is its natural language date processing. The AI automatically converts relative dates like "tomorrow," "next week," or "last month" into precise ISO format dates that the weather API can understand. This seamless translation makes the system incredibly user-friendly.

Forecast vs. Historical Data

The system intelligently determines whether to fetch forecast data for future dates or historical data for past dates. This dual capability means users can both plan upcoming trips and analyze weather patterns from previous travel periods to make better decisions.




Setting Up Your Development Environment

Getting this powerful system running on your machine is straightforward. Here's everything you need:

Step 1: Clone the Repository

git clone https://github.com/piyushghughu-superteams-dotcom/trip_planner_weather_report_ai_agent.git
cd trip_planner_weather_report_ai_agent

Step 2: Launch Qdrant Database

Qdrant runs beautifully in Docker, making setup effortless:

docker run -p 6333:6333 qdrant/qdrant

Step 3: Create Python Environment

python -m venv venv
source venv/bin/activate

Step 4: Install Dependencies

pip install -r requirements.txt

Step 5: Configure API Keys

Create a .env file with your credentials:‍

OPENAI_API_KEY=your_openai_key
WEATHER_API_KEY=your_weatherapi_key

Step 6: Run the Applications

For trip planning:

python backend/data_loading.py

For weather reports:

python backend/weather_report.py

Real-World Applications and Use Cases

This system isn't just a technical demonstration—it solves real problems for various user groups:

  • Travel Agencies can use it to quickly generate initial trip proposals for clients, then refine them based on weather insights and preferences.
  • Individual Travelers benefit from having a personal AI travel assistant that remembers their preferences and provides weather-informed recommendations.
  • Corporate Travel Managers can streamline business trip planning while ensuring travelers are prepared for destination weather conditions.
  • Travel Bloggers and Influencers can use the historical weather data to plan content around seasonal destinations and optimal visiting times.

Technical Deep Dive: Why These Choices Matter

Vector Database Selection

Qdrant was chosen for its excellent performance with embedding similarity searches. When users search for "trips like my Paris adventure," the system can find semantically similar trips even if the exact words don't match. 

Pydantic for Data Validation

Using Pydantic ensures that all data flowing through our system is properly structured and validated. This prevents the kind of data inconsistencies that can plague AI applications and makes debugging much easier.

Async Processing

The trip planner uses asyncio for handling AI API calls efficiently. This becomes crucial when scaling to handle multiple concurrent users or when integrating additional data sources.

Future Enhancement Opportunities

This foundation opens up numerous possibilities for expansion:

  • Social Features: Allow users to share trip plans and discover popular destinations through the vector database.
  • Budget Optimization: Integrate flight and hotel pricing APIs to suggest cost-saving alternatives while maintaining trip quality.
  • Real-time Updates: Add monitoring for weather changes and automatic trip adjustment suggestions.
  • Mobile Integration: Build mobile apps that can access the stored trip data and provide location-aware recommendations.
  • Machine Learning: Use the accumulated trip data to train models that can predict user preferences and suggest personalized destinations.

Key Takeaways

Building AI-powered applications requires thoughtful architecture that balances user experience with technical capabilities. Our trip planner demonstrates several important principles:

  1. Structure First: Using Pydantic models ensures data consistency and makes the system easier to extend and maintain.
  2. Smart Storage: Vector databases like Qdrant enable semantic search capabilities that traditional databases can't match.
  3. API Integration: Combining multiple data sources (AI, weather, potentially flights/hotels) creates more valuable user experiences than any single source alone.
  4. Natural Language Processing: Modern AI can bridge the gap between how people naturally describe their needs and how systems process structured data.

The complete source code for this project is available on GitHub, ready for you to explore, modify, and extend for your own applications.

Whether you're building travel applications, exploring AI integration patterns, or learning about vector databases, this project provides a solid foundation and demonstrates practical approaches to common challenges in modern software development.

To learn more, speak to us.

Authors

We use cookies to ensure the best experience on our website. Learn more