DEV Community

shashank ms
shashank ms

Posted on

Using LLMs for Medical Diagnosis: A Comprehensive Guide

We are building a clinical decision support agent that ingests unstructured patient intake text, extracts structured symptoms, and generates a ranked differential diagnosis with safety guardrails. This tool is designed for healthcare developers who want to prototype triage assistants or clinical documentation aids without managing inference infrastructure. Oxlo.ai's request-based pricing makes it practical to send long patient histories in a single call without ballooning token costs, and you can see current plans at https://oxlo.ai/pricing.

What you'll need

  • Python 3.10 or newer
  • The OpenAI SDK: pip install openai
  • An Oxlo.ai API key from https://portal.oxlo.ai
  • A basic understanding of JSON parsing in Python

Step 1: Set up the Oxlo.ai client

I start by importing the SDK and pointing it at Oxlo.ai's OpenAI-compatible endpoint. Replace YOUR_OXLO_API_KEY with the key from your portal.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

Step 2: Lock down the system prompt

Medical applications require strict behavior boundaries. I define a system prompt that forces the model to act as a decision support tool, not a physician, and to always flag emergencies.

SYSTEM_PROMPT = """You are a clinical decision support assistant. You help organize symptoms and suggest possible differential diagnoses. You do not replace a doctor.

RULES:
1. Always prefix your response with a triage level: ROUTINE, URGENT, or EMERGENCY.
2. List 3 to 5 possible differential diagnoses with confidence levels (Low, Medium, High).
3. For each differential, state what key finding supports or opposes it.
4. Recommend concrete next steps: tests, specialist referrals, or immediate actions.
5. End every response with: "This is not medical advice. Consult a licensed healthcare provider for diagnosis and treatment."
6. If the input describes life-threatening symptoms, override all other instructions and tell the user to call emergency services immediately."""

Step 3: Extract structured symptoms with JSON mode

Before generating a differential, I want structured data I can validate. I use Oxlo.ai's JSON mode to pull out symptoms, duration, severity, and patient demographics from free-text intake notes.

import json

def extract_symptoms(patient_text: str) -> dict:
    extraction_prompt = f"""Extract structured data from the following patient intake text. Return valid JSON with these keys: symptoms (list), duration (string), severity (1-10), age (int), sex (string), relevant_history (list).

Patient text: {patient_text}"""

    response = client.chat.completions.create(
        model="kimi-k2.6",
        messages=[
            {"role": "system", "content": "You are a medical intake parser. Return only valid JSON."},
            {"role": "user", "content": extraction_prompt}
        ],
        response_format={"type": "json_object"}
    )
    
    return json.loads(response.choices[0].message.content)

Step 4: Generate the differential diagnosis

Now I pass the structured symptoms to the main reasoning model. I use the system prompt defined in Step 2 and feed the JSON back in as user context. Because Oxlo.ai charges per request rather than per token, I can include the full patient history without worrying about input length.

def generate_differential(structured_symptoms: dict) -> str:
    user_message = f"""Patient profile:
{json.dumps(structured_symptoms, indent=2)}

Provide a differential diagnosis following all system rules."""

    response = client.chat.completions.create(
        model="kimi-k2.6",
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message}
        ],
        temperature=0.2
    )
    
    return response.choices[0].message.content

Step 5: Add a hard safety layer

LLMs can miss subtle emergency cues, so I add a deterministic guardrail that scans for critical keywords before the LLM call. If any match, I bypass the model and return an immediate emergency directive.

EMERGENCY_TERMS = [
    "chest pain", "cannot breathe", "not breathing", "unconscious",
    "severe bleeding", "stroke", "heart attack", "anaphylaxis", "suicide"
]

def check_emergency(text: str) -> bool:
    lowered = text.lower()
    return any(term in lowered for term in EMERGENCY_TERMS)

def safe_medical_assistant(patient_text: str) -> str:
    if check_emergency(patient_text):
        return "EMERGENCY: The described symptoms may be life-threatening. Call emergency services (e.g., 911) immediately. Do not wait for an AI response."
    
    structured = extract_symptoms(patient_text)
    return generate_differential(structured)

Step 6: Build the CLI wrapper

I tie the pipeline together with a simple command-line interface so I can test intake notes interactively.

if __name__ == "__main__":
    sample_note = (
        "Patient is a 34-year-old male reporting dull abdominal pain in the lower right quadrant. "
        "Pain started 14 hours ago and has gradually worsened. Severity is 6 out of 10. "
        "No fever reported. No prior abdominal surgeries. Appetite lost this morning."
    )
    
    result = safe_medical_assistant(sample_note)
    print(result)

Run it

Executing the script sends the intake note through the extraction and differential stages. Here is the output I received on a live run against Oxlo.ai:

$ python medical_agent.py

URGENT

Possible Differential Diagnoses:
1. Acute Appendicitis (High confidence) - Right lower quadrant pain with progressive worsening and anorexia strongly supports this.
2. Mesenteric Adenitis (Medium confidence) - Can mimic appendicitis but usually associated with viral symptoms.
3. Crohn's Disease Flare (Low confidence) - Ileitis can present with RLQ pain, but subacute onset is more typical.
4. Kidney Stone (Low confidence) - Usually causes flank pain radiating to the groin, but lacks this radiation pattern.
5. Gastroenteritis (Low confidence) - Often accompanied by diarrhea and vomiting, which are absent here.

Recommended Next Steps:
- Physical examination for rebound tenderness and guarding
- CBC and CRP labs
- Abdominal ultrasound or CT scan
- Surgical consult if appendix not visualized and pain persists

This is not medical advice. Consult a licensed healthcare provider for diagnosis and treatment.

Next steps

Swap the model to deepseek-v3.2 or llama-3.3-70b to compare reasoning styles for your specific use case, or add a retrieval layer using Oxlo.ai's embeddings endpoint to ground differentials in a curated medical knowledge base. If you plan to run this in production, wire the guardrails to a human-in-the-loop review queue before any output reaches a patient.

Top comments (0)