Skip to main content

Building an AI agent for campsite availability monitoring

·5 mins

If you’ve ever tried to book a campsite at a popular national park, you know the frustration. Sites disappear within seconds of becoming available. You refresh the page endlessly, hoping to catch that perfect spot.

But what if an AI agent could watch for availability changes, and recommend campsites before they vanish?

Why don’t we experiment with this idea?

Enter the AI agent approach #

Our agent doesn’t just detect changes - it analyzes them, understands their significance, and decides what action to take.

Here’s the core agent loop:

AI agent monitoring logic
def monitor_loop():
    while True:
        try:
            # 1. Fetch current availability from API
            current_state = fetch_availability(campground_id)

            # 2. Load previous state from JSON
            previous_state = load_state()

            # 3. Detect newly available sites
            new_availability = find_new_sites(current_state, previous_state)

            # 4. AI agent analyzes if availability is worth alerting about
            if new_availability:
                ai_analysis = agent.analyze_availability(new_availability, current_state)

                # 5. AI generates personalized email content
                if ai_analysis.should_alert:
                    email_content = agent.generate_alert_email(ai_analysis)
                    send_alert(email_content)

            # 6. Save current state
            save_state(current_state)

            # 7. Wait for next check
            time.sleep(check_interval)

        except Exception as e:
            log_error(e)
            time.sleep(300)  # Wait 5 minutes on error

The magic happens in steps 4 and 5. The agent doesn’t just check if sites are available - it analyzes whether they’re worth alerting about and generates personalized messages.

Building with Pydantic AI #

We chose Pydantic AI as our agent framework for its structured approach to LLM interactions. Instead of raw prompts and string parsing, we get type-safe data models and validated responses.

Agent setup with Pydantic AI
from pydantic_ai import Agent
from pydantic import BaseModel, Field

class AvailabilityAnalysis(BaseModel):
    should_alert: bool = Field(..., description="Whether this availability warrants an alert")
    urgency_level: str = Field(..., description="How quickly the user should act: low, medium, high")
    key_insights: List[str] = Field(..., description="Important observations about this availability")
    booking_advice: str = Field(..., description="Specific advice for booking these sites")

agent = Agent(
    "anthropic/claude-3-haiku",
    system_prompt="""You are a campsite availability expert who helps users secure hard-to-get camping spots.

    Analyze availability changes and determine if the change is significant enough to alert the user,
    how urgently they need to act, key insights about the availability (popular dates, site features),
    and specific booking advice.

    Consider factors like weekend vs weekday availability, holiday periods, number of consecutive
    nights available, and historical popularity of specific sites.
    """
)

This structured approach means the agent’s decisions are predictable and reliable. No parsing errors, no unexpected formats - just clean data we can act on.

Intelligent alert generation #

The second AI component generates the actual alert emails. Instead of templates with placeholders, each email is uniquely crafted based on the availability context.

Email generation with context
@agent.system_prompt
def email_generation_prompt():
    return """Generate concise, actionable email alerts for campsite availability.

    Lead with the most important information and explain why this availability matters.
    Provide specific booking recommendations with a clear call-to-action. Sound friendly
    and helpful, not robotic. Keep it under 5 sentences - users need to act fast.
    """

async def generate_alert_email(analysis: AvailabilityAnalysis, sites: List[Site]) -> EmailContent:
    result = await agent.run(
        f"Generate an email alert for these newly available sites: {sites}. "
        f"Analysis: {analysis.model_dump()}"
    )

    return EmailContent(
        subject=result.data.subject,
        body=result.data.body
    )

The agent crafts emails like: “Site 15A at Wawona just opened for Presidents Day weekend (Feb 14-16). This is one of the most requested weekends - I’d book within the next 30 minutes. Direct link: recreation.gov/camping/…”

No generic “New availability detected!” subject lines. Every alert is personalized and actionable.

The power of constraints #

We intentionally started with a tracer bullet - the thinnest possible slice that proves the concept. Just a single campground (Yosemite Wawona), a fixed 30-day window, email notifications only, and no user preferences or filtering.

These constraints let us focus on the core agent behavior. Does it make good decisions? Are the alerts helpful? Can it run reliably for days?

The architecture stays simple:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   CLI Interface │────│  Agent Core     │────│  Notification   │
│   (Typer)       │    │  (Pydantic AI)  │    │  System (Email) │
└─────────────────┘    └─────────────────┘    └─────────────────┘
                              │
                       ┌─────────────────┐
                       │   Data Layer    │
                       │ (JSON/SQLite)   │
                       └─────────────────┘

It just wakes up, thinks, and acts.

State management and reliability #

The agent needs to remember what it’s seen before. We use simple JSON files for state persistence:

State tracking structure
# Availability state structure
{
    "campground_id": "232447",
    "last_check": "2024-01-15T14:30:00Z",
    "sites": {
        "site_001": {
            "available_dates": ["2024-02-01", "2024-02-02"],
            "site_name": "Site 1A"
        }
    }
}

Demo #

Want to see the agent in action? Check out this terminal recording showing the full workflow:

What makes this approach powerful #

Traditional monitoring tools are reactive. They report what happened. Our agent is proactive - it understands what matters and helps you act on it.

The AI components work together seamlessly. The analysis agent filters out noise to focus on meaningful availability, while the email agent translates that analysis into clear, actionable advice. Both adapt to the specific context of each availability change.

The agent approach means less noise, better alerts, and ultimately more successful bookings.

Building on the foundation #

The tracer bullet proves the concept. Next iterations could add multiple campground monitoring with preference learning, SMS alerts for ultra-high priority availability, booking automation where permitted, historical pattern analysis for smarter predictions, and collaborative alerts for group camping.

But the core insight remains: treating availability monitoring as an agent problem - not just a data problem - creates a fundamentally better experience.

The code is structured to make these extensions natural. New campgrounds just need IDs. New notification channels plug into the existing alert system. The agent’s intelligence grows with better prompts and more context.

Sometimes the best solution isn’t the most complex one. It’s the one that understands the problem deeply and solves it intelligently. That’s what AI agents excel at - and why this approach works so well for the frustrating problem of campsite availability.