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
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.