IDAgentsFreshTest / tools /generate_board_exam_question.py
IDAgents Developer
Deploy COMPLETE ID Agents - Medical AI system (clean, no cache files)
8120936
raw
history blame
188 kB
"""
generate_board_exam_question.py
-------------------------------
Tool for generating comprehensive board exam style questions with detailed explanations.
This tool creates long vignette-style multiple choice questions similar to those found in medical
board exams, complete with detailed explanations for each answer choice that teach key concepts.
Key Features:
- Generates realistic clinical vignettes using OpenAI
- Creates 4-5 multiple choice options
- Provides detailed explanations for why each wrong answer is incorrect
- Explains why the correct answer is right with educational content
- Focuses on high-yield board exam topics
- Uses AI-powered quality review for educational excellence
"""
import asyncio
import json
from typing import Any, Dict, List, Union, Optional
from tools.base import Tool
from tools.utils import ToolExecutionError, logger, load_prompt
class GenerateBoardExamQuestionTool(Tool):
"""
Tool for generating comprehensive board exam style questions with detailed explanations.
This tool creates realistic clinical vignettes with multiple choice questions and
provides educational explanations for each answer choice.
"""
def __init__(self) -> None:
"""Initialize the GenerateBoardExamQuestionTool."""
super().__init__()
self.name = "generate_board_exam_question"
self.description = "Generate comprehensive board exam style questions with detailed explanations for medical education."
self.args_schema = {
"type": "object",
"properties": {
"topic": {
"type": "string",
"description": "The medical topic or condition to create a board exam question about (e.g., 'pneumonia', 'heart failure', 'diabetes management')"
},
"difficulty_level": {
"type": "string",
"description": "The difficulty level of the question",
"enum": ["medical_student", "resident", "board_exam", "advanced"],
"default": "board_exam"
},
"question_type": {
"type": "string",
"description": "The type of question to generate",
"enum": ["diagnosis", "management", "pathophysiology", "pharmacology", "complications"],
"default": "diagnosis"
}
},
"required": ["topic"]
}
def openai_spec(self, legacy=False):
"""Return OpenAI function specification."""
return {
"name": self.name,
"description": self.description,
"parameters": self.args_schema
}
async def run(
self,
topic: str,
difficulty_level: str = "board_exam",
question_type: str = "diagnosis"
) -> Dict[str, Any]:
"""
Generate a comprehensive board exam question using a streamlined 2-step AI pipeline.
Step 1: Generate question blueprint (differential, clues, reasoning strategy)
Step 2: Draft initial question + critique + enhance in one combined step
Args:
topic (str): The medical topic to create a question about
difficulty_level (str): The difficulty level (medical_student, resident, board_exam, advanced)
question_type (str): The type of question (diagnosis, management, pathophysiology, pharmacology, complications)
Returns:
Dict[str, Any]: Complete board exam question with vignette, options, and explanations
"""
try:
logger.info(f"Generating streamlined 2-step board exam question for topic: {topic}")
# Step 1: Generate Question Blueprint (differential strategy and clues)
blueprint = await self._generate_question_blueprint(topic, difficulty_level, question_type)
# Step 2: Draft + Critique + Enhance in one combined step
final_result = await self._draft_critique_enhance(blueprint, topic, difficulty_level, question_type)
return final_result
# Format the complete question
complete_question = {
"topic": topic,
"difficulty_level": difficulty_level,
"question_type": question_type,
"blueprint": blueprint,
"vignette": final_result["vignette"],
"question_stem": final_result["question_stem"],
"answer_choices": final_result["answer_choices"],
"correct_answer": "A", # First choice is always correct in our format
"explanations": final_result["explanations"],
"enhancement_notes": final_result.get("enhancement_notes", ""),
"clinical_reasoning_notes": "Streamlined 2-step AI generation with blueprint + critique",
"question_level": "ID Fellowship Board-Level Difficulty",
"generation_method": "2-step OpenAI pipeline: Blueprint + Draft/Critique/Enhance"
}
logger.info(f"Successfully generated 2-step board exam question for {topic}")
return complete_question
except Exception as e:
logger.error(f"AI-powered GenerateBoardExamQuestionTool failed: {e}", exc_info=True)
raise ToolExecutionError(f"Failed to generate AI board exam question: {e}")
async def _generate_question_blueprint(self, topic: str, difficulty_level: str, question_type: str) -> Dict[str, Any]:
"""
Step 1: Generate the strategic blueprint for the question.
Creates the differential diagnosis strategy, clues, and reasoning approach.
"""
try:
logger.info(f"Generating question blueprint for {topic}")
from core.utils.llm_connector import call_llm
# Load the blueprint prompt template
prompt = load_prompt("generate_question_blueprint.j2",
topic=topic,
difficulty_level=difficulty_level,
question_type=question_type
)
# Call OpenAI to generate the blueprint
try:
response = await asyncio.wait_for(
call_llm(prompt),
timeout=30.0
)
logger.info(f"Blueprint generated: {len(response) if response else 0} characters")
except asyncio.TimeoutError:
logger.warning("Blueprint generation timed out")
return self._fallback_blueprint(topic)
# Parse JSON response
if not response or response.strip() == "":
logger.warning("Empty blueprint response")
return self._fallback_blueprint(topic)
# Clean and parse response
response = response.strip()
if response.startswith("```json"):
response = response[7:]
if response.endswith("```"):
response = response[:-3]
blueprint = json.loads(response.strip())
return blueprint
except Exception as e:
logger.error(f"Error generating question blueprint: {str(e)}")
return self._fallback_blueprint(topic)
async def _draft_critique_enhance(self, blueprint: Dict[str, Any], topic: str, difficulty_level: str, question_type: str) -> Dict[str, Any]:
"""
Step 2: Draft the initial question, critique it, and enhance in one combined step.
"""
try:
logger.info(f"Drafting, critiquing, and enhancing question for {topic}")
from core.utils.llm_connector import call_llm
# Load the combined draft+critique+enhance prompt template
prompt = load_prompt("draft_critique_enhance_board_exam.j2",
blueprint=blueprint,
topic=topic,
difficulty_level=difficulty_level,
question_type=question_type
)
# Call OpenAI for the combined draft+critique+enhance step
try:
response = await asyncio.wait_for(
call_llm(prompt),
timeout=45.0 # Longer timeout for combined step
)
logger.info(f"Draft+Critique+Enhance completed: {len(response) if response else 0} characters")
except asyncio.TimeoutError:
logger.warning("Draft+Critique+Enhance timed out")
return await self._fallback_question_generation(topic, difficulty_level, question_type)
# Parse JSON response
if not response or response.strip() == "":
logger.warning("Empty draft+critique+enhance response")
return await self._fallback_question_generation(topic, difficulty_level, question_type)
# Clean and parse response
response = response.strip()
if response.startswith("```json"):
response = response[7:]
if response.endswith("```"):
response = response[:-3]
final_result = json.loads(response.strip())
return final_result
except Exception as e:
logger.error(f"Error in draft+critique+enhance: {str(e)}")
return await self._fallback_question_generation(topic, difficulty_level, question_type)
def _fallback_blueprint(self, topic: str) -> Dict[str, Any]:
"""Fallback blueprint if AI generation fails"""
return {
"scenario_description": f"Clinical presentation involving {topic}",
"primary_diagnosis": topic,
"differential_diagnoses": [
"Primary target condition",
"Common alternative 1",
"Common alternative 2",
"Less likely option 1",
"Less likely option 2"
],
"diagnostic_clues": {
"supporting_primary": ["Clinical finding 1", "Clinical finding 2", "Lab finding"],
"misleading_clues": ["Distracting finding 1", "Distracting finding 2"]
},
"reasoning_strategy": f"Question will test differential diagnosis skills for {topic}"
}
async def _fallback_question_generation(self, topic: str, difficulty_level: str, question_type: str) -> Dict[str, Any]:
"""Fallback question generation if AI steps fail"""
return {
"topic": topic,
"vignette": f"A patient presents with clinical findings consistent with {topic}. Further evaluation is needed to determine the most appropriate diagnosis and management.",
"question_stem": "What is the most likely diagnosis?",
"answer_choices": [
topic,
"Alternative diagnosis 1",
"Alternative diagnosis 2",
"Alternative diagnosis 3",
"Alternative diagnosis 4"
],
"explanations": {
"correct": f"The clinical presentation is most consistent with {topic}.",
"incorrect": "The other options are less likely given the clinical presentation."
},
"enhancement_notes": "Fallback question generated due to AI processing error"
}
async def _search_medical_guidelines(self, topic: str, question_type: str) -> Dict[str, Any]:
"""
NEW Step 0: Search medical guidelines and evidence-based sources for the topic.
This provides rich, current medical knowledge to inform question generation.
"""
try:
logger.info(f"Searching medical guidelines for topic: {topic}")
# Use internet search tool to find current medical guidelines
from tools.internet_search import InternetSearchTool
search_tool = InternetSearchTool()
# Create focused search queries for medical guidelines
guideline_queries = [
f"{topic} clinical practice guidelines 2024 2023",
f"{topic} diagnosis guidelines AHA ACC ESC",
f"{topic} management protocol evidence based medicine",
f"{topic} differential diagnosis clinical criteria"
]
guideline_findings = {
"references": [],
"summary": "",
"key_findings": [],
"diagnostic_criteria": [],
"differential_points": []
}
# Search for guidelines and evidence
for query in guideline_queries[:2]: # Limit to 2 searches to avoid timeout
try:
logger.info(f"Searching: {query}")
search_results = await search_tool.run(q=query, max_results=3)
if search_results and isinstance(search_results, dict) and 'results' in search_results:
for result in search_results['results'][:2]: # Top 2 results per query
if any(source in result.get('href', '').lower() for source in
['guidelines', 'aha.org', 'acc.org', 'esc.org', 'uptodate', 'nejm', 'cochrane']):
guideline_findings["references"].append({
"title": result.get('title', ''),
"url": result.get('href', ''),
"snippet": result.get('snippet', '')
})
# Extract key findings from snippet
snippet = result.get('snippet', '')
if snippet:
guideline_findings["key_findings"].append(snippet)
except Exception as e:
logger.warning(f"Search query failed: {query} - {e}")
continue
# Summarize findings
if guideline_findings["key_findings"]:
guideline_findings["summary"] = f"Found {len(guideline_findings['references'])} guideline sources for {topic}"
logger.info(f"Successfully found {len(guideline_findings['references'])} guideline references")
else:
guideline_findings["summary"] = f"No specific guidelines found, using general medical knowledge for {topic}"
logger.warning("No guideline sources found, will use general medical knowledge")
return guideline_findings
except Exception as e:
logger.warning(f"Guideline search failed: {e}")
# Return empty findings - the system will work with general knowledge
return {
"references": [],
"summary": f"Guideline search unavailable, using general medical knowledge for {topic}",
"key_findings": [],
"diagnostic_criteria": [],
"differential_points": []
}
async def _generate_ai_comparison_table(self, topic: str, difficulty_level: str, question_type: str, guideline_findings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""
Step 1: Use OpenAI to generate a comprehensive comparison differential table.
Now enhanced with evidence-based guideline findings.
"""
try:
from core.utils.llm_connector import call_llm
import asyncio
logger.info(f"Loading prompt template for comparison table...")
# Prepare guideline context for the prompt
guideline_context = ""
if guideline_findings and guideline_findings.get("key_findings"):
guideline_context = f"""
EVIDENCE-BASED CONTEXT from Medical Guidelines:
{chr(10).join(guideline_findings["key_findings"][:3])}
GUIDELINE SOURCES:
{chr(10).join([f"- {ref.get('title', 'Unknown')}" for ref in guideline_findings.get("references", [])[:3]])}
"""
else:
guideline_context = "No specific guidelines found. Use standard medical knowledge."
prompt = load_prompt(
"generate_comparison_table.j2",
topic=topic,
difficulty_level=difficulty_level,
question_type=question_type,
guideline_context=guideline_context
)
logger.info(f"Prompt loaded successfully, making OpenAI API call...")
# Call OpenAI with timeout to generate the comparison table
try:
response = await asyncio.wait_for(
call_llm(prompt),
timeout=30.0 # 30 second timeout
)
logger.info(f"OpenAI response received: {len(response) if response else 0} characters")
except asyncio.TimeoutError:
logger.warning("OpenAI API call timed out after 30 seconds")
return await self._fallback_comparison_table(topic, difficulty_level, question_type, guideline_findings)
# Validate and parse the JSON response
if not response or response.strip() == "":
logger.warning("OpenAI returned empty response")
return await self._fallback_comparison_table(topic, difficulty_level, question_type, guideline_findings)
# Clean the response - remove markdown code blocks if present
cleaned_response = response.strip()
if cleaned_response.startswith("```json"):
cleaned_response = cleaned_response[7:] # Remove ```json
if cleaned_response.endswith("```"):
cleaned_response = cleaned_response[:-3] # Remove ```
cleaned_response = cleaned_response.strip()
try:
comparison_data = json.loads(cleaned_response)
logger.info("Successfully parsed OpenAI JSON response")
return {
"table": comparison_data,
"generation_method": "AI-powered differential reasoning with evidence-based guidelines",
"model_used": "OpenAI",
"guideline_sources": guideline_findings.get("references", []) if guideline_findings else [],
"evidence_summary": guideline_findings.get("summary", "") if guideline_findings else ""
}
except json.JSONDecodeError as e:
logger.error(f"Failed to parse comparison table JSON: {e}")
logger.error(f"Raw response: {response[:200]}...")
logger.error(f"Cleaned response: {cleaned_response[:200]}...")
# Fallback to deterministic method if JSON parsing fails
return await self._fallback_comparison_table(topic, difficulty_level, question_type, guideline_findings)
except Exception as e:
logger.error(f"AI comparison table generation failed: {e}")
# Fallback to deterministic method
return await self._fallback_comparison_table(topic, difficulty_level, question_type, guideline_findings)
async def _generate_ai_vignette_and_question(self, topic: str, difficulty_level: str, question_type: str, comparison_table: Dict[str, Any]) -> Dict[str, Any]:
"""
Step 2: Use OpenAI to generate complete vignette, question stem, answer choices, and explanations.
Replaces the old deterministic steps 4-6 with AI-powered clinical scenario creation.
"""
try:
from core.utils.llm_connector import call_llm
import asyncio
# Convert comparison table to JSON string for the prompt
table_json = json.dumps(comparison_table.get("table", {}), indent=2)
logger.info(f"Loading vignette prompt template...")
prompt = load_prompt(
"generate_board_exam_vignette.j2",
topic=topic,
difficulty_level=difficulty_level,
question_type=question_type,
comparison_table=table_json
)
logger.info(f"Making OpenAI API call for vignette generation...")
# Call OpenAI with timeout to generate the complete question
try:
response = await asyncio.wait_for(
call_llm(prompt),
timeout=45.0 # 45 second timeout for more complex generation
)
logger.info(f"OpenAI vignette response received: {len(response) if response else 0} characters")
except asyncio.TimeoutError:
logger.warning("OpenAI vignette API call timed out after 45 seconds")
return await self._fallback_vignette_generation(topic, comparison_table)
# Validate and parse the JSON response
if not response or response.strip() == "":
logger.warning("OpenAI returned empty vignette response")
return await self._fallback_vignette_generation(topic, comparison_table)
# Clean the response - remove markdown code blocks if present
cleaned_response = response.strip()
if cleaned_response.startswith("```json"):
cleaned_response = cleaned_response[7:] # Remove ```json
if cleaned_response.endswith("```"):
cleaned_response = cleaned_response[:-3] # Remove ```
cleaned_response = cleaned_response.strip()
try:
vignette_data = json.loads(cleaned_response)
logger.info("Successfully parsed OpenAI vignette JSON response")
return {
**vignette_data,
"generation_method": "AI-powered clinical scenario creation",
"model_used": "OpenAI"
}
except json.JSONDecodeError as e:
logger.error(f"Failed to parse vignette JSON: {e}")
logger.error(f"Raw response: {response[:200]}...")
logger.error(f"Cleaned response: {cleaned_response[:200]}...")
# Fallback to deterministic method if JSON parsing fails
return await self._fallback_vignette_generation(topic, comparison_table)
except Exception as e:
logger.error(f"AI vignette generation failed: {e}")
# Fallback to deterministic method
return await self._fallback_vignette_generation(topic, comparison_table)
async def _ai_quality_review(self, topic: str, vignette_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Step 3: Use OpenAI to perform comprehensive quality review of the generated question.
Provides AI-powered assessment of clinical accuracy, educational value, and appropriateness.
"""
try:
from core.utils.llm_connector import call_llm
import asyncio
logger.info(f"Loading quality review prompt template...")
prompt = load_prompt(
"quality_review_board_exam.j2",
topic=topic,
vignette=vignette_data.get("vignette", ""),
question_stem=vignette_data.get("question_stem", ""),
answer_choices=json.dumps(vignette_data.get("answer_choices", [])),
explanations=json.dumps(vignette_data.get("explanations", {}))
)
logger.info(f"Making OpenAI API call for quality review...")
# Call OpenAI for quality assessment with timeout
try:
response = await asyncio.wait_for(
call_llm(prompt),
timeout=30.0 # 30 second timeout
)
logger.info(f"OpenAI quality review response received: {len(response) if response else 0} characters")
except asyncio.TimeoutError:
logger.warning("OpenAI quality review API call timed out after 30 seconds")
return self._fallback_quality_review(vignette_data)
# Validate and parse the JSON response
if not response or response.strip() == "":
logger.warning("OpenAI returned empty quality review response")
return self._fallback_quality_review(vignette_data)
# Clean the response - remove markdown code blocks if present
cleaned_response = response.strip()
if cleaned_response.startswith("```json"):
cleaned_response = cleaned_response[7:] # Remove ```json
if cleaned_response.endswith("```"):
cleaned_response = cleaned_response[:-3] # Remove ```
cleaned_response = cleaned_response.strip()
try:
quality_data = json.loads(cleaned_response)
logger.info("Successfully parsed OpenAI quality review JSON response")
return {
**quality_data,
"review_method": "AI-powered quality assessment",
"model_used": "OpenAI"
}
except json.JSONDecodeError as e:
logger.error(f"Failed to parse quality review JSON: {e}")
logger.error(f"Raw response: {response[:200]}...")
logger.error(f"Cleaned response: {cleaned_response[:200]}...")
# Fallback to deterministic quality scoring
return self._fallback_quality_review(vignette_data)
except Exception as e:
logger.error(f"AI quality review failed: {e}")
# Fallback to deterministic quality scoring
return self._fallback_quality_review(vignette_data)
async def _ai_final_enhancement(self, topic: str, vignette_data: Dict[str, Any], quality_review: Dict[str, Any]) -> Dict[str, Any]:
"""
NEW Step 4: Final AI enhancement - Apply critique and ensure board-level ID fellowship difficulty.
This step takes the quality review feedback and enhances the question to ensure:
1. Board-level difficulty appropriate for ID fellowship
2. Correct answer is not given away in the vignette
3. All critique points from quality review are addressed
"""
try:
from core.utils.llm_connector import call_llm
import asyncio
logger.info(f"Loading final enhancement prompt template...")
# Prepare quality review context
quality_feedback = json.dumps(quality_review, indent=2)
current_vignette = vignette_data.get("vignette", "")
current_question = vignette_data.get("question_stem", "")
current_choices = json.dumps(vignette_data.get("answer_choices", []))
current_explanations = json.dumps(vignette_data.get("explanations", {}))
prompt = load_prompt(
"final_enhancement_board_exam.j2",
topic=topic,
current_vignette=current_vignette,
current_question=current_question,
current_choices=current_choices,
current_explanations=current_explanations,
quality_feedback=quality_feedback
)
logger.info(f"Making OpenAI API call for final enhancement...")
# Call OpenAI for final enhancement with timeout
try:
response = await asyncio.wait_for(
call_llm(prompt),
timeout=45.0 # 45 second timeout for complex enhancement
)
logger.info(f"OpenAI final enhancement response received: {len(response) if response else 0} characters")
except asyncio.TimeoutError:
logger.warning("OpenAI final enhancement API call timed out after 45 seconds")
return self._fallback_final_enhancement(vignette_data, quality_review)
# Validate and parse the JSON response
if not response or response.strip() == "":
logger.warning("OpenAI returned empty final enhancement response")
return self._fallback_final_enhancement(vignette_data, quality_review)
# Clean the response - remove markdown code blocks if present
cleaned_response = response.strip()
if cleaned_response.startswith("```json"):
cleaned_response = cleaned_response[7:] # Remove ```json
if cleaned_response.endswith("```"):
cleaned_response = cleaned_response[:-3] # Remove ```
cleaned_response = cleaned_response.strip()
try:
enhanced_data = json.loads(cleaned_response)
logger.info("Successfully parsed OpenAI final enhancement JSON response")
return {
**enhanced_data,
"enhancement_method": "AI-powered final enhancement for ID board difficulty",
"model_used": "OpenAI"
}
except json.JSONDecodeError as e:
logger.error(f"Failed to parse final enhancement JSON: {e}")
logger.error(f"Raw response: {response[:200]}...")
logger.error(f"Cleaned response: {cleaned_response[:200]}...")
# Fallback to original vignette if JSON parsing fails
return self._fallback_final_enhancement(vignette_data, quality_review)
except Exception as e:
logger.error(f"AI final enhancement failed: {e}")
# Fallback to original vignette
return self._fallback_final_enhancement(vignette_data, quality_review)
def _fallback_final_enhancement(self, vignette_data: Dict[str, Any], quality_review: Dict[str, Any]) -> Dict[str, Any]:
"""Fallback method for final enhancement when AI fails."""
logger.warning("Using fallback for final enhancement - returning original question with minor adjustments")
# Return original with minor enhancement notes
enhanced_data = {
"vignette": vignette_data.get("vignette", ""),
"question_stem": vignette_data.get("question_stem", ""),
"answer_choices": vignette_data.get("answer_choices", []),
"explanations": vignette_data.get("explanations", {}),
"enhancement_notes": "Fallback enhancement: Original question maintained due to API limitations",
"enhancement_method": "Fallback - no enhancement applied",
"model_used": "Deterministic"
}
return enhanced_data
async def _fallback_comparison_table(self, topic: str, difficulty_level: str, question_type: str, guideline_findings: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
"""Fallback method for comparison table generation when AI fails."""
logger.warning("Using fallback deterministic comparison table generation")
# Simple fallback - create a basic comparison table
fallback_table = {
"correct_diagnosis": topic,
"differential_diagnoses": ["Alternative diagnosis 1", "Alternative diagnosis 2", "Alternative diagnosis 3", "Alternative diagnosis 4"],
"comparison_criteria": ["Clinical presentation", "Laboratory findings", "Imaging findings", "Treatment response"]
}
# Include guideline references if available
references = []
if guideline_findings and guideline_findings.get("references"):
references = guideline_findings["references"]
return {
"table": fallback_table,
"generation_method": "Fallback deterministic method",
"model_used": "Deterministic",
"guideline_sources": references,
"evidence_summary": guideline_findings.get("summary", "") if guideline_findings else ""
}
async def _fallback_vignette_generation(self, topic: str, comparison_table: Dict[str, Any]) -> Dict[str, Any]:
"""Fallback method for vignette generation when AI fails."""
logger.warning("Using fallback deterministic vignette generation")
# Simple fallback vignette
fallback_vignette = {
"vignette": f"A patient presents with symptoms consistent with {topic}. Clinical evaluation reveals relevant findings.",
"question_stem": f"What is the most likely diagnosis?",
"answer_choices": [
topic, # Correct answer
"Alternative diagnosis 1",
"Alternative diagnosis 2",
"Alternative diagnosis 3",
"Alternative diagnosis 4"
],
"explanations": {
"correct": f"The clinical presentation is most consistent with {topic}.",
"incorrect": "The other options are less likely given the clinical presentation."
}
}
return {
**fallback_vignette,
"generation_method": "Fallback deterministic method",
"model_used": "Deterministic"
}
def _fallback_quality_review(self, vignette_data: Dict[str, Any]) -> Dict[str, Any]:
"""Fallback method for quality review when AI fails."""
logger.warning("Using fallback deterministic quality review")
# Simple quality scoring
return {
"percentage_score": 75,
"clinical_accuracy": "Adequate",
"educational_value": "Moderate",
"improvements_needed": ["Enhance clinical specificity", "Add more detailed explanations"],
"review_method": "Fallback deterministic scoring",
"model_used": "Deterministic"
}
def _identify_discriminating_features(self, topic: str, question_type: str) -> Dict[str, List[str]]:
"""
Identify 3 key discriminating features that establish the diagnosis.
This ensures the question tests true clinical reasoning rather than pattern recognition.
"""
discriminating_features = {
"diagnostic_clues": [],
"differentials_ruled_out": [],
"reasoning_pathway": []
}
if "histoplasmosis" in topic.lower() or ("dimorphic" in topic.lower() and "histoplasma" in topic.lower()):
discriminating_features = {
"diagnostic_clues": [
"Geographic exposure to Ohio River Valley with cave/soil activities",
"Bilateral hilar lymphadenopathy with multiple small pulmonary nodules",
"Lymphopenia (characteristic vs. neutrophilia in bacterial infections)"
],
"differentials_ruled_out": [
"Coccidioidomycosis (wrong geographic region, different antigen)",
"Blastomycosis (typically unilateral, skin lesions, different morphology)",
"Sarcoidosis (no geographic exposure, different antigen pattern)"
],
"reasoning_pathway": [
"Endemic mycosis in appropriate geographic region",
"Characteristic imaging pattern and immune response",
"Specific laboratory confirmation with urine antigen"
]
}
elif "coccidioidomycosis" in topic.lower():
discriminating_features = {
"diagnostic_clues": [
"Geographic exposure to southwestern US (Arizona, California)",
"Erythema nodosum with arthralgias (Valley fever syndrome)",
"Peripheral eosinophilia and elevated ESR"
],
"differentials_ruled_out": [
"Histoplasmosis (wrong geographic region, no cave exposure)",
"Community-acquired pneumonia (no eosinophilia, different imaging)",
"Sarcoidosis (geographic exposure history, different serology)"
],
"reasoning_pathway": [
"Desert southwest exposure with dust inhalation",
"Characteristic immune-mediated manifestations",
"Specific serologic and antigen testing"
]
}
elif "blastomycosis" in topic.lower():
discriminating_features = {
"diagnostic_clues": [
"Geographic exposure to Great Lakes region or southeastern US",
"Verrucous skin lesions with central ulceration",
"Broad-based budding yeast on histology"
],
"differentials_ruled_out": [
"Histoplasmosis (skin lesions rare, different morphology)",
"Sporotrichosis (different lesion pattern, occupational exposure)",
"Squamous cell carcinoma (histology shows organisms)"
],
"reasoning_pathway": [
"Endemic region with outdoor recreational activities",
"Characteristic cutaneous manifestations",
"Distinctive microscopic morphology"
]
}
elif "candida auris" in topic.lower():
discriminating_features = {
"diagnostic_clues": [
"Healthcare exposure with invasive devices (central lines, ventilators)",
"Multi-drug resistance to fluconazole, amphotericin B, and echinocandins",
"Rapid transmission in healthcare settings with environmental persistence"
],
"differentials_ruled_out": [
"Candida albicans (typically fluconazole-sensitive, different MALDI-TOF pattern)",
"Candida parapsilosis (usually echinocandin-sensitive, lower MIC patterns)",
"Candida glabrata (different resistance profile, distinct molecular identification)"
],
"reasoning_pathway": [
"High-risk healthcare environment with device exposure",
"Distinctive antifungal resistance pattern requiring molecular ID",
"Infection control implications requiring immediate isolation"
]
}
elif "pneumonia" in topic.lower():
discriminating_features = {
"diagnostic_clues": [
"Positive urinary pneumococcal antigen with lobar consolidation",
"Procalcitonin >2.0 ng/mL indicating bacterial etiology",
"CURB-65 score elements for severity assessment"
],
"differentials_ruled_out": [
"Atypical pneumonia (different imaging, lower procalcitonin)",
"Viral pneumonia (no positive bacterial antigen)",
"Pulmonary embolism (no consolidation, different biomarkers)"
],
"reasoning_pathway": [
"Bacterial vs. atypical vs. viral etiology determination",
"Severity assessment for treatment location",
"Targeted antibiotic selection based on pathogen"
]
}
else:
# Generic discriminating features
discriminating_features = {
"diagnostic_clues": [
f"Specific laboratory finding unique to {topic}",
f"Characteristic imaging pattern for {topic}",
f"Epidemiologic factor supporting {topic}"
],
"differentials_ruled_out": [
f"Common differential #1 with different laboratory pattern",
f"Common differential #2 with different imaging findings",
f"Common differential #3 with different epidemiology"
],
"reasoning_pathway": [
f"Recognition of {topic} pattern",
f"Systematic differential diagnosis approach",
f"Integration of clinical and laboratory data"
]
}
return discriminating_features
def _analyze_clinical_context(self, topic: str, question_type: str, difficulty_level: str) -> Dict[str, Any]:
"""
Step 1: Medical Knowledge Reasoning
Analyze the topic to understand its clinical context, pathophysiology, and key characteristics.
This provides the foundation for creating clinically accurate content.
"""
clinical_context = {
"condition_category": "",
"pathophysiology": "",
"key_clinical_features": [],
"diagnostic_approach": "",
"treatment_principles": "",
"complications": [],
"epidemiology": "",
"board_exam_focus": ""
}
# Analyze specific conditions with medical reasoning
if "aspergillus" in topic.lower():
if "niger" in topic.lower():
clinical_context = {
"condition_category": "Invasive fungal infection - Aspergillosis",
"pathophysiology": "Aspergillus niger causes invasive pulmonary aspergillosis in immunocompromised hosts, with angioinvasion leading to tissue necrosis and hemorrhage",
"key_clinical_features": [
"Fever unresponsive to antibiotics in neutropenic patients",
"Hemoptysis and pleuritic chest pain",
"Rapid progression with tissue necrosis",
"Elevated galactomannan antigen"
],
"diagnostic_approach": "CT chest showing nodules with halo sign, galactomannan testing, tissue biopsy with septate hyphae",
"treatment_principles": "Voriconazole or amphotericin B for invasive disease, requires prolonged therapy",
"complications": ["Massive hemoptysis", "Disseminated infection", "CNS invasion"],
"epidemiology": "Immunocompromised patients, particularly neutropenic patients and stem cell transplant recipients",
"board_exam_focus": "Recognition in immunocompromised host, differentiation from other molds, antifungal selection"
}
else:
# Generic Aspergillus
clinical_context = {
"condition_category": "Invasive fungal infection - Aspergillosis",
"pathophysiology": "Aspergillus species cause spectrum from allergic reactions to invasive disease with angioinvasion",
"key_clinical_features": [
"Varies by host: ABPA in asthmatics, aspergilloma in cavitary disease, invasive in immunocompromised",
"Hemoptysis, cough, dyspnea",
"Fever and systemic symptoms in invasive disease"
],
"diagnostic_approach": "Imaging, galactomannan antigen, tissue diagnosis",
"treatment_principles": "Voriconazole first-line for invasive, surgical resection for aspergilloma",
"complications": ["Massive bleeding", "Respiratory failure", "Dissemination"],
"epidemiology": "Ubiquitous environmental mold, opportunistic pathogen",
"board_exam_focus": "Clinical syndrome recognition, diagnostic approach, treatment selection"
}
elif "candida auris" in topic.lower():
clinical_context = {
"condition_category": "Emerging multidrug-resistant fungal pathogen",
"pathophysiology": "C. auris causes invasive candidiasis with unique resistance mechanisms and environmental persistence",
"key_clinical_features": [
"Healthcare-associated bloodstream infection",
"Fever, hypotension, organ dysfunction",
"Multi-drug resistance pattern",
"Environmental contamination and transmission"
],
"diagnostic_approach": "Blood cultures, molecular identification (MALDI-TOF often misidentifies), susceptibility testing",
"treatment_principles": "Echinocandin first-line, infection control isolation mandatory",
"complications": ["Endocarditis", "Endophthalmitis", "Healthcare outbreaks"],
"epidemiology": "Healthcare settings, ICU patients with devices, international spread",
"board_exam_focus": "Recognition of resistance pattern, infection control implications, treatment challenges"
}
elif "histoplasmosis" in topic.lower():
clinical_context = {
"condition_category": "Endemic dimorphic fungal infection",
"pathophysiology": "Histoplasma capsulatum causes pulmonary infection via inhalation, can disseminate in immunocompromised",
"key_clinical_features": [
"Geographic exposure to Ohio/Mississippi River Valley",
"Cave or soil exposure with bird/bat droppings",
"Acute: fever, cough, weight loss, bilateral hilar lymphadenopathy",
"Chronic: progressive pulmonary disease"
],
"diagnostic_approach": "Urine histoplasma antigen, complement fixation, tissue diagnosis",
"treatment_principles": "Itraconazole for moderate disease, amphotericin B for severe",
"complications": ["Disseminated histoplasmosis", "Chronic pulmonary disease", "Mediastinal fibrosis"],
"epidemiology": "Endemic to central US, spelunkers, construction workers",
"board_exam_focus": "Geographic correlation, diagnostic testing, treatment duration"
}
elif "pneumonia" in topic.lower():
clinical_context = {
"condition_category": "Community-acquired bacterial pneumonia",
"pathophysiology": "Bacterial invasion of alveolar space causing inflammatory response and consolidation",
"key_clinical_features": [
"Acute onset fever, productive cough, pleuritic pain",
"Lobar consolidation on imaging",
"Elevated inflammatory markers"
],
"diagnostic_approach": "Clinical presentation, chest imaging, urinary antigens, cultures",
"treatment_principles": "Empirical antibiotics based on severity and risk factors",
"complications": ["Respiratory failure", "Sepsis", "Pleural effusion"],
"epidemiology": "Common community infection, seasonal variation",
"board_exam_focus": "Severity assessment, pathogen prediction, antibiotic selection"
}
else:
# Generic analysis for unknown conditions
clinical_context = {
"condition_category": f"Medical condition: {topic}",
"pathophysiology": f"Underlying disease mechanisms of {topic}",
"key_clinical_features": [f"Primary manifestations of {topic}"],
"diagnostic_approach": f"Diagnostic workup for {topic}",
"treatment_principles": f"Evidence-based management of {topic}",
"complications": [f"Potential complications of {topic}"],
"epidemiology": f"Population and risk factors for {topic}",
"board_exam_focus": f"Key clinical decision points for {topic}"
}
return clinical_context
def _reason_differential_diagnoses(self, clinical_context: Dict[str, Any]) -> Dict[str, Any]:
"""
Step 2: Differential Diagnosis Reasoning
Systematically identify the most clinically relevant differential diagnoses
based on the condition category and clinical features.
"""
condition_category = clinical_context["condition_category"]
key_features = clinical_context["key_clinical_features"]
# Reasoning-based differential selection
if "aspergillosis" in condition_category.lower():
differential_analysis = {
"primary_differentials": [
{
"condition": "Invasive pulmonary aspergillosis",
"discriminating_features": ["Galactomannan positive", "Halo sign on CT", "Septate hyphae on biopsy"],
"reasoning": "Most likely in neutropenic patient with characteristic imaging"
},
{
"condition": "Mucormycosis (Rhizopus)",
"discriminating_features": ["Non-septate hyphae", "Tissue necrosis", "Diabetic ketoacidosis risk"],
"reasoning": "Key differential in immunocompromised with angioinvasive mold infection"
},
{
"condition": "Bacterial pneumonia with lung abscess",
"discriminating_features": ["Positive bacterial cultures", "Response to antibiotics", "Different imaging pattern"],
"reasoning": "Must exclude bacterial cause before antifungal therapy"
},
{
"condition": "Pulmonary tuberculosis",
"discriminating_features": ["AFB positive", "Cavitary lesions", "Different epidemiology"],
"reasoning": "Important differential in endemic areas with cavitary disease"
},
{
"condition": "Lung cancer with secondary infection",
"discriminating_features": ["Mass lesion", "Malignant cells on biopsy", "Progressive disease"],
"reasoning": "Must consider malignancy in differential of pulmonary nodules"
}
],
"reasoning_framework": "Focus on immunocompromised host with angioinvasive infection patterns"
}
elif "candida auris" in condition_category.lower():
differential_analysis = {
"primary_differentials": [
{
"condition": "Candida auris candidemia",
"discriminating_features": ["Multi-drug resistance", "MALDI-TOF misidentification", "Healthcare transmission"],
"reasoning": "Emerging pathogen with unique resistance and transmission characteristics"
},
{
"condition": "Candida albicans candidemia",
"discriminating_features": ["Fluconazole sensitivity", "Correct MALDI-TOF ID", "Standard resistance pattern"],
"reasoning": "Most common Candida species, typically more susceptible"
},
{
"condition": "Candida glabrata candidemia",
"discriminating_features": ["Fluconazole resistance", "Echinocandin sensitivity", "Predictable pattern"],
"reasoning": "Known for azole resistance but different from C. auris"
},
{
"condition": "Bacterial sepsis (MRSA)",
"discriminating_features": ["Gram-positive cocci", "Different antibiotic resistance", "No environmental persistence"],
"reasoning": "Similar clinical presentation but different pathogen class"
},
{
"condition": "Central line-associated bloodstream infection",
"discriminating_features": ["Line-related organism", "Responds to line removal", "Less organ dysfunction"],
"reasoning": "Device-related infection with different management approach"
}
],
"reasoning_framework": "Focus on healthcare-associated resistant organisms with infection control implications"
}
elif "histoplasmosis" in condition_category.lower():
differential_analysis = {
"primary_differentials": [
{
"condition": "Acute pulmonary histoplasmosis",
"discriminating_features": ["Ohio River Valley exposure", "Positive urine antigen", "Bilateral hilar LAD"],
"reasoning": "Geographic and exposure history key to diagnosis"
},
{
"condition": "Coccidioidomycosis",
"discriminating_features": ["Southwest US exposure", "Eosinophilia", "Spherules on histology"],
"reasoning": "Different endemic mycosis with distinct geography"
},
{
"condition": "Blastomycosis",
"discriminating_features": ["Great Lakes region", "Skin lesions", "Broad-based budding"],
"reasoning": "Endemic mycosis with characteristic morphology and geography"
},
{
"condition": "Sarcoidosis",
"discriminating_features": ["No geographic exposure", "Elevated ACE", "Non-caseating granulomas"],
"reasoning": "Similar imaging pattern but different pathophysiology"
},
{
"condition": "Tuberculosis",
"discriminating_features": ["AFB positive", "Different epidemiology", "Caseating granulomas"],
"reasoning": "Important granulomatous disease differential"
}
],
"reasoning_framework": "Focus on endemic mycoses with geographic discrimination"
}
elif "pneumonia" in condition_category.lower():
differential_analysis = {
"primary_differentials": [
{
"condition": "Community-acquired pneumonia (S. pneumoniae)",
"discriminating_features": ["Lobar consolidation", "Positive urinary antigen", "High procalcitonin"],
"reasoning": "Most common CAP pathogen with characteristic presentation"
},
{
"condition": "Atypical pneumonia (Legionella)",
"discriminating_features": ["Patchy infiltrates", "Hyponatremia", "Travel exposure"],
"reasoning": "Different clinical syndrome and epidemiology"
},
{
"condition": "Viral pneumonia",
"discriminating_features": ["Bilateral infiltrates", "Low procalcitonin", "Viral PCR positive"],
"reasoning": "Non-bacterial etiology with different treatment"
},
{
"condition": "Healthcare-associated pneumonia",
"discriminating_features": ["Resistant organisms", "Healthcare exposure", "Multilobar disease"],
"reasoning": "Different risk factor profile and pathogen spectrum"
},
{
"condition": "Pulmonary embolism",
"discriminating_features": ["No consolidation", "D-dimer elevated", "Travel/immobilization"],
"reasoning": "Non-infectious cause of acute dyspnea and chest pain"
}
],
"reasoning_framework": "Focus on bacterial vs atypical vs viral etiologies with severity assessment"
}
else:
# Generic differential reasoning
differential_analysis = {
"primary_differentials": [
{"condition": f"Primary diagnosis: {clinical_context['condition_category']}", "discriminating_features": ["Specific diagnostic features"], "reasoning": "Primary condition based on clinical context"},
{"condition": "Common differential #1", "discriminating_features": ["Alternative features"], "reasoning": "Important alternative diagnosis"},
{"condition": "Common differential #2", "discriminating_features": ["Different pattern"], "reasoning": "Must-consider differential"},
{"condition": "Common differential #3", "discriminating_features": ["Distinct characteristics"], "reasoning": "Key distinguishing diagnosis"},
{"condition": "Common differential #4", "discriminating_features": ["Alternative findings"], "reasoning": "Additional consideration"}
],
"reasoning_framework": f"Systematic differential approach for {clinical_context['condition_category']}"
}
return differential_analysis
def _generate_reasoned_comparison_table(self, clinical_context: Dict[str, Any], differential_analysis: Dict[str, Any]) -> Dict[str, Any]:
"""
Step 3: Generate a clinically sophisticated comparison table based on medical reasoning.
Uses the clinical context and differential analysis to create specific, accurate comparisons.
"""
# Extract the primary differentials from reasoning
differentials = differential_analysis["primary_differentials"]
# Create detailed comparison table with clinical specificity
comparison_table = {}
for i, differential in enumerate(differentials):
key = "correct_answer" if i == 0 else f"distractor_{i}"
condition = differential["condition"]
discriminating_features = differential["discriminating_features"]
reasoning = differential["reasoning"]
# Generate detailed clinical characteristics based on condition type
if "aspergillus" in condition.lower():
if "invasive" in condition.lower():
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Fever unresponsive to antibiotics, hemoptysis, pleuritic chest pain in neutropenic patient",
"epidemiology_risk_factors": "Neutropenia, hematologic malignancy, stem cell transplant, prolonged corticosteroids",
"laboratory_findings": "Neutrophil count <500/μL, elevated galactomannan (>0.5), negative bacterial cultures",
"imaging_characteristics": "Pulmonary nodules with halo sign on CT, cavitation in later stages",
"diagnostic_tests": "Serum galactomannan positive, tissue biopsy shows septate hyphae with dichotomous branching",
"treatment": "Voriconazole 6mg/kg IV q12h x2 then 4mg/kg q12h, or amphotericin B 5mg/kg/day",
"reasoning": reasoning
}
else:
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Variable based on syndrome - allergic, chronic, or invasive manifestations",
"epidemiology_risk_factors": "Depends on host status and environmental exposure",
"laboratory_findings": "May have eosinophilia in ABPA, galactomannan variable",
"imaging_characteristics": "Aspergilloma shows 'air crescent sign', invasive shows nodules",
"diagnostic_tests": "Specific testing depends on clinical syndrome",
"treatment": "Syndrome-specific approach",
"reasoning": reasoning
}
elif "mucormycosis" in condition.lower():
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Rapid onset fever, facial pain, black eschar, altered mental status",
"epidemiology_risk_factors": "Diabetic ketoacidosis, neutropenia, iron overload, corticosteroids",
"laboratory_findings": "Elevated glucose, acidosis, negative galactomannan",
"imaging_characteristics": "Rapid progression, tissue necrosis, 'black turbinate sign'",
"diagnostic_tests": "Tissue biopsy shows broad, non-septate hyphae with right-angle branching",
"treatment": "High-dose amphotericin B 10mg/kg/day, urgent surgical debridement",
"reasoning": reasoning
}
elif "candida auris" in condition.lower():
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Healthcare-associated fever, hypotension, multi-organ dysfunction",
"epidemiology_risk_factors": "ICU stay >14 days, central venous catheter, broad-spectrum antibiotics, mechanical ventilation",
"laboratory_findings": "Positive blood cultures, elevated lactate, multi-drug resistance pattern",
"imaging_characteristics": "May show endophthalmitis, endocarditis vegetations, hepatosplenic candidiasis",
"diagnostic_tests": "Molecular identification required (MALDI-TOF misidentifies), MIC testing shows resistance",
"treatment": "Empirical echinocandin (micafungin 100mg/day), contact isolation protocols",
"reasoning": reasoning
}
elif "candida albicans" in condition.lower():
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Fever, hypotension, but typically less severe organ dysfunction",
"epidemiology_risk_factors": "Healthcare or community exposure, shorter duration of risk factors",
"laboratory_findings": "Positive blood cultures, typically fluconazole-sensitive (MIC <2 μg/mL)",
"imaging_characteristics": "Similar complications but lower frequency of metastatic seeding",
"diagnostic_tests": "MALDI-TOF correctly identifies, standard antifungal susceptibility pattern",
"treatment": "Fluconazole 800mg loading then 400mg daily, or echinocandin for severe disease",
"reasoning": reasoning
}
elif "histoplasmosis" in condition.lower():
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Gradual onset fever, nonproductive cough, weight loss, fatigue",
"epidemiology_risk_factors": "Ohio/Mississippi River Valley exposure, cave exploration, soil disturbance activities",
"laboratory_findings": "Lymphopenia, elevated LDH, urine histoplasma antigen >10 ng/mL",
"imaging_characteristics": "Bilateral hilar lymphadenopathy with multiple small pulmonary nodules",
"diagnostic_tests": "Urine histoplasma antigen, complement fixation titers >1:32, tissue shows oval yeasts",
"treatment": "Itraconazole 200mg BID for 6-12 weeks for moderate disease",
"reasoning": reasoning
}
elif "coccidioidomycosis" in condition.lower():
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Fever, cough, pleuritic pain, arthralgias, erythema nodosum",
"epidemiology_risk_factors": "Southwestern US exposure (Arizona, California), dust storm exposure, construction work",
"laboratory_findings": "Eosinophilia >4%, elevated ESR, negative urine histoplasma antigen",
"imaging_characteristics": "Often unilateral infiltrate, thin-walled cavities, hilar adenopathy",
"diagnostic_tests": "Coccidioides IgM/IgG serology, spherules on tissue examination",
"treatment": "Fluconazole 400mg daily for mild-moderate disease, amphotericin B for severe",
"reasoning": reasoning
}
elif "pneumonia" in condition.lower() and "community" in condition.lower():
comparison_table[key] = {
"condition": condition,
"clinical_presentation": "Acute onset fever, productive cough with rust-colored sputum, pleuritic chest pain",
"epidemiology_risk_factors": "Age >65, COPD, diabetes, recent travel (cruise ship), smoking history",
"laboratory_findings": "Leukocytosis >12,000/μL with left shift, procalcitonin >2.0 ng/mL, positive urinary antigen",
"imaging_characteristics": "Lobar consolidation with air bronchograms, typically unilateral",
"diagnostic_tests": "Pneumococcal urinary antigen positive, blood cultures may grow S. pneumoniae",
"treatment": "Ceftriaxone 2g IV daily plus azithromycin 500mg IV daily for hospitalized patients",
"reasoning": reasoning
}
else:
# Generic entry for unknown conditions
comparison_table[key] = {
"condition": condition,
"clinical_presentation": f"Clinical features characteristic of {condition}",
"epidemiology_risk_factors": f"Risk factors specific to {condition}",
"laboratory_findings": f"Laboratory pattern for {condition}",
"imaging_characteristics": f"Imaging findings in {condition}",
"diagnostic_tests": f"Diagnostic approach for {condition}",
"treatment": f"Treatment approach for {condition}",
"reasoning": reasoning
}
return {
"table": comparison_table,
"reasoning_framework": differential_analysis["reasoning_framework"],
"clinical_context": clinical_context["condition_category"],
"correct_condition": comparison_table["correct_answer"]["condition"]
}
def _develop_vignette_strategy(self, comparison_table: Dict[str, Any], clinical_context: Dict[str, Any]) -> Dict[str, Any]:
"""
Step 4: Vignette Construction Reasoning
Develop a strategic approach for creating a clinically complex vignette that has one clear correct answer
while including enough complexity to challenge clinical reasoning.
"""
correct_answer = comparison_table["table"]["correct_answer"]
distractors = [comparison_table["table"][f"distractor_{i}"] for i in range(1, 5)]
# Strategy: Include 3-4 strong features from correct answer + 1-2 confounding features
vignette_strategy = {
"primary_discriminating_features": [],
"confounding_features": [],
"clinical_complexity_elements": [],
"diagnostic_breadcrumbs": [],
"reasoning_challenges": []
}
# Select the 3-4 strongest discriminating features from correct answer
if "epidemiology_risk_factors" in correct_answer:
vignette_strategy["primary_discriminating_features"].append({
"category": "epidemiology",
"feature": correct_answer["epidemiology_risk_factors"],
"strength": "strong",
"reasoning": "Key epidemiologic clue that points to correct diagnosis"
})
if "laboratory_findings" in correct_answer:
vignette_strategy["primary_discriminating_features"].append({
"category": "laboratory",
"feature": correct_answer["laboratory_findings"],
"strength": "strong",
"reasoning": "Laboratory pattern specific to correct condition"
})
if "imaging_characteristics" in correct_answer:
vignette_strategy["primary_discriminating_features"].append({
"category": "imaging",
"feature": correct_answer["imaging_characteristics"],
"strength": "strong",
"reasoning": "Imaging findings that distinguish correct diagnosis"
})
if "diagnostic_tests" in correct_answer:
vignette_strategy["primary_discriminating_features"].append({
"category": "diagnostic",
"feature": correct_answer["diagnostic_tests"],
"strength": "strong",
"reasoning": "Specific diagnostic test results confirming diagnosis"
})
# Add 1-2 confounding features from distractors to maintain difficulty
for i, distractor in enumerate(distractors[:2]):
if "clinical_presentation" in distractor:
# Extract overlapping but non-specific symptoms
presentation = distractor["clinical_presentation"]
if "fever" in presentation.lower():
vignette_strategy["confounding_features"].append({
"feature": "fever",
"source": distractor["condition"],
"reasoning": "Non-specific symptom present in multiple conditions"
})
if "cough" in presentation.lower() and len(vignette_strategy["confounding_features"]) < 2:
vignette_strategy["confounding_features"].append({
"feature": "cough",
"source": distractor["condition"],
"reasoning": "Common respiratory symptom in multiple conditions"
})
# Add clinical complexity elements based on condition type
condition_category = clinical_context["condition_category"]
if "fungal" in condition_category.lower() or "aspergillosis" in condition_category.lower():
vignette_strategy["clinical_complexity_elements"] = [
"Immunocompromised host status",
"Timeline of symptom progression",
"Response to initial antibiotics",
"Specific risk factor exposure"
]
elif "candida" in condition_category.lower():
vignette_strategy["clinical_complexity_elements"] = [
"Healthcare exposure duration",
"Device-related factors",
"Resistance pattern complexity",
"Infection control implications"
]
elif "pneumonia" in condition_category.lower():
vignette_strategy["clinical_complexity_elements"] = [
"Severity assessment criteria",
"Pathogen probability factors",
"Comorbidity influences",
"Treatment response indicators"
]
# Diagnostic breadcrumbs - subtle clues that guide toward correct answer
vignette_strategy["diagnostic_breadcrumbs"] = [
"Specific temporal relationships",
"Characteristic physical exam findings",
"Laboratory value patterns",
"Imaging evolution over time"
]
# Reasoning challenges - elements that test clinical thinking
vignette_strategy["reasoning_challenges"] = [
"Must integrate multiple data points",
"Requires prioritization of findings",
"Tests knowledge of disease mechanisms",
"Challenges recognition of patterns"
]
return vignette_strategy
def _generate_reasoned_vignette(self, vignette_strategy: Dict[str, Any]) -> str:
"""
Step 5: Generate a clinically sophisticated vignette based on the strategic reasoning.
Creates a complex but focused clinical scenario with one clear correct answer.
"""
# Extract key elements from strategy
primary_features = vignette_strategy["primary_discriminating_features"]
confounding_features = vignette_strategy["confounding_features"]
complexity_elements = vignette_strategy["clinical_complexity_elements"]
# Identify the condition type from primary features
condition_indicators = []
for feature in primary_features:
condition_indicators.append(feature["feature"].lower())
combined_indicators = " ".join(condition_indicators)
# Generate condition-specific vignette
if "neutropenia" in combined_indicators or "galactomannan" in combined_indicators:
# Aspergillosis vignette
vignette = (
"A 34-year-old man with acute myeloid leukemia is admitted for induction chemotherapy. "
"On day 12 of hospitalization, he develops fever to 102.8°F (39.3°C) despite broad-spectrum "
"antibiotics (piperacillin-tazobactam and vancomycin) for 48 hours. He reports new onset "
"of right-sided pleuritic chest pain and has had two episodes of small-volume hemoptysis. "
"Physical examination reveals an ill-appearing man with temperature 102.8°F, heart rate 115 bpm, "
"blood pressure 110/65 mmHg, respiratory rate 24/min, and oxygen saturation 94% on 2L nasal cannula. "
"Lung examination shows decreased breath sounds at the right base with dullness to percussion. "
"Laboratory studies show: WBC 400/μL (normal 4,500-11,000) with 85% neutrophils, absolute neutrophil "
"count 340/μL, platelet count 45,000/μL, and creatinine 1.2 mg/dL. Chest CT demonstrates a 2.5-cm "
"right lower lobe nodule with surrounding ground-glass opacity ('halo sign') and a smaller left "
"upper lobe nodule. Serum galactomannan index is 2.8 (normal <0.5). Blood cultures remain negative "
"after 72 hours. The patient has no known drug allergies and has been receiving prophylactic "
"fluconazole 400mg daily since admission."
)
elif "icu" in combined_indicators and "resistance" in combined_indicators:
# Candida auris vignette
vignette = (
"A 67-year-old woman with end-stage renal disease on hemodialysis is admitted to the ICU "
"following complications from abdominal surgery 18 days ago. She has required multiple "
"invasive procedures including central venous catheter placement, mechanical ventilation, "
"and broad-spectrum antibiotic therapy with vancomycin, meropenem, and fluconazole. "
"On hospital day 20, she develops new fever to 101.6°F (38.7°C) and hypotension requiring "
"vasopressor support. Physical examination reveals temperature 101.6°F, heart rate 125 bpm, "
"blood pressure 85/45 mmHg on norepinephrine, and clear lungs. The central line insertion "
"site appears clean without erythema. Laboratory studies show: WBC 14,200/μL with 78% neutrophils "
"and 15% bands, lactate 3.8 mmol/L (normal <2.0), and procalcitonin 1.2 ng/mL. Blood cultures "
"drawn from both central and peripheral sites grow yeast after 16 hours. The isolate demonstrates "
"resistance to fluconazole (MIC >64 μg/mL) and intermediate resistance to amphotericin B (MIC 2 μg/mL). "
"MALDI-TOF mass spectrometry reports 'Candida haemulonii' with low confidence score (1.6). "
"The microbiology laboratory requests molecular identification due to the unusual resistance pattern."
)
elif "ohio" in combined_indicators or "cave" in combined_indicators:
# Histoplasmosis vignette
vignette = (
"A 42-year-old construction worker from Cincinnati, Ohio, presents to the emergency department "
"with a 4-week history of persistent nonproductive cough, low-grade fever, and 15-pound "
"unintentional weight loss. He reports recent recreational spelunking activities at Mammoth Cave "
"in Kentucky approximately 7 weeks ago with several friends. Initial symptoms began gradually "
"2 weeks after the cave trip with fatigue and intermittent fever, progressing to persistent cough "
"and night sweats. He denies chest pain initially but now reports mild bilateral chest discomfort. "
"Physical examination reveals an afebrile man (temperature 99.8°F) with scattered tender erythematous "
"nodules on both anterior shins and mild bilateral ankle swelling. Vital signs show heart rate 88 bpm, "
"blood pressure 135/82 mmHg, respiratory rate 18/min, and oxygen saturation 96% on room air. "
"Laboratory studies reveal: WBC 3,400/μL (normal 4,500-11,000) with 68% lymphocytes, ESR 82 mm/hr, "
"and LDH 445 U/L (normal <250). Chest CT demonstrates bilateral hilar lymphadenopathy with multiple "
"small (<1 cm) pulmonary nodules scattered throughout both lung fields. Urine Histoplasma antigen "
"is 18.5 ng/mL (normal <0.6 ng/mL)."
)
elif "cruise" in combined_indicators and "consolidation" in combined_indicators:
# Pneumonia vignette
vignette = (
"A 71-year-old man with COPD (FEV1 42% predicted) and well-controlled type 2 diabetes mellitus "
"presents to the emergency department with a 36-hour history of acute onset productive cough "
"with rust-colored sputum, right-sided pleuritic chest pain, and fever. He recently returned "
"from a 10-day cruise to the Caribbean 4 days ago and felt well during the entire trip. "
"Symptoms began abruptly yesterday evening with rigors and high fever, followed by productive "
"cough and sharp chest pain that worsens with deep inspiration. He denies recent antibiotic use "
"or hospitalization. Physical examination reveals an ill-appearing man with temperature 103.1°F "
"(39.5°C), heart rate 118 bmp, blood pressure 125/78 mmHg, respiratory rate 28/min, and oxygen "
"saturation 88% on room air improving to 95% on 3L nasal cannula. Lung examination shows dullness "
"to percussion and bronchial breath sounds over the right lower lobe posteriorly. Laboratory studies "
"reveal: WBC 17,200/μL with 86% neutrophils and 10% bands, procalcitonin 4.2 ng/mL (normal <0.1), "
"lactate 1.9 mmol/L, and creatinine 1.1 mg/dL. Chest X-ray demonstrates right lower lobe consolidation "
"with air bronchograms. Pneumococcal urinary antigen is positive. His calculated CURB-65 score is 2 "
"(age >65, respiratory rate >30)."
)
else:
# Generic sophisticated vignette
vignette = (
f"A patient with relevant clinical risk factors presents with characteristic symptoms and signs. "
f"The presentation includes key discriminating findings from the primary features: "
f"{', '.join([f['feature'] for f in primary_features[:2]])}. "
f"Physical examination and diagnostic studies reveal significant findings that help establish "
f"the diagnosis through systematic clinical reasoning. The case includes some overlapping features "
f"that could suggest alternative diagnoses, requiring careful analysis of all discriminating elements."
)
return vignette
def _create_reasoned_question_stem(self, vignette: str, clinical_context: Dict[str, Any]) -> str:
"""
Step 6: Create a focused question stem that tests the specific clinical reasoning objective.
"""
condition_category = clinical_context["condition_category"]
# Generate condition-appropriate question stems
if "antifungal" in condition_category.lower() or "aspergillosis" in condition_category.lower():
question_stem = (
f"{vignette}\n\n"
"Which of the following is the most appropriate next step in management?"
)
elif "identification" in condition_category.lower() or "candida" in condition_category.lower():
question_stem = (
f"{vignette}\n\n"
"Which of the following is the most likely causative organism?"
)
elif "treatment" in condition_category.lower():
question_stem = (
f"{vignette}\n\n"
"Which of the following is the most appropriate initial treatment?"
)
elif "diagnosis" in condition_category.lower():
question_stem = (
f"{vignette}\n\n"
"Which of the following is the most likely diagnosis?"
)
else:
question_stem = (
f"{vignette}\n\n"
"Based on the clinical presentation and findings, which of the following is most appropriate?"
)
return question_stem
def _generate_reasoned_answer_choices(self, comparison_table: Dict[str, Any], clinical_context: Dict[str, Any]) -> List[str]:
"""
Step 7: Generate answer choices based on the comparison table with clinical reasoning.
Each choice should represent a clinically plausible option with varying degrees of appropriateness.
"""
table = comparison_table["table"]
condition_category = clinical_context["condition_category"]
answer_choices = []
# Correct answer - format based on condition category
correct_answer = table["correct_answer"]
if "antifungal" in condition_category.lower():
if "aspergillosis" in correct_answer["condition"].lower():
answer_choices.append("Initiate voriconazole therapy")
elif "candida" in correct_answer["condition"].lower():
answer_choices.append("Start micafungin therapy")
else:
answer_choices.append("Begin targeted antifungal therapy")
elif "identification" in condition_category.lower():
answer_choices.append(correct_answer["condition"])
elif "treatment" in condition_category.lower():
treatment = correct_answer.get("treatment", "Appropriate targeted therapy")
answer_choices.append(treatment)
else:
answer_choices.append(correct_answer["condition"])
# Generate distractor choices from comparison table
for i in range(1, 5):
distractor_key = f"distractor_{i}"
if distractor_key in table:
distractor = table[distractor_key]
if "antifungal" in condition_category.lower():
if "bacterial" in distractor["condition"].lower():
answer_choices.append("Continue current antibiotic therapy")
elif "viral" in distractor["condition"].lower():
answer_choices.append("Start antiviral therapy")
elif "tuberculosis" in distractor["condition"].lower():
answer_choices.append("Initiate anti-tuberculosis therapy")
else:
answer_choices.append(f"Begin therapy for {distractor['condition']}")
elif "identification" in condition_category.lower():
answer_choices.append(distractor["condition"])
elif "treatment" in condition_category.lower():
treatment = distractor.get("treatment", f"Treatment for {distractor['condition']}")
answer_choices.append(treatment)
else:
answer_choices.append(distractor["condition"])
# Ensure we have exactly 5 choices
while len(answer_choices) < 5:
answer_choices.append("Alternative management approach")
return answer_choices[:5]
def _generate_reasoned_explanations(self, comparison_table: Dict[str, Any], clinical_context: Dict[str, Any]) -> Dict[str, str]:
"""
Step 8: Generate detailed explanations that demonstrate clinical reasoning for each answer choice.
"""
table = comparison_table["table"]
correct_answer = table["correct_answer"]
explanations = {
"correct_explanation": "",
"distractor_explanations": []
}
# Correct answer explanation
condition = correct_answer["condition"]
key_features = []
if "epidemiology_risk_factors" in correct_answer:
key_features.append(f"epidemiologic risk factors ({correct_answer['epidemiology_risk_factors']})")
if "laboratory_findings" in correct_answer:
key_features.append(f"laboratory findings ({correct_answer['laboratory_findings']})")
if "diagnostic_tests" in correct_answer:
key_features.append(f"diagnostic test results ({correct_answer['diagnostic_tests']})")
if "imaging_characteristics" in correct_answer:
key_features.append(f"imaging characteristics ({correct_answer['imaging_characteristics']})")
explanations["correct_explanation"] = (
f"Correct. The clinical presentation is most consistent with {condition}. "
f"Key supporting features include: {', '.join(key_features)}. "
f"These findings in combination provide strong evidence for this diagnosis and guide appropriate management."
)
# Distractor explanations
for i in range(1, 5):
distractor_key = f"distractor_{i}"
if distractor_key in table:
distractor = table[distractor_key]
# Identify why this distractor is incorrect
differences = []
if "epidemiology_risk_factors" in distractor and "epidemiology_risk_factors" in correct_answer:
if distractor["epidemiology_risk_factors"] != correct_answer["epidemiology_risk_factors"]:
differences.append(f"epidemiologic factors differ ({distractor['epidemiology_risk_factors']} vs expected {correct_answer['epidemiology_risk_factors']})")
if "laboratory_findings" in distractor and "laboratory_findings" in correct_answer:
if distractor["laboratory_findings"] != correct_answer["laboratory_findings"]:
differences.append(f"laboratory pattern inconsistent ({distractor['laboratory_findings']} not consistent with findings)")
if "diagnostic_tests" in distractor and "diagnostic_tests" in correct_answer:
if distractor["diagnostic_tests"] != correct_answer["diagnostic_tests"]:
differences.append(f"diagnostic test results do not support this diagnosis")
explanation = (
f"Incorrect. While {distractor['condition']} could present with some similar features, "
f"the clinical presentation is not consistent with this diagnosis. "
f"Key differences include: {', '.join(differences[:2]) if differences else 'clinical pattern and diagnostic findings do not align with this condition'}."
)
explanations["distractor_explanations"].append(explanation)
return explanations
def _clinical_quality_review(self, question_data: Dict[str, Any]) -> Dict[str, Any]:
"""
Step 9: Final clinical quality review and refinement.
Reviews the complete question for clinical accuracy, educational value, and appropriate difficulty.
"""
review_results = {
"clinical_accuracy_score": 0,
"educational_value_score": 0,
"difficulty_appropriateness": 0,
"improvements_needed": [],
"quality_passed": False
}
# Clinical accuracy assessment
vignette = question_data.get("question_stem", "")
answer_choices = question_data.get("answer_choices", [])
clinical_accuracy_points = 0
# Check for specific clinical details (each worth 1 point, max 5)
clinical_details = [
"specific lab values",
"imaging findings",
"epidemiologic factors",
"temporal relationships",
"physical exam findings"
]
for detail in clinical_details:
if any(keyword in vignette.lower() for keyword in detail.split()):
clinical_accuracy_points += 1
review_results["clinical_accuracy_score"] = min(clinical_accuracy_points, 5)
# Educational value assessment
educational_points = 0
if len(answer_choices) == 5:
educational_points += 1
if any("most appropriate" in choice.lower() for choice in [question_data.get("question_stem", "")]):
educational_points += 1
if len(vignette) > 300: # Substantial clinical detail
educational_points += 1
if "reasoning" in str(question_data.get("explanations", {})).lower():
educational_points += 1
review_results["educational_value_score"] = educational_points
# Difficulty appropriateness (board exam level)
difficulty_points = 0
if len(vignette) > 200: # Complex vignette
difficulty_points += 1
if any(term in vignette.lower() for term in ["icu", "immunocompromised", "resistance", "multiple"]):
difficulty_points += 1
if review_results["clinical_accuracy_score"] >= 3:
difficulty_points += 1
review_results["difficulty_appropriateness"] = difficulty_points
# Quality assessment
total_score = (
review_results["clinical_accuracy_score"] +
review_results["educational_value_score"] +
review_results["difficulty_appropriateness"]
)
if total_score >= 8:
review_results["quality_passed"] = True
else:
if review_results["clinical_accuracy_score"] < 3:
review_results["improvements_needed"].append("Increase clinical specificity and accuracy")
if review_results["educational_value_score"] < 3:
review_results["improvements_needed"].append("Enhance educational value and learning objectives")
if review_results["difficulty_appropriateness"] < 2:
review_results["improvements_needed"].append("Adjust difficulty to board exam level")
return review_results
def _generate_comparison_table(self, topic: str, question_type: str, difficulty_level: str) -> Dict[str, Any]:
"""
Generate a comprehensive comparison table with the correct answer and 4 plausible distractors.
This systematic approach creates discriminating features for each option to enable
sophisticated question generation that tests true clinical reasoning.
Returns a table with 5 conditions and their distinguishing characteristics.
"""
# Define categories for comparison based on question type
if question_type == "diagnosis":
categories = [
"clinical_presentation",
"epidemiology_risk_factors",
"laboratory_findings",
"imaging_characteristics",
"diagnostic_tests"
]
elif question_type == "management":
categories = [
"first_line_treatment",
"dosing_regimen",
"duration_therapy",
"monitoring_requirements",
"contraindications"
]
elif question_type == "pharmacology":
categories = [
"mechanism_action",
"absorption_distribution",
"drug_interactions",
"adverse_effects",
"dosing_adjustments"
]
else:
categories = [
"pathophysiology",
"clinical_features",
"diagnostic_approach",
"treatment_principles",
"prognosis_complications"
]
# Generate condition-specific comparison table
if "candida auris" in topic.lower():
comparison_table = {
"correct_answer": {
"condition": "Candida auris invasive candidiasis",
"clinical_presentation": "ICU patient with central line, fever, hypotension, multi-organ dysfunction",
"epidemiology_risk_factors": "Healthcare exposure, invasive devices, broad-spectrum antibiotics, ICU stay >14 days",
"laboratory_findings": "Blood cultures positive for yeast, elevated lactate, multi-drug resistance pattern",
"imaging_characteristics": "May show endophthalmitis, endocarditis vegetations, or hepatosplenic lesions",
"diagnostic_tests": "MALDI-TOF often misidentifies; requires molecular identification (PCR/sequencing)"
},
"distractor_1": {
"condition": "Candida albicans candidemia",
"clinical_presentation": "Similar fever and hypotension but usually less severe organ dysfunction",
"epidemiology_risk_factors": "Healthcare exposure but can occur in community settings, shorter ICU stays",
"laboratory_findings": "Blood cultures positive, typically fluconazole-sensitive (MIC <2 μg/mL)",
"imaging_characteristics": "Similar complications but lower frequency of metastatic seeding",
"diagnostic_tests": "MALDI-TOF correctly identifies as C. albicans, standard susceptibility testing"
},
"distractor_2": {
"condition": "Candida glabrata candidemia",
"clinical_presentation": "Often more indolent course, less acute organ dysfunction",
"epidemiology_risk_factors": "Diabetes, advanced age, previous azole exposure",
"laboratory_findings": "Fluconazole-resistant but echinocandin-sensitive, normal lactate often",
"imaging_characteristics": "Lower rate of metastatic complications compared to C. auris",
"diagnostic_tests": "MALDI-TOF correctly identifies, predictable resistance pattern"
},
"distractor_3": {
"condition": "Bacterial sepsis (MRSA)",
"clinical_presentation": "Similar fever, hypotension, but may have skin/soft tissue source",
"epidemiology_risk_factors": "Healthcare exposure, invasive devices, prior MRSA colonization",
"laboratory_findings": "Blood cultures positive for gram-positive cocci, procalcitonin markedly elevated",
"imaging_characteristics": "May show pneumonia, skin infections, or endocarditis",
"diagnostic_tests": "Gram stain shows gram-positive cocci in clusters, rapid PCR available"
},
"distractor_4": {
"condition": "Candidemia due to central line infection",
"clinical_presentation": "Fever temporally related to line access, may lack organ dysfunction",
"epidemiology_risk_factors": "Recent line placement, total parenteral nutrition, immunosuppression",
"laboratory_findings": "Blood cultures positive from line and peripheral sites",
"imaging_characteristics": "May show line-associated thrombus or vegetation",
"diagnostic_tests": "Standard Candida species identification, usually antifungal-sensitive"
}
}
elif "histoplasmosis" in topic.lower() or ("dimorphic" in topic.lower() and "histoplasma" in topic.lower()):
comparison_table = {
"correct_answer": {
"condition": "Acute pulmonary histoplasmosis",
"clinical_presentation": "Fever, cough, weight loss after cave/soil exposure",
"epidemiology_risk_factors": "Ohio/Mississippi River Valley, cave exploration, soil disturbance",
"laboratory_findings": "Lymphopenia, elevated LDH, positive urine antigen",
"imaging_characteristics": "Bilateral hilar lymphadenopathy with multiple small pulmonary nodules",
"diagnostic_tests": "Urine Histoplasma antigen >10 ng/mL, complement fixation titers >1:32"
},
"distractor_1": {
"condition": "Coccidioidomycosis (Valley Fever)",
"clinical_presentation": "Similar respiratory symptoms but with arthritis, erythema nodosum",
"epidemiology_risk_factors": "Southwestern US (Arizona, California), dust exposure",
"laboratory_findings": "Eosinophilia (>4%), elevated ESR, negative urine histoplasma antigen",
"imaging_characteristics": "Often unilateral infiltrate, thin-walled cavities",
"diagnostic_tests": "Coccidioides IgM/IgG, spherules on histology"
},
"distractor_2": {
"condition": "Blastomycosis",
"clinical_presentation": "Pulmonary symptoms with characteristic skin lesions",
"epidemiology_risk_factors": "Great Lakes region, outdoor activities near water",
"laboratory_findings": "Normal lymphocyte count, negative urine histoplasma antigen",
"imaging_characteristics": "Mass-like consolidation, often unilateral",
"diagnostic_tests": "Broad-based budding yeasts on histology, Blastomyces antigen"
},
"distractor_3": {
"condition": "Sarcoidosis with Löfgren syndrome",
"clinical_presentation": "Similar bilateral hilar lymphadenopathy with erythema nodosum",
"epidemiology_risk_factors": "No specific geographic exposure, autoimmune predisposition",
"laboratory_findings": "Normal lymphocytes, elevated ACE level, negative fungal antigens",
"imaging_characteristics": "Bilateral hilar lymphadenopathy but without pulmonary nodules",
"diagnostic_tests": "Elevated serum ACE, non-caseating granulomas on biopsy"
},
"distractor_4": {
"condition": "Hypersensitivity pneumonitis",
"clinical_presentation": "Cough and dyspnea after organic dust exposure",
"epidemiology_risk_factors": "Occupational/environmental organic dust exposure",
"laboratory_findings": "Normal lymphocytes, negative fungal antigens",
"imaging_characteristics": "Ground-glass opacities, upper lobe predominance",
"diagnostic_tests": "Specific precipitating antibodies, lymphocytosis on BAL"
}
}
elif "pneumonia" in topic.lower():
comparison_table = {
"correct_answer": {
"condition": "Community-acquired pneumonia (S. pneumoniae)",
"clinical_presentation": "Acute onset fever, productive cough with rust-colored sputum, pleuritic pain",
"epidemiology_risk_factors": "Age >65, COPD, diabetes, recent travel (cruise)",
"laboratory_findings": "Leukocytosis with left shift, procalcitonin >2.0 ng/mL, positive urinary antigen",
"imaging_characteristics": "Lobar consolidation with air bronchograms",
"diagnostic_tests": "Positive pneumococcal urinary antigen, blood cultures may be positive"
},
"distractor_1": {
"condition": "Atypical pneumonia (Legionella)",
"clinical_presentation": "Gradual onset, dry cough, GI symptoms, confusion",
"epidemiology_risk_factors": "Recent travel, hotel/cruise exposure, older age",
"laboratory_findings": "Hyponatremia, elevated LDH, lower procalcitonin (<1.0 ng/mL)",
"imaging_characteristics": "Patchy infiltrates, often multilobar",
"diagnostic_tests": "Legionella urinary antigen (type 1 only), respiratory PCR"
},
"distractor_2": {
"condition": "Viral pneumonia (Influenza)",
"clinical_presentation": "Fever, myalgias, dry cough, gradual onset",
"epidemiology_risk_factors": "Winter season, crowded conditions, unvaccinated",
"laboratory_findings": "Normal or low WBC, low procalcitonin (<0.5 ng/mL)",
"imaging_characteristics": "Bilateral interstitial infiltrates",
"diagnostic_tests": "Respiratory viral PCR positive for influenza"
},
"distractor_3": {
"condition": "Healthcare-associated pneumonia (P. aeruginosa)",
"clinical_presentation": "Similar symptoms but in hospitalized/recent healthcare exposure",
"epidemiology_risk_factors": "Recent hospitalization, ventilator, broad-spectrum antibiotics",
"laboratory_findings": "Leukocytosis, elevated procalcitonin, resistant organism pattern",
"imaging_characteristics": "Often multilobar, necrotizing changes",
"diagnostic_tests": "Sputum culture shows resistant gram-negative rods"
},
"distractor_4": {
"condition": "Pulmonary embolism",
"clinical_presentation": "Acute dyspnea, pleuritic pain, but no productive cough",
"epidemiology_risk_factors": "Recent travel, immobilization, hypercoagulable state",
"laboratory_findings": "Elevated D-dimer, normal procalcitonin, no leukocytosis",
"imaging_characteristics": "No consolidation, may show pleural effusion",
"diagnostic_tests": "CT pulmonary angiogram shows filling defects"
}
}
else:
# Generic comparison table for any condition
comparison_table = {
"correct_answer": {
"condition": f"Primary diagnosis: {topic}",
"clinical_presentation": f"Classic presentation of {topic} with characteristic symptoms",
"epidemiology_risk_factors": f"Typical risk factors and demographics for {topic}",
"laboratory_findings": f"Laboratory pattern diagnostic for {topic}",
"imaging_characteristics": f"Imaging findings pathognomonic for {topic}",
"diagnostic_tests": f"Gold standard diagnostic test for {topic}"
},
"distractor_1": {
"condition": f"Common differential diagnosis #1",
"clinical_presentation": f"Similar symptoms but key distinguishing features",
"epidemiology_risk_factors": f"Different risk factor profile",
"laboratory_findings": f"Laboratory pattern that rules out {topic}",
"imaging_characteristics": f"Different imaging pattern",
"diagnostic_tests": f"Different diagnostic test results"
},
"distractor_2": {
"condition": f"Common differential diagnosis #2",
"clinical_presentation": f"Overlapping symptoms with subtle differences",
"epidemiology_risk_factors": f"Alternative epidemiologic pattern",
"laboratory_findings": f"Distinct laboratory abnormalities",
"imaging_characteristics": f"Alternative imaging findings",
"diagnostic_tests": f"Specific tests that distinguish from {topic}"
},
"distractor_3": {
"condition": f"Common differential diagnosis #3",
"clinical_presentation": f"Similar initial presentation",
"epidemiology_risk_factors": f"Different patient population",
"laboratory_findings": f"Laboratory findings inconsistent with {topic}",
"imaging_characteristics": f"Imaging that excludes {topic}",
"diagnostic_tests": f"Testing that rules out {topic}"
},
"distractor_4": {
"condition": f"Common differential diagnosis #4",
"clinical_presentation": f"Mimics {topic} but with key differences",
"epidemiology_risk_factors": f"Distinct epidemiologic factors",
"laboratory_findings": f"Different laboratory profile",
"imaging_characteristics": f"Characteristic imaging for alternative diagnosis",
"diagnostic_tests": f"Confirmatory tests for alternative condition"
}
}
return {
"table": comparison_table,
"categories": categories,
"correct_condition": comparison_table["correct_answer"]["condition"],
"difficulty_level": difficulty_level
}
def _generate_sophisticated_vignette(self, topic: str, question_type: str, comparison_table: Dict[str, Any]) -> str:
"""
Generate a sophisticated clinical vignette using the comparison table.
Strategy:
1. Use 3-4 key features from the correct answer
2. Include 1-2 features that could suggest distractors to maintain difficulty
3. Avoid giving away the answer while providing sufficient discriminating information
"""
correct_features = comparison_table["table"]["correct_answer"]
distractors = [comparison_table["table"][f"distractor_{i}"] for i in range(1, 5)]
# Select 3 strong discriminating features from correct answer
key_correct_features = []
if "clinical_presentation" in correct_features:
key_correct_features.append(correct_features["clinical_presentation"])
if "epidemiology_risk_factors" in correct_features:
key_correct_features.append(correct_features["epidemiology_risk_factors"])
if "laboratory_findings" in correct_features:
key_correct_features.append(correct_features["laboratory_findings"])
# Select 1-2 features that might suggest distractors (to maintain difficulty)
confounding_features = []
for distractor in distractors[:2]: # Use first 2 distractors
if "clinical_presentation" in distractor:
# Extract a non-specific symptom that could apply to multiple conditions
presentation = distractor["clinical_presentation"]
if "fever" in presentation.lower() and "fever" not in key_correct_features[0].lower():
confounding_features.append("fever")
elif "cough" in presentation.lower() and len(confounding_features) < 2:
confounding_features.append("cough")
# Construct vignette based on specific topic
if "candida auris" in topic.lower():
vignette = (
f"A 72-year-old man with end-stage renal disease on hemodialysis develops fever and hypotension "
f"48 hours after central venous catheter placement in the ICU. He has been hospitalized for "
f"3 weeks following complicated abdominal surgery with multiple invasive procedures and "
f"broad-spectrum antibiotic exposure. Physical examination reveals temperature 101.8°F (38.8°C), "
f"heart rate 125 bpm, blood pressure 85/45 mmHg despite fluid resuscitation. The central line "
f"insertion site appears clean without erythema. Laboratory studies show WBC 12,500/μL with "
f"left shift, lactate 3.2 mmol/L, and creatinine 4.8 mg/dL (baseline 4.2 mg/dL). Blood cultures "
f"drawn from both the central line and peripheral sites grow yeast after 18 hours. The organism "
f"demonstrates resistance to fluconazole (MIC >64 μg/mL) and intermediate resistance to "
f"amphotericin B (MIC 2 μg/mL). MALDI-TOF mass spectrometry initially reports the organism as "
f"'C. haemulonii' with low confidence score. The microbiology laboratory requests molecular "
f"identification due to the unusual resistance pattern and concern for an emerging pathogen."
)
elif "histoplasmosis" in topic.lower():
vignette = (
f"A 45-year-old construction worker from Louisville, Kentucky, presents with a 3-week history of "
f"nonproductive cough, low-grade fever, and 12-pound unintentional weight loss. He reports "
f"recent recreational cave exploration activities in Mammoth Cave 6 weeks prior to symptom onset. "
f"Initial symptoms began gradually with fatigue and low-grade fever, progressing to persistent "
f"cough and night sweats. Physical examination reveals temperature 100.8°F (38.2°C), scattered "
f"tender erythematous nodules on both shins, and mild bilateral ankle swelling. Vital signs "
f"are otherwise stable except for mild tachypnea. Laboratory studies show WBC 3,100/μL "
f"(normal 4,500-11,000) with 65% lymphocytes, ESR 75 mm/hr, and LDH 420 U/L (normal <250 U/L). "
f"Chest CT demonstrates bilateral hilar lymphadenopathy with multiple small (<1 cm) pulmonary "
f"nodules scattered throughout both lung fields. Urine Histoplasma antigen is 15.2 ng/mL "
f"(normal <0.6 ng/mL). The patient denies immunocompromising conditions and has no significant "
f"past medical history aside from seasonal allergies."
)
elif "pneumonia" in topic.lower():
vignette = (
f"A 68-year-old man with COPD (FEV1 45% predicted) and well-controlled diabetes mellitus "
f"presents to the emergency department with 48 hours of productive cough with rust-colored "
f"sputum, right-sided pleuritic chest pain, and fever. He recently returned from a 7-day "
f"cruise 5 days ago and reports feeling well during travel. Symptoms began abruptly with "
f"rigors and high fever, followed by productive cough and sharp chest pain that worsens "
f"with inspiration. Physical examination reveals an ill-appearing man with dullness to "
f"percussion and bronchial breath sounds over the right lower lobe. Temperature 102.8°F "
f"(39.3°C), heart rate 110 bpm, blood pressure 118/72 mmHg, respiratory rate 26/min, "
f"oxygen saturation 89% on room air improving to 94% on 2L nasal cannula. Laboratory "
f"studies show WBC 15,800/μL with 82% neutrophils and 8% bands, procalcitonin 3.8 ng/mL "
f"(normal <0.1 ng/mL), and lactate 1.8 mmol/L. Chest X-ray shows right lower lobe "
f"consolidation with air bronchograms. Pneumococcal urinary antigen is positive. "
f"His CURB-65 score is calculated as 2 points (age >65, urea normal, RR >30, BP normal, confusion absent)."
)
else:
# Generic sophisticated vignette using comparison table features
vignette = (
f"A patient with {correct_features.get('epidemiology_risk_factors', 'relevant risk factors')} "
f"presents with {correct_features.get('clinical_presentation', 'characteristic symptoms')}. "
f"Physical examination and initial studies reveal {correct_features.get('laboratory_findings', 'significant findings')}. "
f"Imaging demonstrates {correct_features.get('imaging_characteristics', 'relevant abnormalities')}. "
f"The clinical presentation includes some features that could suggest alternative diagnoses, "
f"requiring careful analysis of the discriminating features to establish the correct diagnosis."
)
return vignette
def _create_advanced_question_stem_from_table(self, topic: str, question_type: str, comparison_table: Dict[str, Any]) -> str:
"""Create an advanced question stem based on the comparison table analysis."""
if question_type == "diagnosis":
# Transform to higher-order management question
if "candida auris" in topic.lower():
return "Given the antifungal resistance pattern and institutional infection control concerns, what is the most appropriate initial management approach?"
elif "histoplasmosis" in topic.lower():
return "Based on the clinical presentation, epidemiologic factors, and diagnostic findings, what is the most appropriate treatment approach?"
elif "pneumonia" in topic.lower():
return "Given the clinical severity assessment and microbiological findings, what is the most appropriate antibiotic management?"
else:
return "Based on the discriminating clinical features, what is the most appropriate next step in management?"
elif question_type == "management":
# Transform to monitoring/complications question
if "candida auris" in topic.lower():
return "What is the most critical monitoring parameter during treatment of this multi-drug resistant organism?"
elif "histoplasmosis" in topic.lower():
return "Which parameter should be monitored most closely during antifungal therapy for this condition?"
elif "pneumonia" in topic.lower():
return "What is the most important clinical parameter to assess treatment response in this patient?"
else:
return "Which monitoring approach is most critical for optimizing treatment outcomes?"
elif question_type == "pharmacology":
# Transform to drug selection/interaction question
return "Which pharmacologic consideration is most important when selecting therapy for this patient?"
elif question_type == "pathophysiology":
return "Based on the underlying pathophysiologic mechanism, why is the recommended approach most appropriate?"
else:
return "What is the most appropriate clinical decision based on the comparative analysis of this presentation?"
def _generate_answer_choices_from_table(self, comparison_table: Dict[str, Any]) -> List[Dict[str, str]]:
"""Generate answer choices based on the comparison table."""
table = comparison_table["table"]
choices = []
# Correct answer from the comparison table
correct_condition = table["correct_answer"]["condition"]
# Generate management-focused choices if this was originally a diagnosis question
if "candida auris" in correct_condition.lower():
choice_texts = [
"Immediate empirical echinocandin therapy with infection control isolation",
"Fluconazole therapy pending final susceptibility results",
"Amphotericin B monotherapy for broad-spectrum coverage",
"Combination antifungal therapy with fluconazole plus echinocandin",
"Observation with repeat cultures in 48 hours"
]
elif "histoplasmosis" in correct_condition.lower():
choice_texts = [
"Itraconazole 200 mg twice daily for 6-12 weeks",
"Amphotericin B 0.7 mg/kg/day for 4-6 weeks",
"Fluconazole 400 mg daily for 12 weeks",
"Observation with repeat imaging in 4 weeks",
"Empirical antibacterial therapy pending culture results"
]
elif "pneumonia" in correct_condition.lower():
choice_texts = [
"Ceftriaxone 2g IV daily plus azithromycin 500mg IV daily",
"Levofloxacin 750mg IV daily monotherapy",
"Piperacillin-tazobactam 4.5g IV every 6 hours plus vancomycin",
"Doxycycline 100mg twice daily",
"Oseltamivir 75mg twice daily"
]
else:
# Generic choices based on the conditions in the table
choice_texts = [
f"Treatment appropriate for {table['correct_answer']['condition']}",
f"Treatment more suitable for {table['distractor_1']['condition']}",
f"Treatment indicated for {table['distractor_2']['condition']}",
f"Treatment appropriate for {table['distractor_3']['condition']}",
f"Treatment suitable for {table['distractor_4']['condition']}"
]
# Format as lettered choices
letters = ["A", "B", "C", "D", "E"]
for i, text in enumerate(choice_texts):
choices.append({
"letter": letters[i],
"text": text,
"is_correct": i == 0 # First choice is always correct
})
return choices
def _generate_explanations_from_table(self, topic: str, question_type: str, comparison_table: Dict[str, Any], answer_choices: List[Dict]) -> Dict[str, str]:
"""Generate detailed explanations using the comparison table analysis."""
explanations = {}
table = comparison_table["table"]
for choice in answer_choices:
letter = choice["letter"]
text = choice["text"]
is_correct = choice["is_correct"]
if is_correct:
correct_features = table["correct_answer"]
if "candida auris" in topic.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the appropriate approach. The clinical presentation suggests "
f"C. auris invasive candidiasis based on: (1) {correct_features['epidemiology_risk_factors']}, "
f"(2) {correct_features['laboratory_findings']}, and (3) {correct_features['diagnostic_tests']}. "
f"C. auris requires immediate echinocandin therapy due to intrinsic fluconazole resistance and "
f"frequent amphotericin B resistance. Infection control isolation is critical due to environmental "
f"persistence and healthcare transmission risk. The resistance pattern and MALDI-TOF misidentification "
f"are characteristic of this emerging pathogen."
)
elif "histoplasmosis" in topic.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the appropriate treatment. The clinical presentation confirms "
f"acute pulmonary histoplasmosis based on: (1) {correct_features['epidemiology_risk_factors']}, "
f"(2) {correct_features['imaging_characteristics']}, and (3) {correct_features['diagnostic_tests']}. "
f"For moderate pulmonary disease with symptoms >4 weeks and weight loss, itraconazole is the "
f"first-line oral therapy per IDSA guidelines. The combination of geographic exposure, bilateral "
f"hilar lymphadenopathy, and positive urine antigen distinguishes this from other endemic mycoses."
)
elif "pneumonia" in topic.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the appropriate antibiotic regimen. The clinical presentation confirms "
f"community-acquired pneumonia with: (1) {correct_features['clinical_presentation']}, "
f"(2) {correct_features['laboratory_findings']}, and (3) {correct_features['diagnostic_tests']}. "
f"The CURB-65 score of 2 indicates moderate severity requiring hospitalization. The combination "
f"provides coverage for typical pathogens (ceftriaxone) and atypical pathogens (azithromycin) "
f"as recommended by IDSA/ATS guidelines."
)
else:
explanations[letter] = (
f"**CORRECT**: {text} is appropriate based on the discriminating features: "
f"{correct_features.get('clinical_presentation', '')} with "
f"{correct_features.get('laboratory_findings', '')} and "
f"{correct_features.get('diagnostic_tests', '')}."
)
else:
# Generate specific explanations for incorrect choices using comparison table
choice_index = ord(letter) - ord('A')
if choice_index <= 4:
distractor_key = f"distractor_{choice_index}" if choice_index > 0 else "correct_answer"
if distractor_key in table and choice_index > 0:
distractor_features = table[distractor_key]
explanations[letter] = (
f"**INCORRECT**: {text} would be more appropriate for "
f"{distractor_features['condition']}. This condition differs because it typically presents with "
f"{distractor_features.get('clinical_presentation', 'different clinical features')} and "
f"{distractor_features.get('laboratory_findings', 'different laboratory findings')}. "
f"The epidemiology ({distractor_features.get('epidemiology_risk_factors', 'different risk factors')}) "
f"and diagnostic testing ({distractor_features.get('diagnostic_tests', 'different test results')}) "
f"help distinguish this from the correct diagnosis."
)
else:
explanations[letter] = (
f"**INCORRECT**: {text} is not appropriate for this clinical scenario based on the "
f"discriminating features presented in the vignette."
)
else:
explanations[letter] = f"**INCORRECT**: {text} is not indicated for this clinical presentation."
return explanations
def _generate_clinical_vignette_advanced(self, topic: str, difficulty_level: str, question_type: str, discriminating_features: Dict[str, List[str]]) -> str:
"""Generate a clinical vignette that includes discriminating features but doesn't give away the answer."""
if "histoplasmosis" in topic.lower() or ("dimorphic" in topic.lower() and "histoplasma" in topic.lower()):
vignette = (
"A 45-year-old construction worker from Louisville, Kentucky, presents with a 3-week history of "
"nonproductive cough, low-grade fever (100.8°F), and 12-pound unintentional weight loss. He reports "
"recent recreational cave exploration activities 6 weeks prior to symptom onset. Physical examination "
"reveals scattered tender erythematous nodules on both shins. Vital signs are stable except for mild "
"tachypnea. Laboratory studies show WBC 3,100/μL (normal 4,500-11,000) with 65% lymphocytes. "
"Chest CT demonstrates bilateral hilar lymphadenopathy with multiple small (<1 cm) pulmonary nodules "
"scattered throughout both lung fields. The patient denies any known immunocompromising conditions "
"and has no significant past medical history."
)
elif "coccidioidomycosis" in topic.lower():
vignette = (
"A 28-year-old graduate student from Phoenix, Arizona, presents with a 2-week history of dry cough, "
"pleuritic chest pain, and arthralgias affecting knees and ankles. She reports recent field research "
"in the Sonoran Desert involving soil sampling. Physical examination reveals tender erythematous "
"nodules on the anterior shins and mild ankle swelling. Temperature is 101.4°F with otherwise normal "
"vital signs. Laboratory studies show WBC 8,200/μL with 12% eosinophils (normal <4%) and ESR 78 mm/hr. "
"Chest X-ray shows a right lower lobe infiltrate with hilar prominence. The patient is otherwise "
"healthy with no known drug allergies or significant medical history."
)
elif "blastomycosis" in topic.lower():
vignette = (
"A 52-year-old avid fisherman from northern Wisconsin presents with a 6-week history of productive "
"cough, low-grade fever, and a progressively enlarging skin lesion on his right forearm. He frequently "
"camps near lake shores and clears fallen timber. Physical examination reveals a 3-cm verrucous plaque "
"with central ulceration and rolled borders on the forearm. Pulmonary examination shows decreased "
"breath sounds at the right base. Chest CT demonstrates a right lower lobe mass-like consolidation. "
"Tissue biopsy of the skin lesion shows numerous thick-walled yeast forms with broad-based budding. "
"The patient has diabetes mellitus type 2 but is otherwise healthy."
)
elif "candida auris" in topic.lower():
vignette = (
"A 72-year-old man with end-stage renal disease on hemodialysis develops fever and hypotension 48 hours "
"after central venous catheter placement in the ICU. He has been hospitalized for 3 weeks following "
"complicated abdominal surgery with multiple invasive procedures. Blood cultures drawn from both the "
"central line and peripheral sites grow yeast identified as Candida species. The organism demonstrates "
"resistance to fluconazole (MIC >64 μg/mL) and intermediate resistance to amphotericin B (MIC 2 μg/mL). "
"MALDI-TOF mass spectrometry initially misidentifies the organism as C. haemulonii. The microbiology "
"laboratory requests molecular identification and antifungal susceptibility testing. The infection "
"control team is notified due to concerns about a multi-drug resistant Candida species."
)
elif "pneumonia" in topic.lower():
vignette = (
"A 68-year-old man with COPD (FEV1 45% predicted) and well-controlled diabetes presents with 48 hours "
"of productive cough with rust-colored sputum, right-sided pleuritic chest pain, and fever. He recently "
"returned from a 7-day cruise 5 days ago. Physical examination reveals dullness to percussion and "
"bronchial breath sounds over the right lower lobe. Temperature 102.8°F, heart rate 110 bpm, blood "
"pressure 118/72 mmHg, respiratory rate 26/min, oxygen saturation 89% on room air. Laboratory studies "
"show WBC 15,800/μL with 82% neutrophils, procalcitonin 3.8 ng/mL. Chest X-ray shows right lower lobe "
"consolidation with air bronchograms. His CURB-65 score is calculated as 2 points."
)
else:
# Generic advanced vignette
vignette = (
f"A patient with appropriate risk factors for {topic} presents with characteristic clinical features. "
f"The presentation includes key discriminating findings that help establish the diagnosis through "
f"systematic clinical reasoning. Physical examination and initial diagnostic studies reveal findings "
f"consistent with the suspected condition. Additional specialized testing is being considered to "
f"confirm the diagnosis and guide management decisions."
)
return vignette
def _create_advanced_question_stem(self, topic: str, question_type: str, discriminating_features: Dict[str, List[str]]) -> str:
"""Create a higher-order question stem that tests clinical reasoning."""
if question_type == "diagnosis":
# Upgrade to management question
if "histoplasmosis" in topic.lower() or "dimorphic" in topic.lower():
return "Based on the clinical presentation and epidemiologic factors, what is the most appropriate initial treatment approach?"
elif "pneumonia" in topic.lower():
return "Given the severity assessment and likely pathogen, what is the most appropriate antibiotic regimen?"
else:
return f"What is the most appropriate next step in management for this patient?"
elif question_type == "management":
# Upgrade to monitoring/complications question
if "histoplasmosis" in topic.lower() or "dimorphic" in topic.lower():
return "After initiating the appropriate antifungal therapy, which laboratory parameter should be monitored most closely?"
elif "pneumonia" in topic.lower():
return "What is the most important parameter to monitor for treatment response in this patient?"
else:
return f"Which monitoring parameter is most critical during treatment of this condition?"
elif question_type == "pharmacology":
# Upgrade to drug interactions/side effects question
if "histoplasmosis" in topic.lower() or "dimorphic" in topic.lower():
return "What is the most important drug interaction to consider with the first-line oral antifungal therapy?"
elif "pneumonia" in topic.lower():
return "Which adverse effect requires monitoring with the recommended β-lactam antibiotic?"
else:
return f"What is the most significant drug interaction concern with first-line therapy?"
elif question_type == "pathophysiology":
# Upgrade to mechanism-based treatment question
return f"Based on the underlying pathophysiology, why is the recommended treatment approach most appropriate?"
elif question_type == "complications":
# Upgrade to prognostic factors question
return f"Which factor most significantly influences the prognosis in this condition?"
else:
return f"What is the most appropriate clinical decision-making approach for this patient?"
def _generate_advanced_answer_choices(self, topic: str, question_type: str, difficulty_level: str, discriminating_features: Dict[str, List[str]]) -> List[Dict[str, str]]:
"""Generate answer choices for higher-order questions."""
if "histoplasmosis" in topic.lower() or "dimorphic" in topic.lower():
if question_type == "diagnosis": # Now management question
choices = [
"Itraconazole 200 mg twice daily for 6-12 weeks",
"Amphotericin B 0.7 mg/kg/day for 4-6 weeks",
"Fluconazole 400 mg daily for 12 weeks",
"Observation with repeat imaging in 4 weeks",
"Empirical antibacterial therapy with ceftriaxone and azithromycin"
]
elif question_type == "management": # Now monitoring question
choices = [
"Itraconazole serum levels after 2 weeks of therapy",
"Complete blood count every 2 weeks",
"Serum creatinine weekly",
"Liver function tests monthly",
"Chest imaging every 4 weeks"
]
elif question_type == "pharmacology": # Drug interactions
choices = [
"Proton pump inhibitors reducing itraconazole absorption",
"Warfarin enhancing anticoagulation effects",
"Statins increasing rhabdomyolysis risk",
"Calcium channel blockers causing hypotension",
"ACE inhibitors potentiating hyperkalemia"
]
elif "pneumonia" in topic.lower():
if question_type == "diagnosis": # Now management question
choices = [
"Ceftriaxone 2g IV daily plus azithromycin 500mg IV daily",
"Levofloxacin 750mg IV daily monotherapy",
"Piperacillin-tazobactam 4.5g IV every 6 hours",
"Vancomycin plus cefepime",
"Oseltamivir 75mg twice daily"
]
elif question_type == "management": # Now monitoring question
choices = [
"Clinical improvement within 48-72 hours",
"White blood cell count normalization",
"Chest X-ray clearing within 24 hours",
"Procalcitonin reduction by 50% daily",
"Oxygen saturation improvement within 6 hours"
]
elif question_type == "pharmacology": # Drug interactions
choices = [
"QTc prolongation with azithromycin",
"Nephrotoxicity with ceftriaxone",
"Hepatotoxicity with beta-lactams",
"Ototoxicity with macrolides",
"Photosensitivity with cephalosporins"
]
else:
# Generic advanced choices
choices = [
f"Evidence-based first-line approach for {topic}",
f"Alternative therapy with higher risk profile",
f"Inappropriate therapy for this clinical scenario",
f"Treatment with contraindication in this patient",
f"Suboptimal therapy with inadequate coverage"
]
# Format as lettered choices
letters = ["A", "B", "C", "D", "E"]
formatted_choices = []
for i, choice in enumerate(choices):
formatted_choices.append({
"letter": letters[i],
"text": choice,
"is_correct": i == 0 # First choice is always correct
})
return formatted_choices
def _generate_advanced_explanations(self, topic: str, answer_choices: List[Dict], question_type: str, discriminating_features: Dict[str, List[str]]) -> Dict[str, str]:
"""Generate explanations that emphasize clinical reasoning."""
explanations = {}
for choice in answer_choices:
letter = choice["letter"]
text = choice["text"]
is_correct = choice["is_correct"]
if is_correct:
if "itraconazole" in text.lower() and "histoplasmosis" in topic.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the appropriate treatment. The clinical presentation suggests acute "
f"pulmonary histoplasmosis based on: (1) Geographic exposure to Ohio River Valley with cave activities, "
f"(2) Bilateral hilar lymphadenopathy with multiple small nodules characteristic of histoplasmosis, and "
f"(3) Lymphopenia rather than neutrophilia, which helps distinguish from bacterial infections. "
f"For moderate pulmonary disease with symptoms lasting >4 weeks, itraconazole is the first-line "
f"oral therapy per IDSA guidelines. Amphotericin B is reserved for severe or disseminated disease."
)
elif "ceftriaxone" in text.lower() and "azithromycin" in text.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the appropriate antibiotic regimen. The clinical presentation suggests "
f"community-acquired pneumonia with: (1) Classic symptoms and consolidation pattern, "
f"(2) CURB-65 score of 2 indicating moderate severity requiring hospitalization, and "
f"(3) High procalcitonin (3.8 ng/mL) suggesting bacterial etiology. The combination provides "
f"coverage for typical pathogens (ceftriaxone) and atypical pathogens (azithromycin) as "
f"recommended by IDSA/ATS guidelines for hospitalized CAP patients."
)
elif "itraconazole serum levels" in text.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the most important monitoring parameter. Itraconazole has significant "
f"pharmacokinetic variability and drug interactions that can affect absorption and metabolism. "
f"Therapeutic levels (>1.0 mcg/mL) should be checked after 2 weeks of therapy to ensure adequate "
f"exposure. Subtherapeutic levels are associated with treatment failure, while supratherapeutic "
f"levels increase toxicity risk. Other monitoring is important but less critical for treatment success."
)
elif "clinical improvement" in text.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the most important parameter to monitor. Clinical response should occur "
f"within 48-72 hours of appropriate antibiotic therapy, including improved vital signs, decreased "
f"oxygen requirements, and reduced cough/sputum production. Lack of improvement suggests treatment "
f"failure, resistant organism, or complications requiring reassessment. Laboratory parameters and "
f"imaging may lag behind clinical improvement."
)
else:
explanations[letter] = (
f"**CORRECT**: {text} is the appropriate clinical decision based on the discriminating features "
f"presented in this case and current evidence-based guidelines."
)
else:
# Generate specific incorrect explanations
if "amphotericin" in text.lower() and "histoplasmosis" in topic.lower():
explanations[letter] = (
f"**INCORRECT**: {text} is inappropriate for this patient. Amphotericin B is reserved for "
f"severe pulmonary histoplasmosis or disseminated disease. This patient has moderate disease "
f"without signs of respiratory failure or dissemination. Starting with amphotericin exposes "
f"the patient to unnecessary nephrotoxicity and infusion-related reactions when oral itraconazole "
f"is equally effective for this severity."
)
elif "observation" in text.lower():
explanations[letter] = (
f"**INCORRECT**: {text} is inappropriate. The patient has symptomatic disease lasting >4 weeks "
f"with significant weight loss, indicating moderate severity that requires antifungal treatment. "
f"Observation is only appropriate for asymptomatic patients or those with very mild, self-limited "
f"disease. Untreated moderate disease can progress to chronic or disseminated forms."
)
elif "levofloxacin" in text.lower() and "pneumonia" in topic.lower():
explanations[letter] = (
f"**INCORRECT**: {text} monotherapy is suboptimal. While levofloxacin covers both typical and "
f"atypical pathogens, current IDSA/ATS guidelines recommend combination therapy (β-lactam plus "
f"macrolide) for hospitalized CAP patients to improve outcomes. Monotherapy is reserved for "
f"outpatients or specific clinical scenarios with contraindications to combination therapy."
)
else:
explanations[letter] = (
f"**INCORRECT**: {text} is not appropriate based on the clinical presentation and discriminating "
f"features that establish the diagnosis and guide optimal management decisions."
)
return explanations
def _generate_clinical_vignette(self, topic: str, difficulty_level: str, question_type: str) -> str:
"""Generate a realistic clinical vignette with at least 3 diagnostic hints."""
# Enhanced vignette templates with specific diagnostic hints
vignette_templates = {
"pneumonia": {
"patient": "A 65-year-old man with a history of COPD and diabetes mellitus",
"presentation": "presents to the emergency department with a 3-day history of productive cough with purulent sputum, fever to 102.5°F (39.2°C), and shortness of breath",
"exam": "On examination, he appears ill and has decreased breath sounds and crackles in the right lower lobe",
"vitals": "Temperature 102.5°F (39.2°C), heart rate 110 bpm, blood pressure 140/85 mmHg, respiratory rate 24/min, oxygen saturation 88% on room air",
"diagnostic_hints": [
"Chest X-ray shows a right lower lobe consolidation",
"White blood cell count is 18,000/μL with 85% neutrophils and left shift",
"Procalcitonin level is elevated at 2.5 ng/mL (normal <0.1 ng/mL)"
]
},
"heart failure": {
"patient": "A 72-year-old woman with a history of hypertension and coronary artery disease",
"presentation": "presents with a 2-week history of progressive dyspnea on exertion, orthopnea, and bilateral lower extremity edema",
"exam": "Physical examination reveals jugular venous distension, bilateral pulmonary rales, and 2+ pitting edema to the knees",
"vitals": "Temperature 98.6°F (37°C), heart rate 95 bpm, blood pressure 160/95 mmHg, respiratory rate 20/min, oxygen saturation 92% on room air",
"diagnostic_hints": [
"Echocardiogram shows ejection fraction of 35% with global hypokinesis",
"B-type natriuretic peptide (BNP) is elevated at 1,200 pg/mL (normal <100 pg/mL)",
"Chest X-ray demonstrates cardiomegaly with bilateral pulmonary vascular congestion"
]
},
"diabetes": {
"patient": "A 58-year-old obese man with a 10-year history of type 2 diabetes mellitus",
"presentation": "presents for routine follow-up. He reports good adherence to metformin but continues to have elevated blood glucose readings",
"exam": "Physical examination is notable for mild diabetic retinopathy on fundoscopy and diminished vibration sensation in both feet",
"vitals": "Temperature 98.4°F (36.9°C), heart rate 78 bpm, blood pressure 145/90 mmHg, BMI 32 kg/m²",
"diagnostic_hints": [
"Hemoglobin A1c is 9.2% (target <7%)",
"Fasting glucose levels average 180-220 mg/dL over the past month",
"Microalbumin-to-creatinine ratio is 45 mg/g (normal <30 mg/g), indicating early diabetic nephropathy"
]
},
"sepsis": {
"patient": "A 45-year-old woman with no significant medical history",
"presentation": "presents to the emergency department with a 12-hour history of fever, chills, and altered mental status",
"exam": "She appears acutely ill with warm, flushed skin and is confused to time and place",
"vitals": "Temperature 101.8°F (38.8°C), heart rate 120 bpm, blood pressure 85/50 mmHg, respiratory rate 28/min, oxygen saturation 94% on room air",
"diagnostic_hints": [
"Lactate level is 4.2 mmol/L (normal <2.0 mmol/L)",
"White blood cell count is 18,500/μL with 20% immature forms (bands)",
"Procalcitonin is markedly elevated at 8.5 ng/mL, and blood cultures are pending"
]
},
"myocardial infarction": {
"patient": "A 62-year-old man with a history of hypertension and smoking",
"presentation": "presents with 2 hours of severe substernal chest pain radiating to his left arm, associated with diaphoresis and nausea",
"exam": "He appears diaphoretic and anxious, with normal heart sounds and clear lung fields",
"vitals": "Temperature 98.2°F (36.8°C), heart rate 95 bpm, blood pressure 150/90 mmHg, respiratory rate 18/min, oxygen saturation 98% on room air",
"diagnostic_hints": [
"ECG shows ST-segment elevation in leads II, III, and aVF",
"Troponin I is elevated at 8.5 ng/mL (normal <0.04 ng/mL)",
"Echocardiogram reveals new wall motion abnormality in the inferior wall"
]
}
}
# Get template or create enhanced generic one
if topic.lower() in vignette_templates:
template = vignette_templates[topic.lower()]
else:
template = self._create_enhanced_generic_template(topic, question_type)
# Add question-type specific hints
additional_hints = self._get_question_type_hints(topic, question_type)
all_hints = template["diagnostic_hints"] + additional_hints
# Ensure we have at least 3 hints
if len(all_hints) < 3:
all_hints.extend(self._generate_additional_hints(topic, question_type, 3 - len(all_hints)))
# Add laboratory/imaging based on question type
additional_info = ""
if question_type == "diagnosis":
additional_info = " Additional diagnostic studies are obtained to confirm the diagnosis."
elif question_type == "management":
additional_info = " The diagnosis has been established and treatment options are being considered."
elif question_type == "pharmacology":
additional_info = " Medication management is being optimized based on the clinical findings."
# Combine into complete vignette with diagnostic hints
vignette = f"{template['patient']} {template['presentation']}. {template['exam']}. Vital signs: {template['vitals']}. "
# Add diagnostic hints
for i, hint in enumerate(all_hints[:3], 1): # Use top 3 hints
vignette += f"Laboratory/imaging finding {i}: {hint}. "
vignette += additional_info
return vignette
def _create_enhanced_generic_template(self, topic: str, question_type: str) -> Dict[str, Any]:
"""Create an enhanced generic template with diagnostic hints."""
return {
"patient": f"A 55-year-old patient with relevant medical history for {topic}",
"presentation": f"presents with classic symptoms and signs consistent with {topic}",
"exam": f"Physical examination reveals key findings suggestive of {topic}",
"vitals": "Vital signs show relevant abnormalities for the condition",
"diagnostic_hints": [
f"Key diagnostic test result #1 specific to {topic}",
f"Key diagnostic test result #2 that confirms {topic}",
f"Key diagnostic test result #3 that rules out alternatives to {topic}"
]
}
def _get_question_type_hints(self, topic: str, question_type: str) -> List[str]:
"""Get additional hints specific to the question type."""
hints = []
if question_type == "diagnosis":
hints.extend([
"Imaging findings are consistent with the suspected diagnosis",
"Biomarker levels support the clinical impression"
])
elif question_type == "management":
hints.extend([
"Current vital signs indicate urgency of intervention",
"Comorbidities influence treatment selection"
])
elif question_type == "pharmacology":
hints.extend([
"Renal function affects medication dosing",
"Drug interactions must be considered"
])
elif question_type == "pathophysiology":
hints.extend([
"Underlying mechanism explains the clinical presentation",
"Pathophysiologic process accounts for laboratory abnormalities"
])
return hints
def _generate_additional_hints(self, topic: str, question_type: str, num_needed: int) -> List[str]:
"""Generate additional diagnostic hints when needed."""
generic_hints = [
f"Clinical course is typical for {topic}",
f"Response to initial interventions supports {topic} diagnosis",
f"Differential diagnosis considerations favor {topic}",
f"Risk factors align with {topic} presentation",
f"Timeline of symptoms is characteristic of {topic}"
]
return generic_hints[:num_needed]
def _create_question_stem(self, topic: str, question_type: str) -> str:
"""Create the question stem based on type."""
question_stems = {
"diagnosis": f"What is the most likely diagnosis?",
"management": f"What is the most appropriate next step in management?",
"pathophysiology": f"What is the most likely underlying pathophysiology?",
"pharmacology": f"What is the most appropriate medication for this patient?",
"complications": f"What is the most likely complication to monitor for?"
}
return question_stems.get(question_type, f"What is the most appropriate approach for this patient with {topic}?")
def _generate_answer_choices(self, topic: str, question_type: str, difficulty_level: str) -> List[Dict[str, str]]:
"""Generate 5 answer choices with the correct answer first, designed to have only one correct option based on vignette hints."""
# Enhanced topic-specific answer choices with clear distinctions
choices_map = {
"pneumonia": {
"diagnosis": [
"Community-acquired pneumonia",
"Pulmonary embolism", # Would not have consolidation, elevated WBC, or procalcitonin
"Congestive heart failure", # Would not have fever, purulent sputum, or elevated procalcitonin
"Chronic obstructive pulmonary disease exacerbation", # Would not have consolidation or elevated procalcitonin
"Lung cancer" # Would not have acute fever, elevated WBC, or procalcitonin
],
"management": [
"Start empirical antibiotic therapy and obtain cultures",
"Prescribe bronchodilators only", # Inadequate for bacterial pneumonia with elevated procalcitonin
"Immediate intubation", # Not indicated with current O2 sat and mental status
"High-dose corticosteroids", # Not first-line for bacterial pneumonia
"Observe without treatment" # Inappropriate with clear signs of bacterial infection
]
},
"heart failure": {
"diagnosis": [
"Acute decompensated heart failure",
"Pneumonia", # Would not have elevated BNP, cardiomegaly, or reduced EF
"Pulmonary embolism", # Would not have elevated BNP, cardiomegaly, or bilateral edema
"Chronic kidney disease", # Would not have reduced EF or pulmonary congestion
"Liver cirrhosis" # Would not have elevated BNP, reduced EF, or pulmonary congestion
],
"management": [
"Diuretics and ACE inhibitor optimization",
"Immediate cardiac catheterization", # Not indicated for chronic heart failure exacerbation
"High-dose beta-blockers", # Inappropriate in acute decompensated state
"Fluid resuscitation", # Contraindicated with volume overload
"Antibiotics" # Not indicated without signs of infection
]
},
"sepsis": {
"diagnosis": [
"Sepsis with organ dysfunction",
"Pneumonia without systemic involvement", # Would not have hypotension, altered mental status, or elevated lactate
"Hypovolemic shock", # Would not have warm, flushed skin or elevated WBC with left shift
"Cardiogenic shock", # Would not have warm skin, elevated WBC, or elevated procalcitonin
"Anaphylactic shock" # Would not have fever, elevated WBC, or elevated procalcitonin
],
"management": [
"Immediate IV antibiotics and fluid resuscitation",
"Wait for culture results before antibiotics", # Inappropriate delay in sepsis
"Vasopressors as first-line therapy", # Should try fluid resuscitation first
"High-dose corticosteroids", # Not first-line for septic shock
"Observation only" # Inappropriate with clear signs of sepsis
]
},
"myocardial infarction": {
"diagnosis": [
"ST-elevation myocardial infarction (STEMI)",
"Unstable angina", # Would not have ST elevation or markedly elevated troponin
"Pericarditis", # Would not have focal ST elevation or wall motion abnormality
"Aortic dissection", # Would not have ST elevation or elevated troponin
"Pulmonary embolism" # Would not have ST elevation in inferior leads or wall motion abnormality
],
"management": [
"Immediate cardiac catheterization for primary PCI",
"Medical management with anticoagulation only", # Inadequate for STEMI
"Thrombolytic therapy", # PCI preferred when available within time window
"Observation with serial troponins", # Inappropriate delay for STEMI
"High-dose aspirin only" # Inadequate monotherapy for STEMI
]
},
"diabetes": {
"diagnosis": [
"Poorly controlled type 2 diabetes mellitus",
"Type 1 diabetes mellitus", # Would typically present at younger age, different history
"Gestational diabetes", # Not applicable to 58-year-old male
"Secondary diabetes due to pancreatitis", # Would need history of pancreatitis
"Prediabetes" # HbA1c 9.2% exceeds diagnostic threshold
],
"management": [
"Intensify diabetes management with additional oral agent or insulin",
"Continue metformin alone", # Inadequate given HbA1c 9.2%
"Dietary modification only", # Insufficient for HbA1c 9.2%
"Discontinue all medications", # Inappropriate and dangerous
"Refer to endocrinology without changes" # Should initiate intensification while arranging referral
]
}
}
# Get choices or create enhanced generic ones
if topic.lower() in choices_map and question_type in choices_map[topic.lower()]:
choice_list = choices_map[topic.lower()][question_type]
else:
choice_list = self._generate_enhanced_generic_choices(topic, question_type)
# Format as lettered choices
letters = ["A", "B", "C", "D", "E"]
formatted_choices = []
for i, choice in enumerate(choice_list):
formatted_choices.append({
"letter": letters[i],
"text": choice,
"is_correct": i == 0 # First choice is always correct
})
return formatted_choices
def _generate_enhanced_generic_choices(self, topic: str, question_type: str) -> List[str]:
"""Generate enhanced generic choices that are clearly distinguishable."""
if question_type == "diagnosis":
return [
f"Correct diagnosis: {topic}",
f"Common differential that lacks key diagnostic features",
f"Alternative condition with different laboratory pattern",
f"Condition with different imaging findings",
f"Condition with different clinical course"
]
elif question_type == "management":
return [
f"Evidence-based first-line treatment for {topic}",
f"Treatment appropriate for different condition",
f"Treatment with contraindication in this case",
f"Treatment that is premature or excessive",
f"Treatment that is inadequate for severity"
]
elif question_type == "pharmacology":
return [
f"Appropriate medication choice for {topic}",
f"Medication with contraindication in this patient",
f"Medication inappropriate for this condition",
f"Medication with significant drug interaction",
f"Medication with inappropriate dosing"
]
else:
return [
f"Correct answer for {topic} {question_type}",
f"Common misconception about {topic}",
f"Less likely but possible option",
f"Incorrect but plausible choice",
f"Clearly incorrect option"
]
def _generate_explanations(self, topic: str, answer_choices: List[Dict], question_type: str) -> Dict[str, str]:
"""Generate detailed explanations for each answer choice."""
explanations = {}
for choice in answer_choices:
letter = choice["letter"]
text = choice["text"]
is_correct = choice["is_correct"]
if is_correct:
explanations[letter] = f"**CORRECT**: {text} is the correct answer. This diagnosis fits the clinical presentation because [detailed explanation of why this is correct, including pathophysiology, clinical features, and supporting evidence]. Key teaching points include [relevant educational content about the condition, management, and important clinical pearls]."
else:
explanations[letter] = f"**INCORRECT**: {text} is incorrect because [detailed explanation of why this option is wrong, including how it differs from the correct diagnosis]. However, this is an important differential to consider because [educational content about this condition, when it should be considered, and key distinguishing features]."
return explanations
def _enhance_question_difficulty(
self,
topic: str,
difficulty_level: str,
question_type: str,
vignette: str,
question_stem: str,
answer_choices: List[Dict[str, str]],
explanations: Dict[str, str]
) -> Dict[str, Any]:
"""
Quality control method to enhance question difficulty and clinical accuracy.
This method reviews the initial question and makes it more challenging while
maintaining clinical accuracy, especially for infectious disease specialists.
"""
enhancement_notes = []
# Enhanced vignette with more specific clinical details
enhanced_vignette = self._enhance_vignette_specificity(topic, vignette, difficulty_level)
if enhanced_vignette != vignette:
enhancement_notes.append("Enhanced vignette with more specific clinical details")
# Enhanced answer choices with more nuanced distractors
enhanced_choices = self._enhance_answer_choices_specificity(topic, answer_choices, question_type)
if enhanced_choices != answer_choices:
enhancement_notes.append("Enhanced answer choices with more specific distractors")
# Enhanced explanations with more detailed clinical reasoning
enhanced_explanations = self._enhance_explanations_depth(topic, enhanced_choices, question_type)
return {
"vignette": enhanced_vignette,
"question_stem": question_stem,
"answer_choices": enhanced_choices,
"explanations": enhanced_explanations,
"enhancement_notes": "; ".join(enhancement_notes) if enhancement_notes else "No enhancements needed"
}
def _enhance_vignette_specificity(self, topic: str, vignette: str, difficulty_level: str) -> str:
"""Enhance vignette with more specific clinical details for higher difficulty."""
# Topic-specific enhancements for infectious disease focus
if "dimorphic fungi" in topic.lower() or "fungal" in topic.lower():
# Make it more specific for ID specialists
enhanced_vignette = (
"A 45-year-old construction worker from the Ohio River Valley presents with a 3-week history of "
"fever, nonproductive cough, and 15-pound weight loss. He reports recent spelunking activities "
"in Kentucky caves 6 weeks ago. Physical examination reveals scattered erythematous nodules on "
"the shins consistent with erythema nodosum, and bilateral hilar lymphadenopathy on chest imaging. "
"Temperature 101.2°F (38.4°C), heart rate 88 bpm, blood pressure 135/80 mmHg, respiratory rate 20/min, "
"oxygen saturation 94% on room air. Laboratory findings: WBC 3,200/μL with lymphopenia, "
"ESR 85 mm/hr, and LDH 420 U/L. Chest CT shows bilateral hilar lymphadenopathy with multiple "
"small pulmonary nodules. Histoplasma urine antigen is positive at 15.2 ng/mL (normal <0.6 ng/mL). "
"Serum complement fixation titers for Histoplasma are elevated at 1:32 for both mycelial and yeast forms."
)
return enhanced_vignette
elif "pneumonia" in topic.lower():
# Make pneumonia more challenging for ID specialists
enhanced_vignette = (
"A 68-year-old man with COPD (FEV1 45% predicted) and diabetes mellitus presents with acute onset "
"of productive cough with rust-colored sputum, pleuritic chest pain, and fever for 48 hours. "
"He recently returned from a cruise ship 5 days ago. Physical examination reveals dullness to "
"percussion and bronchial breath sounds over the right lower lobe. Vital signs: temperature 102.8°F "
"(39.3°C), heart rate 115 bpm, blood pressure 110/70 mmHg, respiratory rate 28/min, oxygen saturation "
"89% on room air. Laboratory findings: WBC 16,800/μL with 85% neutrophils and 12% bands, procalcitonin "
"4.2 ng/mL, lactate 2.8 mmol/L. Chest X-ray shows right lower lobe consolidation with air bronchograms. "
"Urinary pneumococcal antigen is positive, and blood cultures are pending. His CURB-65 score is 2."
)
return enhanced_vignette
# If no specific enhancement available, return original
return vignette
def _enhance_answer_choices_specificity(self, topic: str, answer_choices: List[Dict[str, str]], question_type: str) -> List[Dict[str, str]]:
"""Enhance answer choices with more specific, challenging distractors."""
if "dimorphic fungi" in topic.lower() or "fungal" in topic.lower():
if question_type == "diagnosis":
enhanced_choices = [
{"letter": "A", "text": "Acute pulmonary histoplasmosis", "is_correct": True},
{"letter": "B", "text": "Chronic pulmonary coccidioidomycosis", "is_correct": False},
{"letter": "C", "text": "Pulmonary blastomycosis", "is_correct": False},
{"letter": "D", "text": "Sarcoidosis with Löfgren syndrome", "is_correct": False},
{"letter": "E", "text": "Hypersensitivity pneumonitis", "is_correct": False}
]
return enhanced_choices
elif question_type == "management":
enhanced_choices = [
{"letter": "A", "text": "Itraconazole 200 mg twice daily for 6-12 weeks", "is_correct": True},
{"letter": "B", "text": "Amphotericin B 0.7 mg/kg/day for 2 weeks", "is_correct": False},
{"letter": "C", "text": "Fluconazole 400 mg daily for 12 weeks", "is_correct": False},
{"letter": "D", "text": "Voriconazole 200 mg twice daily for 8 weeks", "is_correct": False},
{"letter": "E", "text": "Observation without antifungal therapy", "is_correct": False}
]
return enhanced_choices
elif "pneumonia" in topic.lower():
if question_type == "diagnosis":
enhanced_choices = [
{"letter": "A", "text": "Community-acquired pneumonia (CAP) with Streptococcus pneumoniae", "is_correct": True},
{"letter": "B", "text": "Healthcare-associated pneumonia (HCAP) with Pseudomonas aeruginosa", "is_correct": False},
{"letter": "C", "text": "Atypical pneumonia with Legionella pneumophila", "is_correct": False},
{"letter": "D", "text": "Viral pneumonia with influenza A", "is_correct": False},
{"letter": "E", "text": "Aspiration pneumonia with anaerobic bacteria", "is_correct": False}
]
return enhanced_choices
elif question_type == "management":
enhanced_choices = [
{"letter": "A", "text": "Ceftriaxone 2g IV daily plus azithromycin 500mg IV daily", "is_correct": True},
{"letter": "B", "text": "Piperacillin-tazobactam 4.5g IV every 6 hours", "is_correct": False},
{"letter": "C", "text": "Levofloxacin 750mg IV daily monotherapy", "is_correct": False},
{"letter": "D", "text": "Vancomycin 15mg/kg IV every 12 hours plus cefepime 2g IV every 8 hours", "is_correct": False},
{"letter": "E", "text": "Doxycycline 100mg PO twice daily", "is_correct": False}
]
return enhanced_choices
# Return original if no specific enhancement
return answer_choices
def _enhance_explanations_depth(self, topic: str, answer_choices: List[Dict[str, str]], question_type: str) -> Dict[str, str]:
"""Generate more detailed, clinically sophisticated explanations."""
explanations = {}
for choice in answer_choices:
letter = choice["letter"]
text = choice["text"]
is_correct = choice["is_correct"]
if is_correct:
if "histoplasmosis" in text.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the correct diagnosis. This patient's presentation is classic for "
f"acute pulmonary histoplasmosis following cave exposure in an endemic area (Ohio River Valley). "
f"Key diagnostic features include: (1) Geographic risk factor with recent spelunking in Kentucky, "
f"(2) Constellation of fever, weight loss, and erythema nodosum, (3) Bilateral hilar lymphadenopathy "
f"with pulmonary nodules on CT, (4) Positive urine antigen (highly sensitive and specific), and "
f"(5) Elevated complement fixation titers for both mycelial and yeast forms. The lymphopenia is "
f"characteristic of histoplasmosis and helps distinguish it from bacterial infections. Treatment "
f"with itraconazole is indicated for moderate symptoms lasting >4 weeks."
)
elif "pneumonia" in text.lower() and "streptococcus" in text.lower():
explanations[letter] = (
f"**CORRECT**: {text} is the correct diagnosis. This patient presents with classic features "
f"of pneumococcal pneumonia: (1) Acute onset with rust-colored sputum and pleuritic chest pain, "
f"(2) Lobar consolidation on chest imaging, (3) Positive urinary pneumococcal antigen, and "
f"(4) High procalcitonin suggesting bacterial etiology. The recent cruise ship exposure increases "
f"risk for pneumococcal disease. CURB-65 score of 2 indicates moderate severity requiring "
f"hospitalization. The combination of β-lactam plus macrolide is recommended for hospitalized "
f"CAP patients to cover both typical and atypical pathogens."
)
else:
explanations[letter] = (
f"**CORRECT**: {text} is the correct answer based on the clinical presentation, "
f"diagnostic findings, and epidemiologic factors presented in this case."
)
else:
# Enhanced incorrect explanations
if "coccidioidomycosis" in text.lower():
explanations[letter] = (
f"**INCORRECT**: {text} is incorrect. While coccidioidomycosis can present with similar "
f"pulmonary symptoms and erythema nodosum, this patient's geographic exposure (Ohio River Valley "
f"caves) and positive Histoplasma urine antigen are not consistent with coccidioidomycosis. "
f"Coccidioidomycosis is endemic to the southwestern United States, particularly Arizona and "
f"California. The urine antigen test is specific for Histoplasma and would not be positive "
f"in coccidioidomycosis."
)
elif "sarcoidosis" in text.lower():
explanations[letter] = (
f"**INCORRECT**: {text} is incorrect. While sarcoidosis with Löfgren syndrome can present with "
f"bilateral hilar lymphadenopathy and erythema nodosum, several features argue against this "
f"diagnosis: (1) Recent cave exposure in endemic area, (2) Positive Histoplasma urine antigen, "
f"(3) Elevated complement fixation titers, and (4) Significant weight loss and fever. Löfgren "
f"syndrome typically has a more acute onset and better prognosis than chronic sarcoidosis."
)
elif "legionella" in text.lower():
explanations[letter] = (
f"**INCORRECT**: {text} is incorrect. While Legionella pneumonia can occur in cruise ship "
f"outbreaks, the positive urinary pneumococcal antigen specifically identifies S. pneumoniae. "
f"Legionella would typically present with hyponatremia, elevated LDH, and diarrhea, and would "
f"require urinary Legionella antigen testing for diagnosis. The clinical picture and positive "
f"pneumococcal antigen confirm pneumococcal etiology."
)
else:
explanations[letter] = (
f"**INCORRECT**: {text} is incorrect based on the clinical presentation and diagnostic findings. "
f"This option can be ruled out by the specific laboratory and imaging findings presented."
)
return explanations
async def _convert_generic_to_clinical_vignette(self, generic_vignette: str, clinical_context: Dict[str, Any], comparison_table: Dict[str, Any]) -> str:
"""
Step 10: Convert generic placeholder vignette into realistic clinical scenario
This addresses the issue of generic vignettes with placeholder text that gives away answers.
"""
condition_category = clinical_context["condition_category"]
correct_answer = comparison_table["table"]["correct_answer"]
# Create realistic clinical vignettes based on condition type
if "aspergillosis" in condition_category.lower() or "aspergillus" in condition_category.lower():
realistic_vignette = (
"A 34-year-old man with acute myeloid leukemia is admitted for induction chemotherapy. "
"On day 12 of hospitalization, he develops fever to 102.8°F (39.3°C) despite broad-spectrum "
"antibiotics (piperacillin-tazobactam and vancomycin) for 48 hours. He reports new onset "
"of right-sided pleuritic chest pain and has had two episodes of small-volume hemoptysis. "
"Physical examination reveals an ill-appearing man with temperature 102.8°F, heart rate 115 bpm, "
"blood pressure 110/65 mmHg, respiratory rate 24/min, and oxygen saturation 94% on 2L nasal cannula. "
"Lung examination shows decreased breath sounds at the right base with dullness to percussion. "
"Laboratory studies show: WBC 400/μL (normal 4,500-11,000) with 85% neutrophils, absolute neutrophil "
"count 340/μL, platelet count 45,000/μL, and creatinine 1.2 mg/dL. Chest CT demonstrates a 2.5-cm "
"right lower lobe nodule with surrounding ground-glass opacity ('halo sign') and a smaller left "
"upper lobe nodule. Serum galactomannan index is 2.8 (normal <0.5). Blood cultures remain negative "
"after 72 hours. The patient has no known drug allergies and has been receiving prophylactic "
"fluconazole 400mg daily since admission."
)
elif "candida" in condition_category.lower() and "auris" in condition_category.lower():
realistic_vignette = (
"A 67-year-old woman with end-stage renal disease on hemodialysis is admitted to the ICU "
"following complications from abdominal surgery 18 days ago. She has required multiple "
"invasive procedures including central venous catheter placement, mechanical ventilation, "
"and broad-spectrum antibiotic therapy with vancomycin, meropenem, and fluconazole. "
"On hospital day 20, she develops new fever to 101.6°F (38.7°C) and hypotension requiring "
"vasopressor support. Physical examination reveals temperature 101.6°F, heart rate 125 bpm, "
"blood pressure 85/45 mmHg on norepinephrine, and clear lungs. The central line insertion "
"site appears clean without erythema. Laboratory studies show: WBC 14,200/μL with 78% neutrophils "
"and 15% bands, lactate 3.8 mmol/L (normal <2.0), and procalcitonin 1.2 ng/mL. Blood cultures "
"drawn from both central and peripheral sites grow yeast after 16 hours. The isolate demonstrates "
"resistance to fluconazole (MIC >64 μg/mL) and intermediate resistance to amphotericin B (MIC 2 μg/mL). "
"MALDI-TOF mass spectrometry reports 'Candida haemulonii' with low confidence score (1.6). "
"The microbiology laboratory requests molecular identification due to the unusual resistance pattern."
)
elif "histoplasmosis" in condition_category.lower() or "cave" in condition_category.lower():
realistic_vignette = (
"A 42-year-old construction worker from Cincinnati, Ohio, presents to the emergency department "
"with a 4-week history of persistent nonproductive cough, low-grade fever, and 15-pound "
"unintentional weight loss. He reports recent recreational spelunking activities at Mammoth Cave "
"in Kentucky approximately 7 weeks ago with several friends. Initial symptoms began gradually "
"2 weeks after the cave trip with fatigue and intermittent fever, progressing to persistent cough "
"and night sweats. He denies chest pain initially but now reports mild bilateral chest discomfort. "
"Physical examination reveals an afebrile man (temperature 99.8°F) with scattered tender erythematous "
"nodules on both anterior shins and mild bilateral ankle swelling. Vital signs show heart rate 88 bpm, "
"blood pressure 135/82 mmHg, respiratory rate 18/min, and oxygen saturation 96% on room air. "
"Laboratory studies reveal: WBC 3,400/μL (normal 4,500-11,000) with 68% lymphocytes, ESR 82 mm/hr, "
"and LDH 445 U/L (normal <250). Chest CT demonstrates bilateral hilar lymphadenopathy with multiple "
"small (<1 cm) pulmonary nodules scattered throughout both lung fields. Urine Histoplasma antigen "
"is 18.5 ng/mL (normal <0.6 ng/mL)."
)
elif "pneumonia" in condition_category.lower() and "pneumococcal" in condition_category.lower():
realistic_vignette = (
"A 71-year-old man with COPD (FEV1 42% predicted) and well-controlled type 2 diabetes mellitus "
"presents to the emergency department with a 36-hour history of acute onset productive cough "
"with rust-colored sputum, right-sided pleuritic chest pain, and fever. He recently returned "
"from a 10-day cruise to the Caribbean 4 days ago and felt well during the entire trip. "
"Symptoms began abruptly yesterday evening with rigors and high fever, followed by productive "
"cough and sharp chest pain that worsens with deep inspiration. He denies recent antibiotic use "
"or hospitalization. Physical examination reveals an ill-appearing man with temperature 103.1°F "
"(39.5°C), heart rate 118 bmp, blood pressure 125/78 mmHg, respiratory rate 28/min, and oxygen "
"saturation 88% on room air improving to 95% on 3L nasal cannula. Lung examination shows dullness "
"to percussion and bronchial breath sounds over the right lower lobe posteriorly. Laboratory studies "
"reveal: WBC 17,200/μL with 86% neutrophils and 10% bands, procalcitonin 4.2 ng/mL (normal <0.1), "
"lactate 1.9 mmol/L, and creatinine 1.1 mg/dL. Chest X-ray demonstrates right lower lobe consolidation "
"with air bronchograms. Pneumococcal urinary antigen is positive. His calculated CURB-65 score is 2 "
"(age >65, respiratory rate >30)."
)
elif "bacterial" in condition_category.lower() and "pneumonia" in condition_category.lower():
realistic_vignette = (
"A 45-year-old homeless man with a history of alcohol use disorder presents to the emergency "
"department with a 5-day history of fever, productive cough with foul-smelling sputum, and "
"progressive dyspnea. He reports poor dental hygiene and recalls choking on food 2 weeks ago "
"followed by several days of coughing. He has been living in a shelter and denies recent "
"hospitalization or antibiotic use. Physical examination reveals a cachectic man with temperature "
"101.8°F (38.8°C), heart rate 105 bpm, blood pressure 95/60 mmHg, respiratory rate 26/min, "
"and oxygen saturation 91% on room air. Oral examination shows poor dentition with multiple "
"carious teeth. Lung examination reveals dullness and decreased breath sounds over the right "
"lower lobe with coarse crackles. Laboratory studies show: WBC 18,500/μL with 82% neutrophils "
"and 12% bands, hemoglobin 9.8 g/dL, and albumin 2.4 g/dL. Chest CT demonstrates a thick-walled "
"cavity in the right lower lobe with an air-fluid level and surrounding consolidation."
)
else:
# Generic fallback that's still better than placeholder text
realistic_vignette = (
f"A patient presents to the hospital with a complex clinical syndrome. The presentation "
f"includes multiple clinical findings that require systematic analysis to establish the "
f"correct diagnosis. Laboratory studies, imaging findings, and epidemiologic factors "
f"provide important diagnostic clues. The case challenges clinical reasoning skills "
f"and requires integration of multiple data points to arrive at the most likely diagnosis."
)
return realistic_vignette
def _generate_learning_objectives(self, topic: str, question_type: str) -> List[str]:
"""Generate learning objectives for the question."""
objectives = [
f"Recognize the clinical presentation of {topic}",
f"Differentiate {topic} from other common conditions",
f"Understand the pathophysiology underlying {topic}",
f"Apply appropriate diagnostic and management strategies"
]
if question_type == "pharmacology":
objectives.append("Understand medication mechanisms and contraindications")
elif question_type == "complications":
objectives.append("Identify and manage potential complications")
return objectives
def _generate_references(self, topic: str, guideline_sources: Optional[List[Dict[str, str]]] = None) -> List[str]:
"""Generate reference suggestions for further reading, including discovered guidelines."""
references = []
# Add discovered guideline sources first
if guideline_sources:
for source in guideline_sources[:3]: # Limit to top 3
title = source.get("title", "")
url = source.get("url", "")
if title and url:
references.append(f"{title} - {url}")
# Add standard references
standard_refs = [
f"Harrison's Principles of Internal Medicine - {topic.title()} chapter",
f"UpToDate - {topic.title()} clinical topic",
f"Relevant clinical practice guidelines for {topic}",
f"NEJM Clinical Practice articles on {topic}"
]
# Add standard references if we don't have many guideline sources
references.extend(standard_refs[:max(1, 4 - len(references))])
return references