We are building a clinical intake analyzer that turns unstructured patient notes into structured JSON summaries for care teams. It extracts medications, allergies, symptoms, and flags safety concerns so clinicians can prep before an appointment. Because medical notes often run long, I run it on Oxlo.ai so each analysis costs one flat request regardless of note length.
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 sample intake note for testing
Step 1: Set up the Oxlo.ai client
I start by importing the OpenAI SDK and pointing it at Oxlo.ai. I use llama-3.3-70b as the workhorse model because it handles structured extraction reliably.
from openai import OpenAI
import json
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")
print("Client initialized")
Step 2: Define the system prompt
The system prompt is the only manual clinical engineering in the pipeline. It tells the model how to read messy notes and what schema to return. I keep it strict so downstream code can trust the shape.
SYSTEM_PROMPT = """
You are a clinical intake analyzer. Read the raw patient note and return a single JSON object with these exact keys:
- medications: list of current medications with dosages if mentioned
- allergies: list of known allergies
- symptoms: list of current symptoms with onset if noted
- missing_info: list of required data points that are absent
Be concise. Do not guess dosages. If a field has no data, return an empty list.
"""
Step 3: Extract structured data from a raw note
Now I feed a real note into the model. I enable JSON mode so I do not have to parse markdown around the output.
raw_note = """
Patient: Jane Doe, 58F
CC: follow-up for hypertension and recent dizziness
History: BP has been running 150s/90s at home. She ran out of lisinopril 10 mg two weeks ago because of insurance issues. She also takes metformin 500 mg BID for type 2 diabetes. She complains of intermittent dizziness for the past 3 days, worse when standing. No chest pain or shortness of breath. Allergies: NKDA. She denies smoking. Family history of stroke in father.
"""
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": raw_note},
],
response_format={"type": "json_object"},
)
structured = json.loads(response.choices[0].message.content)
print(json.dumps(structured, indent=2))
Step 4: Add summary and safety flags
Entity lists alone are not enough for a handoff. I update the prompt to ask for a plain-language summary and red flags. This gives nurses and physicians context without re-reading the source note.
SYSTEM_PROMPT_V2 = """
You are a clinical intake analyzer. Read the raw patient note and return a single JSON object with these exact keys:
- summary: a one-paragraph clinical summary for the care team
- medications: list of current medications with dosages if mentioned
- allergies: list of known allergies
- symptoms: list of current symptoms with onset if noted
- red_flags: list of urgent concerns or potential drug interactions
- missing_info: list of required data points that are absent
Be concise. Do not guess dosages. If a field has no data, return an empty list.
"""
Step 5: Wrap the call in a reusable function
I bundle the API call and JSON parsing into a single function. I also add basic error handling so a malformed response does not crash the pipeline.
def analyze_intake(note_text, model="llama-3.3-70b"):
try:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": SYSTEM_PROMPT_V2},
{"role": "user", "content": note_text},
],
response_format={"type": "json_object"},
)
return json.loads(response.choices[0].message.content)
except Exception as e:
return {"error": str(e), "raw_note": note_text}
# Test with the same note
result = analyze_intake(raw_note)
print(json.dumps(result, indent=2))
Step 6: Build a batch handoff report
Most clinics process more than one note at a time. I loop over a list of notes and print a compact handoff report that a charge nurse can scan in seconds.
notes = [
raw_note,
"""
Patient: Alice Chen, 34F. CC: medication refill and rash review.
Meds: amoxicillin 500 mg TID started 3 days ago for strep throat. Also on oral contraceptives.
Allergies: none documented.
Symptoms: pruritic maculopapular rash on trunk since yesterday. No angioedema, no SOB.
""",
]
for idx, note in enumerate(notes, 1):
r = analyze_intake(note)
print(f"Case {idx}: {r.get('summary', 'N/A')}")
if r.get("red_flags"):
print(f" RED FLAGS: {', '.join(r['red_flags'])}")
print("-" * 40)
Run it
Save the script as intake_analyzer.py, export your key, and run it. Here is a standalone example with a challenging note that includes a head strike and anticoagulation.
if __name__ == "__main__":
intake_note = """
Patient: Robert Jones, 71M.
PMH: atrial fibrillation, hypertension.
Medications: warfarin 5 mg daily, metoprolol 50 mg BID, lisinopril 10 mg daily.
Allergies: sulfa.
Symptoms: fell at home yesterday, hit head, no LOC, mild headache, small hematoma on forehead. No neuro deficits reported.
"""
result = analyze_intake(intake_note)
print(json.dumps(result, indent=2))
Expected output:
{
"summary": "71-year-old male with afib and HTN on warfarin who fell yesterday with head strike and mild headache.",
"medications": ["warfarin 5 mg daily", "metoprolol 50 mg BID", "lisinopril 10 mg daily"],
"allergies": ["sulfa"],
"symptoms": ["fell at home yesterday", "hit head", "mild headache", "small hematoma on forehead"],
"red_flags": ["head trauma while on anticoagulation", "fall risk"],
"missing_info": ["INR value", "neurological exam", "imaging results", "witness account"]
}
Wrap-up and next steps
From here, you can feed the JSON into a FHIR converter and write it directly to an EHR. If you want to catch more nuanced drug interactions, swap in kimi-k2.6 or deepseek-v3.2 for heavier reasoning without rewriting any client code.
If you process hundreds of long intake notes daily, flat per-request pricing on Oxlo.ai keeps costs predictable. See https://oxlo.ai/pricing for plan details.
Top comments (0)