Update agent.py
Browse files
agent.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
import os
|
2 |
import base64
|
3 |
import requests
|
|
|
4 |
from openai import OpenAI
|
5 |
from duckduckgo_search import DDGS
|
6 |
|
@@ -39,23 +40,11 @@ class BasicAgent:
|
|
39 |
return None, None, None
|
40 |
|
41 |
def describe_image(self, img_path: str) -> str:
|
42 |
-
#
|
43 |
-
|
44 |
-
r = requests.get(img_path, timeout=10)
|
45 |
-
image_bytes = r.content
|
46 |
-
image_base64 = base64.b64encode(image_bytes).decode("utf-8")
|
47 |
-
prompt = (
|
48 |
-
"You're a chess assistant. Answer only with the best move in algebraic notation (e.g., Qd1#)."
|
49 |
-
)
|
50 |
-
# Stub for vision support; return message for now
|
51 |
-
return "[Image analysis not implemented in this agent.]"
|
52 |
-
except Exception as e:
|
53 |
-
return f"Error extracting text: {str(e)}"
|
54 |
|
55 |
def __call__(self, question: str, task_id: str = None) -> str:
|
56 |
-
# Step 1: Always web search and build prompt
|
57 |
search_snippet = self.web_search(question)
|
58 |
-
# Step 2: Compose the mandated prompt exactly as specified
|
59 |
full_prompt = (
|
60 |
"You are a general AI assistant. I will ask you a question. "
|
61 |
"Report your thoughts, and finish your answer with the following template: "
|
@@ -65,20 +54,58 @@ class BasicAgent:
|
|
65 |
"If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.\n\n"
|
66 |
f"Here are web search results and the question:\n{search_snippet}\n\nQuestion: {question}"
|
67 |
)
|
68 |
-
|
69 |
-
# Step 3: Get answer from OpenAI, temperature 0 for max determinism
|
70 |
response = self.llm.chat.completions.create(
|
71 |
model="gpt-4o",
|
72 |
-
messages=[
|
73 |
-
{"role": "system", "content": full_prompt},
|
74 |
-
],
|
75 |
temperature=0.0,
|
76 |
max_tokens=512,
|
77 |
)
|
78 |
answer = response.choices[0].message.content.strip()
|
79 |
-
|
80 |
for line in answer.splitlines():
|
81 |
if line.strip().lower().startswith("final answer:"):
|
82 |
-
|
83 |
-
|
84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import os
|
2 |
import base64
|
3 |
import requests
|
4 |
+
import re
|
5 |
from openai import OpenAI
|
6 |
from duckduckgo_search import DDGS
|
7 |
|
|
|
40 |
return None, None, None
|
41 |
|
42 |
def describe_image(self, img_path: str) -> str:
|
43 |
+
# Stub for vision support; return message for now
|
44 |
+
return "[Image analysis not implemented in this agent.]"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
def __call__(self, question: str, task_id: str = None) -> str:
|
|
|
47 |
search_snippet = self.web_search(question)
|
|
|
48 |
full_prompt = (
|
49 |
"You are a general AI assistant. I will ask you a question. "
|
50 |
"Report your thoughts, and finish your answer with the following template: "
|
|
|
54 |
"If you are asked for a comma separated list, apply the above rules depending of whether the element to be put in the list is a number or a string.\n\n"
|
55 |
f"Here are web search results and the question:\n{search_snippet}\n\nQuestion: {question}"
|
56 |
)
|
57 |
+
# First LLM call
|
|
|
58 |
response = self.llm.chat.completions.create(
|
59 |
model="gpt-4o",
|
60 |
+
messages=[{"role": "system", "content": full_prompt}],
|
|
|
|
|
61 |
temperature=0.0,
|
62 |
max_tokens=512,
|
63 |
)
|
64 |
answer = response.choices[0].message.content.strip()
|
65 |
+
final_line = ""
|
66 |
for line in answer.splitlines():
|
67 |
if line.strip().lower().startswith("final answer:"):
|
68 |
+
final_line = line.split(":", 1)[-1].strip(" .\"'")
|
69 |
+
break
|
70 |
+
|
71 |
+
# Retry on weak answers
|
72 |
+
bads = [
|
73 |
+
"", "unknown", "unable to determine", "unable to provide page numbers",
|
74 |
+
"unable to access video content directly", "unable to analyze video content",
|
75 |
+
"unable to determine without code", "unable to determine without file",
|
76 |
+
"follow the steps to locate the paper and find the nasa award number in the acknowledgment section",
|
77 |
+
"i am unable to view images or access external content directly", "unable to determine without access to the file",
|
78 |
+
"no results found", "n/a"
|
79 |
+
]
|
80 |
+
if final_line.lower() in bads or final_line.lower().startswith("unable") or final_line.lower().startswith("follow the steps") or final_line.lower().startswith("i am unable"):
|
81 |
+
retry_prompt = (
|
82 |
+
"Return only the answer to the following question, in the correct format and with no explanation or apologies. "
|
83 |
+
f"Here are web search results:\n{search_snippet}\n\nQuestion: {question}\nFINAL ANSWER:"
|
84 |
+
)
|
85 |
+
response2 = self.llm.chat.completions.create(
|
86 |
+
model="gpt-4o",
|
87 |
+
messages=[{"role": "system", "content": retry_prompt}],
|
88 |
+
temperature=0.0,
|
89 |
+
max_tokens=128,
|
90 |
+
)
|
91 |
+
retry_answer = response2.choices[0].message.content.strip()
|
92 |
+
for line in retry_answer.splitlines():
|
93 |
+
if line.strip().lower().startswith("final answer:"):
|
94 |
+
final_line = line.split(":", 1)[-1].strip(" .\"'")
|
95 |
+
break
|
96 |
+
elif retry_answer:
|
97 |
+
final_line = retry_answer.strip(" .\"'")
|
98 |
+
# If still blank, fallback to first number in web search
|
99 |
+
if not final_line:
|
100 |
+
numbers = re.findall(r'\b\d+\b', search_snippet)
|
101 |
+
if numbers:
|
102 |
+
final_line = numbers[0]
|
103 |
+
else:
|
104 |
+
# Or fallback to first plausible word or phrase
|
105 |
+
match = re.search(r"Description:\s*(.*)", search_snippet)
|
106 |
+
if match:
|
107 |
+
final_line = match.group(1).split('.')[0]
|
108 |
+
# Remove enclosing quotes from lists
|
109 |
+
if final_line.startswith('"') and final_line.endswith('"'):
|
110 |
+
final_line = final_line[1:-1]
|
111 |
+
return final_line
|