Skip to content
Learn Agentic AI11 min read0 views

AI Agent for Parks and Recreation: Program Registration, Facility Booking, and Event Info

Build an AI agent for municipal parks and recreation departments that handles program catalog search, class registration, facility reservations, and seasonal event information for community members.

Parks and Recreation in the Digital Age

Municipal parks and recreation departments run hundreds of programs — youth swimming lessons, adult pottery classes, senior fitness programs, summer camps, sports leagues, and community events. They manage facility rentals for pavilions, athletic fields, community rooms, and pools. The catalog changes seasonally, programs fill up fast, and residents want to know what is available, what it costs, and whether there are spots left.

Traditional registration systems involve browsing a PDF catalog or navigating a clunky web portal. An AI agent can provide a conversational interface: "What swim classes are available for my 6-year-old on Tuesdays?" gets an immediate, filtered answer instead of a 20-minute search through a 50-page catalog.

Modeling the Program Catalog

Parks and rec programs have rich metadata: age ranges, schedules, locations, instructors, skill levels, fees, and availability. We model this comprehensively so the agent can filter effectively.

from dataclasses import dataclass, field
from datetime import date, time
from enum import Enum


class AgeGroup(Enum):
    TODDLER = "toddler"        # 2-4
    YOUTH = "youth"            # 5-12
    TEEN = "teen"              # 13-17
    ADULT = "adult"            # 18-54
    SENIOR = "senior"          # 55+
    ALL_AGES = "all_ages"


class Season(Enum):
    SPRING = "spring"          # Mar-May
    SUMMER = "summer"          # Jun-Aug
    FALL = "fall"              # Sep-Nov
    WINTER = "winter"          # Dec-Feb


@dataclass
class Program:
    program_id: str
    name: str
    category: str            # swimming, arts, fitness, sports, camps
    description: str
    age_group: AgeGroup
    min_age: int
    max_age: int
    skill_level: str         # beginner, intermediate, advanced, all
    instructor: str
    location: str
    days_of_week: list[str]  # ["Tuesday", "Thursday"]
    start_time: time
    end_time: time
    start_date: date
    end_date: date
    season: Season
    fee: float
    resident_fee: float      # discounted rate for city residents
    max_enrollment: int
    current_enrollment: int
    waitlist_count: int = 0
    materials_included: bool = True
    prerequisites: list[str] = field(default_factory=list)


PROGRAM_CATALOG: list[Program] = []  # Populated from database

Program Search and Filtering

The search engine is the core of the agent. It must handle natural language queries like "Saturday morning art classes for my 8-year-old" and translate them into structured filters.

from openai import OpenAI
import json

client = OpenAI()

SEARCH_EXTRACTION_PROMPT = """Extract search filters from the user's query
about parks and recreation programs.

Return JSON with any of these fields (omit fields not mentioned):
- "category": string (swimming, arts, fitness, sports, camps, dance, music)
- "age": integer (child's age)
- "days": list of day names
- "time_preference": "morning" | "afternoon" | "evening"
- "skill_level": "beginner" | "intermediate" | "advanced"
- "season": "spring" | "summer" | "fall" | "winter"
- "max_fee": float
- "keyword": string (free text search term)
"""


def extract_search_filters(user_query: str) -> dict:
    """Use LLM to extract structured filters from natural language."""
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": SEARCH_EXTRACTION_PROMPT},
            {"role": "user", "content": user_query},
        ],
        response_format={"type": "json_object"},
        temperature=0.0,
    )
    return json.loads(response.choices[0].message.content)


def search_programs(
    filters: dict,
    catalog: list[Program] = None,
) -> list[Program]:
    """Search the program catalog using extracted filters."""
    results = catalog or PROGRAM_CATALOG

    if "category" in filters:
        cat = filters["category"].lower()
        results = [p for p in results if cat in p.category.lower()]

    if "age" in filters:
        age = filters["age"]
        results = [p for p in results if p.min_age <= age <= p.max_age]

    if "days" in filters:
        query_days = {d.lower() for d in filters["days"]}
        results = [
            p for p in results
            if any(d.lower() in query_days for d in p.days_of_week)
        ]

    if "time_preference" in filters:
        pref = filters["time_preference"]
        if pref == "morning":
            results = [p for p in results if p.start_time.hour < 12]
        elif pref == "afternoon":
            results = [p for p in results if 12 <= p.start_time.hour < 17]
        elif pref == "evening":
            results = [p for p in results if p.start_time.hour >= 17]

    if "skill_level" in filters:
        level = filters["skill_level"].lower()
        results = [
            p for p in results
            if p.skill_level.lower() in (level, "all")
        ]

    if "max_fee" in filters:
        max_fee = filters["max_fee"]
        results = [p for p in results if p.resident_fee <= max_fee]

    # Sort by availability (programs with open spots first)
    results.sort(key=lambda p: (
        p.current_enrollment >= p.max_enrollment,  # full programs last
        p.start_date,
    ))

    return results

Registration Flow

Once a resident finds a program, the agent handles the registration process including eligibility checks, fee calculation, and waitlist management.

See AI Voice Agents Handle Real Calls

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

from datetime import datetime
import uuid


@dataclass
class Registration:
    registration_id: str
    program_id: str
    participant_name: str
    participant_age: int
    guardian_name: str | None = None
    guardian_email: str = ""
    guardian_phone: str = ""
    fee_charged: float = 0.0
    is_resident: bool = True
    status: str = "confirmed"  # confirmed, waitlisted, cancelled
    registered_at: datetime = field(default_factory=datetime.utcnow)
    waitlist_position: int | None = None


def register_for_program(
    program: Program,
    participant_name: str,
    participant_age: int,
    is_resident: bool = True,
    guardian_name: str | None = None,
) -> Registration:
    """Register a participant for a program."""

    # Age eligibility check
    if not (program.min_age <= participant_age <= program.max_age):
        raise ValueError(
            f"Participant age {participant_age} is outside the "
            f"{program.min_age}-{program.max_age} age range"
        )

    # Determine fee
    fee = program.resident_fee if is_resident else program.fee

    # Check availability
    if program.current_enrollment >= program.max_enrollment:
        # Add to waitlist
        program.waitlist_count += 1
        return Registration(
            registration_id=str(uuid.uuid4())[:8],
            program_id=program.program_id,
            participant_name=participant_name,
            participant_age=participant_age,
            guardian_name=guardian_name,
            fee_charged=0,  # no charge until off waitlist
            is_resident=is_resident,
            status="waitlisted",
            waitlist_position=program.waitlist_count,
        )

    # Confirm registration
    program.current_enrollment += 1

    return Registration(
        registration_id=str(uuid.uuid4())[:8],
        program_id=program.program_id,
        participant_name=participant_name,
        participant_age=participant_age,
        guardian_name=guardian_name,
        fee_charged=fee,
        is_resident=is_resident,
        status="confirmed",
    )

Facility Booking System

Beyond programs, parks departments rent facilities. The agent handles availability checking and reservation creation.

@dataclass
class Facility:
    facility_id: str
    name: str
    facility_type: str  # pavilion, field, pool, room, gym
    location: str
    capacity: int
    hourly_rate: float
    resident_hourly_rate: float
    amenities: list[str] = field(default_factory=list)
    available_hours: dict[str, str] = field(default_factory=dict)


@dataclass
class Reservation:
    reservation_id: str
    facility_id: str
    date: date
    start_time: time
    end_time: time
    reserved_by: str
    purpose: str
    total_cost: float
    status: str = "confirmed"


def check_facility_availability(
    facility: Facility,
    requested_date: date,
    start: time,
    end: time,
    existing_reservations: list[Reservation] = None,
) -> dict:
    """Check if a facility is available for the requested time."""
    conflicts = []
    for res in existing_reservations or []:
        if res.facility_id != facility.facility_id:
            continue
        if res.date != requested_date:
            continue
        if res.status == "cancelled":
            continue
        # Check time overlap
        if start < res.end_time and end > res.start_time:
            conflicts.append({
                "existing_start": res.start_time.isoformat(),
                "existing_end": res.end_time.isoformat(),
            })

    hours = (
        datetime.combine(requested_date, end)
        - datetime.combine(requested_date, start)
    ).seconds / 3600

    return {
        "facility": facility.name,
        "date": requested_date.isoformat(),
        "requested_time": f"{start.isoformat()} - {end.isoformat()}",
        "available": len(conflicts) == 0,
        "conflicts": conflicts,
        "estimated_cost": round(facility.resident_hourly_rate * hours, 2),
        "hours": hours,
    }

FAQ

How does the agent handle scholarship or fee reduction requests for low-income families?

Most parks departments offer fee assistance programs. The agent checks whether the resident has an active fee reduction on file and automatically applies the discounted rate during registration. If no reduction is on file, the agent explains the assistance program, lists the eligibility criteria (typically based on income or enrollment in programs like SNAP or free school lunch), and provides the application form. The agent never asks for proof of income directly — it directs the resident to the fee assistance application process, which is handled by department staff with proper privacy controls.

What happens when a program is full and a resident wants to be notified of openings?

The agent adds the resident to the program's waitlist and provides their position number. When a spot opens (due to a cancellation or enrollment increase), the system sends an automated notification to the next person on the waitlist. They have 48 hours to confirm their registration before the spot moves to the next person. The agent can also suggest alternative programs with similar content, age range, and schedule that still have openings.

Can the agent recommend programs based on a child's interests and past enrollments?

Yes. The agent builds a participation profile from enrollment history — if a child has taken three swim classes and a diving class, the agent recognizes an interest in aquatic programs. When the parent asks "what should we sign up for this summer," the agent suggests the next skill level in swimming, introduces new aquatic programs like water polo or lifeguard training (if age-appropriate), and also surfaces programs in related categories the family has not tried. Recommendations are transparent: "Based on Sarah's swim history, she may be ready for Intermediate Swim (Tue/Thu 4 PM, $45)."


#GovernmentAI #ParksAndRecreation #ProgramRegistration #FacilityBooking #CommunityServices #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.