EWARS — Epidemic Early Warning & Response System
Inspiration
In November 2002, a cluster of eight patients in Foshan, China were admitted with severe atypical pneumonia. Pharmacies nearby logged an unusual spike in antipyretic sales. A handful of nurses fell ill after treating those patients. Each signal, viewed in isolation, looked like noise. Together, they were the opening chapter of the SARS outbreak that would kill 774 people across 37 countries over the next eight months.
The part that haunted me: WHO did not declare SARS a global health threat until March 15, 2003 — 119 days after that first cluster.
Manual surveillance systems failed not because the data didn't exist, but because no single human could hold all of it — clinic logs, pharmacy sales, lab results, environmental conditions — in their head at once, weight each signal against historical baselines, and recognise the pattern before it became a catastrophe.
That gap is what EWARS is built to close. Not to replace epidemiologists, but to be the system that never sleeps, never misses a spike, and hands a human a structured, evidence-grounded brief — fast enough to matter.
What it does
EWARS is a six-agent AI pipeline deployed on Google ADK and Gemini Enterprise Agent Platform that continuously monitors heterogeneous health signals — clinic visits, pharmacy OTC sales, social symptom reports, and laboratory results — and synthesises them into tiered epidemic threat alerts 48–72 hours faster than manual reporting systems.
The pipeline runs every six hours across five monitored regions covering roughly 3.1 billion people. When the composite threat score crosses a threshold, it generates a WHO-style Situation Report, stores it in MongoDB Atlas, and flags it for epidemiologist review — with status always set to draft until a qualified human signs off.
How I built it
Architecture
The system is a SequentialAgent orchestrating five stages, with a ParallelAgent at stage two:
Signal Collector
│
┌───┴────────────┐
Syndromic Analyst Geo-Cluster Agent ← parallel
└───┬────────────┘
│
Environmental Agent
│
Risk Escalation Agent
│
Alert Synthesis Agent
Each agent is an ADK LlmAgent with a single responsibility and a scoped MongoDB MCP toolset — read-only for analyst agents, read-write for agents that persist results. The ParallelAgent at stage two cuts pipeline latency roughly in half by running syndromic and geospatial analysis concurrently.
The scoring system
The Risk Escalation Agent applies a weighted composite score across three signal dimensions:
$$ S_{composite} = 0.40 \cdot S_{syndromic} + 0.30 \cdot S_{geo} + 0.20 \cdot S_{env} + 0.10 \cdot B_{concordance} $$
where B_concordance is a source concordance bonus:
$$ B_{concordance} = \begin{cases} 100 & \text{if } \geq 3 \text{ source types concordant} \ 50 & \text{if exactly 2 source types} \ 0 & \text{if single source} \end{cases} $$
Tier classification follows:
$$ \text{Tier} = \begin{cases} \text{EMERGENCY} & S_{composite} \geq 75 \ \text{ALERT} & 50 \leq S_{composite} < 75 \ \text{WATCH} & S_{composite} < 50 \end{cases} $$
Three hard overrides apply regardless of score: any haemorrhagic fever syndrome, R > 3.0, or doubling time < 3 days automatically escalates to EMERGENCY.
Anomaly detection
The Syndromic Analyst compares current weekly case counts against three-year historical baselines using standard deviation thresholds:
$$ \text{Alert level} = \begin{cases} \text{EPIDEMIC} & C_{current} \geq \mu + 3\sigma \ \text{ALERT} & C_{current} \geq \mu + 2\sigma \ \text{ELEVATED} & C_{current} \geq \mu + \sigma \ \text{NORMAL} & C_{current} < \mu + \sigma \end{cases} $$
When sufficient case count data exists across at least three time points, doubling time is estimated from an exponential growth fit:
$$ T_2 = \frac{7 \ln 2}{\ln \left( \dfrac{C_{week_n}}{C_{week_{n-1}}} \right)} $$
A simplified compartmental R estimate follows:
$$ \hat{R} = 2^{\, T_{infectious} \, / \, T_2} $$
where T_infectious = 5 days is the default assumed infectious period for respiratory pathogens.
MongoDB as the system's memory
Every agent reads from and writes to MongoDB Atlas through the official @mongodb-js/mongodb-mcp-server via ADK's McpToolset. The database is not just storage — it is the shared reasoning substrate that lets six independent agents collaborate without direct coupling.
Key MongoDB features the system relies on:
- Time-series collections for the
signalscollection, enabling efficient range queries over years of surveillance data - 2dsphere geospatial indexes powering
$geoNearaggregation pipelines in the Geo-Cluster Agent for proximity-based cluster detection - Compound indexes on
(region_id, syndrome, week_of_year)for sub-millisecond baseline lookups during anomaly detection - MongoDB Atlas MCP Server as the agent-database interface — agents describe what they need in natural language and the MCP server translates that into typed MongoDB operations
Stack
| Layer | Technology |
|---|---|
| Agent framework | Google ADK (LlmAgent, SequentialAgent, ParallelAgent) |
| Models | Gemini 2.5 Flash (ingestion, geo, env) · Gemini 2.5 Pro (analysis, risk, synthesis) |
| Database | MongoDB Atlas M10 · MCP Server |
| Deployment | Google Cloud Run · Cloud Scheduler |
| External APIs | Open-Meteo (weather, free) · NASA SEDAC (population density) |
| Frontend | Next.js 14 · MapLibre GL JS · CARTO Dark tiles |
What I learned
Multi-agent systems fail at the seams, not the centres. Each individual agent worked perfectly in isolation. The failures always happened in the handoffs — when the Geo-Cluster Agent needed to receive structured JSON from the Syndromic Analyst, but got natural language prose. ADK's SequentialAgent passes text context between stages, which means every agent's output format becomes a contract that downstream agents must parse. I learned to treat agent output schemas with the same rigour as API contracts.
Prompt engineering is software engineering. The difference between an agent that asks the user "what date is week 1?" and one that autonomously queries MongoDB with {"demo_tag": "sars_replay", "demo_week": 1} is entirely in the instruction. Vague instructions produce vague agents. The system prompts in EWARS are explicit to the level of specifying MongoDB filter syntax, collection names, and what to do when a query returns zero results.
The hardest part of a health AI system is knowing what it must never do. Every agent instruction ends with constraints: never diagnose diseases, never speculate on pathogen identity without lab confirmation, always set SitRep status to draft, always include the disclaimer. These aren't afterthoughts — they are load-bearing parts of the system that prevent a surveillance tool from becoming a misinformation vector.
MongoDB's geospatial capabilities removed an entire layer I expected to build. I anticipated needing a separate spatial processing service. The $geoNear aggregation pipeline with a 2dsphere index handled proximity clustering directly in the database layer — no PostGIS, no external service, no ETL step.
Free and open wins for dashboards. Replacing Mapbox with MapLibre GL JS and CARTO Dark tiles required changing five lines of code and saved the cost and friction of API key management. The visual result is identical. For a system intended to be deployable in resource-constrained public health environments, that matters.
Challenges
There has been MCP write issue to the designated Collection. I thought MCP completely ignored the agent instructions, later found out due to improper instruction which ignored schema validation caused the agent and following the MCP cause write error.
ADK adk web is not a pipeline debugger. The web interface is designed for single conversational agents. Running a SequentialAgent wrapping a ParallelAgent through it produced confusing behaviour where sub-agents sent their clarifying questions directly to the user instead of querying MongoDB autonomously. The correct debugging approach was agents-cli run with fully explicit prompts, and switching to the FastAPI /run endpoint as the primary interface.
Agents asking questions instead of acting. Early pipeline runs would stall with the Geo-Cluster Agent asking "could you provide the start date for SARS replay week 1?" instead of querying the signals collection. The root cause was underspecified prompts — the agent knew its job but not the MongoDB filter that mapped "SARS replay week 1" to actual documents. The fix was making the orchestrator inject the exact filter into the prompt, and adding an explicit instruction: "Do not ask the user for information. All data is in MongoDB. If a query returns zero results, return an empty summary and continue."
Balancing statistical rigour with LLM reasoning. The anomaly detection thresholds ($\mu + 2\sigma$, $\mu + 3\sigma$) and the composite scoring formula are mathematically precise. But they live inside LLM system prompts, not deterministic code. An early version of the Risk Escalation Agent would occasionally produce scores outside the defined range or misapply the override rules. The solution was two-layered: tighten the instruction to require JSON-only output with explicit field names, and add an orchestrator write-back layer in api/main.py that parses and validates the response before persisting it, falling back to regex extraction if the agent produced prose instead of JSON.
Replaying history with synthetic baselines. The SARS 2002/03 signals are real epidemiological data. But the three-year baseline data they are compared against had to be synthetically generated — no public dataset exists for weekly syndrome counts across these specific regions at that granularity. Building realistic seasonal profiles (ILI peaking in cool-dry season, AGE peaking in monsoon, UF peaking in pre-monsoon heat) required reading the epidemiological literature and encoding those patterns into the seed_baselines.py script. Getting the baseline realistic enough that Week 1 scores WATCH (not NORMAL, not ALERT) was a calibration challenge that took several iterations.
What's next
EWARS is built to be extended. The immediate next steps are:
- DHIS2 integration — connect directly to the WHO's open-source health data platform used across 73 countries to replace synthetic clinic data with real facility reporting
- Pathogen-specific agents — specialist sub-agents for dengue, cholera, and influenza with syndrome-specific transmission parameters
- Retrospective validation — run the system against historical WHO disease outbreak news archives to measure detection advantage across multiple outbreak types, not just SARS
- Federated deployment — per-country instances with shared alert aggregation, respecting data sovereignty requirements that prevent cross-border signal sharing
Built With
- cloud-run
- docker
- gcp
- gemini
- google-adk
- maplibre
- next.js
- openstreetmap
- tailwind

Log in or sign up for Devpost to join the conversation.