Polaris — Your north star in critical patient care.
A realtime, multi-agent paging system that classifies clinical alerts, routes them to the right clinician in seconds, and hands off with a sub-100-word SBAR brief.
Inspiration
Hospital paging is still stuck in the 1990s. A nurse spots a deteriorating patient and then has to guess: Who's on call for cardiology? Are they in the OR? Is Dr. Chen closer? Has Dr. Rodriguez already been paged six times this hour? The result is a game of telephone that costs minutes — and in a code blue, minutes are lives.
We kept hearing the same story from ER nurses and residents: the clinical decision is easy, but the coordination around it is brutal. Pages get sent to the wrong person, escalations are manual, and the receiving clinician arrives at the bedside with no context beyond "Room 412, call back."
We wanted to build the thing we wished existed during our own hospital shadowing and family ER visits: a system that thinks like a charge nurse, routes like an air-traffic controller, and briefs like a senior resident — all in under five seconds. That system is Polaris.
What it does
Polaris is an end-to-end paging operating system for hospital floors.
For the nurse / operator:
- Submit an alert in plain English ("chest pain, room 412, diaphoretic, hx CABG") or via voice.
- Polaris classifies it P1–P4 in realtime, extracts the room, specialty, and symptoms, and pulls the matching patient's EHR.
- The operator dashboard shows the dispatch decision, the reasoning, backup clinicians, and a live queue with priority breakdown.
For the clinician:
- Receives a page on their phone/tablet with the alert and a one-tap Accept / Decline.
- The instant they accept, Polaris generates a sub-100-word SBAR brief (Situation, Background, Assessment, Request) from the patient's EHR — optimized to be read while walking.
- Declines and timeouts auto-escalate to the next-best clinician without human intervention.
Under the hood, four AI agents coordinate every dispatch:
- Operator Agent — the orchestrator. Receives alerts (via REST, voice, or ASI:One Chat Protocol), runs the pipeline, and makes the final dispatch decision.
- Priority Handler — classifies severity P1–P4 using ASI-1 Mini with deterministic fallback.
- Case Handler — ranks clinicians by specialty match, zone proximity (ICU, OR, nurses' station, floor corridor), on-call status, and current caseload, then re-ranks with ASI-1 for soft reasoning.
- Sentinel Agent — a passive observer running on a 30-second timer. It never pages anything itself; instead it detects systemic risk patterns — alert concentration in one zone, coverage holes for a specialty, a single clinician getting slammed with seven pages an hour — and surfaces proactive recommendations to the operator.
Guardrails and policies that actually matter:
- Autonomy config per zone and per priority — ICU + P1 always requires operator review; routine P4 pages on the med-surg floor can dispatch autonomously.
- Consecutive paging protection — no clinician gets paged twice within 5 minutes for different cases.
- Workload balancing — P3/P4 pages skip any clinician already holding >3 pages in the last hour.
- Zone escalation — for P1/P2, candidates in the target zone get a score boost; off-shift clinicians get heavily penalized on critical zones.
- Load distribution among near-ties — when the top 3 candidates are within 10% of each other, Polaris prefers the one with the lowest current caseload. The "best" doctor doesn't always get the page — the right one does.
- Time-queued dispatch — if no one is immediately available but someone finishes a procedure in 12 minutes, Polaris can hold a non-urgent page and auto-dispatch it on availability.
- Auto-escalation queue — every page enters a timeout-tracked queue. No ack in N seconds? Next backup gets it. No human babysitting required.
How we built it
Architecture
Browser ─ REST ─> FastAPI (:8000) POST /dispatch ─> full agent pipeline
│
Browser ─ REST ─> Flask (:8001) /api/* doctors, pages, queue, EHR, voice
Browser ─ WS ─> Flask (:8001) Socket.IO snapshot, incoming_page,
sbar_brief, page_escalated, ...
nginx (:80) routes /, /api/, /socket.io/, /fastapi/
Stack
- Agents: Python + uAgents (Fetch.ai) with the Chat Protocol from
uagents_core.contrib.protocols.chatso the whole system is callable from ASI:One. - LLM: ASI-1 Mini for priority classification, candidate re-ranking, SBAR generation, and sentinel pattern interpretation. Every LLM call has a deterministic fallback — the system never blocks on a model.
- APIs: FastAPI (dispatch + clinician roster + health) and Flask + Flask-SocketIO (pages, queue, EHR, voice, proactive recommendations).
- Realtime: Socket.IO for
incoming_page,doctor_status_changed,page_escalated,sbar_brief,proactive_recommendation,pattern_detected. - Data: TinyDB JSON stores for clinicians, EHR records, schedules, alerts, and room/floor layout — zero DB setup, easy to seed.
- Frontend: Next.js 14 (App Router) + TypeScript + Tailwind. Two main surfaces:
/operator(dashboard, alert intake, queue, escalation chain visualizer, proactive insights modal) and/clinician?id=<doctor_id>(page receiver, accept/decline, SBAR card). - Voice: A voice-to-page endpoint (
POST /api/voice/urgent) lets nurses radio in alerts hands-free; transcripts get fed into the same pipeline and are also surfaced as voice context on the operator's dispatch card. - Deploy: EC2 + nginx + systemd (
medpage-backend,medpage-fastapi), one-shot install viadeploy/install.sh. Live athttp://18.145.218.29/operator.
The dispatch pipeline (agents/operator_agent.py::process_alert)
- Classify priority (ASI-1 + regex fallback).
- Extract room; resolve target zone.
- EHR lookup by room with priority-aware timeouts (urgent pages use shorter timeouts and skip the cache).
- Pull recent voice events for that room/channel as extra context.
- Determine autonomy mode via zone + priority policy.
- Fetch live clinician roster from the backend (cached for non-urgent, fresh for P1/P2).
- Filter by specialty → score by zone/caseload/on-call → apply workload + zone + load-distribution guardrails → ASI-1 re-rank.
- If no one's free now, check the schedule for who finishes a procedure soon (time-queued).
- Create the page via backend, register it with the queue manager (which handles timeouts + escalation), and emit the dispatch decision.
End-to-end latency: ~2–5 seconds for a fully-reasoned dispatch including LLM calls.
Challenges we ran into
- ASI-1 re-ranking vs. latency budgets. We wanted the LLM in the loop for nuanced tie-breaking, but we also needed P1s out the door in under 5s. We solved it with parallel fetches, priority-aware timeouts, a tight 8s cap on the SBAR generation call, and deterministic fallbacks at every single LLM touchpoint. If ASI-1 is slow or down, Polaris still pages correctly — it just loses the "soft" reasoning layer.
- Two backends, one realtime truth. We split FastAPI (agent-facing dispatch) from Flask (management + Socket.IO) for clean separation, but then had to make sure both agreed on page state. The
backend_clientin the agents layer became the single source of truth; every page created by an agent goes through the Flask/api/pageendpoint so the Socket.IO broadcasts fire automatically. - Escalation without race conditions. The queue manager tracks per-page timeouts and auto-dispatches to backups. Getting this right with async tasks, clinician accept/decline events, and manual operator overrides all racing on the same page ID took real care.
- Guardrails that don't become deadlocks. Our first cut was too strict — workload limits + consecutive-paging protection + on-call requirements could leave no candidate for a P1 in the ICU at 3am. We made guardrails priority-aware: hard filters for routine cases, soft score penalties for critical ones.
- Voice + text convergence. Merging async voice events with text alerts for the same room without double-paging required a voice-context dedupe on
event.idplus a short 15-minute lookback window. - uAgents + FastAPI coexistence. We needed the agents callable both standalone (for ASI:One) and as in-process functions (for FastAPI
/dispatch). The fix was making the Chat Protocol import optional and exposingprocess_alertas a plain async function that both entrypoints call. - Nginx routing five services on one port. One Next.js, one FastAPI, one Flask REST, one Socket.IO upgrade path, plus static — nginx config took more iterations than we'd like to admit.
Accomplishments that we're proud of
- A fully autonomous dispatch pipeline that makes real decisions — priority, clinician, backup chain, SBAR — in seconds, and gracefully degrades to deterministic logic if every LLM call fails.
- A multi-agent architecture with a clear separation of concerns (orchestrator, classifier, ranker, observer) that is genuinely callable from ASI:One via the Chat Protocol — not a demo stub.
- Sentinel Agent. Building a passive, observation-only agent that only surfaces systemic-risk patterns (and never routes or interrupts) felt like the right kind of AI: quiet, high-signal, and additive rather than intrusive.
- Realtime everything. Every state change — incoming page, acceptance, escalation, status flip — fires over Socket.IO to both the operator and clinician views. Nothing polls.
- Hard-won SBAR brief generator that stays under 100 words and verifies its own format before shipping — with a hand-written deterministic fallback that's still clinically coherent.
- Deploying the whole thing behind nginx on EC2 with systemd services and a one-command install, so demoing is as simple as opening a URL.
What we learned
- LLMs make excellent rankers, terrible single-points-of-failure. Every model call in Polaris is wrapped in a timeout and a fallback. Treat the LLM as a nice-to-have layer over a system that already works without it.
- Agent design is systems design. uAgents + Chat Protocol got us a clean ASI:One integration, but the real work was the orchestration contract between agents: who owns state, who owns decisions, who only observes.
- Hospitals care about guardrails more than accuracy. The most valuable features weren't the LLM classifications — they were the workload limits, consecutive-paging protection, and zone policies. Trust comes from predictable, inspectable behavior.
- Realtime UX is a feature in itself. The difference between a 3s page with a live "accepted" checkmark and a 3s page with a spinner-then-refresh is the difference between "I'd use this" and "nope."
- Ship the fallback first. We built the deterministic ranker before we built the ASI-1 ranker. It meant every demo worked even when Wi-Fi didn't.
What's next for Polaris
- Real pilot data. Partner with a teaching hospital to run Polaris in shadow mode alongside their existing paging system and measure time-to-acknowledge, time-to-bedside, and escalation rates.
- HL7 / FHIR integration. Replace the TinyDB EHR mock with a real FHIR adapter so Polaris can pull live vitals, labs, and problem lists.
- Clinician learning loop. Feed accept/decline/timeout outcomes back into the case handler so Polaris learns which doctors actually respond fastest for which alert types on which shifts.
- Predictive sentinel. Move Sentinel from reactive pattern detection to forecasting — predict the next coverage hole 30 minutes before it happens.
- Multi-hospital federation. A single operator dashboard covering several facilities, with cross-facility escalation for rare specialties (neurosurg, peds cardiology).
- Native mobile clinician app with push, haptics, and offline queueing for dead-zone floors.
- Auditability & compliance. Full signed log of every dispatch decision, every LLM prompt/response, and every guardrail trigger — the kind of paper trail a hospital safety committee actually wants.
- Quiet hours / do-not-disturb for physicians, with automatic rerouting that still respects on-call contracts.
Polaris is what we think hospital paging should have looked like a decade ago. We're just glad we finally got to build it.
Built With
- amazon-web-services
- asi:one
- bash
- css
- fastapi
- fetch.ai
- flask
- gunicorn
- html
- httpx
- javascript
- lucide
- next.js
- nginx
- pydantic
- python
- python-dotenv
- react
- shadcn/ui
- systemd
- tailwindcss
- tinydb
- typescript
- uagents-core
- uvicorn



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