Project Info
Inspiration
You know the kind of pain that’s not "go to the ER" serious, but also not nothing? A weird rash that's probably fine. A headache that won't go away. A prescription you're not sure is safe to mix with your other meds. So you do what everyone else does: you ask ChatGPT or Claude. LLMs are incredible at generating medical-sounding explanations. But they hallucinate, justify confidently, and over-dramaticize... because that’s how medical literature is written. Clinical papers are trained to list out catastrophic edge cases. So the model does the same. And most importantly, they don’t take responsibility for action, they say, “consult your doctor”. That’s autocomplete, not care. We wanted to build the thing that should already exist. An AI that can look at your symptoms (described, photographed, or spoken aloud), reason about them the way a clinician would, and tell you what to actually do. Not a chatbot that hedges or hallucinates. An agent that builds a real plan: triage level, possible conditions, recommended actions, drug safety checks, then helps you execute it. What We Built Symbio is a multi-turn conversational healthcare agent. You talk to it like you'd talk to a nurse practitioner (describe what's wrong, upload a photo, or just speak) and it runs a full clinical pipeline behind the scenes to give you an actionable care plan. Under the hood, it's five systems working together: 1. Multimodal Intake. The agent accepts text, voice (via Whisper), and images. Images are classified first — is this a skin photo? A prescription bottle? An X-ray? An insurance card? — then routed to specialized analyzers. A photo of a rash gets feature extraction (color, texture, distribution, morphology). A prescription gets OCR and drug identification. Everything unifies into a structured patient event. 2. Mixture of Experts Planner. When generating a care plan, we fan out to four models in parallel: A judge model (Claude) then synthesizes the four opinions into a single plan. The synthesis isn't naive — it uses weighted consensus: [ \text{triage}_{\text{final}} = \text{consensus}(\text{structured experts}) \oplus \text{escalate_only_if}(\text{specialist explicitly recommends ER}) ] The large models set the triage baseline. The specialists contribute additional differentials and clinical nuances. But a small model merely mentioning stroke in a differential list doesn't override two large models saying "this is self-care." 3. Clinical Knowledge Graph. A SNOMED CT-inspired graph, combined with ontologies like RxNorm and LOINIC, encoding symptoms, conditions, medications, and their relationships — HAS_FINDING, RED_FLAG_FOR, CONTRAINDICATED_WITH, INTERACTS_WITH. This is the deterministic backbone. If you're on warfarin and ask about ibuprofen, the graph catches the bleed risk interaction. No LLM hallucination can bypass it. 4. Constraint Engine. A symbolic safety layer that validates every plan before it reaches the patient. It checks hard-coded emergency rules (chest pain + shortness of breath → call 911), blocks dangerous medication recommendations, and enforces scope-of-practice (the agent says "possible conditions," never "you have"). This runs after the AI and before the response — a deterministic safety net over a probabilistic system. 5. Conversational Agent. The agent orchestrates everything through Claude's tool-use API. It decides when to run intake, plan, validate, check drugs, or escalate — chaining up to six tool calls per message. Session state accumulates patient context across turns (conditions, medications, allergies, demographics), so the agent gets more informed as the conversation continues. How We Built It The backend is FastAPI (Python), chosen for async support — essential when calling four models simultaneously with asyncio.gather. The frontend is Next.js with Tailwind CSS. Voice input uses OpenAI Whisper for speech-to-text, and responses can be read aloud via OpenAI TTS. The biomedical models run on Modal for efficient GPU inference. Claude receives conversation history plus tool definitions, decides which tool to call, we execute it and feed the result back, and Claude decides the next step — looping until it's ready to respond to the patient. The MoE pipeline uses a two-phase wait strategy: API experts (Claude, GPT-4o) return in ~15 seconds, then we wait up to 7 minutes for Modal experts to handle potential cold starts. The backend pre-warms Modal containers on startup so they're ready by the first user message. Challenges The over-triage problem was the hardest thing we dealt with, and it taught us the most. Our first MoE implementation had a safety rule: "if ANY expert recommends emergency, the final triage MUST be emergency." Sounds responsible. In practice, it meant that BioMistral-7B listing "1. Stroke 2. Meningitis 3. Migraine" as a differential for a mild headache forced the entire system to tell the patient to call 911. Two well-calibrated models saying "self-care, risk score 0.2" were overridden by one small model doing what medical literature trained it to do — list the worst things first. The fix required rethinking what "safety" means. Telling someone with a tension headache to call 911 isn't safe — it erodes trust, wastes emergency resources, and desensitizes people to real warnings. We redesigned the judge to use weighted majority consensus: structured experts set the baseline, specialist models contribute insights proportionally, and escalation only happens when specialists explicitly recommend emergency action — not just when they mention a scary condition in passing. A differential diagnosis is a thinking tool, not an alarm. Modal cold starts were a constant UX challenge. The biomedical models need 3-5 minutes on first invocation (downloading weights, loading onto GPU). We couldn't block the user for that long, so we implemented two-phase waiting: return API expert results immediately, then incorporate Modal expert results when they arrive. On startup, the backend fires warm-up requests in the background so containers are hot by the time the user types their first message. The Future Just 2 years ago, this was impossible. But by combining symbolic research with agent capabilities we can build AI that reasons, validates, and acts. Imagine a world where your first line of care isn’t Google, or ChatGPT. Where AI can Schedule a telehealth visit, escalate appropriately, and guide you through home care steps. We believe this is the future of healthcare AI. Probabilistic intelligence. Deterministic safety. What We Learned Ensemble AI needs opinionated synthesis, not naive aggregation. "Take the most urgent assessment" and "include all red flags from all experts" sound like safe defaults. In practice, they produce plans that are simultaneously thorough and useless — treating every symptom like it could be fatal. A good judge model needs to understand confidence weighting, the difference between a differential and a recommendation, and when a minority opinion should be noted versus when it should set the triage level. Symbolic safety layers are non-negotiable for healthcare AI. The constraint engine and knowledge graph don't hallucinate. They don't have off days. If chest pain plus shortness of breath appears in the symptoms, triage goes to emergency — no prompt engineering can change that. LLMs handle the nuanced reasoning; deterministic systems handle the bright-line rules. About Us Undergrad & Master's students at UC Berkeley (EECS, CS, math, data science).
CareGraph
Multimodal healthcare agent that helps patients navigate their care. Upload images (symptoms, prescriptions, lab results, insurance cards), use voice input, and get AI-powered clinical plans with triage, possible conditions, and recommended actions.
Architecture
frontend/ Next.js 16 React app (chat UI, voice recorder, image upload)
backend/ FastAPI Python server
app/
main.py API endpoints (sessions, messages, voice, TTS)
agent.py Conversational agent loop
intake.py Multimodal intake — image classification + analysis (Claude Vision),
text extraction, voice transcription (Whisper)
planner.py Clinical reasoning engine — generates triage + care plan
moe.py Mixture of Experts — fans out to 4 planner models, judge synthesizes
knowledge.py SNOMED CT clinical knowledge graph
constraints.py Safety constraints + red flag detection
executor.py Task execution (booking, prescriptions, referrals)
sessions.py Session state management
schemas.py Pydantic models for all data types
config.py App configuration (env vars)
modal_services/ GPU-backed biomedical LLMs on Modal
planner_experts.py OpenBioLLM-8B + BioMistral-7B endpoints
Planner Mixture of Experts (MoE)
When MOE_ENABLED=true, the planner runs 4 models in parallel and a judge synthesizes the best plan:
| Expert | Source | What it does |
|---|---|---|
| Claude Sonnet | Anthropic API | General clinical planner (structured JSON) |
| GPT-4o | OpenAI API | Different model family perspective (structured JSON) |
| OpenBioLLM-8B | Modal (A10G GPU) | Llama 3 fine-tuned on PubMed + clinical trials (free-text reasoning) |
| BioMistral-7B | Modal (A10G GPU) | Mistral fine-tuned on PubMed Central (free-text reasoning) |
The judge (Claude) applies strict safety rules: triage is always at least as urgent as the most urgent expert, red flags from any expert are included, and specialist-only findings are never discarded.
Prerequisites
- Python 3.11+
- Node.js 18+
- API keys: Anthropic (required), OpenAI (optional — for voice, TTS, GPT-4o MoE expert)
- Modal account (optional — for biomedical MoE experts)
Setup
1. Backend
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate
# Install dependencies
pip install -r backend/requirements.txt
# Create your .env file
cp backend/.env.example backend/.env # then fill in your API keys
Create backend/.env with:
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-proj-... # optional — needed for voice + GPT-4o expert
MOE_ENABLED=true # optional — enables multi-model planner ensemble
MOE_OPENBIO_URL= # set after Modal deploy (see below)
MOE_BIOMISTRAL_URL= # set after Modal deploy (see below)
2. Frontend
cd frontend
npm install
3. Modal (optional — for biomedical MoE experts)
pip install modal
modal setup # one-time auth
# Deploy the biomedical planner experts
modal deploy modal_services/planner_experts.py
This prints endpoint URLs. Add them to backend/.env:
MOE_OPENBIO_URL=https://YOUR_USERNAME--caregraph-planner-experts-openbiollmexpert-plan.modal.run
MOE_BIOMISTRAL_URL=https://YOUR_USERNAME--caregraph-planner-experts-biomistralexpert-plan.modal.run
Running
Start both servers (from the repo root):
# Terminal 1 — Backend (port 8000)
source .venv/bin/activate
cd backend
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# Terminal 2 — Frontend (port 3000)
cd frontend
npm run dev
Open http://localhost:3000 in your browser.
If MoE is enabled, the backend automatically warms up Modal containers on startup (takes 2-3 min on first boot, then stays warm for 5 minutes between requests).
Features
- Text chat — describe symptoms, ask health questions
- Image upload — photos of symptoms, prescriptions, medications, lab results, insurance cards, X-rays
- Voice input/output — record voice messages (Whisper STT), listen to responses (OpenAI TTS)
- Clinical knowledge graph — SNOMED CT-based symptom-to-condition mapping, red flag detection, drug interaction checks
- Safety constraints — automatic triage escalation, red flag detection, medication contraindication alerts
- Mixture of Experts — 4-model ensemble for more thorough clinical reasoning
API Docs
With the backend running, visit http://localhost:8000/docs for the interactive Swagger UI.
care-ai
arch
┌─────────────────────────────────────────────────────────────────┐
│ CAREGRAPH PIPELINE │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. INTAKE │
│ ↓ Multimodal processing (text, images) │
│ PatientEvent │
│ │
│ 2. PLANNER │
│ ↓ Clinical reasoning + RAG │
│ ClinicalPlan (initial) │
│ │
│ 3. CONSTRAINT SERVICE (uses Knowledge Graph) │
│ ├─ Medical Safety Rules │
│ │ └─ queries KG for contraindications, red flags │
│ ├─ Drug Interaction Checker │
│ │ └─ queries KG for interactions │
│ ├─ Scope-of-Practice Validator (LLM) │
│ └─ queries KG for drug classes │
│ └─ Auto-Fix Engine │
│ ↓ │
│ ClinicalPlan (validated) + ConstraintViolations │
│ │
│ 4. EXECUTOR (TODO) │
│ ↓ Browser automation, booking, calling │
│ ExecutorTasks │
│ │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────┐
│ KNOWLEDGE GRAPH (Dual Mode) │
├─────────────────────────────────┤
│ │
│ In-Memory (Default) │
│ • ~150 concepts │
│ • 300+ relationships │
│ • No setup required │
│ │
│ OR │
│ │
│ Neo4j (Production) │
│ • Unlimited concepts │
│ • Graph queries │
│ • SNOMED CT integration │
│ │
└─────────────────────────────────┘
knowledge graph (app/knowledge.py)
SNOMED CT-inspired clinical ontology:
Concept Types
Condition(50+ conditions: MI, pneumonia, cellulitis, etc.)Symptom(40+ symptoms: chest pain, fever, rash, etc.)Medication(30+ drugs: ibuprofen, warfarin, metformin, etc.)DrugClass(NSAID, ACE inhibitor, statin, etc.)
Relationship Types
-
IS_A— Taxonomy (ibuprofen IS_A NSAID) -
HAS_FINDING— Symptoms (MI HAS_FINDING chest pain) -
CONTRAINDICATED_WITH— Safety (NSAID ↔ CKD) -
INTERACTS_WITH— Drug interactions (warfarin ↔ aspirin) -
RED_FLAG_FOR— Emergency signs (chest pain + SOB → MI) -
TREATS— Therapeutics (amoxicillin TREATS infection)Current Graph is Production-Ready The 103 nodes you have cover:
Top 30 prescribed medications
- NSAIDs (ibuprofen, naproxen)
- Antihypertensives (lisinopril, enalapril)
- Diabetes meds (metformin)
- Anticoagulants (warfarin)
- Statins (atorvastatin)
Critical contraindications
- NSAID + CKD → BLOCKED
- Warfarin + pregnancy → BLOCKED
- ACE inhibitors + pregnancy → BLOCKED
Red flag patterns
- Chest pain + SOB → EMERGENCY
- Stroke signs → EMERGENCY
- Suicidal ideation → CRISIS
Drug interactions
- Warfarin + NSAIDs
- SSRIs + tramadol
- Methotrexate + NSAIDs
This covers 80%+ of common clinical scenarios in primary care.
Extended with
Safety Guardrails RxNorm + SNOMED (Drug)-[CONTRAINDICATED_WITH]->(Condition) Lab Data Grounding LOINC + SNOMED (Lab_Test)-[INTERPRETS_AS]->(Finding)
- RxNorm (The Safety Hook)
- Why: Patient Safety is a high-value category. RxNorm allows us to demonstrate "Hard Constraint" checks for medication errors.
- Demo: Have the agent suggest a common medication (like Ibuprofen) for a patient whose profile (in Neo4j) contains a contraindication (like Chronic Kidney Disease or a stomach ulcer). The agent should "catch" itself using the graph.
- Takeaway: This isn't just a chatbot; it's a safe clinical tool.
- Technical Wow: LOINC (The Lab Interpreter) LOINC covers lab tests and measurements.
- Why: It allows our Neurosymbolic agent to "read" raw data.
- Demo: Instead of the user saying "I have high blood sugar," have the user upload a mock lab report (or paste a value like HbA1c: 7.2%). Your agent uses LOINC to identify that 7.2% is a "High" finding and then traverses the SNOMED graph to link it to "Diabetes Mellitus."
- Takeaway: It can bridge the gap between raw data and clinical diagnosis.
Query Methods
# Check drug safety
kg.check_patient_safety("ibuprofen", ["chronic kidney disease"], ["warfarin"])
# Returns: [{"type": "contraindication", "severity": "serious", ...}]
# Get drug classes
kg.get_drug_class("ibuprofen")
# Returns: ["NSAID", "analgesic", "anti-inflammatory"]
# Find red flags
kg.get_red_flags(["chest pain", "shortness of breath"])
# Returns: [{"red_flag_for": "myocardial infarction", "urgency": "emergency", ...}]
Q&A:
Q: what is the benefit of using knowledge graph vs relational database A: multi-hop reasoning (no need for complex joins), flexible schema, Verifiable Truth (provides an explicit audit trail)
Analysis
View
Metric
- 7
- 6
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- AnthropicIn code
- CSSIn code
- FastAPIIn code
- Next.jsIn code
- OpenAIIn code
- PythonIn code
- ReactIn code
- Tailwind CSSIn code
- TypeScriptIn code
9 of 9 appear in the indexed code.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
344 KB
Source files
40
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
kwangel/care-ai
55 files · 654 KB · @ 855687e
Structure
Interface
26 files · 47%Screens, components and styles rendered to the user.
Application logic
4 files · 7%Domain rules, services and shared utilities.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- Python77%
- TypeScript19%
- Markdown4%
- CSS1%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
backend/requirements.txt
pypi · 14- anthropic
- fastapi
- httpx
- neo4j
- openai
- pydantic
- pydantic-settings
- pytest
- pytest-asyncio
- pytest-cov
- python-dotenv
- python-multipart
- requests
- uvicorn[standard]
frontend/package.json
npm · 12- next
- react
- react-dom
- react-markdown
- +8 more
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
This project’s features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.