Output Format Control
Constrain and shape LLM outputs with format instructions, examples, and schema definitions. Get consistent JSON, structured text, and typed responses every time.
Why Output Format Matters
Uncontrolled model output is prose — inconsistent structure, varying levels of detail, and difficult to parse programmatically. For production systems, you need predictable structure. Format control techniques range from simple prompt instructions to schema-constrained generation.
Format Instructions in Prompts
The simplest approach: tell the model exactly what format to produce:
from openai import OpenAI
client = OpenAI()
def extract_drug_interactions(clinical_text: str) -> str:
prompt = f"""Extract all drug interactions from the following clinical note.
Format your response EXACTLY as follows:INTERACTION 1: Drug A: [drug name] Drug B: [drug name] Severity: [Major | Moderate | Minor] Mechanism: [one sentence] Action Required: [specific clinical action]
INTERACTION 2: ...
If no interactions are present, respond with: NO INTERACTIONS IDENTIFIED
Clinical note:
{clinical_text}"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0,
)
return response.choices[0].message.content
note = """
Patient is a 68-year-old male on warfarin 5mg daily and aspirin 81mg daily
for a history of atrial fibrillation and coronary artery disease.
New prescription: clarithromycin 500mg BID for 10 days for community-acquired pneumonia.
"""
print(extract_drug_interactions(note))JSON Output with Prompt Instructions
import json
def extract_medications_json(clinical_text: str) -> list[dict]:
prompt = f"""Extract all medications from the following clinical text.
Return a JSON array. Each medication must have these exact keys:
- "name": string (drug name as written)
- "dose": string (dose and unit, or null if not specified)
- "frequency": string (e.g., "daily", "twice daily", or null if not specified)
- "route": string (e.g., "oral", "IV", or null if not specified)
- "indication": string (why the drug is given, or null if not stated)
Return ONLY valid JSON, no other text, no markdown code blocks.
Clinical text:
{clinical_text}"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0,
)
try:
return json.loads(response.choices[0].message.content)
except json.JSONDecodeError as e:
raise ValueError(f"Model returned invalid JSON: {e}\nOutput: {response.choices[0].message.content}")
medications = extract_medications_json("""
Mr. Johnson takes warfarin 5mg orally each evening for atrial fibrillation,
metoprolol succinate 50mg oral daily for rate control,
and atorvastatin 40mg at bedtime for hyperlipidemia.
New: clarithromycin 500mg PO BID × 10 days for CAP.
""")
for med in medications:
print(json.dumps(med, indent=2))Structured Output with OpenAI (JSON Mode)
OpenAI's response_format parameter guarantees valid JSON:
from openai import OpenAI
import json
client = OpenAI()
response = client.chat.completions.create(
model="gpt-4o",
response_format={"type": "json_object"}, # Guarantees valid JSON output
messages=[
{
"role": "system",
"content": """You are a drug interaction analyzer.
Always respond with a JSON object containing:
{
"interactions": [array of interaction objects],
"summary": "one sentence overall assessment",
"action_required": "immediate | monitor | routine | none"
}""",
},
{
"role": "user",
"content": "Analyze: patient on warfarin starting clarithromycin",
},
],
temperature=0,
)
result = json.loads(response.choices[0].message.content)
print(f"Action required: {result['action_required']}")
print(f"Interactions: {len(result['interactions'])}")Structured Output with Pydantic (OpenAI beta)
from openai import OpenAI
from pydantic import BaseModel, Field
from typing import Literal
client = OpenAI()
class DrugInteraction(BaseModel):
drug_a: str
drug_b: str
severity: Literal["major", "moderate", "minor"]
mechanism: str
management: str
monitoring_parameters: list[str]
class InteractionAnalysis(BaseModel):
interactions: list[DrugInteraction]
overall_risk: Literal["high", "moderate", "low", "none"]
primary_concern: str = Field(description="The most clinically significant issue in one sentence")
recommend_pharmacist_review: bool
# OpenAI structured output (beta API)
response = client.beta.chat.completions.parse(
model="gpt-4o",
messages=[
{
"role": "system",
"content": "You are a clinical pharmacologist. Analyze drug interactions and return structured data.",
},
{
"role": "user",
"content": "Analyze interactions for a patient on warfarin 5mg daily, starting clarithromycin 500mg BID.",
},
],
response_format=InteractionAnalysis,
)
analysis: InteractionAnalysis = response.choices[0].message.parsed
print(f"Overall risk: {analysis.overall_risk}")
print(f"Primary concern: {analysis.primary_concern}")
for interaction in analysis.interactions:
print(f"\n{interaction.drug_a} + {interaction.drug_b}: {interaction.severity}")
print(f" Management: {interaction.management}")Markdown and Structured Text
For human-readable output with consistent structure:
def generate_drug_monograph(drug_name: str) -> str:
"""Generate a consistently structured drug monograph."""
prompt = f"""Write a clinical drug monograph for {drug_name}.
Use EXACTLY this structure (use the exact headers shown):
# {drug_name}
## Classification
[Drug class and subclass]
## Mechanism of Action
[2-3 sentences on MOA]
## Indications
- [Indication 1]
- [Indication 2]
...
## Dosing
| Indication | Dose | Frequency | Notes |
|---|---|---|---|
| ... | ... | ... | ... |
## Monitoring Parameters
- [Parameter]: [Target / Frequency]
...
## Key Drug Interactions
| Interacting Drug | Severity | Mechanism | Management |
|---|---|---|---|
| ... | ... | ... | ... |
## Contraindications
- [Contraindication 1]
...
## Patient Counseling Points
1. [Point 1]
2. [Point 2]
...
Keep the entire monograph under 600 words."""
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
temperature=0,
)
return response.choices[0].message.content
monograph = generate_drug_monograph("warfarin")
print(monograph)Output Length Control
LENGTH_INSTRUCTIONS = {
"ultra_brief": "Respond in exactly one sentence.",
"brief": "Keep your response under 100 words.",
"standard": "Keep your response under 300 words.",
"detailed": "Provide a comprehensive response. No length limit.",
"tabular": "Present your answer as a table. Use no prose.",
"bullet_only": "Respond ONLY with a bulleted list. No paragraphs, no introduction.",
}
def ask_with_length_control(question: str, length_mode: str = "standard") -> str:
instruction = LENGTH_INSTRUCTIONS.get(length_mode, "")
system = f"You are a clinical pharmacology assistant. {instruction}"
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": system},
{"role": "user", "content": question},
],
temperature=0,
)
return response.choices[0].message.content
question = "What are the main drug interactions with warfarin?"
for mode in ["ultra_brief", "brief", "tabular", "bullet_only"]:
print(f"\n=== {mode.upper()} ===")
print(ask_with_length_control(question, mode))Few-Shot Format Examples
Showing examples of desired format is often more reliable than describing it:
FEW_SHOT_SYSTEM = """You are a drug interaction classifier.
For each interaction, classify it and provide the output in this format:
Example 1:
Input: warfarin + aspirin
Output:
severity: major
type: pharmacodynamic
mechanism: additive bleeding risk via platelet inhibition
action: use with extreme caution; monitor for bleeding; consider risk-benefit
Example 2:
Input: metformin + alcohol
Output:
severity: moderate
type: pharmacodynamic
mechanism: both increase lactic acid risk; alcohol impairs hepatic gluconeogenesis
action: advise patient to limit alcohol; monitor for lactic acidosis symptoms
Now classify the following interaction in the same format:"""
def classify_interaction(drug_a: str, drug_b: str) -> str:
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": FEW_SHOT_SYSTEM},
{"role": "user", "content": f"{drug_a} + {drug_b}"},
],
temperature=0,
)
return response.choices[0].message.content
print(classify_interaction("clarithromycin", "warfarin"))Parsing and Validation
Always validate structured output before using it:
import json
from typing import Any
def safe_parse_json(raw: str, fallback: Any = None) -> Any:
"""Parse JSON, stripping markdown code fences if present."""
# Remove markdown code blocks that models sometimes add despite instructions
clean = raw.strip()
if clean.startswith("```"):
clean = clean.split("\n", 1)[1] # Remove first line (```json)
if clean.endswith("```"):
clean = clean.rsplit("\n", 1)[0] # Remove last line (```)
clean = clean.strip()
try:
return json.loads(clean)
except json.JSONDecodeError:
if fallback is not None:
return fallback
raise
def validate_interaction_structure(data: dict) -> bool:
"""Validate that interaction data has required fields."""
required = {"drug_a", "drug_b", "severity", "mechanism", "management"}
valid_severities = {"major", "moderate", "minor"}
if not isinstance(data, dict):
return False
if not required.issubset(data.keys()):
return False
if data.get("severity") not in valid_severities:
return False
if not all(isinstance(data.get(k), str) for k in ["drug_a", "drug_b", "mechanism", "management"]):
return False
return TrueFormat control + validation is the foundation of reliable LLM pipelines. Never trust raw model output in production without parsing and validation.
Found this helpful?
Leave a comment
Have a question, correction, or just found this helpful? Leave a note below.