ResQAI 2.0 — Project Story
Inspiration
On June 24, 2026, two earthquakes struck Venezuela less than 40 seconds apart — a magnitude 7.2 foreshock followed by a 7.5 mainshock centered off the coast near La Guaira. In La Guaira state, roughly 80% of buildings collapsed. By August, the confirmed death toll had passed 6,500, with over 226,000 injured and tens of thousands still missing. Rescue teams worked through collapsed structures with a health system already strained past capacity, trying to decide, building by building, who to reach first.
Two months later, on August 26, 2026, a glacier collapse on Langtang Lirung in the Himalayas triggered a catastrophic flash flood down the Trishuli River, tearing through settlements along the Nepal–China border. Existing flood early-warning systems in the region — built and tuned for ordinary monsoon flooding — didn't catch it, because nothing about this event looked like the floods they were designed to detect. Over 900 people died, thousands more went missing, and 32 bridges and nearly 25 miles of road were swept away, cutting off exactly the communities that needed help fastest.
Two disasters, on opposite sides of the world, months apart, with almost nothing in common structurally — an earthquake versus a glacier-triggered flood — except for the same underlying problem: in the first hours, responders don't have too little information, they have too much of it, arriving too fast, with no way to tell which report needs a team sent right now versus which can wait. Volunteers and rescue teams are always scarce relative to the number of people who need them. Every emergency response ends up being triage, whether anyone calls it that or not — and triage done by exhausted humans under pressure, purely in the order reports happen to arrive, is not the same as triage done by urgency.
That gap — between how many people need help and how fast anyone can correctly figure out who needs it most — is what ResQAI is built to close.
What it does
ResQAI is an AI-assisted disaster decision-support system with two sides: a mobile app a citizen uses to report their situation (with an optional photo), and a live responder dashboard coordinators use to see the same data, allocate scarce volunteers, and generate routes.
Underneath, six purpose-built modules do the actual work:
- Risk Assessment — scores each report using hazard proximity, disaster severity, personal vulnerability (age, mobility, medical needs), and terrain risk
- Damage Assessment — a real YOLOv8 model that reads an uploaded photo and estimates severity, rather than relying on a citizen's own self-report of "how bad is it"
- Shelter Recommendation — ranks nearby shelters by distance, occupancy, accessibility match, and medical capability
- Resource Allocation — solves volunteer-to-request assignment as a minimum-cost bipartite matching problem (the Hungarian algorithm), rather than simple nearest-available matching
- Evacuation Routing — A* pathfinding over a hazard-weighted grid, routing around blocked or dangerous areas
- Emergency Assistant — grounded, explainable guidance generated from the same numbers the other modules used, not a black box
The risk score is a weighted combination:
$$R = w_1 \cdot H_{proximity} + w_2 \cdot S_{disaster} + w_3 \cdot V_{personal} + w_4 \cdot T_{terrain}$$
and shelter ranking similarly balances distance against occupancy, accessibility, and medical fit rather than optimizing for any single factor alone.
How we built it
Backend: Python + FastAPI, deployed on Render, backed by Firebase Firestore as the live database. Mobile: React Native + Expo (Router, SDK 54), talking to the same live backend. The responder dashboard is a single dependency-free HTML file, so it can be opened anywhere with no build step. The damage model is a YOLOv8 network, trained on a synthetic dataset we generated in-house (400+ images with randomized building damage patterns, lighting, and color variation) after confirming — repeatedly, over the course of the project — that real training data and pretrained weights simply weren't reachable in our development environment.
We built this over a 20-day plan, working in a genuine two-person split: one of us on mobile/UI, one on backend/AI, integrating daily rather than in one pass at the end.
Challenges we ran into
A Firestore connection bug that looked like a data problem but wasn't. Midway through, citizen submissions started silently failing to save, even though the database was demonstrably reachable moments before and after. The actual bug: our connection code cached a failed attempt permanently, so one bad moment during a cold server start (entirely plausible on a free hosting tier that sleeps when idle) would quietly break every submission after it until the whole server restarted. We only caught it by building a diagnostic endpoint that exposed the real connection state directly, rather than trusting an indirect signal.
Our own calibration disagreed with us. We built 10 hand-crafted test scenarios with outcomes we reasoned through by hand before running any code — the same way a domain expert might sanity-check the system. Two of the ten disagreed with our own engine. The more serious one: a citizen who explicitly reported needing medical care could still get routed to a shelter with zero medical capability, simply because it was closer — the distance weight in our shelter-ranking formula was strong enough to overwhelm the entire medical-match bonus. We hadn't anticipated that failure mode until we deliberately went looking for disagreements instead of only confirming the cases we expected to pass.
Making sure a bad photo could never cost someone their submission. Stress-testing surfaced a case where a citizen's entire emergency report could be lost — not degraded, entirely lost, with a raw server error — if their photo upload had any issue at all. For a system whose whole purpose is making sure real emergencies get logged and triaged, that was the single most important bug we found and fixed in the project.
Genuine infrastructure constraints, documented rather than hidden. We don't have access to the real xBD disaster-imagery dataset or pretrained weights in our build environment — confirmed by repeatedly trying and hitting network restrictions specific to that sandbox, not by assumption. Rather than pretend the damage model is production-grade, we say clearly where it stands: trained and evaluated rigorously on synthetic data (0.974 mAP50 on held-out synthetic validation), genuinely strong at the task it was trained for, and honestly unproven yet on real disaster photos.
Accomplishments that we're proud of
Every piece was actually tested against real infrastructure, not just written and assumed to work. Not a small claim — over the course of this project we deployed to a real live backend, connected a real Firestore database, and repeatedly proved things worked by calling live endpoints and checking real responses, not just reading code and assuming it was correct. Several real bugs (see above) were only ever found this way.
A real, measurable calibration improvement. Our 10-scenario evaluation went from 8/10 matching expert-reasoned expectations to 10/10, after we identified exactly why the two failures happened and fixed the underlying weights — not by adjusting numbers until the tests passed, but by understanding the actual mechanism (e.g. distance overwhelming medical need) and correcting it specifically.
The damage model went from barely-functional to genuinely strong, within the constraints we had. Early on, our synthetic-trained model scored around 0.25 mAP50 — not much better than guessing. By expanding and diversifying the synthetic dataset (60 images to 400+, with real visual variety in color, lighting, and damage patterns) and retraining, we reached 0.974 mAP50 on held-out validation — verified through the actual production code path and the live API, not just an offline metric.
A demo scenario that tells a true story, not a staged one. The clearest moment in our demo — where naive first-come-first-served allocation leaves the single most critical citizen unserved while our optimizer correctly catches and prioritizes them — isn't scripted. We ran the real scoring and allocation engine against candidate scenarios until we found one where this happens, then kept the real numbers.
Two people, fully offline-resilient, end-to-end. A request submitted with no connectivity is saved locally and automatically retried once the connection returns, using a client-generated ID the backend recognizes — so a citizen's phone losing signal at the worst possible moment doesn't mean their emergency gets lost or duplicated.
What we learned
The most important lesson wasn't technical — it's that a system whose job is triage has to be tested by trying to break its judgment, not just by confirming it runs without crashing. Code that runs without error and code that makes the right call under pressure are two different claims, and only one of them matters when the thing being triaged is a real emergency. Every real bug we found in this project — the Firestore caching issue, the medical-routing miscalibration, the photo-upload crash — was invisible to "does it run," and only showed up once we went looking specifically for where the system's judgment could be wrong, not just broken.
What's next for ResQAI 2.0
The honest next step is closing the gap between synthetic and real: fine-tuning the damage model on real disaster imagery (xBD), validating the risk and shelter-scoring weights against real historical incident data rather than hand-reasoned scenarios, and replacing the current deterministic Emergency Assistant with a real grounded LLM call. None of that changes the architecture — it's the same six modules, made more accurate against ground truth rather than rebuilt.
Built With
- async-storage
- css
- expo-router
- expo.io
- fastapi
- firebase
- firestore
- git
- github
- google-maps
- html
- javascript
- pydantic
- python
- pytorch
- react-native
- render
- scipy
- typescript
- ultralytics
- yolov8
Log in or sign up for Devpost to join the conversation.