AI Agent for Emergency Management: Disaster Information, Shelter Locations, and Updates
Learn how to build an AI agent for emergency management agencies that distributes disaster alerts, maps shelter locations, coordinates resource information, and provides real-time updates to affected citizens.
When Every Second of Communication Matters
During natural disasters — hurricanes, wildfires, floods, earthquakes — emergency management agencies face a communications crisis of their own. Hundreds of thousands of people need answers simultaneously: "Is my area under evacuation?" "Where is the nearest shelter?" "Is the shelter pet-friendly?" "When will power be restored?" "Where can I get drinking water?"
911 and emergency hotlines are overwhelmed. Social media fills with rumors. Official websites crash under traffic spikes. An AI agent can serve as a scalable, always-available information channel that provides accurate, location-specific answers to these questions. It does not coordinate the actual emergency response — it handles the citizen-facing communication layer so that emergency managers can focus on operations.
Designing for Disaster Conditions
Emergency management agents must work under constraints that normal government agents do not face. Internet connectivity may be intermittent. Power may be out. People are stressed, frightened, and may not speak English. The agent must be designed for degraded conditions from day one.
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
class DisasterType(Enum):
HURRICANE = "hurricane"
WILDFIRE = "wildfire"
FLOOD = "flood"
EARTHQUAKE = "earthquake"
TORNADO = "tornado"
WINTER_STORM = "winter_storm"
HAZMAT = "hazardous_materials"
TSUNAMI = "tsunami"
class AlertLevel(Enum):
ADVISORY = "advisory" # be aware
WATCH = "watch" # be prepared
WARNING = "warning" # take action
EMERGENCY = "emergency" # take action immediately
@dataclass
class DisasterEvent:
event_id: str
disaster_type: DisasterType
name: str # e.g., "Hurricane Maria"
alert_level: AlertLevel
affected_zones: list[str] # zip codes, zone IDs
start_time: datetime
summary: str
instructions: list[str]
evacuation_zones: list[str] = field(default_factory=list)
curfew_hours: str | None = None
updated_at: datetime = field(default_factory=datetime.utcnow)
@dataclass
class EmergencyUpdate:
update_id: str
event_id: str
timestamp: datetime
message: str
category: str # shelter, power, water, roads, rescue
affected_zones: list[str]
source: str # "County EOC", "National Weather Service"
Shelter Management System
During evacuations, shelter information is the most critical data the agent provides. People need to know where to go, whether the shelter has capacity, and whether it accommodates their specific needs (pets, medical equipment, accessibility).
@dataclass
class Shelter:
shelter_id: str
name: str
address: str
latitude: float
longitude: float
capacity: int
current_occupancy: int
status: str # open, full, closed, opening_soon
amenities: list[str] = field(default_factory=list)
pet_friendly: bool = False
ada_accessible: bool = True
medical_staff: bool = False
accepts_medical_equipment: bool = False
generator_powered: bool = False
last_updated: datetime = field(default_factory=datetime.utcnow)
SHELTER_REGISTRY: list[Shelter] = []
def find_nearest_shelters(
user_lat: float,
user_lon: float,
needs_pet_friendly: bool = False,
needs_medical: bool = False,
needs_accessible: bool = False,
max_results: int = 5,
) -> list[dict]:
"""Find nearest open shelters matching the user's needs."""
from math import radians, sin, cos, sqrt, atan2
def haversine(lat1, lon1, lat2, lon2):
R = 3959
dlat = radians(lat2 - lat1)
dlon = radians(lon2 - lon1)
a = sin(dlat/2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon/2)**2
return R * 2 * atan2(sqrt(a), sqrt(1-a))
candidates = []
for shelter in SHELTER_REGISTRY:
if shelter.status not in ("open", "opening_soon"):
continue
if needs_pet_friendly and not shelter.pet_friendly:
continue
if needs_medical and not shelter.medical_staff:
continue
if needs_accessible and not shelter.ada_accessible:
continue
distance = haversine(user_lat, user_lon, shelter.latitude, shelter.longitude)
spots_remaining = shelter.capacity - shelter.current_occupancy
candidates.append({
"name": shelter.name,
"address": shelter.address,
"distance_miles": round(distance, 1),
"status": shelter.status,
"spots_remaining": max(0, spots_remaining),
"capacity_pct": round(shelter.current_occupancy / shelter.capacity * 100),
"pet_friendly": shelter.pet_friendly,
"ada_accessible": shelter.ada_accessible,
"has_medical_staff": shelter.medical_staff,
"generator_powered": shelter.generator_powered,
"amenities": shelter.amenities,
"last_updated": shelter.last_updated.isoformat(),
})
candidates.sort(key=lambda x: x["distance_miles"])
return candidates[:max_results]
Alert Distribution Engine
The alert engine determines which information to push to which citizens based on their location and the disaster's affected zones.
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 timedelta
class AlertDistributor:
"""Distribute disaster alerts to affected citizens."""
def __init__(self):
self.active_events: dict[str, DisasterEvent] = {}
self.updates: list[EmergencyUpdate] = []
def get_alerts_for_location(self, zipcode: str) -> list[dict]:
"""Get all active alerts affecting a specific location."""
relevant = []
for event in self.active_events.values():
if zipcode in event.affected_zones or "all" in event.affected_zones:
is_evacuation = zipcode in event.evacuation_zones
relevant.append({
"event_name": event.name,
"type": event.disaster_type.value,
"alert_level": event.alert_level.value,
"summary": event.summary,
"instructions": event.instructions,
"evacuation_required": is_evacuation,
"curfew": event.curfew_hours,
"last_updated": event.updated_at.isoformat(),
})
# Sort by severity (emergency first)
level_order = {
"emergency": 0, "warning": 1, "watch": 2, "advisory": 3,
}
relevant.sort(key=lambda x: level_order.get(x["alert_level"], 4))
return relevant
def get_recent_updates(
self, event_id: str, since_hours: int = 6
) -> list[dict]:
"""Get recent updates for a specific disaster event."""
cutoff = datetime.utcnow() - timedelta(hours=since_hours)
recent = [
u for u in self.updates
if u.event_id == event_id and u.timestamp >= cutoff
]
recent.sort(key=lambda u: u.timestamp, reverse=True)
return [
{
"time": u.timestamp.strftime("%I:%M %p"),
"category": u.category,
"message": u.message,
"source": u.source,
}
for u in recent
]
Resource Coordination Information
Beyond shelters, citizens need to know about water distribution points, food banks, fuel availability, and medical facilities that are operational.
@dataclass
class ResourcePoint:
resource_id: str
resource_type: str # water, food, fuel, medical, charging_station
name: str
address: str
latitude: float
longitude: float
hours: str
status: str # open, closed, limited
notes: str = ""
last_verified: datetime = field(default_factory=datetime.utcnow)
def find_resources(
user_lat: float,
user_lon: float,
resource_type: str,
resources: list[ResourcePoint] = None,
max_distance: float = 15.0,
) -> list[dict]:
"""Find nearby resource distribution points."""
from math import radians, sin, cos, sqrt, atan2
def haversine(lat1, lon1, lat2, lon2):
R = 3959
dlat = radians(lat2 - lat1)
dlon = radians(lon2 - lon1)
a = sin(dlat/2)**2 + cos(radians(lat1)) * cos(radians(lat2)) * sin(dlon/2)**2
return R * 2 * atan2(sqrt(a), sqrt(1-a))
results = []
for r in resources or []:
if r.resource_type != resource_type:
continue
if r.status == "closed":
continue
distance = haversine(user_lat, user_lon, r.latitude, r.longitude)
if distance > max_distance:
continue
results.append({
"name": r.name,
"address": r.address,
"distance_miles": round(distance, 1),
"hours": r.hours,
"status": r.status,
"notes": r.notes,
"last_verified": r.last_verified.strftime("%b %d, %I:%M %p"),
})
results.sort(key=lambda x: x["distance_miles"])
return results[:10]
The Emergency Agent with Crisis Communication Principles
The agent's language during emergencies must follow established crisis communication principles: be first, be right, be credible.
EMERGENCY_AGENT_PROMPT = """You are an emergency information agent for the
county emergency management agency.
CRISIS COMMUNICATION RULES:
1. Be SPECIFIC and ACTIONABLE. Not "seek shelter" but "go to Lincoln High
School shelter at 1234 Oak Street, capacity available, pet-friendly."
2. Lead with the most critical information. Evacuation orders first,
then shelter locations, then resource points.
3. Include timestamps on all information. "As of 3:00 PM" matters during
rapidly evolving situations.
4. Acknowledge uncertainty. If you do not have current shelter occupancy
data, say so rather than guessing.
5. Never minimize danger. If an evacuation order is active, communicate
urgency clearly.
6. Provide information in the user's language if possible.
7. For life-threatening emergencies, always direct to 911 first.
8. Include accessibility information for shelters proactively.
You have access to these tools:
- get_alerts(zipcode): Get active disaster alerts for a location
- find_shelters(lat, lon, needs): Find nearby open shelters
- find_resources(lat, lon, type): Find water, food, fuel, medical points
- get_updates(event_id): Get latest situational updates
Always end emergency responses with the local emergency hotline number.
"""
The language design is critical. During Hurricane Harvey, official communications that said "GET OUT OR DIE" were more effective than polite suggestions because they conveyed urgency without ambiguity. The agent should calibrate its tone to the alert level — advisory messages are informational, but emergency-level messages should convey urgency clearly.
FAQ
How does the agent function when internet connectivity is degraded during a disaster?
The agent is designed with a fallback architecture. The primary mode is full LLM-powered conversation over the internet. If connectivity is limited, the agent falls back to a keyword-matching mode that runs on cached data locally — it can still answer "nearest shelter" and "am I in an evacuation zone" using pre-downloaded shelter data and zone maps. For complete outage scenarios, the emergency management agency deploys SMS-based querying where citizens text a keyword (SHELTER, WATER, POWER) to a short code and receive automated responses from a lightweight backend that does not require internet access for the citizen.
How do you keep shelter occupancy data current during a rapidly evolving disaster?
Shelter occupancy is updated through multiple channels. Staff at each shelter report check-ins through a mobile app or phone call to the Emergency Operations Center (EOC). The EOC dashboard updates the central database, which the agent queries in real-time. Updates flow every 15-30 minutes during active operations. The agent always displays the "last updated" timestamp so citizens can assess how current the information is. If a shelter has not been updated in over 2 hours, the agent flags this: "Occupancy data for Lincoln High School was last updated at 2:00 PM — call the shelter directly at (555) 123-4567 for current availability."
Can the agent help citizens report damage or request assistance during recovery?
Yes. After the immediate emergency phase, the agent shifts to recovery mode. It helps citizens report property damage to the local emergency management agency, guides them through FEMA Individual Assistance applications, provides information about SBA disaster loans, and connects them with volunteer organizations (Red Cross, local relief agencies). It also tracks the status of utility restoration by neighborhood, road closures and reopenings, and boil-water advisories. The agent's role evolves with the disaster lifecycle: from preparedness (before), to response (during), to recovery (after).
#GovernmentAI #EmergencyManagement #DisasterResponse #ShelterMapping #CrisisCommunication #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.