We are going to build a clinical decision support agent that consumes free-text symptom descriptions and returns structured differential diagnoses with confidence scores, reasoning, and safety disclaimers. This tool is aimed at healthcare developers who need to prototype clinical NLP pipelines without managing infrastructure. Because Oxlo.ai uses flat per-request pricing instead of per-token metering, iterating on long patient histories stays predictable as context grows. See https://oxlo.ai/pricing for details.
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
1. Set Up the Oxlo.ai Client
First, we instantiate the OpenAI-compatible client pointing at Oxlo.ai and verify connectivity with a lightweight ping. I use llama-3.3-70b here because it is a reliable general-purpose flagship for structured tasks.
from openai import OpenAI
import os
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=os.environ.get("OXLO_API_KEY"))
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "user", "content": "Respond with OK"}
],
max_tokens=10
)
print(response.choices[0].message.content)
2. Define the System Prompt
The system prompt constrains the model to act as a clinical decision support assistant. It demands structured JSON, confidence scores, and a mandatory disclaimer that this is not a substitute for a licensed clinician.
SYSTEM_PROMPT = """You are a clinical decision support assistant. Your job is to suggest possible differential diagnoses based on the symptoms and history provided by a clinician.
Rules:
- Output ONLY valid JSON. Do not include markdown fences.
- Include a top-level field "disclaimer" with the exact text: "This is not medical advice. Final diagnosis requires evaluation by a qualified healthcare professional."
- Include "differentials": a list of up to 5 objects.
- Each object must have:
- "condition": string
- "confidence": integer between 1 and 100
- "reasoning": one-sentence clinical justification
- "recommended_tests": list of suggested tests or evaluations
- Rank by confidence, highest first.
- If symptoms are vague or life-threatening, note urgency in a top-level "urgency_note" field."""
print("System prompt loaded.")
3. Build the Diagnosis Function
Next, we wrap the API call in a function that accepts a raw symptom string, injects it into the user message, and parses the JSON response. I enable JSON mode via response_format to avoid parsing brittle text.
import json
from openai import OpenAI
import os
client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key=os.environ.get("OXLO_API_KEY"))
def generate_differentials(symptoms_text: str) -> dict:
user_message = f"Patient presentation: {symptoms_text}"
response = client.chat.completions.create(
model="llama-3.3-70b",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": user_message},
],
response_format={"type": "json_object"},
temperature=0.2,
max_tokens=1024
)
raw = response.choices[0].message.content
return json.loads(raw)
4. Add Validation and Safety Guardrails
Before we hit the API, we validate that the input is non-empty and reasonably bounded. After the response, we enforce that the disclaimer field exists. This layer keeps the tool predictable in a clinical workflow.
def safe_diagnose(symptoms_text: str) -> dict:
if not symptoms_text or len(symptoms_text.strip()) < 5:
raise ValueError("Symptom description is too short.")
if len(symptoms_text) > 8000:
raise ValueError("Input exceeds safe context window for this endpoint.")
result = generate_differentials(symptoms_text)
if "disclaimer" not in result:
result["disclaimer"] = (
"This is not medical advice. Final diagnosis requires evaluation "
"by a qualified healthcare professional."
)
return result
5. Create a Simple CLI
Finally, we add a small interactive loop so we can test cases quickly from the terminal. It catches JSON errors and prints formatted output.
import json
if __name__ == "__main__":
print("Clinical Decision Support Agent")
print("Type symptoms and press Enter. Ctrl+C to exit.\n")
while True:
try:
user_input = input("Symptoms: ").strip()
if not user_input:
continue
output = safe_diagnose(user_input)
print(json.dumps(output, indent=2))
print("-" * 40)
except KeyboardInterrupt:
print("\nExiting.")
break
except Exception as e:
print(f"Error: {e}")
print("-" * 40)
Run It
Export your key and run the script. Here is a real session using the example input.
$ export OXLO_API_KEY="sk-oxlo.ai-..."
$ python diagnose.py
Clinical Decision Support Agent
Type symptoms and press Enter. Ctrl+C to exit.
Symptoms: 38-year-old male, fever 39C for 3 days, severe headache, neck stiffness, photophobia. No rash.
{
"disclaimer": "This is not medical advice. Final diagnosis requires evaluation by a qualified healthcare professional.",
"urgency_note": "Possible acute CNS infection. Urgent evaluation required.",
"differentials": [
{
"condition": "Acute Bacterial Meningitis",
"confidence": 85,
"reasoning": "Classic triad of fever, headache, and nuchal rigidity with photophobia strongly suggests meningeal irritation.",
"recommended_tests": ["Lumbar puncture with CSF analysis", "Blood cultures", "CT head before LP if indicated"]
},
{
"condition": "Viral Meningitis",
"confidence": 60,
"reasoning": "Similar presentation but often less severe; remains a key differential in the absence of rash.",
"recommended_tests": ["Lumbar puncture with CSF PCR panel", "Basic metabolic panel"]
},
{
"condition": "Subarachnoid Hemorrhage",
"confidence": 25,
"reasoning": "Sudden severe headache can indicate SAH, though fever is less typical.",
"recommended_tests": ["Non-contrast CT head", "CT angiography if CT negative"]
}
]
}
Next Steps
Wire this agent into a FastAPI service and store outputs in a PostgreSQL audit table for clinician review. If you want to ground the reasoning in recent literature, pipe the condition names through Oxlo.ai's embeddings endpoint with BGE-Large and query a vector store of PubMed abstracts before returning results.
Top comments (0)