Andrei Nazarov commited on
Commit
3efbcf4
·
1 Parent(s): 5d3c229
Files changed (1) hide show
  1. app.py +151 -66
app.py CHANGED
@@ -7,6 +7,7 @@ from smolagents import CodeAgent, DuckDuckGoSearchTool, load_tool, tool
7
  from smolagents.models import Model, ChatMessage, MessageRole, Tool
8
  from tools import FinalAnswerTool
9
  import google.generativeai as genai
 
10
 
11
  # (Keep Constants as is)
12
  # --- Constants ---
@@ -24,32 +25,75 @@ class GeminiModel(Model):
24
  self.model = genai.GenerativeModel('models/gemini-2.0-flash-lite')
25
 
26
  # System prompt for smolagents format
27
- self.system_prompt = """You are an expert assistant who can solve any task using code blobs. You will be given a task to solve as best you can.
28
- To do so, you have been given access to a list of tools: these tools are basically Python functions which you can call with code.
29
- To solve the task, you must plan forward to proceed in a series of steps, in a cycle of 'Thought:', 'Code:', and 'Observation:' sequences.
30
- At each step, in the 'Thought:' sequence, you should first explain your reasoning towards solving the task and the tools that you want to use.
31
- Then in the 'Code:' sequence, you should write the code in simple Python. The code sequence must end with '<end_code>' sequence.
32
- During each intermediate step, you can use 'print()' to save whatever important information you will then need.
33
- These print outputs will then appear in the 'Observation:' field, which will be available as input for the next step.
34
- In the end you have to return a final answer using the `final_answer` tool.
35
-
36
- Here are the rules you should always follow to solve your task:
37
- 1. Always provide a 'Thought:' sequence, and a 'Code:\n```py' sequence ending with '```<end_code>' sequence, else you will fail.
38
- 2. Use only variables that you have defined!
39
- 3. Always use the right arguments for the tools. DO NOT pass the arguments as a dict as in 'answer = wiki({'query': "What is the place where James Bond lives?"})', but use the arguments directly as in 'answer = wiki(query="What is the place where James Bond lives?")'.
40
- 4. Take care to not chain too many sequential tool calls in the same code block, especially when the output format is unpredictable. For instance, a call to search has an unpredictable return format, so do not have another tool call that depends on its output in the same block: rather output results with print() to use them in the next block.
41
- 5. Call a tool only when needed, and never re-do a tool call that you previously did with the exact same parameters.
42
- 6. Don't name any new variable with the same name as a tool: for instance don't name a variable 'final_answer'.
43
- 7. Never create any notional variables in our code, as having these in your logs will derail you from the true variables.
44
- 8. You can use imports in your code, but only from the following list of modules: ['random', 'collections', 'unicodedata', 'time', 'math', 'datetime', 're', 'stat', 'statistics', 'queue', 'itertools']
45
- 9. The state persists between code executions: so if in one step you've created variables or imported modules, these will all persist.
46
- 10. Don't give up! You're in charge of solving the task, not providing directions to solve it.
47
-
48
- Available tools:
49
- - final_answer(answer): Provides a final answer to the given problem.
50
- - web_search(query): Search the web for information using DuckDuckGo.
51
-
52
- Now Begin! If you solve the task correctly, you will receive a reward of $1,000,000."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
 
54
  def generate(
55
  self,
@@ -146,52 +190,93 @@ class MyAgent:
146
  FinalAnswerTool(),
147
  DuckDuckGoSearchTool()
148
  ],
149
- model=self.model
 
150
  )
151
 
152
  def __call__(self, question: str) -> str:
153
  # Run the agent and get the full response
 
154
  full_response = self.agent.run(question)
155
 
156
- # Extract only the final answer from the response
157
- # The final answer is typically returned by the final_answer tool
158
- # Look for patterns like "Out - Final answer:" or similar
159
- if "Out - Final answer:" in full_response:
160
- # Extract everything after "Out - Final answer:"
161
- final_answer = full_response.split("Out - Final answer:")[-1].strip()
162
- return final_answer
163
- elif "Final answer:" in full_response:
164
- # Alternative pattern
165
- final_answer = full_response.split("Final answer:")[-1].strip()
166
- return final_answer
167
- elif "final_answer(answer=" in full_response:
168
- # Extract from final_answer tool call
169
- import re
170
- match = re.search(r'final_answer\(answer="([^"]+)"\)', full_response)
171
- if match:
172
- return match.group(1)
173
- else:
174
- # If no clear final answer pattern is found, return the last meaningful line
175
- lines = full_response.strip().split('\n')
176
- for line in reversed(lines):
177
- line = line.strip()
178
- if (line and
179
- not line.startswith('[') and
180
- not line.startswith('─') and
181
- not line.startswith('╭') and
182
- not line.startswith('╰') and
183
- not line.startswith('Out:') and
184
- not line.startswith('Execution logs:') and
185
- not line.startswith('Code parsing') and
186
- not line.startswith('Error:') and
187
- not line.startswith('```') and
188
- not line.startswith('Thought:') and
189
- not line.startswith('Code:') and
190
- not line.startswith('<end_code>') and
191
- len(line) > 3): # Avoid very short lines
192
- return line
193
- # Fallback to the full response if no clean answer is found
194
- return full_response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
  def run_and_submit_all( profile: gr.OAuthProfile | None):
197
  """
 
7
  from smolagents.models import Model, ChatMessage, MessageRole, Tool
8
  from tools import FinalAnswerTool
9
  import google.generativeai as genai
10
+ import re
11
 
12
  # (Keep Constants as is)
13
  # --- Constants ---
 
25
  self.model = genai.GenerativeModel('models/gemini-2.0-flash-lite')
26
 
27
  # System prompt for smolagents format
28
+ self.system_prompt = """You are a highly focused AI assistant tasked with answering specific questions accurately using available tools. Your primary goal is to find and provide precise answers to questions using the tools provided.
29
+
30
+ Key Guidelines:
31
+ 1. Stay EXACTLY focused on what the question asks for - do not get sidetracked by related information
32
+ 2. Break down the question into its key components (e.g., time period, specific type of information needed)
33
+ 3. Use web_search with specific terms related to those components
34
+ 4. When analyzing search results, ONLY look for information that directly answers the question
35
+ 5. If you find a good answer, STOP and provide it immediately - do not continue searching
36
+ 6. ALWAYS provide your final answer using the final_answer tool with ONLY the information asked for
37
+ 7. If web searches fail repeatedly, provide the best answer you can based on your knowledge
38
+
39
+ For each question, use this exact format:
40
+
41
+ Thought: Break down what EXACTLY is being asked and how you'll find it
42
+ Code:
43
+ ```py
44
+ # Your python code here using only the available tools:
45
+ # - web_search(query): Search the web for information
46
+ # - final_answer(answer): Provide the final answer
47
+ ```<end_code>
48
+
49
+ Examples:
50
+
51
+ 1. Question about albums:
52
+ Q: "How many studio albums were released by Artist X between 2000-2005?"
53
+ Thought: Need to find the count of ONLY studio albums by Artist X released between 2000-2005
54
+ Code:
55
+ ```py
56
+ # Search for Artist X's albums in that period
57
+ results = web_search(query="Artist X studio albums 2000 2001 2002 2003 2004 2005")
58
+ # After analyzing results, if I find the answer, STOP and provide it
59
+ final_answer("Artist X released 3 studio albums between 2000-2005: Album1 (2000), Album2 (2002), Album3 (2004)")
60
+ ```<end_code>
61
+
62
+ 2. Question about video content:
63
+ Q: "In the video [URL], how many different species appear?"
64
+ Thought: Need to find information about this specific video's content and identify all unique species shown
65
+ Code:
66
+ ```py
67
+ # First search for the video title and description
68
+ results = web_search(query="[video-id] title description")
69
+ # If I find the answer in the first search, STOP and provide it
70
+ final_answer("The video shows 3 different species: Species1, Species2, and Species3")
71
+ ```<end_code>
72
+
73
+ 3. When web searches fail:
74
+ Q: "How many albums did Artist X release in 2000?"
75
+ Thought: Need to find Artist X's albums from 2000, but web search might fail
76
+ Code:
77
+ ```py
78
+ # Try web search first
79
+ results = web_search(query="Artist X albums 2000")
80
+ # If search fails, provide best available answer and STOP
81
+ final_answer("Based on available information, Artist X released 2 albums in 2000: Album1 and Album2")
82
+ ```<end_code>
83
+
84
+ CRITICAL: Once you find a good answer, STOP immediately and provide it. Do not continue searching or trying different queries unless the first search completely fails to find any relevant information.
85
+
86
+ Remember:
87
+ 1. Stay LASER-FOCUSED on the specific information requested
88
+ 2. Don't get sidetracked by biographical or other related information
89
+ 3. If you find a good answer, STOP and provide it immediately
90
+ 4. ALWAYS end with a final_answer that ONLY includes the exact information asked for
91
+ 5. For video questions:
92
+ - First try searching with the video ID to find the title and description
93
+ - Then search with the title to find detailed reviews or descriptions
94
+ - If you can't find the exact information, say so clearly
95
+ 6. If web searches fail repeatedly, provide the best answer you can and acknowledge the limitation
96
+ 7. MOST IMPORTANT: STOP after finding a good answer - do not continue searching unnecessarily"""
97
 
98
  def generate(
99
  self,
 
190
  FinalAnswerTool(),
191
  DuckDuckGoSearchTool()
192
  ],
193
+ model=self.model,
194
+ max_steps=2 # Limit to 2 steps to prevent unnecessary continuation
195
  )
196
 
197
  def __call__(self, question: str) -> str:
198
  # Run the agent and get the full response
199
+ print(f"\n=== Processing Question: {question} ===")
200
  full_response = self.agent.run(question)
201
 
202
+ print(f"\n=== Raw Response from Agent ===\n{full_response}\n===")
203
+
204
+ # First try to find a final_answer tool call
205
+ if "final_answer(" in full_response:
206
+ # Look for both quoted and unquoted versions
207
+ patterns = [
208
+ r'final_answer\(answer="([^"]+)"\)', # Double quoted
209
+ r"final_answer\(answer='([^']+)'\)", # Single quoted
210
+ r'final_answer\(answer=([^,\)]+)\)', # Unquoted
211
+ r'final_answer\("([^"]+)"\)', # Simple double quoted
212
+ r"final_answer\('([^']+)'\)", # Simple single quoted
213
+ r'final_answer\(([^,\)]+)\)', # Simple unquoted
214
+ ]
215
+
216
+ for pattern in patterns:
217
+ match = re.search(pattern, full_response)
218
+ if match:
219
+ answer = match.group(1).strip()
220
+ print(f"Found answer via final_answer tool: {answer}")
221
+ return answer
222
+
223
+ # Look for explicit final answer markers
224
+ markers = [
225
+ "Out - Final answer:",
226
+ "Final answer:",
227
+ "Answer:",
228
+ "The answer is:"
229
+ ]
230
+
231
+ for marker in markers:
232
+ if marker in full_response:
233
+ parts = full_response.split(marker)
234
+ answer = parts[-1].strip()
235
+ # Clean up the answer - remove any following sections
236
+ answer = answer.split("\n")[0].strip()
237
+ print(f"Found answer via marker '{marker}': {answer}")
238
+ return answer
239
+
240
+ # If the raw response is just a simple answer (like a number or short text)
241
+ # and doesn't contain execution logs or other markers, use it directly
242
+ clean_response = full_response.strip()
243
+ if (len(clean_response) < 100 and
244
+ not any(marker in clean_response for marker in [
245
+ '[', '─', '╭', '╰', 'Out:', 'Execution logs:',
246
+ 'Code parsing', 'Error:', '```', 'Thought:',
247
+ 'Code:', '<end_code>', 'Observation:', 'Step', 'Duration',
248
+ 'New run', 'Executing parsed code'
249
+ ])):
250
+ print(f"Using raw response as answer: {clean_response}")
251
+ return clean_response
252
+
253
+ # If we get here, we need to try to extract a meaningful answer from the response
254
+ print("No explicit answer format found, analyzing response content...")
255
+
256
+ # Split into lines and look for meaningful content
257
+ lines = full_response.strip().split('\n')
258
+
259
+ # First look for lines that look like direct answers (not prefixed with common markers)
260
+ for line in lines:
261
+ line = line.strip()
262
+ if (line and
263
+ not any(line.startswith(x) for x in [
264
+ '[', '─', '╭', '╰', 'Out:', 'Execution logs:',
265
+ 'Code parsing', 'Error:', '```', 'Thought:',
266
+ 'Code:', '<end_code>', 'Observation:', 'Step', 'Duration'
267
+ ]) and
268
+ not any(line.endswith(x) for x in ['seconds', 'seconds)']) and
269
+ len(line) > 1 and
270
+ not line.startswith('─') and
271
+ not line.startswith('╭') and
272
+ not line.startswith('╰')):
273
+ print(f"Found potential answer from content: {line}")
274
+ return line
275
+
276
+ # If we still haven't found anything, return a clear error
277
+ error_msg = "Could not extract a clear answer from the agent's response"
278
+ print(error_msg)
279
+ return error_msg
280
 
281
  def run_and_submit_all( profile: gr.OAuthProfile | None):
282
  """