Andrei Nazarov
commited on
Commit
·
f41535e
1
Parent(s):
9b6d6f3
updated 9
Browse files
app.py
CHANGED
|
@@ -55,63 +55,57 @@ class GeminiModel(Model):
|
|
| 55 |
self.rate_limiter = RateLimiter(requests_per_minute=25) # Setting to 25 to be safe
|
| 56 |
|
| 57 |
# System prompt for smolagents format
|
| 58 |
-
self.system_prompt = """You are a
|
| 59 |
|
| 60 |
-
|
| 61 |
-
1. **Thought:** First, explain your reasoning. Break down the user's question into smaller, manageable steps. Describe the search queries you will use and how you will process the results to arrive at the final answer.
|
| 62 |
-
2. **Code:** Write Python code to execute your plan. You have two tools available:
|
| 63 |
-
* `web_search(query: str)`: Searches the web and returns information.
|
| 64 |
-
* `final_answer(answer: str)`: Submits your final, formatted answer.
|
| 65 |
-
3. **Execution:** After your code is executed, you will see an "Observation" with the results. You can then continue with more `Thought` and `Code` blocks if needed.
|
| 66 |
-
4. **Final Answer:** Once you have the answer, you MUST call `final_answer()` with the correctly formatted response. Do not output the answer in any other way.
|
| 67 |
|
| 68 |
-
**
|
|
|
|
| 69 |
|
| 70 |
-
**
|
| 71 |
-
|
| 72 |
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
Code:
|
| 76 |
```py
|
| 77 |
-
|
| 78 |
-
# I will analyze the results. Let's assume a search result says "The type specimens are deposited at the Zoological Museum of Moscow University."
|
| 79 |
-
final_answer("Moscow")
|
| 80 |
```<end_code>
|
| 81 |
|
| 82 |
-
**
|
| 83 |
-
|
| 84 |
|
| 85 |
-
|
| 86 |
-
Thought: I
|
| 87 |
Code:
|
| 88 |
```py
|
| 89 |
-
|
| 90 |
-
# Observation from search: Haiti (2 athletes), Panama (1 athlete), Rhodesia (2 athletes).
|
| 91 |
-
# The lowest number is 1, for Panama.
|
| 92 |
-
# I need the IOC code for Panama.
|
| 93 |
-
ioc_results = web_search(query="IOC code for Panama")
|
| 94 |
-
# Observation from search: The IOC code for Panama is PAN.
|
| 95 |
-
final_answer("PAN")
|
| 96 |
```<end_code>
|
| 97 |
|
| 98 |
-
**
|
| 99 |
-
|
| 100 |
|
| 101 |
-
|
| 102 |
-
Thought:
|
| 103 |
Code:
|
| 104 |
```py
|
| 105 |
-
tamai_info = web_search(query="Taisho Tamai jersey number July 2023")
|
| 106 |
-
# Observation: Taishō Tamai is number 61 for the Orix Buffaloes.
|
| 107 |
-
roster_info = web_search(query="Orix Buffaloes pitchers roster 2023")
|
| 108 |
-
# Observation: Pitcher roster includes... Mizuno (60), Tamai (61), Onaga (62)...
|
| 109 |
-
# The pitcher before is Mizuno. The pitcher after is Onaga.
|
| 110 |
-
# The format should be "LastNameBefore, LastNameAfter".
|
| 111 |
final_answer("Mizuno, Onaga")
|
| 112 |
```<end_code>
|
| 113 |
-
|
| 114 |
-
CRITICAL: Always follow the Thought -> Code -> final_answer workflow. Do not output an answer without using the `final_answer` tool."""
|
| 115 |
|
| 116 |
def generate(
|
| 117 |
self,
|
|
@@ -229,42 +223,17 @@ class MyAgent:
|
|
| 229 |
DuckDuckGoSearchTool()
|
| 230 |
],
|
| 231 |
model=self.model,
|
| 232 |
-
max_steps=
|
| 233 |
)
|
| 234 |
|
| 235 |
def clean_and_format_answer(self, answer: str, question: str) -> str:
|
| 236 |
-
"""
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
for pattern in final_answer_patterns:
|
| 245 |
-
match = re.search(pattern, answer)
|
| 246 |
-
if match:
|
| 247 |
-
return match.group(1).strip(' \'')
|
| 248 |
-
|
| 249 |
-
# Fallback for answers that are not in final_answer
|
| 250 |
-
# Remove code blocks and tool calls
|
| 251 |
-
answer = re.sub(r'```.*?```', '', answer, flags=re.DOTALL)
|
| 252 |
-
answer = re.sub(r'web_search\(.*?\)', '', answer)
|
| 253 |
-
answer = re.sub(r'Thought:.*?\n', '', answer)
|
| 254 |
-
answer = re.sub(r'Code:.*?<end_code>', '', answer, flags=re.DOTALL)
|
| 255 |
-
|
| 256 |
-
# Clean up the answer
|
| 257 |
-
lines = answer.split('\n')
|
| 258 |
-
clean_lines = []
|
| 259 |
-
for line in lines:
|
| 260 |
-
line = line.strip()
|
| 261 |
-
if line and not line.startswith(('Thought:', 'Code:', '#', 'web_search', 'final_answer', 'Out:')):
|
| 262 |
-
clean_lines.append(line)
|
| 263 |
-
|
| 264 |
-
if clean_lines:
|
| 265 |
-
return " ".join(clean_lines).strip()
|
| 266 |
-
|
| 267 |
-
return answer.strip()
|
| 268 |
|
| 269 |
def __call__(self, question: str) -> str:
|
| 270 |
print(f"\n=== Processing Question: {question} ===")
|
|
@@ -273,7 +242,20 @@ class MyAgent:
|
|
| 273 |
full_response = self.agent.run(question)
|
| 274 |
print(f"\n=== Raw Response from Agent ===\n{full_response}\n===")
|
| 275 |
answer = self.clean_and_format_answer(full_response, question)
|
| 276 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 277 |
except Exception as e:
|
| 278 |
print(f"Error processing question: {e}")
|
| 279 |
return f"Error: {str(e)}"
|
|
|
|
| 55 |
self.rate_limiter = RateLimiter(requests_per_minute=25) # Setting to 25 to be safe
|
| 56 |
|
| 57 |
# System prompt for smolagents format
|
| 58 |
+
self.system_prompt = """You are a reasoning agent. You are in a loop where you will alternate between thinking (Thought) and acting (Code).
|
| 59 |
|
| 60 |
+
Your goal is to answer the user's question.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
|
| 62 |
+
**Step 1: Think**
|
| 63 |
+
In the `Thought:` block, analyze the user's question. Break it down into the smallest possible steps. If you need information, decide on a search query.
|
| 64 |
|
| 65 |
+
**Step 2: Act**
|
| 66 |
+
In the `Code:` block, write Python code to execute ONE step of your plan. You can use `web_search()` or `final_answer()`. Only perform one logical action at a time.
|
| 67 |
|
| 68 |
+
**Step 3: Observe**
|
| 69 |
+
After you act, you will receive an `Observation:`. This is the result of your code. Use this new information to plan your next step.
|
| 70 |
+
|
| 71 |
+
Repeat this process until you have the final answer.
|
| 72 |
+
|
| 73 |
+
**CRITICAL RULES:**
|
| 74 |
+
- Always use the `Thought:` and `Code:` blocks.
|
| 75 |
+
- **NEVER** put your final answer in the `Thought:` block.
|
| 76 |
+
- **ONLY** use `final_answer()` to provide the final answer.
|
| 77 |
+
- Keep your steps small. Do not try to do everything in one `Code:` block.
|
| 78 |
+
|
| 79 |
+
**Example: Multi-step question**
|
| 80 |
+
Question: Who are the pitchers with the number before and after Taishō Tamai's number as of July 2023? Give them to me in the form Pitcher Before, Pitcher After.
|
| 81 |
+
|
| 82 |
+
**Your Turn 1:**
|
| 83 |
+
Thought: I need to find Taishō Tamai's jersey number and team first.
|
| 84 |
Code:
|
| 85 |
```py
|
| 86 |
+
web_search(query="Taisho Tamai jersey number and team July 2023")
|
|
|
|
|
|
|
| 87 |
```<end_code>
|
| 88 |
|
| 89 |
+
**Observation 1:** `(Result from web_search)`
|
| 90 |
+
Taishō Tamai is a pitcher for the Orix Buffaloes, jersey number 61.
|
| 91 |
|
| 92 |
+
**Your Turn 2:**
|
| 93 |
+
Thought: Now I have the team and number. I need to find the team's roster to find the pitchers with numbers 60 and 62.
|
| 94 |
Code:
|
| 95 |
```py
|
| 96 |
+
web_search(query="Orix Buffaloes 2023 pitcher roster with jersey numbers")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
```<end_code>
|
| 98 |
|
| 99 |
+
**Observation 2:** `(Result from web_search)`
|
| 100 |
+
...Mizuno #60, ...Onaga #62, ...
|
| 101 |
|
| 102 |
+
**Your Turn 3:**
|
| 103 |
+
Thought: I have the names. The pitcher before is Mizuno and the one after is Onaga. I need to format the answer as "LastNameBefore, LastNameAfter".
|
| 104 |
Code:
|
| 105 |
```py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
final_answer("Mizuno, Onaga")
|
| 107 |
```<end_code>
|
| 108 |
+
"""
|
|
|
|
| 109 |
|
| 110 |
def generate(
|
| 111 |
self,
|
|
|
|
| 223 |
DuckDuckGoSearchTool()
|
| 224 |
],
|
| 225 |
model=self.model,
|
| 226 |
+
max_steps=5 # Increased max_steps to allow for multi-step reasoning
|
| 227 |
)
|
| 228 |
|
| 229 |
def clean_and_format_answer(self, answer: str, question: str) -> str:
|
| 230 |
+
"""Extracts the argument from the final_answer() call."""
|
| 231 |
+
match = re.search(r'final_answer\((?:"(.*?)"|\'(.*?)\'|(.*?))\)', answer, re.DOTALL)
|
| 232 |
+
if match:
|
| 233 |
+
# The result could be in group 1 (double quotes), group 2 (single quotes), or group 3 (no quotes)
|
| 234 |
+
result = match.group(1) or match.group(2) or match.group(3)
|
| 235 |
+
return result.strip() if result else ""
|
| 236 |
+
return ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
|
| 238 |
def __call__(self, question: str) -> str:
|
| 239 |
print(f"\n=== Processing Question: {question} ===")
|
|
|
|
| 242 |
full_response = self.agent.run(question)
|
| 243 |
print(f"\n=== Raw Response from Agent ===\n{full_response}\n===")
|
| 244 |
answer = self.clean_and_format_answer(full_response, question)
|
| 245 |
+
|
| 246 |
+
if answer:
|
| 247 |
+
return answer
|
| 248 |
+
else:
|
| 249 |
+
print("Could not find a final_answer call in the agent's response.")
|
| 250 |
+
# As a fallback, check if the model just returned a simple response
|
| 251 |
+
# without following the format.
|
| 252 |
+
if "Thought:" not in full_response and "Code:" not in full_response:
|
| 253 |
+
cleaned_response = full_response.strip()
|
| 254 |
+
if 'final_answer' not in cleaned_response and 'web_search' not in cleaned_response:
|
| 255 |
+
return cleaned_response
|
| 256 |
+
|
| 257 |
+
return "I was unable to find a definitive answer."
|
| 258 |
+
|
| 259 |
except Exception as e:
|
| 260 |
print(f"Error processing question: {e}")
|
| 261 |
return f"Error: {str(e)}"
|