An agentic AI system for non-clinical patient administration and care coordination: registration → intent detection → department routing → appointment booking → document coordination → confirmation & reminders → follow-up — with a human sitting on every decision and medical decisions kept out of the AI.
Built for the AgentCare Build Challenge 2026. This is not a diagnosis or treatment system. The AI performs administrative routing only; it never diagnoses, prescribes, or replaces a clinician (RULE-5).
| Layer | Choice |
|---|---|
| Backend | Python + FastAPI |
| UI | Server-rendered Jinja2 templates |
| Persistence | SQLite via SQLAlchemy (on disk — survives restart) |
| Orchestration | LangGraph (Coordinator + specialist nodes) |
| LLM | Groq (llama-3.3-70b-versatile) via langchain-groq |
- Coordinator — plans the request path, delegates, assembles the confirmation from persisted data.
- Intake & Routing (conversational) — drives a live chat; on entry it reads the patient's real record and detects one of four scenarios (new / follow-up / reschedule / cancel), maps the stated purpose to a valid department, asks when unclear, flags emergencies. The LLM does the NLU; a deterministic phase machine + DB tools decide the truth (never a fabricated doctor or slot).
- Appointment — pulls doctors/slots, checks conflicts, books/reschedules/cancels (always starts
PENDING). - Document — classifies uploads into a controlled vocabulary, SHA-256 de-duplicates, maps to patient, routes each report to the department that reads it (ECG → Cardiology, MRI/X-Ray → Orthopaedics) so staff see only reports relevant to their specialty, flags missing required docs.
- Safety & Escalation — blocks diagnosis/prescription language; screens every chat turn (self-harm → halt + crisis helplines + staff escalation; abuse/off-topic → redirect; emergency → urge 108 but still offer booking).
- Follow-up — reminders, follow-up scheduling, pre-visit lab alerts, notifications.
The LLM is advisory: every appointment is PENDING until dept-scoped staff
confirm / redirect / escalate / cancel / reschedule it (the human-approval gate).
State for each run persists in a WorkflowRun row (never an in-memory-only
store — RULE-4). Every action appends an AuditEvent.
Full architecture reference:
docs/architecture.html— agent roster, the workflow mapped to agents, the data model, and how the build clears every §6 mandatory requirement and §10 disqualifier.
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # then add your GROQ_API_KEY (free: console.groq.com)
python -m app.seed # load synthetic Bengaluru-clinic data
uvicorn app.main:app --reloadOpen http://127.0.0.1:8000. Every login uses OTP 123456. The header has a
split Patient Login / Staff Login — and because the two sessions live in
separate cookie slots, you can be signed in as a patient and a staff member in
the same browser at once.
Demo patients (OTP 123456) — each exercises a different chat scenario:
| Patient | Phone | Scenario |
|---|---|---|
| Krishnan | +916364842536 |
Cardiology follow-up; ECG already on file → books straight through |
| Priya | +919000100001 |
General-medicine follow-up; no prep needed → straight to doctors |
| Arjun | +919000100002 |
Gastro follow-up; blood test (LFT) missing → agent gates on upload first |
| Ravi | +919000100003 |
Booked cardiology follow-up, ECG missing → drives the staff pre-visit reminder |
| Sana | +919000100004 |
Pending new cardiology request → drives the staff confirm queue |
Demo staff (OTP 123456, phone +9190000000NN, NN = department index):
Cardiology 01 is the richest demo (Sana pending + Ravi pre-visit + Krishnan
history); General-Medicine 00, Gastro 06 also have appointments.
Admin +919900000099 sees all departments.
No key? The app still boots; agent calls fall back to a deterministic stub
(LLM_STUB=1), so the UI and database work offline.
As the patient:
- Open Appointments → a chat starts. Describe your visit in natural language (e.g. "persistent cough and mild fever") → the Intake agent routes you to a department and offers real doctor cards + slot chips. Try "sudden chest pain and can't breathe" to see the emergency flag.
- Pick a doctor + slot in the chat → the visit is created PENDING (awaiting staff).
- When a follow-up needs a report you don't have (e.g. Arjun's LFT), Lab
Results shows a "please upload" prompt. Pick any PDF/PNG named like
resting_ecg.pdf→ it auto-submits; the Document agent classifies, de-duplicates, and routes it to the reading department.
As the staff (Cardiology — you only see your department): 4. The queue shows new requests to review; the doctor calendar shows free and booked slots (green = confirmed, yellow = pending). 5. Open a request → confirm / reschedule / cancel / redirect to emergency, or schedule a follow-up. The patient is notified (the bell). The appointment view also shows a pre-visit documents panel (on file vs. missing) and any lab reminders raised for the visit.
Every step writes an AuditEvent; the agent reasoning trace is visible to
staff only (never on the patient's screen).
pytest # 37 tests, all greenThe suite (in tests/) covers each layer end-to-end — auth/RBAC, the agent
routing + conversational booking, the staff lifecycle, and document handling:
| File | Tests | What it locks down |
|---|---|---|
test_smoke.py |
3 | health check, home renders, seed populates departments |
test_auth.py |
6 | register→login flow, T&C required, wrong OTP rejected, unauthenticated portal redirects, a patient cannot reach the staff console, staff login is department-scoped |
test_portal.py |
2 | seeded portal sections render, profile edits persist |
test_agents.py |
8 | symptom→department routing, emergency language flagged, advice-seeking flagged but still routed, out-of-scope declined, vague purpose asks to clarify, cancel intent handled, chat→booking creates a PENDING appointment, emergency chat booking opens an escalation |
test_staff.py |
7 | queue is department-scoped, out-of-department staff cannot act, confirm sets status + notifies, cancel frees the slot, reschedule moves the slot, emergency redirect, follow-up creates a parent-linked appointment |
test_documents.py |
11 | classify from filename, explicit-type hint wins, route report to department, upload persists routed dept, staff see only their department's docs, upload de-dupes, unsupported type rejected, required-doc status + upload satisfies it, pre-visit reminder is idempotent, confirming a follow-up raises the reminder, seed ships the demo reminder |
Tests run fully offline against a throwaway SQLite DB with the LLM stub — no
GROQ_API_KEY needed.
See app/models.py. Core entities follow the challenge §9 schema
(User, PatientProfile, Department, Doctor, AppointmentSlot,
Appointment, PatientDocument, WorkflowRun, Reminder, Escalation,
AuditEvent) plus extensions the clinic workflow needs (InsuranceInfo,
MedicalHistoryEntry, Notification, RequiredDocument, and follow-up links
on Appointment).
- All sample data is synthetic; no real patient information (RULE-6).
- Secrets live only in a gitignored
.env;.env.exampledocuments the shape. - The assistant maps symptoms to a department, never to a diagnosis, and emergencies are routed to human staff.