Spaces:
Sleeping
Sleeping
react_removed
Browse files
agent.py
CHANGED
@@ -1,13 +1,10 @@
|
|
1 |
from __future__ import annotations
|
2 |
import os
|
3 |
from langchain_openai import ChatOpenAI
|
4 |
-
from
|
5 |
-
from
|
6 |
-
from state import AgentState
|
7 |
from typing import Any, Dict, List, Optional
|
8 |
import json
|
9 |
-
from langgraph.prebuilt import create_react_agent
|
10 |
-
import signal
|
11 |
|
12 |
# βββββββββββββββββββββββββββ External tools ββββββββββββββββββββββββββββββ
|
13 |
from tools import (
|
@@ -26,21 +23,43 @@ from tools import (
|
|
26 |
# βββββββββββββββββββββββββββ Configuration βββββββββββββββββββββββββββββββ
|
27 |
MAX_TOOL_CALLS = 5
|
28 |
|
29 |
-
|
|
|
|
|
|
|
30 |
|
31 |
-
|
|
|
|
|
|
|
|
|
32 |
|
33 |
-
|
|
|
|
|
|
|
34 |
|
35 |
-
#
|
|
|
|
|
36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
|
38 |
-
|
39 |
|
40 |
def build_graph():
|
41 |
-
"""Build and return a
|
42 |
-
|
43 |
-
|
44 |
llm_tools = [
|
45 |
wikipedia_search_tool,
|
46 |
arxiv_search_tool,
|
@@ -54,7 +73,7 @@ def build_graph():
|
|
54 |
divide_tool
|
55 |
]
|
56 |
|
57 |
-
# Create the react agent
|
58 |
-
agent =
|
59 |
|
60 |
return agent
|
|
|
1 |
from __future__ import annotations
|
2 |
import os
|
3 |
from langchain_openai import ChatOpenAI
|
4 |
+
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
|
5 |
+
from langgraph.prebuilt import ToolExecutor
|
|
|
6 |
from typing import Any, Dict, List, Optional
|
7 |
import json
|
|
|
|
|
8 |
|
9 |
# βββββββββββββββββββββββββββ External tools ββββββββββββββββββββββββββββββ
|
10 |
from tools import (
|
|
|
23 |
# βββββββββββββββββββββββββββ Configuration βββββββββββββββββββββββββββββββ
|
24 |
MAX_TOOL_CALLS = 5
|
25 |
|
26 |
+
class CustomReActAgent:
|
27 |
+
def __init__(self, tools: List, model_name="gpt-4o-mini"):
|
28 |
+
self.llm = ChatOpenAI(model_name=model_name, temperature=0.3)
|
29 |
+
self.tool_executor = ToolExecutor(tools)
|
30 |
|
31 |
+
def run(self, question: str, task_id: Optional[str] = None, max_turns: int = 15, system_prompt: str = "") -> str:
|
32 |
+
messages = [SystemMessage(content=system_prompt)]
|
33 |
+
if task_id:
|
34 |
+
messages[0].content += f"\n\nIMPORTANT: Your current task_id is: {task_id}."
|
35 |
+
messages.append(HumanMessage(content=question))
|
36 |
|
37 |
+
for _ in range(max_turns):
|
38 |
+
ai_response = self.llm.invoke(messages)
|
39 |
+
messages.append(ai_response)
|
40 |
+
print("AI said:", ai_response.content)
|
41 |
|
42 |
+
# Final answer?
|
43 |
+
if "FINAL ANSWER:" in ai_response.content.upper():
|
44 |
+
return ai_response.content.split("FINAL ANSWER:")[-1].strip()
|
45 |
|
46 |
+
# Tool call expected
|
47 |
+
if "Action:" in ai_response.content and "Action Input:" in ai_response.content:
|
48 |
+
try:
|
49 |
+
tool_name = ai_response.content.split("Action:")[1].split("Action Input:")[0].strip()
|
50 |
+
tool_input = ai_response.content.split("Action Input:")[1].strip()
|
51 |
+
tool_result = self.tool_executor.run(tool_name, tool_input)
|
52 |
+
messages.append(HumanMessage(content=f"Observation: {tool_result}"))
|
53 |
+
except Exception as e:
|
54 |
+
messages.append(HumanMessage(content=f"Observation: Tool execution failed: {str(e)}"))
|
55 |
+
else:
|
56 |
+
messages.append(HumanMessage(content="Observation: No valid action. Please clarify."))
|
57 |
|
58 |
+
return "No FINAL ANSWER found within allowed steps."
|
59 |
|
60 |
def build_graph():
|
61 |
+
"""Build and return a CustomReActAgent."""
|
62 |
+
|
|
|
63 |
llm_tools = [
|
64 |
wikipedia_search_tool,
|
65 |
arxiv_search_tool,
|
|
|
73 |
divide_tool
|
74 |
]
|
75 |
|
76 |
+
# Create the custom react agent
|
77 |
+
agent = CustomReActAgent(tools=llm_tools)
|
78 |
|
79 |
return agent
|
app.py
CHANGED
@@ -14,20 +14,46 @@ from state import AgentState
|
|
14 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
15 |
|
16 |
SYSTEM_PROMPT = """
|
17 |
-
You are a general AI assistant
|
18 |
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
SEARCH STRATEGY:
|
22 |
-
- If wikipedia_search_tool fails
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
You MUST always provide a FINAL ANSWER. If you reach the recursion limit or tools fail, provide the best answer possible with available information.
|
31 |
"""
|
32 |
|
33 |
|
@@ -37,48 +63,15 @@ class BasicAgent:
|
|
37 |
self.graph = build_graph()
|
38 |
|
39 |
def __call__(self, question: str, task_id: Optional[str] = None) -> str:
|
40 |
-
"""Run the agent and return whatever FINAL_ANSWER the
|
41 |
print(f"Agent received question: {question}")
|
42 |
|
43 |
try:
|
44 |
-
#
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
# Initialize the state properly with all required fields
|
50 |
-
init_state = {
|
51 |
-
"messages": [
|
52 |
-
SystemMessage(content=system_prompt_with_task),
|
53 |
-
HumanMessage(content=question)
|
54 |
-
]
|
55 |
-
}
|
56 |
-
|
57 |
-
# Run the agent with increased steps
|
58 |
-
out_state = self.graph.invoke(init_state, {"recursion_limit": 15})
|
59 |
-
|
60 |
-
# Extract the final answer from the last message
|
61 |
-
if out_state and "messages" in out_state:
|
62 |
-
last_message = out_state["messages"][-1]
|
63 |
-
if hasattr(last_message, 'content') and last_message.content:
|
64 |
-
content = last_message.content
|
65 |
-
print("content: ", content)
|
66 |
-
print("\n\n\n\n")
|
67 |
-
|
68 |
-
# Look for FINAL ANSWER: pattern (case insensitive)
|
69 |
-
if "FINAL ANSWER:" in content.upper():
|
70 |
-
# Find the last occurrence of FINAL ANSWER
|
71 |
-
parts = content.upper().split("FINAL ANSWER:")
|
72 |
-
if len(parts) > 1:
|
73 |
-
# Get the original case version
|
74 |
-
answer_start = content.upper().rfind("FINAL ANSWER:") + len("FINAL ANSWER:")
|
75 |
-
final_answer = content[answer_start:].strip()
|
76 |
-
return final_answer
|
77 |
-
|
78 |
-
# If no FINAL ANSWER found, return the whole content
|
79 |
-
return content
|
80 |
-
|
81 |
-
return "No valid response generated."
|
82 |
|
83 |
except Exception as e:
|
84 |
print(f"Agent execution error: {e}")
|
|
|
14 |
DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space"
|
15 |
|
16 |
SYSTEM_PROMPT = """
|
17 |
+
You are a general AI assistant using the ReAct (Reasoning and Acting) approach. I will ask you a question, and you should think step by step and use tools when needed.
|
18 |
|
19 |
+
FORMAT:
|
20 |
+
For each step, use this exact format:
|
21 |
+
Thought: [your reasoning about what to do next]
|
22 |
+
Action: [tool name]
|
23 |
+
Action Input: [input to the tool]
|
24 |
+
|
25 |
+
After using a tool, you'll receive:
|
26 |
+
Observation: [tool result]
|
27 |
+
|
28 |
+
Continue this cycle until you have enough information, then provide:
|
29 |
+
FINAL ANSWER: [YOUR FINAL ANSWER]
|
30 |
+
|
31 |
+
ANSWER FORMAT:
|
32 |
+
YOUR FINAL ANSWER should be a number OR as few words as possible OR a comma separated list of numbers and/or strings. If you are asked for a number, don't use comma to write your number neither use units such as $ or percent sign unless specified otherwise. If you are asked for a string, don't use articles, neither abbreviations (e.g. for cities), and write the digits in plain text unless specified otherwise.
|
33 |
+
|
34 |
+
IMPORTANT: When using tools that require file access (such as audio_transcriber_tool, excel_tool, analyze_code_tool, or image_tool), ALWAYS use the task_id parameter only. Do NOT use any file names mentioned by the user.
|
35 |
+
|
36 |
+
AVAILABLE TOOLS:
|
37 |
+
- wikipedia_search_tool: For historical, biographical, scientific facts
|
38 |
+
- arxiv_search_tool: For academic research papers
|
39 |
+
- audio_transcriber_tool: For transcribing audio files
|
40 |
+
- excel_tool: For analyzing spreadsheet data
|
41 |
+
- analyze_code_tool: For understanding code files
|
42 |
+
- image_tool: For describing images
|
43 |
+
- add_tool: For adding numbers
|
44 |
+
- subtract_tool: For subtracting numbers
|
45 |
+
- multiply_tool: For multiplying numbers
|
46 |
+
- divide_tool: For dividing numbers
|
47 |
|
48 |
SEARCH STRATEGY:
|
49 |
+
- If wikipedia_search_tool fails, try with broader queries or use arxiv_search_tool for academic topics
|
50 |
+
- When you see [END_OF_SEARCH] in results, stop searching and provide your final answer
|
51 |
+
- You MUST always provide a FINAL ANSWER, even if tools fail
|
52 |
+
|
53 |
+
Example:
|
54 |
+
Thought: I need to find information about quantum computing.
|
55 |
+
Action: wikipedia_search_tool
|
56 |
+
Action Input: quantum computing
|
|
|
57 |
"""
|
58 |
|
59 |
|
|
|
63 |
self.graph = build_graph()
|
64 |
|
65 |
def __call__(self, question: str, task_id: Optional[str] = None) -> str:
|
66 |
+
"""Run the agent and return whatever FINAL_ANSWER the agent produces."""
|
67 |
print(f"Agent received question: {question}")
|
68 |
|
69 |
try:
|
70 |
+
# Run the custom react agent
|
71 |
+
result = self.graph.run(question=question, task_id=task_id, max_turns=15, system_prompt=SYSTEM_PROMPT)
|
72 |
+
print("Final result: ", result)
|
73 |
+
print("\n\n\n\n")
|
74 |
+
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
|
76 |
except Exception as e:
|
77 |
print(f"Agent execution error: {e}")
|
tools.py
CHANGED
@@ -219,18 +219,12 @@ def wikipedia_search_tool(wiki_query: str) -> str:
|
|
219 |
- "Explain quantum entanglement"
|
220 |
- "Tell me about the French Revolution"
|
221 |
"""
|
222 |
-
print(f"DEBUG: reached wikipedia_search_tool with query: {wiki_query}")
|
223 |
try:
|
224 |
docs = WikipediaLoader(query=wiki_query, load_max_docs=3).load() # Reduced from 5 to 3
|
225 |
-
print(f"DEBUG: WikipediaLoader returned {len(docs)} documents")
|
226 |
|
227 |
result = ""
|
228 |
counter = 1
|
229 |
for doc in docs:
|
230 |
-
print(f"DEBUG: Processing Wikipedia document {counter}")
|
231 |
-
print(f"DEBUG: Document metadata: {doc.metadata}")
|
232 |
-
print(f"DEBUG: Document content length: {len(doc.page_content)}")
|
233 |
-
|
234 |
# Handle different metadata structures
|
235 |
title = "Unknown Title"
|
236 |
if hasattr(doc, 'metadata') and doc.metadata:
|
@@ -247,8 +241,6 @@ def wikipedia_search_tool(wiki_query: str) -> str:
|
|
247 |
first_key = list(doc.metadata.keys())[0]
|
248 |
title = f"Wikipedia: {doc.metadata[first_key]}"
|
249 |
|
250 |
-
print(f"DEBUG: Using Wikipedia title: {title}")
|
251 |
-
|
252 |
# Trim content to key information only (reduced from 2000 to 800 characters)
|
253 |
content = doc.page_content[:800] if len(doc.page_content) > 800 else doc.page_content
|
254 |
|
@@ -266,12 +258,11 @@ def wikipedia_search_tool(wiki_query: str) -> str:
|
|
266 |
# Add clear end marker
|
267 |
result += "\n\n[END_OF_SEARCH] - Wikipedia search complete. Use this information to answer the question."
|
268 |
|
269 |
-
print(
|
270 |
return result
|
271 |
|
272 |
except Exception as e:
|
273 |
error_msg = f"Error during Wikipedia search: {str(e)} [END_OF_SEARCH]"
|
274 |
-
print(f"DEBUG: {error_msg}")
|
275 |
return error_msg
|
276 |
|
277 |
@tool
|
|
|
219 |
- "Explain quantum entanglement"
|
220 |
- "Tell me about the French Revolution"
|
221 |
"""
|
|
|
222 |
try:
|
223 |
docs = WikipediaLoader(query=wiki_query, load_max_docs=3).load() # Reduced from 5 to 3
|
|
|
224 |
|
225 |
result = ""
|
226 |
counter = 1
|
227 |
for doc in docs:
|
|
|
|
|
|
|
|
|
228 |
# Handle different metadata structures
|
229 |
title = "Unknown Title"
|
230 |
if hasattr(doc, 'metadata') and doc.metadata:
|
|
|
241 |
first_key = list(doc.metadata.keys())[0]
|
242 |
title = f"Wikipedia: {doc.metadata[first_key]}"
|
243 |
|
|
|
|
|
244 |
# Trim content to key information only (reduced from 2000 to 800 characters)
|
245 |
content = doc.page_content[:800] if len(doc.page_content) > 800 else doc.page_content
|
246 |
|
|
|
258 |
# Add clear end marker
|
259 |
result += "\n\n[END_OF_SEARCH] - Wikipedia search complete. Use this information to answer the question."
|
260 |
|
261 |
+
print("Wikipedia search completed successfully")
|
262 |
return result
|
263 |
|
264 |
except Exception as e:
|
265 |
error_msg = f"Error during Wikipedia search: {str(e)} [END_OF_SEARCH]"
|
|
|
266 |
return error_msg
|
267 |
|
268 |
@tool
|