triage-flow / README.md
Anusha Parashar
readme changes
63beec1 unverified
|
Raw
History Blame Contribute Delete
9.76 kB
---
title: TriageFlow
emoji: πŸ₯
colorFrom: red
colorTo: blue
sdk: docker
app_port: 8000
tags:
- openenv
pinned: false
---
# TriageFlow: AI-Assisted Patient Intake Prioritization
[![OpenEnv](https://img.shields.io/badge/OpenEnv-compatible-brightgreen)](https://github.com/meta-pytorch/OpenEnv)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Docker](https://img.shields.io/badge/Docker-ready-blue)](https://www.docker.com/)
---
## Motivation
When emergency departments are overwhelmed, the initial intake assessment β€” deciding who needs immediate attention and who can safely wait β€” becomes a critical cognitive load for clinical staff.
TriageFlow simulates the intake desk of a busy hospital emergency department. Patients arrive **one at a time**. For each patient, the AI agent must:
1. **Classify** the patient's urgency (IMMEDIATE / URGENT / LESS_URGENT / NON_URGENT / ESCALATE)
2. **Output the full reordered priority queue** β€” the correct ordering of all patients seen so far
This dual-output design tests both clinical judgment (is this patient critical?) and queue management (where does this patient rank against everyone else?).
**Why this matters:** Training AI agents to assist with intake prioritization β€” flagging obvious high-acuity cases and managing queue state β€” could reduce cognitive load on clinical staff. This environment provides a safe, reproducible benchmark for evaluating such agents.
**What the agent simulates:** The role of a triage nurse or intake coordinator, NOT a doctor. This is an operational, administrative, safety-critical workflow β€” not diagnosis.
---
## Environment Overview
### Episode Lifecycle
```
reset(task_name) β†’ first patient presented with empty queue
↓
agent observes incoming patient + current queue state
↓
agent outputs: classification + full reordered priority queue
↓
step(action) β†’ reward computed (classification accuracy + queue correctness + escalation judgement)
↓
next patient arrives β†’ repeat
↓
all patients seen β†’ episode ends
↓
grader scores full trajectory β†’ score in [0.0, 1.0]
```
### What the Agent Can Do
- **Classify patients:** IMMEDIATE, URGENT, LESS_URGENT, NON_URGENT
- **Escalate patients** with missing critical data (sends to human, removes from queue)
- **Reorder the priority queue** after each classification
### What the Agent Must Never Do
- Issue a medical diagnosis
- Prescribe or recommend treatments
- Make clinical judgments about disease causation
---
## Observation Space
| Field | Type | Description |
|---|---|---|
| `incoming_patient` | `dict` | The patient who just arrived (symptoms, vitals, age, complaint, history) |
| `current_queue` | `List[str]` | Patient IDs in current priority order (agent's queue so far) |
| `step_number` | `int` | Current step in the episode |
| `total_expected_patients` | `int` | Total patients the agent will see |
| `previous_feedback` | `str` | Feedback on last classification |
| `task_name` | `str` | Name of the active task |
| `done` | `bool` | Whether the episode has ended |
| `reward` | `float` | Reward for the last action |
**Hidden from agent (grader-only):** Per-step answer keys, full agent trajectory, internal scoring components.
---
## Action Space
Each step, the agent outputs a single JSON object:
```json
{
"classification": "immediate|urgent|less_urgent|non_urgent|escalate",
"reordered_queue": ["P001", "P003", "P002"]
}
```
| Field | Type | Description |
|---|---|---|
| `classification` | `str` | Priority level for the incoming patient, or `"escalate"` if data is missing |
| `reordered_queue` | `List[str]` | Full queue of all non-escalated patients in priority order |
**Rules:**
- IMMEDIATE patients go first, then URGENT, LESS_URGENT, NON_URGENT
- Escalated patients are **removed** from the queue entirely
- The queue should contain all patients classified so far (excluding escalated ones)
---
## Reward Function
Three scoring components per step, weighted:
| Component | Weight | What It Measures |
|---|---|---|
| **Classification Accuracy** | 50% | Did the agent assign the correct priority label? |
| **Queue Ordering** | 30% | Does the agent's queue match the ideal queue? (position-weighted) |
| **Escalation Judgment** | 20% | Did the agent correctly identify missing data? |
### Classification Penalties (Asymmetric)
| Mistake | Severity |
|---|---|
| Correct classification | Full credit |
| Off by one level (e.g., URGENT instead of IMMEDIATE) | Small penalty |
| Off by two levels (e.g., LESS_URGENT instead of IMMEDIATE) | Heavy penalty |
| IMMEDIATE classified as NON_URGENT | **Maximum penalty** (patient could die) |
| NON_URGENT classified as IMMEDIATE | Mild penalty (wastes resources but safe) |
| Correctly escalating incomplete patient | Full credit |
| Failing to escalate missing data (guessing) | Heavy penalty |
| Escalating complete patient unnecessarily | Moderate penalty |
**Philosophy:** Under-triaging dangerous patients is punished far harder than over-triaging safe patients.
---
## Tasks
### Task 1: Basic Triage (Easy) β€” 3 patients
- **Scenario:** 3 patients with complete records and clearly distinct urgency levels
- **Patients:** Chest pain with cardiac history (IMMEDIATE), sprained ankle (NON_URGENT), persistent fever with cough (URGENT)
- **Challenge:** Classify correctly and build the priority queue β€” no escalations needed
- **Max Steps:** 10
### Task 2: Incomplete Records Triage (Medium) β€” 5 patients (3 + 1 escalation + 1 re-entry)
- **Scenario:** One patient arrives with missing vitals β†’ must be **escalated**. They return later with complete data as a new patient.
- **Challenge:** Escalation judgment + correct re-classification after data completion
- **Max Steps:** 15
### Task 3: Mass Casualty Triage (Hard) β€” 10 patients (8 + 1 escalation + 1 re-entry)
- **Scenario:** Building collapse. Multiple patients at similar urgency levels. Drug interactions (warfarin + head injury). Missing data requiring escalation.
- **Challenge:** Ranking 8+ patients correctly, escalation judgment under pressure, intra-priority ordering
- **Max Steps:** 20
---
## Grading Formula
```
final_score = (classification_accuracy Γ— 0.5) + (queue_ordering Γ— 0.3) + (escalation_judgment Γ— 0.2)
```
All scores are clipped to `[0.0, 1.0]`. Deterministic: same state always produces the same score.
---
## Setup
### Prerequisites
- Python 3.10+
- Docker (for containerized deployment)
### Installation
```bash
git clone <repo-url>
cd triage-flow
pip install -r requirements.txt
```
---
## Running Locally
### Start the server:
```bash
ENABLE_WEB_INTERFACE=true uvicorn server.app:app --host 0.0.0.0 --port 8000
```
### Run the baseline inference:
```bash
# Set environment variables (Linux/Mac)
export API_BASE_URL="https://router.huggingface.co/v1"
export MODEL_NAME="meta-llama/Llama-3.1-8B-Instruct"
export HF_TOKEN="your-hf-token"
# Windows CMD
set API_BASE_URL=https://router.huggingface.co/v1
set MODEL_NAME=meta-llama/Llama-3.1-8B-Instruct
set HF_TOKEN=your-hf-token
# Run inference
python inference.py
```
### Validate:
```bash
openenv validate
```
---
## Docker
```bash
# Build
docker build -t triage-flow .
# Run
docker run -p 8000:8000 triage-flow
# Test
curl -X POST http://localhost:8000/reset -H "Content-Type: application/json" -d '{}'
```
---
## Baseline Scores (Llama 3.1 8B Instruct)
| Task | Score | Notes |
|---|---|---|
| basic-triage | **0.85** | Correctly identifies IMMEDIATE chest pain case |
| incomplete-records-triage | **0.76** | Successfully escalates patient with missing vitals |
| mass-casualty-triage | **0.86** | Handles escalation + 8-patient queue ordering |
---
## Limitations
- Patient cases are synthetic and simplified β€” real triage involves much more nuanced assessment
- The environment does not model patient deterioration over time
- Vitals are static snapshots, not continuous monitoring data
- The queue ordering is evaluated against a single "ideal" ordering β€” real triage may have multiple valid orderings
- Graders focus on priority accuracy and queue correctness, not clinical reasoning quality
- The step budget abstraction does not capture real-time constraints
---
## Repository Structure
```
triage-flow/
β”œβ”€β”€ inference.py # Baseline inference script (project root)
β”œβ”€β”€ models.py # Pydantic models: TriageAction, TriageObservation, TriageState
β”œβ”€β”€ openenv.yaml # OpenEnv spec metadata
β”œβ”€β”€ Dockerfile # Container build
β”œβ”€β”€ README.md # This file
β”œβ”€β”€ requirements.txt # Python dependencies
β”œβ”€β”€ pyproject.toml # Package config
β”œβ”€β”€ client.py # OpenEnv client for remote access
β”œβ”€β”€ triage_flow/ # Main environment package
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ environment.py # Core env: reset(), step(), state
β”‚ β”œβ”€β”€ tasks.py # Task definitions with per-step answer keys
β”‚ β”œβ”€β”€ graders.py # Deterministic 3-component grading
β”‚ └── reward.py # Per-step reward (classification + queue + escalation)
β”œβ”€β”€ server/
β”‚ β”œβ”€β”€ __init__.py
β”‚ β”œβ”€β”€ app.py # FastAPI server (OpenEnv create_app)
β”‚ β”œβ”€β”€ ui.py # Custom Gradio clinical dashboard
β”‚ └── triage_flow_environment.py # Server-side environment re-export
└── .github/
└── workflows/
└── sync_to_hub.yml # GitHub Actions β†’ HF Spaces sync
```
---
## License
MIT
---
*Built by Team Squirrel for OpenEnv Round 1 Hackathon.*