Train AI agents to catch dangerous prescriptions before they reach patients.
Medication errors harm 1.5 million people per year in the US alone. Pharmacists manually review every prescription for dosage safety, drug interactions, allergy conflicts, and contraindications β a critical but error-prone process under time pressure.
RxSafe creates a standardized RL environment where AI agents learn to perform this validation. The agent must investigate patient records (multi-step) and identify safety issues before approving, modifying, or rejecting a prescription.
This directly maps to a future product: an AI clinical pharmacist assistant that catches dangerous prescriptions in real-time.
Unlike single-step classify-and-done environments, RxSafe requires the agent to actively investigate before making a decision:
reset() β Agent sees prescription (drug, dose, patient name/age)
β
step(request_info: "allergies") β Reveals patient allergies
step(request_info: "medications") β Reveals current medications
step(request_info: "conditions") β Reveals medical conditions
β
step(submit: findings + recommendation) β Graded by environment
Skipping investigation means missing critical safety issues. Thorough investigation is part of the score.
| Field | Type | Description |
|---|---|---|
action_type |
"request_info" or "submit" |
Investigation vs. final answer |
info_requested |
string | allergies, medications, conditions, full_profile |
dosage_safe |
bool | Is the dose within safe limits? |
interactions_found |
list[str] | Drug pairs, e.g. ["warfarin-aspirin"] |
allergy_conflicts |
list[str] | e.g. ["penicillin-amoxicillin"] |
contraindications_found |
list[str] | e.g. ["kidney_disease-metformin"] |
recommendation |
string | approve, modify, or reject |
reasoning |
string | Clinical reasoning (hard task) |
| Field | Type | Description |
|---|---|---|
prescription_id |
string | Unique ID |
patient_name |
string | Patient name |
patient_age |
int | Patient age |
drug_name |
string | Prescribed drug |
drug_dose |
string | Dose amount |
drug_frequency |
string | How often |
steps_remaining |
int | Steps left in episode |
known_allergies |
list or null | Revealed after request |
current_medications |
list or null | Revealed after request |
medical_conditions |
list or null | Revealed after request |
feedback |
string | Grading feedback (after submit) |
score_breakdown |
dict | Per-component scores |
issues_found_correctly |
list | What agent caught |
issues_missed |
list | What agent failed to catch |
Check if the prescribed dosage is within safe daily maximum.
- Max steps: 2
- Scenarios: 6 (overdose detection: ibuprofen/tramadol/simvastatin, safe prescriptions: metformin/lisinopril/ciprofloxacin)
- Scoring:
dosage Γ 0.50 + recommendation Γ 0.50 - Example: Ibuprofen 2400mg TID = 7200mg/day (max safe: 3200mg) β modify
Check dosage + drug-drug interactions + allergy conflicts.
- Max steps: 4 (investigate, then submit)
- Scenarios: 8 (warfarin+aspirin, SSRI+tramadol, penicillin allergy, NSAID allergy, simvastatin+amiodarone, and more)
- Scoring:
dosage Γ 0.20 + interactions Γ 0.30 + allergies Γ 0.25 + recommendation Γ 0.25 - Example: Patient on sertraline prescribed tramadol β serotonin syndrome risk β reject
Complete multi-step prescription validation with investigation scoring.
- Max steps: 6 (investigate all sources, then submit)
- Scenarios: 8 (complex multi-issue cases with contraindications, cross-allergies, and cascading interactions)
- Scoring:
dosage Γ 0.15 + interactions Γ 0.20 + allergies Γ 0.15 + contraindications Γ 0.15 + recommendation Γ 0.20 + investigation Γ 0.15 - Example: Patient with myasthenia gravis prescribed ciprofloxacin while on warfarin β interaction + contraindication β reject
- Investigation rewards: +0.05 per info request (encourages thorough investigation)
- Interaction recall: Partial credit for finding some but not all interactions
- Priority adjacency: modify vs reject both indicate "something wrong" β 0.6 partial credit
- False positive penalty: Small penalty for reporting non-existent issues (-0.1 per false positive)
- Critical miss penalty: Approving a dangerous prescription scores 0.0 on recommendation
All scenarios have predetermined correct answers based on a curated drug interaction database with 12 drugs and their known interactions, allergy classes, and contraindicated conditions.
The grader produces varied scores across episodes β never constant. Tested across all scenarios with multiple answer combinations.
13 drugs with realistic interaction profiles:
| Drug | Class | Key Interactions |
|---|---|---|
| Warfarin | Anticoagulant | aspirin, ibuprofen, cipro (bleeding risk) |
| Aspirin | NSAID/antiplatelet | warfarin, methotrexate, heparin |
| Ibuprofen | NSAID | warfarin, lisinopril, methotrexate, lithium |
| Metformin | Antidiabetic | Contraindicated in severe kidney disease |
| Lisinopril | ACE inhibitor | potassium, spironolactone, NSAIDs |
| Amoxicillin | Penicillin antibiotic | Cross-allergy with penicillin class |
| Ciprofloxacin | Fluoroquinolone | warfarin, theophylline (seizure risk) |
| Simvastatin | Statin | amiodarone, erythromycin (rhabdomyolysis) |
| Sertraline | SSRI | tramadol (serotonin syndrome β fatal) |
| Tramadol | Opioid | SSRIs (serotonin syndrome β fatal) |
| Methotrexate | Immunosuppressant | NSAIDs (toxicity), many others |
| Digoxin | Cardiac glycoside | amiodarone, verapamil (toxicity) |
| Amiodarone | Antiarrhythmic | digoxin, simvastatin, warfarin (toxicity) |
# Install dependencies
pip install -r requirements.txt
# Run locally
export ENABLE_WEB_INTERFACE=true
uvicorn server.app:app --host 0.0.0.0 --port 8000
# Validate local packaging
openenv validate
# Run lightweight regression checks
python3 -m unittest discover -s tests -v
# Open web UI (available when ENABLE_WEB_INTERFACE=true)
open http://localhost:8000/webdocker build -t rx-safe-env .
docker run -p 8000:8000 rx-safe-envexport HF_TOKEN="your_hugging_face_token"
export LOCAL_IMAGE_NAME="rx-safe-env"
python3 inference.py# Reset with medium task
curl -X POST http://localhost:8000/reset \
-H "Content-Type: application/json" \
-d '{"task": "interaction_check", "seed": 42}'
# Request patient allergies
curl -X POST http://localhost:8000/step \
-H "Content-Type: application/json" \
-d '{"action": {"action_type": "request_info", "info_requested": "allergies"}}'
# Submit validation
curl -X POST http://localhost:8000/step \
-H "Content-Type: application/json" \
-d '{"action": {"action_type": "submit", "dosage_safe": true, "interactions_found": ["warfarin-aspirin"], "allergy_conflicts": [], "recommendation": "reject"}}'Before submission, replace the placeholder values below with exact scores from a real run of python3 inference.py.
Recommended baseline run:
export HF_TOKEN="your_hugging_face_token"
export API_BASE_URL="https://router.huggingface.co/v1"
export MODEL_NAME="Qwen/Qwen2.5-72B-Instruct"
export LOCAL_IMAGE_NAME="rx-safe-env"
python3 inference.pyIf you are evaluating against a deployed Hugging Face Space instead of a local Docker image:
export ENV_URL="https://YOUR_SPACE_URL.hf.space"
python3 inference.pyRecord the score from the final [END] line for each task:
| Task | Difficulty | Measured Score | Reproducibility Notes |
|---|---|---|---|
dosage_check |
Easy | 1.00 |
5-seed average via HF Space, Qwen/Qwen2.5-72B-Instruct |
interaction_check |
Medium | 0.65 |
5-seed average via HF Space, Qwen/Qwen2.5-72B-Instruct |
full_validation |
Hard | 0.48 |
5-seed average via HF Space, Qwen/Qwen2.5-72B-Instruct |
rx-safe-env/
βββ server/
β βββ __init__.py
β βββ app.py β Canonical OpenEnv server entrypoint
βββ rx_safe/
β βββ __init__.py
β βββ models.py β Drug database, scenarios, Pydantic types
β βββ client.py β HTTP client
β βββ server/
β βββ __init__.py
β βββ app.py β Compatibility shim to the root server
β βββ environment.py β Core RL environment logic
βββ tests/
β βββ test_environment.py β 18-test regression suite
βββ Dockerfile
βββ inference.py β Mandatory inference script (5-seed multi-task)
βββ openenv.yaml β OpenEnv manifest
βββ pyproject.toml
βββ requirements.txt
βββ README.md
- β Typed Pydantic models (Action, Observation, State)
- β
step()/reset()/state()API - β
openenv.yamlwith metadata - β 3 tasks: easy β medium β hard
- β Graders return scores in [0.0, 1.0]
- β Multi-step trajectories with investigation
- β Partial rewards (not binary)
- β Deterministic, reproducible grading with seed support
- β Working Dockerfile
- β Baseline inference script with [START]/[STEP]/[END] format
- β Runs on vcpu=2, memory=8gb (no GPU needed)
MIT