|
|
|
import os |
|
from langchain_google_genai import ChatGoogleGenerativeAI |
|
from langchain.agents import AgentExecutor, create_structured_chat_agent |
|
|
|
|
|
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder |
|
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage |
|
|
|
|
|
|
|
from tools import ( |
|
BioPortalLookupTool, |
|
UMLSLookupTool, |
|
QuantumTreatmentOptimizerTool, |
|
|
|
|
|
) |
|
|
|
from config.settings import settings |
|
from services.logger import app_logger |
|
|
|
|
|
try: |
|
if not (settings.GEMINI_API_KEY or os.getenv("GOOGLE_API_KEY")): |
|
|
|
app_logger.error("CRITICAL: GOOGLE_API_KEY (for Gemini) not found in settings or environment. Agent cannot initialize.") |
|
raise ValueError("GOOGLE_API_KEY (for Gemini) not configured.") |
|
|
|
llm = ChatGoogleGenerativeAI( |
|
model="gemini-1.5-pro-latest", |
|
temperature=0.2, |
|
|
|
convert_system_message_to_human=True, |
|
|
|
) |
|
app_logger.info(f"ChatGoogleGenerativeAI ({llm.model}) initialized successfully for agent.") |
|
except Exception as e: |
|
app_logger.error(f"Failed to initialize ChatGoogleGenerativeAI for agent: {e}", exc_info=True) |
|
|
|
raise ValueError(f"Gemini LLM initialization failed: {e}. Check API key and configurations in HF Secrets.") |
|
|
|
|
|
|
|
|
|
|
|
tools_list = [ |
|
UMLSLookupTool(), |
|
BioPortalLookupTool(), |
|
QuantumTreatmentOptimizerTool(), |
|
|
|
] |
|
app_logger.info(f"Agent tools initialized: {[tool.name for tool in tools_list]}") |
|
|
|
|
|
|
|
SYSTEM_PROMPT_TEMPLATE = ( |
|
"You are 'Quantum Health Navigator', an advanced AI assistant for healthcare professionals. " |
|
"Your primary goal is to provide accurate information and insights based on user queries and available tools. " |
|
"You must adhere to the following guidelines:\n" |
|
"1. Disclaimers: Always remind the user that you are an AI, not a human medical professional, and your information " |
|
"is for support, not a substitute for clinical judgment. Do not provide direct medical advice for specific patient cases " |
|
"unless it's the direct output of a specialized tool like 'quantum_treatment_optimizer'.\n" |
|
"2. Patient Context: The user may provide patient context at the start of the session. This context is available as: {patient_context}. " |
|
"You MUST consider this context when it's relevant to the query, especially for the 'quantum_treatment_optimizer' tool.\n" |
|
"3. Tool Usage: You have access to the following tools:\n{tools}\n" |
|
" To use a tool, respond with a JSON markdown code block with the 'action' and 'action_input' keys. " |
|
" The 'action_input' should match the schema for the specified tool. Examples:\n" |
|
" For `umls_lookup`: ```json\n{{\"action\": \"umls_lookup\", \"action_input\": \"myocardial infarction\"}}\n```\n" |
|
" For `bioportal_lookup`: ```json\n{{\"action\": \"bioportal_lookup\", \"action_input\": {{\"term\": \"diabetes mellitus\", \"ontology\": \"SNOMEDCT\"}}}}\n```\n" |
|
" For `quantum_treatment_optimizer`: ```json\n{{\"action\": \"quantum_treatment_optimizer\", \"action_input\": {{\"patient_data\": {{\"age\": 55, \"gender\": \"Male\"}}, \"current_treatments\": [\"metformin\"], \"conditions\": [\"Type 2 Diabetes\"]}}}}\n```\n" |
|
" Ensure the `action_input` for `quantum_treatment_optimizer` includes a `patient_data` dictionary populated from the overall {patient_context}.\n" |
|
"4. Responding to User: After using a tool, you will receive an observation. Use this observation and your knowledge to formulate a comprehensive answer. Cite the tool if you used one (e.g., 'According to UMLS Lookup...').\n" |
|
"5. Specific Tool Guidance:\n" |
|
" - If asked about treatment optimization for a specific patient (especially if patient context is provided), you MUST use the `quantum_treatment_optimizer` tool.\n" |
|
" - For definitions, codes, or general medical concepts, `umls_lookup` or `bioportal_lookup` are appropriate.\n" |
|
|
|
"6. Conversation Flow: Refer to the `Previous conversation history` to maintain context.\n\n" |
|
"Begin!\n\n" |
|
"Previous conversation history:\n" |
|
"{chat_history}\n\n" |
|
"New human question: {input}\n" |
|
"{agent_scratchpad}" |
|
) |
|
|
|
|
|
|
|
|
|
|
|
prompt = ChatPromptTemplate.from_messages([ |
|
("system", SYSTEM_PROMPT_TEMPLATE), |
|
MessagesPlaceholder(variable_name="agent_scratchpad"), |
|
]) |
|
app_logger.info("Agent prompt template created for Gemini structured chat agent.") |
|
|
|
|
|
try: |
|
|
|
|
|
agent = create_structured_chat_agent(llm=llm, tools=tools_list, prompt=prompt) |
|
app_logger.info("Structured chat agent created successfully with Gemini LLM and tools.") |
|
except Exception as e: |
|
app_logger.error(f"Failed to create structured chat agent: {e}", exc_info=True) |
|
raise ValueError(f"Gemini agent creation failed: {e}") |
|
|
|
|
|
|
|
agent_executor = AgentExecutor( |
|
agent=agent, |
|
tools=tools_list, |
|
verbose=True, |
|
handle_parsing_errors=True, |
|
max_iterations=10, |
|
|
|
early_stopping_method="generate", |
|
) |
|
app_logger.info("AgentExecutor with Gemini agent created successfully.") |
|
|
|
|
|
|
|
def get_agent_executor(): |
|
""" |
|
Returns the configured agent executor for Gemini. |
|
Initialization of LLM, tools, agent, and executor happens when this module is imported. |
|
""" |
|
|
|
if not (settings.GEMINI_API_KEY or os.getenv("GOOGLE_API_KEY")): |
|
app_logger.critical("CRITICAL: GOOGLE_API_KEY (for Gemini) is not available when get_agent_executor is called. This indicates an earlier init failure or misconfiguration.") |
|
raise ValueError("Google API Key for Gemini not configured. Agent cannot function.") |
|
return agent_executor |
|
|
|
|
|
if __name__ == "__main__": |
|
if not (settings.GEMINI_API_KEY or os.getenv("GOOGLE_API_KEY")): |
|
print("π¨ Please set your GOOGLE_API_KEY in .env file or as an environment variable to run the test.") |
|
else: |
|
print("\nπ Quantum Health Navigator (Gemini Agent Test Console) π") |
|
print("-----------------------------------------------------------") |
|
print("Type 'exit' or 'quit' to stop.") |
|
print("Example topics: medical definitions, treatment optimization (will use simulated patient context).") |
|
print("-" * 59) |
|
|
|
test_executor = get_agent_executor() |
|
current_chat_history_for_test_run = [] |
|
|
|
|
|
test_patient_context_summary_str = ( |
|
"Age: 62; Gender: Female; Chief Complaint: Fatigue and increased thirst; " |
|
"Key Medical History: Obesity, family history of diabetes; " |
|
"Current Medications: None reported; Allergies: Sulfa drugs." |
|
) |
|
print(f"βΉοΈ Simulated Patient Context for this test run: {test_patient_context_summary_str}\n") |
|
|
|
|
|
while True: |
|
user_input_str = input("π€ You: ") |
|
if user_input_str.lower() in ["exit", "quit"]: |
|
print("π Exiting test console.") |
|
break |
|
|
|
if not user_input_str.strip(): |
|
continue |
|
|
|
try: |
|
app_logger.info(f"__main__ test: Invoking agent with input: '{user_input_str}'") |
|
|
|
|
|
response_dict = test_executor.invoke({ |
|
"input": user_input_str, |
|
"chat_history": current_chat_history_for_test_run, |
|
"patient_context": test_patient_context_summary_str |
|
}) |
|
|
|
ai_output_str = response_dict.get('output', "Agent did not produce an 'output' key.") |
|
print(f"π€ Agent: {ai_output_str}") |
|
|
|
|
|
current_chat_history_for_test_run.append(HumanMessage(content=user_input_str)) |
|
current_chat_history_for_test_run.append(AIMessage(content=ai_output_str)) |
|
|
|
|
|
if len(current_chat_history_for_test_run) > 10: |
|
current_chat_history_for_test_run = current_chat_history_for_test_run[-10:] |
|
|
|
except Exception as e: |
|
print(f"β οΈ Error during agent invocation: {e}") |
|
app_logger.error(f"Error in __main__ agent test invocation: {e}", exc_info=True) |