mgbam commited on
Commit
00f82d0
Β·
verified Β·
1 Parent(s): 2a0415a

Update agent.py

Browse files
Files changed (1) hide show
  1. agent.py +121 -45
agent.py CHANGED
@@ -4,8 +4,9 @@ from langchain_openai import ChatOpenAI
4
  from langchain.agents import AgentExecutor, create_openai_functions_agent
5
 
6
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
7
- from langchain_core.messages import AIMessage, HumanMessage # SystemMessage not explicitly needed in prompt list for this agent type
8
 
 
9
  from tools import (
10
  BioPortalLookupTool,
11
  UMLSLookupTool,
@@ -17,26 +18,33 @@ from tools import (
17
  from config.settings import settings
18
  from services.logger import app_logger
19
 
20
- llm = None
 
21
  try:
22
  if not settings.OPENAI_API_KEY:
23
- app_logger.error("CRITICAL: OPENAI_API_KEY not found. Agent cannot initialize.")
24
- raise ValueError("OpenAI API Key not configured.")
 
25
  llm = ChatOpenAI(
26
- model_name="gpt-4-turbo-preview", # More capable model for better tool use
27
  # model_name="gpt-3.5-turbo-0125", # More cost-effective alternative
28
- temperature=0.1, # Low temperature for more deterministic tool calls
29
  openai_api_key=settings.OPENAI_API_KEY
30
  )
31
  app_logger.info(f"ChatOpenAI ({llm.model_name}) initialized successfully for agent.")
 
32
  except Exception as e:
33
  detailed_error_message = str(e)
34
- user_facing_error = f"OpenAI LLM initialization failed: {detailed_error_message}."
35
  if "api_key" in detailed_error_message.lower() or "authenticate" in detailed_error_message.lower():
36
- user_facing_error = "OpenAI LLM initialization failed: API key issue. Check HF Secrets."
37
- app_logger.error(user_facing_error, exc_info=True)
38
- raise ValueError(user_facing_error)
 
 
 
39
 
 
40
  tools_list = [
41
  UMLSLookupTool(),
42
  BioPortalLookupTool(),
@@ -44,72 +52,140 @@ tools_list = [
44
  ]
45
  app_logger.info(f"Agent tools initialized: {[tool.name for tool in tools_list]}")
46
 
47
- OPENAI_SYSTEM_PROMPT_TEXT = (
 
 
 
 
 
 
48
  "You are 'Quantum Health Navigator', an AI assistant for healthcare professionals. "
49
  "Your goal is to assist with medical information lookup, treatment optimization queries, and general medical Q&A. "
50
- "You have access to a set of tools (names: {tool_names}) detailed here: {tools}. Use them when appropriate.\n"
51
  "Disclaimers: Always state that you are for informational support and not a substitute for clinical judgment. Do not provide direct medical advice for specific patient cases without using the 'quantum_treatment_optimizer' tool if relevant.\n"
52
- "Patient Context for this session (if provided by the user earlier): {patient_context}\n"
53
- "When using the 'quantum_treatment_optimizer' tool, ensure you populate its 'patient_data' argument using the available {patient_context}. "
54
  "For `bioportal_lookup`, if the user doesn't specify an ontology, you may ask or default to 'SNOMEDCT_US'.\n"
55
  "Always be clear and concise. Cite tools if their output forms a key part of your answer."
56
  )
57
 
 
 
58
  prompt = ChatPromptTemplate.from_messages([
59
- ("system", OPENAI_SYSTEM_PROMPT_TEXT),
60
- MessagesPlaceholder(variable_name="chat_history"),
61
- ("human", "{input}"),
62
- MessagesPlaceholder(variable_name="agent_scratchpad")
63
  ])
64
- app_logger.info("Agent prompt template created for OpenAI Functions agent.")
 
 
 
 
 
65
 
66
- if llm is None:
67
- app_logger.critical("LLM object is None at agent creation (OpenAI). Cannot proceed.")
68
- raise SystemExit("Agent LLM failed to initialize.")
69
  try:
 
 
70
  agent = create_openai_functions_agent(llm=llm, tools=tools_list, prompt=prompt)
71
  app_logger.info("OpenAI Functions agent created successfully.")
72
  except Exception as e:
73
  app_logger.error(f"Failed to create OpenAI Functions agent: {e}", exc_info=True)
 
 
74
  raise ValueError(f"OpenAI agent creation failed: {e}")
75
 
 
 
76
  agent_executor = AgentExecutor(
77
  agent=agent,
78
  tools=tools_list,
79
- verbose=True,
80
- handle_parsing_errors=True,
81
- max_iterations=7, # Adjusted max_iterations
82
- # return_intermediate_steps=True, # Enable for deep debugging
83
  )
84
  app_logger.info("AgentExecutor with OpenAI agent created successfully.")
85
 
86
- _agent_executor_instance = agent_executor
 
 
 
87
  def get_agent_executor():
 
 
 
 
88
  global _agent_executor_instance
89
  if _agent_executor_instance is None:
90
- app_logger.critical("CRITICAL: Agent executor is None (OpenAI).")
91
- raise RuntimeError("Agent executor (OpenAI) not initialized.")
 
 
 
 
 
 
 
92
  return _agent_executor_instance
93
 
 
94
  if __name__ == "__main__":
95
  if not settings.OPENAI_API_KEY:
96
- print("🚨 Set OPENAI_API_KEY in .env or environment.")
97
  else:
98
  print("\nπŸš€ Quantum Health Navigator (OpenAI Agent Test Console) πŸš€")
99
- # ... (The __main__ test block from the last full OpenAI agent.py version) ...
100
- try: test_executor = get_agent_executor()
101
- except ValueError as e_init: print(f"⚠️ Agent init failed: {e_init}"); exit()
102
- history = []
103
- context = "Age: 50; Gender: Male; History: Hypertension."
104
- print(f"ℹ️ Simulated Context: {context}\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  while True:
106
- usr_in = input("πŸ‘€ You: ").strip()
107
- if usr_in.lower() in ["exit", "quit"]: print("πŸ‘‹ Exiting."); break
108
- if not usr_in: continue
 
 
 
 
109
  try:
110
- res = test_executor.invoke({"input": usr_in, "chat_history": history, "patient_context": context})
111
- ai_out = res.get('output', "No output.")
112
- print(f"πŸ€– Agent: {ai_out}")
113
- history.extend([HumanMessage(content=usr_in), AIMessage(content=ai_out)])
114
- if len(history) > 8: history = history[-8:]
115
- except Exception as e_invoke: print(f"⚠️ Invoke Error: {e_invoke}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
  from langchain.agents import AgentExecutor, create_openai_functions_agent
5
 
6
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
7
+ from langchain_core.messages import AIMessage, HumanMessage # SystemMessage can be implicitly created or explicit
8
 
9
+ # --- Import your defined tools FROM THE 'tools' PACKAGE ---
10
  from tools import (
11
  BioPortalLookupTool,
12
  UMLSLookupTool,
 
18
  from config.settings import settings
19
  from services.logger import app_logger
20
 
21
+ # --- Initialize LLM (OpenAI) ---
22
+ llm = None # Initialize to None for robust error handling if init fails
23
  try:
24
  if not settings.OPENAI_API_KEY:
25
+ app_logger.error("CRITICAL: OPENAI_API_KEY not found in settings. Agent cannot initialize.")
26
+ raise ValueError("OpenAI API Key not configured. Please set it in Hugging Face Space secrets as OPENAI_API_KEY.")
27
+
28
  llm = ChatOpenAI(
29
+ model_name="gpt-4-turbo-preview", # More capable model for function calling
30
  # model_name="gpt-3.5-turbo-0125", # More cost-effective alternative
31
+ temperature=0.1, # Lower for more deterministic tool use and function calls
32
  openai_api_key=settings.OPENAI_API_KEY
33
  )
34
  app_logger.info(f"ChatOpenAI ({llm.model_name}) initialized successfully for agent.")
35
+
36
  except Exception as e:
37
  detailed_error_message = str(e)
38
+ user_facing_error = f"OpenAI LLM initialization failed: {detailed_error_message}. Check API key and model name."
39
  if "api_key" in detailed_error_message.lower() or "authenticate" in detailed_error_message.lower():
40
+ user_facing_error = "OpenAI LLM initialization failed: API key issue. Ensure OPENAI_API_KEY is correctly set in Hugging Face Secrets and is valid."
41
+ app_logger.error(user_facing_error + f" Original: {detailed_error_message}", exc_info=False)
42
+ else:
43
+ app_logger.error(user_facing_error, exc_info=True)
44
+ raise ValueError(user_facing_error) # Propagate error to stop further agent setup
45
+
46
 
47
+ # --- Initialize Tools List ---
48
  tools_list = [
49
  UMLSLookupTool(),
50
  BioPortalLookupTool(),
 
52
  ]
53
  app_logger.info(f"Agent tools initialized: {[tool.name for tool in tools_list]}")
54
 
55
+
56
+ # --- Agent Prompt (for OpenAI Functions Agent - Simplified System Prompt) ---
57
+ # The create_openai_functions_agent implicitly makes tool descriptions available to the LLM
58
+ # via the function-calling mechanism. Explicitly listing {tools} and {tool_names} in the
59
+ # system prompt string might be redundant or conflict with how this agent type works.
60
+ # We will still provide overall instructions and patient_context placeholder.
61
+ OPENAI_SYSTEM_PROMPT_TEXT_SIMPLIFIED = (
62
  "You are 'Quantum Health Navigator', an AI assistant for healthcare professionals. "
63
  "Your goal is to assist with medical information lookup, treatment optimization queries, and general medical Q&A. "
64
+ "You have access to a set of specialized tools. Use them when a user's query can be best answered by one of them, based on their descriptions.\n"
65
  "Disclaimers: Always state that you are for informational support and not a substitute for clinical judgment. Do not provide direct medical advice for specific patient cases without using the 'quantum_treatment_optimizer' tool if relevant.\n"
66
+ "Patient Context for this session (if provided by the user earlier): {patient_context}\n" # This variable is passed from invoke
67
+ "When using the 'quantum_treatment_optimizer' tool, ensure you populate its 'patient_data' argument using the available {patient_context}.\n"
68
  "For `bioportal_lookup`, if the user doesn't specify an ontology, you may ask or default to 'SNOMEDCT_US'.\n"
69
  "Always be clear and concise. Cite tools if their output forms a key part of your answer."
70
  )
71
 
72
+ # The ChatPromptTemplate defines the sequence of messages sent to the LLM.
73
+ # `create_openai_functions_agent` expects specific placeholders.
74
  prompt = ChatPromptTemplate.from_messages([
75
+ ("system", OPENAI_SYSTEM_PROMPT_TEXT_SIMPLIFIED), # System instructions, expects {patient_context}
76
+ MessagesPlaceholder(variable_name="chat_history"), # For past Human/AI messages
77
+ ("human", "{input}"), # For the current user query
78
+ MessagesPlaceholder(variable_name="agent_scratchpad") # For agent's internal work (function calls/responses)
79
  ])
80
+ app_logger.info("Agent prompt template (simplified for OpenAI Functions) created.")
81
+
82
+ # --- Create Agent ---
83
+ if llm is None: # Defensive check, should have been caught by earlier raise
84
+ app_logger.critical("LLM object is None at agent creation (OpenAI). Application cannot proceed.")
85
+ raise SystemExit("Agent LLM failed to initialize. Application cannot start.")
86
 
 
 
 
87
  try:
88
+ # `create_openai_functions_agent` will use the tools' Pydantic schemas to define
89
+ # the "functions" that the OpenAI model can call.
90
  agent = create_openai_functions_agent(llm=llm, tools=tools_list, prompt=prompt)
91
  app_logger.info("OpenAI Functions agent created successfully.")
92
  except Exception as e:
93
  app_logger.error(f"Failed to create OpenAI Functions agent: {e}", exc_info=True)
94
+ # This is where the "Input to ChatPromptTemplate is missing variables" error would occur
95
+ # if the prompt object was still expecting variables not provided by the agent constructor or invoke.
96
  raise ValueError(f"OpenAI agent creation failed: {e}")
97
 
98
+
99
+ # --- Create Agent Executor ---
100
  agent_executor = AgentExecutor(
101
  agent=agent,
102
  tools=tools_list,
103
+ verbose=True, # Essential for debugging tool usage and agent thoughts
104
+ handle_parsing_errors=True, # Tries to gracefully handle LLM output parsing issues
105
+ max_iterations=7, # Prevent runaway agent loops
106
+ # return_intermediate_steps=True, # Set to True to get detailed thought/action steps in the response
107
  )
108
  app_logger.info("AgentExecutor with OpenAI agent created successfully.")
109
 
110
+
111
+ # --- Getter Function for Streamlit App ---
112
+ _agent_executor_instance = agent_executor # Store the successfully initialized executor
113
+
114
  def get_agent_executor():
115
+ """
116
+ Returns the configured agent executor for OpenAI.
117
+ The executor is initialized when this module is first imported.
118
+ """
119
  global _agent_executor_instance
120
  if _agent_executor_instance is None:
121
+ # This indicates a failure during the initial module load (LLM or agent creation).
122
+ app_logger.critical("CRITICAL: Agent executor is None when get_agent_executor is called (OpenAI). Initialization likely failed.")
123
+ raise RuntimeError("Agent executor (OpenAI) was not properly initialized. Check application startup logs for errors (e.g., API key issues, prompt errors).")
124
+
125
+ # Final check for API key, though LLM initialization should be the primary guard.
126
+ if not settings.OPENAI_API_KEY:
127
+ app_logger.error("OpenAI API Key is missing at get_agent_executor call. Agent will fail.")
128
+ raise ValueError("OpenAI API Key not configured.")
129
+
130
  return _agent_executor_instance
131
 
132
+ # --- Example Usage (for local testing of this agent.py file) ---
133
  if __name__ == "__main__":
134
  if not settings.OPENAI_API_KEY:
135
+ print("🚨 Please set your OPENAI_API_KEY in .env file or as an environment variable to run the test.")
136
  else:
137
  print("\nπŸš€ Quantum Health Navigator (OpenAI Agent Test Console) πŸš€")
138
+ print("-----------------------------------------------------------")
139
+ print("Type 'exit' or 'quit' to stop.")
140
+ print("Example topics: medical definitions, treatment optimization (will use simulated patient context).")
141
+ print("-" * 59)
142
+
143
+ try:
144
+ test_executor = get_agent_executor() # Get the executor
145
+ except ValueError as e_init: # Catch errors from get_agent_executor or LLM/agent init
146
+ print(f"⚠️ Agent initialization failed during test startup: {e_init}")
147
+ print("Ensure your API key is correctly configured and prompt variables are set.")
148
+ exit() # Exit if agent can't be initialized
149
+
150
+ current_chat_history_for_test_run = [] # List of HumanMessage, AIMessage
151
+
152
+ # Simulated patient context for testing the {patient_context} variable
153
+ test_patient_context_summary_str = (
154
+ "Age: 70; Gender: Male; Chief Complaint: Shortness of breath on exertion; "
155
+ "Key Medical History: COPD, Atrial Fibrillation; "
156
+ "Current Medications: Tiotropium inhaler, Apixaban 5mg BID; Allergies: Penicillin."
157
+ )
158
+ print(f"ℹ️ Simulated Patient Context for this test run: {test_patient_context_summary_str}\n")
159
+
160
  while True:
161
+ user_input_str = input("πŸ‘€ You: ").strip()
162
+ if user_input_str.lower() in ["exit", "quit"]:
163
+ print("πŸ‘‹ Exiting test console.")
164
+ break
165
+ if not user_input_str: # Skip empty input
166
+ continue
167
+
168
  try:
169
+ app_logger.info(f"__main__ test (OpenAI): Invoking with input: '{user_input_str}'")
170
+ # These are the keys expected by the ChatPromptTemplate and agent:
171
+ # "input", "chat_history", and "patient_context" (because it's in our system prompt)
172
+ response_dict = test_executor.invoke({
173
+ "input": user_input_str,
174
+ "chat_history": current_chat_history_for_test_run,
175
+ "patient_context": test_patient_context_summary_str
176
+ })
177
+
178
+ ai_output_str = response_dict.get('output', "Agent did not produce an 'output' key.")
179
+ print(f"πŸ€– Agent: {ai_output_str}")
180
+
181
+ # Update history for the next turn
182
+ current_chat_history_for_test_run.append(HumanMessage(content=user_input_str))
183
+ current_chat_history_for_test_run.append(AIMessage(content=ai_output_str))
184
+
185
+ # Optional: Limit history length to prevent overly long contexts
186
+ if len(current_chat_history_for_test_run) > 10: # Keep last 5 pairs
187
+ current_chat_history_for_test_run = current_chat_history_for_test_run[-10:]
188
+
189
+ except Exception as e_invoke:
190
+ print(f"⚠️ Error during agent invocation: {type(e_invoke).__name__} - {e_invoke}")
191
+ app_logger.error(f"Error in __main__ OpenAI agent test invocation: {e_invoke}", exc_info=True)