Agentic-RagBot / src /agents /clinical_guidelines.py
T0X1N's picture
chore: codebase audit and fixes (ruff, mypy, pytest)
9659593
"""
MediGuard AI RAG-Helper
Clinical Guidelines Agent - Retrieves evidence-based recommendations
"""
from pathlib import Path
from langchain_core.prompts import ChatPromptTemplate
from src.llm_config import llm_config
from src.state import AgentOutput, GuildState
class ClinicalGuidelinesAgent:
"""Agent that retrieves clinical guidelines and recommendations using RAG"""
def __init__(self, retriever):
"""
Initialize with a retriever for clinical guidelines.
Args:
retriever: Vector store retriever for guidelines documents
"""
self.retriever = retriever
self.llm = llm_config.explainer
def recommend(self, state: GuildState) -> GuildState:
"""
Retrieve clinical guidelines and generate recommendations.
Args:
state: Current guild state
Returns:
Updated state with clinical recommendations
"""
print("\n" + "=" * 70)
print("EXECUTING: Clinical Guidelines Agent (RAG)")
print("=" * 70)
model_prediction = state["model_prediction"]
disease = model_prediction["disease"]
confidence = model_prediction["confidence"]
# Get biomarker analysis
biomarker_analysis = state.get("biomarker_analysis") or {}
safety_alerts = biomarker_analysis.get("safety_alerts", [])
# Retrieve guidelines
print(f"\nRetrieving clinical guidelines for {disease}...")
query = f"""What are the clinical practice guidelines for managing {disease}?
Include lifestyle modifications, monitoring recommendations, and when to seek medical care."""
docs = self.retriever.invoke(query)
print(f"Retrieved {len(docs)} guideline documents")
# Generate recommendations
if state["sop"].require_pdf_citations and not docs:
recommendations = {
"immediate_actions": [
"Insufficient evidence available in the knowledge base. Please consult a healthcare provider."
],
"lifestyle_changes": [],
"monitoring": [],
"citations": [],
}
else:
recommendations = self._generate_recommendations(disease, docs, safety_alerts, confidence, state)
# Create agent output
output = AgentOutput(
agent_name="Clinical Guidelines",
findings={
"disease": disease,
"immediate_actions": recommendations["immediate_actions"],
"lifestyle_changes": recommendations["lifestyle_changes"],
"monitoring": recommendations["monitoring"],
"guideline_citations": recommendations["citations"],
"safety_priority": len(safety_alerts) > 0,
"citations_missing": state["sop"].require_pdf_citations and not docs,
},
)
# Update state
print("\nRecommendations generated")
print(f" - Immediate actions: {len(recommendations['immediate_actions'])}")
print(f" - Lifestyle changes: {len(recommendations['lifestyle_changes'])}")
print(f" - Monitoring recommendations: {len(recommendations['monitoring'])}")
return {"agent_outputs": [output]}
def _generate_recommendations(
self, disease: str, docs: list, safety_alerts: list, confidence: float, state: GuildState
) -> dict:
"""Generate structured recommendations using LLM and guidelines"""
# Format retrieved guidelines
guidelines_context = "\n\n---\n\n".join(
[f"Source: {doc.metadata.get('source', 'Unknown')}\n\n{doc.page_content}" for doc in docs]
)
# Build safety context
safety_context = ""
if safety_alerts:
safety_context = "\n**CRITICAL SAFETY ALERTS:**\n"
for alert in safety_alerts[:3]:
safety_context += f"- {alert.get('biomarker', 'Unknown')}: {alert.get('message', '')}\n"
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"""You are a clinical decision support system providing evidence-based recommendations.
Based on clinical practice guidelines, provide actionable recommendations for patient self-assessment.
Structure your response with these sections:
1. IMMEDIATE_ACTIONS: Urgent steps (especially if safety alerts present)
2. LIFESTYLE_CHANGES: Diet, exercise, and behavioral modifications
3. MONITORING: What to track and how often
Make recommendations specific, actionable, and guideline-aligned.
Always emphasize consulting healthcare professionals for diagnosis and treatment.""",
),
(
"human",
"""Disease: {disease}
Prediction Confidence: {confidence:.1%}
{safety_context}
Clinical Guidelines Context:
{guidelines}
Please provide structured recommendations for patient self-assessment.""",
),
]
)
chain = prompt | self.llm
try:
response = chain.invoke(
{
"disease": disease,
"confidence": confidence,
"safety_context": safety_context,
"guidelines": guidelines_context,
}
)
recommendations = self._parse_recommendations(response.content)
except Exception as e:
print(f"Warning: LLM recommendation generation failed: {e}")
recommendations = self._get_default_recommendations(disease, safety_alerts)
# Add citations
recommendations["citations"] = self._extract_citations(docs)
return recommendations
def _parse_recommendations(self, content: str) -> dict:
"""Parse LLM response into structured recommendations"""
recommendations = {"immediate_actions": [], "lifestyle_changes": [], "monitoring": []}
current_section = None
lines = content.split("\n")
for line in lines:
line_stripped = line.strip()
line_upper = line_stripped.upper()
# Detect section headers
if "IMMEDIATE" in line_upper or "URGENT" in line_upper:
current_section = "immediate_actions"
elif "LIFESTYLE" in line_upper or "CHANGES" in line_upper or "DIET" in line_upper:
current_section = "lifestyle_changes"
elif "MONITORING" in line_upper or "TRACK" in line_upper:
current_section = "monitoring"
# Add bullet points or numbered items
elif current_section and line_stripped:
# Remove bullet points and numbers
cleaned = line_stripped.lstrip("•-*0123456789. ")
if cleaned and len(cleaned) > 10: # Minimum length filter
recommendations[current_section].append(cleaned)
# If parsing failed, create default structure
if not any(recommendations.values()):
sentences = content.split(".")
recommendations["immediate_actions"] = [s.strip() for s in sentences[:2] if s.strip()]
recommendations["lifestyle_changes"] = [s.strip() for s in sentences[2:4] if s.strip()]
recommendations["monitoring"] = [s.strip() for s in sentences[4:6] if s.strip()]
return recommendations
def _get_default_recommendations(self, disease: str, safety_alerts: list) -> dict:
"""Provide default recommendations if LLM fails"""
recommendations = {"immediate_actions": [], "lifestyle_changes": [], "monitoring": []}
# Add safety-based immediate actions
if safety_alerts:
recommendations["immediate_actions"].append(
"Consult healthcare provider immediately regarding critical biomarker values"
)
recommendations["immediate_actions"].append("Bring this report and recent lab results to your appointment")
else:
recommendations["immediate_actions"].append(
f"Schedule appointment with healthcare provider to discuss {disease} findings"
)
# Generic lifestyle changes
recommendations["lifestyle_changes"].extend(
[
"Follow a balanced, nutrient-rich diet as recommended by healthcare provider",
"Maintain regular physical activity appropriate for your health status",
"Track symptoms and biomarker trends over time",
]
)
# Generic monitoring
recommendations["monitoring"].extend(
[
f"Regular monitoring of {disease}-related biomarkers as advised by physician",
"Keep a health journal tracking symptoms, diet, and activities",
"Schedule follow-up appointments as recommended",
]
)
return recommendations
def _extract_citations(self, docs: list) -> list[str]:
"""Extract citations from retrieved guideline documents"""
citations = []
for doc in docs:
source = doc.metadata.get("source", "Unknown")
# Clean up source path
if "\\" in source or "/" in source:
source = Path(source).name
citations.append(source)
return list(set(citations)) # Remove duplicates
def create_clinical_guidelines_agent(retriever):
"""Factory function to create agent with retriever"""
return ClinicalGuidelinesAgent(retriever)