Update agent.py
Browse files
agent.py
CHANGED
@@ -4,9 +4,8 @@ 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
|
8 |
|
9 |
-
# --- Import your defined tools FROM THE 'tools' PACKAGE ---
|
10 |
from tools import (
|
11 |
BioPortalLookupTool,
|
12 |
UMLSLookupTool,
|
@@ -16,32 +15,25 @@ from tools import (
|
|
16 |
from config.settings import settings
|
17 |
from services.logger import app_logger
|
18 |
|
19 |
-
# --- Initialize LLM (OpenAI) ---
|
20 |
llm = None
|
21 |
try:
|
22 |
if not settings.OPENAI_API_KEY:
|
23 |
app_logger.error("CRITICAL: OPENAI_API_KEY not found in settings. Agent cannot initialize.")
|
24 |
raise ValueError("OpenAI API Key not configured. Please set it in Hugging Face Space secrets as OPENAI_API_KEY.")
|
25 |
-
|
26 |
llm = ChatOpenAI(
|
27 |
-
model_name="gpt-4-turbo-preview",
|
28 |
-
temperature=0.1,
|
29 |
openai_api_key=settings.OPENAI_API_KEY
|
30 |
)
|
31 |
app_logger.info(f"ChatOpenAI ({llm.model_name}) initialized successfully for agent.")
|
32 |
-
|
33 |
except Exception as e:
|
34 |
detailed_error_message = str(e)
|
35 |
-
user_facing_error = f"OpenAI LLM initialization failed: {detailed_error_message}.
|
36 |
if "api_key" in detailed_error_message.lower() or "authenticate" in detailed_error_message.lower():
|
37 |
-
user_facing_error = "OpenAI LLM initialization failed: API key issue.
|
38 |
-
|
39 |
-
else:
|
40 |
-
app_logger.error(user_facing_error, exc_info=True)
|
41 |
raise ValueError(user_facing_error)
|
42 |
|
43 |
-
|
44 |
-
# --- Initialize Tools List ---
|
45 |
tools_list = [
|
46 |
UMLSLookupTool(),
|
47 |
BioPortalLookupTool(),
|
@@ -49,20 +41,19 @@ tools_list = [
|
|
49 |
]
|
50 |
app_logger.info(f"Agent tools initialized: {[tool.name for tool in tools_list]}")
|
51 |
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
"You are 'Quantum Health Navigator', an AI assistant for healthcare professionals. "
|
56 |
"Your primary goal is to assist with medical information lookup, treatment optimization queries, and general medical Q&A. "
|
57 |
-
"You have access to a set of specialized tools (their names are: {tool_names}). Their detailed descriptions are available to you: {tools}. Use them when a user's query can be best answered by one of them.\n"
|
58 |
"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"
|
59 |
-
"Patient Context for this session (if provided by the user earlier): {patient_context}\n"
|
60 |
|
61 |
"Tool Usage Guidelines:\n"
|
62 |
"1. When using the 'quantum_treatment_optimizer' tool, its 'action_input' argument requires three main keys: 'patient_data', 'current_treatments', and 'conditions'.\n"
|
63 |
" - The 'patient_data' key MUST be a dictionary. Populate this dictionary by extracting relevant details from the {patient_context}. "
|
64 |
" For example, if {patient_context} is 'Age: 50; Gender: Male; Key Medical History: Hypertension; Chief Complaint: headache', "
|
65 |
-
" then 'patient_data' could be {{\"age\": 50, \"gender\": \"Male\", \"relevant_history\": [\"Hypertension\"], \"symptoms\": [\"headache\"]}}. "
|
66 |
" Include details like age, gender, chief complaint, key medical history, and current medications from {patient_context} within this 'patient_data' dictionary. If a value is not present in context, omit the key or use null/None if appropriate for the tool, but prioritize providing what is available.\n"
|
67 |
" - 'current_treatments' should be a list of strings derived from the 'Current Medications' part of {patient_context}.\n"
|
68 |
" - 'conditions' should be a list of strings, including primary conditions from the 'Key Medical History' or 'Chief Complaint' parts of {patient_context}, and any conditions explicitly mentioned or implied by the current user query.\n"
|
@@ -74,18 +65,16 @@ OPENAI_SYSTEM_PROMPT_TEXT_ENHANCED = (
|
|
74 |
)
|
75 |
|
76 |
prompt = ChatPromptTemplate.from_messages([
|
77 |
-
("system",
|
78 |
MessagesPlaceholder(variable_name="chat_history"),
|
79 |
("human", "{input}"),
|
80 |
MessagesPlaceholder(variable_name="agent_scratchpad")
|
81 |
])
|
82 |
-
app_logger.info("Agent prompt template (
|
83 |
|
84 |
-
# --- Create Agent ---
|
85 |
if llm is None:
|
86 |
-
app_logger.critical("LLM object is None at agent creation (OpenAI).
|
87 |
raise SystemExit("Agent LLM failed to initialize.")
|
88 |
-
|
89 |
try:
|
90 |
agent = create_openai_functions_agent(llm=llm, tools=tools_list, prompt=prompt)
|
91 |
app_logger.info("OpenAI Functions agent created successfully.")
|
@@ -93,83 +82,51 @@ except Exception as e:
|
|
93 |
app_logger.error(f"Failed to create OpenAI Functions agent: {e}", exc_info=True)
|
94 |
raise ValueError(f"OpenAI agent creation failed: {e}")
|
95 |
|
96 |
-
|
97 |
-
# --- Create Agent Executor ---
|
98 |
agent_executor = AgentExecutor(
|
99 |
agent=agent,
|
100 |
tools=tools_list,
|
101 |
verbose=True,
|
102 |
-
handle_parsing_errors=True,
|
103 |
-
max_iterations=7,
|
104 |
-
# return_intermediate_steps=True, # Enable this for deeper debugging of agent steps
|
105 |
)
|
106 |
app_logger.info("AgentExecutor with OpenAI agent created successfully.")
|
107 |
|
108 |
-
|
109 |
-
# --- Getter Function for Streamlit App ---
|
110 |
_agent_executor_instance = agent_executor
|
111 |
-
|
112 |
def get_agent_executor():
|
113 |
global _agent_executor_instance
|
114 |
if _agent_executor_instance is None:
|
115 |
-
app_logger.critical("CRITICAL: Agent executor is None when get_agent_executor is called (OpenAI).
|
116 |
-
raise RuntimeError("Agent executor (OpenAI) was not properly initialized.
|
117 |
-
if not settings.OPENAI_API_KEY:
|
118 |
app_logger.error("OpenAI API Key is missing at get_agent_executor call. Agent will fail.")
|
119 |
raise ValueError("OpenAI API Key not configured.")
|
120 |
return _agent_executor_instance
|
121 |
|
122 |
-
# --- Example Usage (for local testing) ---
|
123 |
if __name__ == "__main__":
|
124 |
if not settings.OPENAI_API_KEY:
|
125 |
-
print("π¨ Please set your OPENAI_API_KEY in .env
|
126 |
else:
|
127 |
print("\nπ Quantum Health Navigator (OpenAI Agent Test Console) π")
|
128 |
-
|
129 |
-
print("
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
except ValueError as e_init:
|
136 |
-
print(f"β οΈ Agent initialization failed during test startup: {e_init}")
|
137 |
-
print("Ensure your API key is correctly configured and prompt variables are set.")
|
138 |
-
exit()
|
139 |
-
|
140 |
-
current_chat_history_for_test_run = []
|
141 |
-
test_patient_context_summary_str = (
|
142 |
-
"Age: 60; Gender: Male; Chief Complaint: general fatigue and occasional dizziness; "
|
143 |
-
"Key Medical History: Type 2 Diabetes, Hypertension; "
|
144 |
-
"Current Medications: Metformin 1000mg daily, Lisinopril 20mg daily; Allergies: None."
|
145 |
-
)
|
146 |
-
print(f"βΉοΈ Simulated Patient Context for this test run: {test_patient_context_summary_str}\n")
|
147 |
-
|
148 |
while True:
|
149 |
-
|
150 |
-
if
|
151 |
-
|
152 |
-
break
|
153 |
-
if not user_input_str:
|
154 |
-
continue
|
155 |
-
|
156 |
try:
|
157 |
-
|
158 |
-
|
159 |
-
"
|
160 |
-
"
|
161 |
-
"
|
162 |
})
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
current_chat_history_for_test_run.append(AIMessage(content=ai_output_str))
|
169 |
-
|
170 |
-
if len(current_chat_history_for_test_run) > 10:
|
171 |
-
current_chat_history_for_test_run = current_chat_history_for_test_run[-10:]
|
172 |
-
|
173 |
-
except Exception as e_invoke:
|
174 |
-
print(f"β οΈ Error during agent invocation: {type(e_invoke).__name__} - {e_invoke}")
|
175 |
-
app_logger.error(f"Error in __main__ OpenAI agent test invocation: {e_invoke}", exc_info=True)
|
|
|
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
|
8 |
|
|
|
9 |
from tools import (
|
10 |
BioPortalLookupTool,
|
11 |
UMLSLookupTool,
|
|
|
15 |
from config.settings import settings
|
16 |
from services.logger import app_logger
|
17 |
|
|
|
18 |
llm = None
|
19 |
try:
|
20 |
if not settings.OPENAI_API_KEY:
|
21 |
app_logger.error("CRITICAL: OPENAI_API_KEY not found in settings. Agent cannot initialize.")
|
22 |
raise ValueError("OpenAI API Key not configured. Please set it in Hugging Face Space secrets as OPENAI_API_KEY.")
|
|
|
23 |
llm = ChatOpenAI(
|
24 |
+
model_name="gpt-4-turbo-preview",
|
25 |
+
temperature=0.1,
|
26 |
openai_api_key=settings.OPENAI_API_KEY
|
27 |
)
|
28 |
app_logger.info(f"ChatOpenAI ({llm.model_name}) initialized successfully for agent.")
|
|
|
29 |
except Exception as e:
|
30 |
detailed_error_message = str(e)
|
31 |
+
user_facing_error = f"OpenAI LLM initialization failed: {detailed_error_message}."
|
32 |
if "api_key" in detailed_error_message.lower() or "authenticate" in detailed_error_message.lower():
|
33 |
+
user_facing_error = "OpenAI LLM initialization failed: API key issue. Check HF Secrets."
|
34 |
+
app_logger.error(user_facing_error, exc_info=True)
|
|
|
|
|
35 |
raise ValueError(user_facing_error)
|
36 |
|
|
|
|
|
37 |
tools_list = [
|
38 |
UMLSLookupTool(),
|
39 |
BioPortalLookupTool(),
|
|
|
41 |
]
|
42 |
app_logger.info(f"Agent tools initialized: {[tool.name for tool in tools_list]}")
|
43 |
|
44 |
+
# --- Agent Prompt (Reverting to version that EXPLICITLY includes {tools} and {tool_names} in the system message string) ---
|
45 |
+
OPENAI_SYSTEM_PROMPT_TEXT_WITH_TOOLS_EXPLICIT = (
|
46 |
+
"You are 'Quantum Health Navigator', an advanced AI assistant for healthcare professionals. "
|
|
|
47 |
"Your primary goal is to assist with medical information lookup, treatment optimization queries, and general medical Q&A. "
|
48 |
+
"You have access to a set of specialized tools (their names are: {tool_names}). Their detailed descriptions are available to you: {tools}. Use them when a user's query can be best answered by one of them.\n" # Explicit {tools} and {tool_names}
|
49 |
"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"
|
50 |
+
"Patient Context for this session (if provided by the user earlier): {patient_context}\n"
|
51 |
|
52 |
"Tool Usage Guidelines:\n"
|
53 |
"1. When using the 'quantum_treatment_optimizer' tool, its 'action_input' argument requires three main keys: 'patient_data', 'current_treatments', and 'conditions'.\n"
|
54 |
" - The 'patient_data' key MUST be a dictionary. Populate this dictionary by extracting relevant details from the {patient_context}. "
|
55 |
" For example, if {patient_context} is 'Age: 50; Gender: Male; Key Medical History: Hypertension; Chief Complaint: headache', "
|
56 |
+
" then 'patient_data' could be {{\"age\": 50, \"gender\": \"Male\", \"relevant_history\": [\"Hypertension\"], \"symptoms\": [\"headache\"]}}. "
|
57 |
" Include details like age, gender, chief complaint, key medical history, and current medications from {patient_context} within this 'patient_data' dictionary. If a value is not present in context, omit the key or use null/None if appropriate for the tool, but prioritize providing what is available.\n"
|
58 |
" - 'current_treatments' should be a list of strings derived from the 'Current Medications' part of {patient_context}.\n"
|
59 |
" - 'conditions' should be a list of strings, including primary conditions from the 'Key Medical History' or 'Chief Complaint' parts of {patient_context}, and any conditions explicitly mentioned or implied by the current user query.\n"
|
|
|
65 |
)
|
66 |
|
67 |
prompt = ChatPromptTemplate.from_messages([
|
68 |
+
("system", OPENAI_SYSTEM_PROMPT_TEXT_WITH_TOOLS_EXPLICIT), # Using the version with explicit {tools} and {tool_names}
|
69 |
MessagesPlaceholder(variable_name="chat_history"),
|
70 |
("human", "{input}"),
|
71 |
MessagesPlaceholder(variable_name="agent_scratchpad")
|
72 |
])
|
73 |
+
app_logger.info("Agent prompt template (with explicit tools/tool_names in system message) created.")
|
74 |
|
|
|
75 |
if llm is None:
|
76 |
+
app_logger.critical("LLM object is None at agent creation (OpenAI). Cannot proceed.")
|
77 |
raise SystemExit("Agent LLM failed to initialize.")
|
|
|
78 |
try:
|
79 |
agent = create_openai_functions_agent(llm=llm, tools=tools_list, prompt=prompt)
|
80 |
app_logger.info("OpenAI Functions agent created successfully.")
|
|
|
82 |
app_logger.error(f"Failed to create OpenAI Functions agent: {e}", exc_info=True)
|
83 |
raise ValueError(f"OpenAI agent creation failed: {e}")
|
84 |
|
|
|
|
|
85 |
agent_executor = AgentExecutor(
|
86 |
agent=agent,
|
87 |
tools=tools_list,
|
88 |
verbose=True,
|
89 |
+
handle_parsing_errors=True,
|
90 |
+
max_iterations=7,
|
|
|
91 |
)
|
92 |
app_logger.info("AgentExecutor with OpenAI agent created successfully.")
|
93 |
|
|
|
|
|
94 |
_agent_executor_instance = agent_executor
|
|
|
95 |
def get_agent_executor():
|
96 |
global _agent_executor_instance
|
97 |
if _agent_executor_instance is None:
|
98 |
+
app_logger.critical("CRITICAL: Agent executor is None when get_agent_executor is called (OpenAI).")
|
99 |
+
raise RuntimeError("Agent executor (OpenAI) was not properly initialized.")
|
100 |
+
if not settings.OPENAI_API_KEY:
|
101 |
app_logger.error("OpenAI API Key is missing at get_agent_executor call. Agent will fail.")
|
102 |
raise ValueError("OpenAI API Key not configured.")
|
103 |
return _agent_executor_instance
|
104 |
|
|
|
105 |
if __name__ == "__main__":
|
106 |
if not settings.OPENAI_API_KEY:
|
107 |
+
print("π¨ Please set your OPENAI_API_KEY in .env or environment.")
|
108 |
else:
|
109 |
print("\nπ Quantum Health Navigator (OpenAI Agent Test Console) π")
|
110 |
+
try: test_executor = get_agent_executor()
|
111 |
+
except ValueError as e_init: print(f"β οΈ Agent init failed: {e_init}"); exit()
|
112 |
+
history = []
|
113 |
+
context = ("Age: 60; Gender: Male; Chief Complaint: general fatigue and occasional dizziness; "
|
114 |
+
"Key Medical History: Type 2 Diabetes, Hypertension; "
|
115 |
+
"Current Medications: Metformin 1000mg daily, Lisinopril 20mg daily; Allergies: None.")
|
116 |
+
print(f"βΉοΈ Simulated Context: {context}\n")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
while True:
|
118 |
+
usr_in = input("π€ You: ").strip()
|
119 |
+
if usr_in.lower() in ["exit", "quit"]: print("π Exiting."); break
|
120 |
+
if not usr_in: continue
|
|
|
|
|
|
|
|
|
121 |
try:
|
122 |
+
res = test_executor.invoke({
|
123 |
+
"input": usr_in,
|
124 |
+
"chat_history": history,
|
125 |
+
"patient_context": context,
|
126 |
+
# DO NOT PASS "tools" or "tool_names" here; the agent constructor does that
|
127 |
})
|
128 |
+
ai_out = res.get('output', "No output.")
|
129 |
+
print(f"π€ Agent: {ai_out}")
|
130 |
+
history.extend([HumanMessage(content=usr_in), AIMessage(content=ai_out)])
|
131 |
+
if len(history) > 8: history = history[-8:]
|
132 |
+
except Exception as e_invoke: print(f"β οΈ Invoke Error: {type(e_invoke).__name__} - {e_invoke}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|