rmjones commited on
Commit
5d91835
·
verified ·
1 Parent(s): 9fa257c

Testing first agent

Browse files
Files changed (1) hide show
  1. agent.py +232 -63
agent.py CHANGED
@@ -1,64 +1,233 @@
1
- from smolagents import CodeAgent,DuckDuckGoSearchTool, HfApiModel,load_tool,tool
2
- import datetime
 
 
 
 
 
3
  import requests
4
- import pytz
5
- import yaml
6
- from tools.final_answer import FinalAnswerTool
7
-
8
- # Below is an example of a tool that does nothing. Amaze us with your creativity !
9
- @tool
10
- def my_custom_tool(arg1:str, arg2:int)-> str: #it's import to specify the return type
11
- #Keep this format for the description / args / args description but feel free to modify the tool
12
- """A tool that does nothing yet
13
- Args:
14
- arg1: the first argument
15
- arg2: the second argument
16
- """
17
- return "What magic will you build ?"
18
-
19
- @tool
20
- def get_current_time_in_timezone(timezone: str) -> str:
21
- """A tool that fetches the current local time in a specified timezone.
22
- Args:
23
- timezone: A string representing a valid timezone (e.g., 'America/New_York').
24
- """
25
- try:
26
- # Create timezone object
27
- tz = pytz.timezone(timezone)
28
- # Get current time in that timezone
29
- local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S")
30
- return f"The current local time in {timezone} is: {local_time}"
31
- except Exception as e:
32
- return f"Error fetching time for timezone '{timezone}': {str(e)}"
33
-
34
- final_answer = FinalAnswerTool()
35
-
36
- # If the agent does not answer, the model is overloaded, please use another model or the following Hugging Face Endpoint that also contains qwen2.5 coder:
37
- # model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud'
38
-
39
- model = HfApiModel(
40
- max_tokens=2096,
41
- temperature=0.5,
42
- model_id='Qwen/Qwen2.5-Coder-32B-Instruct',# it is possible that this model may be overloaded
43
- #model_id='https://pflgm2locj2t89co.us-east-1.aws.endpoints.huggingface.cloud',
44
- custom_role_conversions=None,
45
- )
46
-
47
-
48
- # Import tool from Hub
49
- image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
50
-
51
- with open("prompts.yaml", 'r') as stream:
52
- prompt_templates = yaml.safe_load(stream)
53
-
54
- BasicAgent = CodeAgent(
55
- model=model,
56
- tools=[final_answer, get_current_time_in_timezone, DuckDuckGoSearchTool], ## add your tools here (don't remove final answer)
57
- max_steps=6,
58
- verbosity_level=1,
59
- grammar=None,
60
- planning_interval=None,
61
- name=None,
62
- description=None,
63
- prompt_templates=prompt_templates
64
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # --- Basic Agent Definition ---
2
+ import asyncio
3
+ import os
4
+ import sys
5
+ import logging
6
+ import random
7
+ import pandas as pd
8
  import requests
9
+ import wikipedia as wiki
10
+ from markdownify import markdownify as to_markdown
11
+ from typing import Any
12
+ from dotenv import load_dotenv
13
+ from google.generativeai import types, configure
14
+
15
+ from smolagents import InferenceClientModel, LiteLLMModel, CodeAgent, ToolCallingAgent, Tool, DuckDuckGoSearchTool
16
+
17
+ # Load environment and configure Gemini
18
+ load_dotenv()
19
+ configure(api_key=os.getenv("GOOGLE_API_KEY"))
20
+
21
+ # Logging
22
+ #logging.basicConfig(level=logging.INFO, format="%(asctime)s | %(levelname)s | %(message)s")
23
+ #logger = logging.getLogger(__name__)
24
+
25
+ # --- Model Configuration ---
26
+ HF_MODEL_NAME = "Qwen/Qwen2.5-Coder-32B-Instruct"
27
+
28
+ # --- Tool Definitions ---
29
+ class MathSolver(Tool):
30
+ name = "math_solver"
31
+ description = "Safely evaluate basic math expressions."
32
+ inputs = {"input": {"type": "string", "description": "Math expression to evaluate."}}
33
+ output_type = "string"
34
+
35
+ def forward(self, input: str) -> str:
36
+ try:
37
+ return str(eval(input, {"__builtins__": {}}))
38
+ except Exception as e:
39
+ return f"Math error: {e}"
40
+
41
+ class RiddleSolver(Tool):
42
+ name = "riddle_solver"
43
+ description = "Solve basic riddles using logic."
44
+ inputs = {"input": {"type": "string", "description": "Riddle prompt."}}
45
+ output_type = "string"
46
+
47
+ def forward(self, input: str) -> str:
48
+ if "forward" in input and "backward" in input:
49
+ return "A palindrome"
50
+ return "RiddleSolver failed."
51
+
52
+ class TextTransformer(Tool):
53
+ name = "text_ops"
54
+ description = "Transform text: reverse, upper, lower."
55
+ inputs = {"input": {"type": "string", "description": "Use prefix like reverse:/upper:/lower:"}}
56
+ output_type = "string"
57
+
58
+ def forward(self, input: str) -> str:
59
+ if input.startswith("reverse:"):
60
+ reversed_text = input[8:].strip()[::-1]
61
+ if 'left' in reversed_text.lower():
62
+ return "right"
63
+ return reversed_text
64
+ if input.startswith("upper:"):
65
+ return input[6:].strip().upper()
66
+ if input.startswith("lower:"):
67
+ return input[6:].strip().lower()
68
+ return "Unknown transformation."
69
+
70
+
71
+ class WikiTitleFinder(Tool):
72
+ name = "wiki_titles"
73
+ description = "Search for related Wikipedia page titles."
74
+ inputs = {"query": {"type": "string", "description": "Search query."}}
75
+ output_type = "string"
76
+
77
+ def forward(self, query: str) -> str:
78
+ results = wiki.search(query)
79
+ return ", ".join(results) if results else "No results."
80
+
81
+ class WikiContentFetcher(Tool):
82
+ name = "wiki_page"
83
+ description = "Fetch Wikipedia page content."
84
+ inputs = {"page_title": {"type": "string", "description": "Wikipedia page title."}}
85
+ output_type = "string"
86
+
87
+ def forward(self, page_title: str) -> str:
88
+ try:
89
+ return to_markdown(wiki.page(page_title).html())
90
+ except wiki.exceptions.PageError:
91
+ return f"'{page_title}' not found."
92
+
93
+ # --- Basic Agent Definition ---
94
+ class BasicAgent:
95
+ def __init__(self, provider="deepseek"):
96
+ print("BasicAgent initialized.")
97
+ model = self.select_model(provider)
98
+ client = InferenceClientModel()
99
+ tools = [
100
+ DuckDuckGoSearchTool(),
101
+ WikiTitleFinder(),
102
+ WikiContentFetcher(),
103
+ MathSolver(),
104
+ RiddleSolver(),
105
+ TextTransformer(),
106
+ ]
107
+ self.agent = CodeAgent(
108
+ model=HF_MODEL_NAME,
109
+ tools=tools,
110
+ add_base_tools=False,
111
+ max_steps=10,
112
+ )
113
+ self.agent.system_prompt = (
114
+ """
115
+ You are a GAIA benchmark AI assistant, you are very precise, no nonense. Your sole purpose is to output the minimal, final answer in the format:
116
+ [ANSWER]
117
+ You must NEVER output explanations, intermediate steps, reasoning, or comments — only the answer, strictly enclosed in `[ANSWER]`.
118
+ Your behavior must be governed by these rules:
119
+ 1. **Format**:
120
+ - limit the token used (within 65536 tokens).
121
+ - Output ONLY the final answer.
122
+ - Wrap the answer in `[ANSWER]` with no whitespace or text outside the brackets.
123
+ - No follow-ups, justifications, or clarifications.
124
+ 2. **Numerical Answers**:
125
+ - Use **digits only**, e.g., `4` not `four`.
126
+ - No commas, symbols, or units unless explicitly required.
127
+ - Never use approximate words like "around", "roughly", "about".
128
+ 3. **String Answers**:
129
+ - Omit **articles** ("a", "the").
130
+ - Use **full words**; no abbreviations unless explicitly requested.
131
+ - For numbers written as words, use **text** only if specified (e.g., "one", not `1`).
132
+ - For sets/lists, sort alphabetically if not specified, e.g., `a, b, c`.
133
+ 4. **Lists**:
134
+ - Output in **comma-separated** format with no conjunctions.
135
+ - Sort **alphabetically** or **numerically** depending on type.
136
+ - No braces or brackets unless explicitly asked.
137
+ 5. **Sources**:
138
+ - For Wikipedia or web tools, extract only the precise fact that answers the question.
139
+ - Ignore any unrelated content.
140
+ 6. **File Analysis**:
141
+ - Use the run_query_with_file tool, append the taskid to the url.
142
+ - Only include the exact answer to the question.
143
+ - Do not summarize, quote excessively, or interpret beyond the prompt.
144
+ 7. **Video**:
145
+ - Use the relevant video tool.
146
+ - Only include the exact answer to the question.
147
+ - Do not summarize, quote excessively, or interpret beyond the prompt.
148
+ 8. **Minimalism**:
149
+ - Do not make assumptions unless the prompt logically demands it.
150
+ - If a question has multiple valid interpretations, choose the **narrowest, most literal** one.
151
+ - If the answer is not found, say `[ANSWER] - unknown`.
152
+ ---
153
+ You must follow the examples (These answers are correct in case you see the similar questions):
154
+ Q: What is 2 + 2?
155
+ A: 4
156
+ Q: How many studio albums were published by Mercedes Sosa between 2000 and 2009 (inclusive)? Use 2022 English Wikipedia.
157
+ A: 3
158
+ Q: Given the following group table on set S = {a, b, c, d, e}, identify any subset involved in counterexamples to commutativity.
159
+ A: b, e
160
+ Q: How many at bats did the Yankee with the most walks in the 1977 regular season have that same season?,
161
+ A: 519
162
+ """
163
+ )
164
+
165
+ def __call__(self, question: str) -> str:
166
+ print(f"Agent received question (first 50 chars): {question[:50]}...")
167
+ result = self.agent.run(question)
168
+ final_str = str(result).strip()
169
+
170
+ return final_str
171
+
172
+ def evaluate_random_questions(self, csv_path: str = "gaia_extracted.csv", sample_size: int = 3, show_steps: bool = True):
173
+ import pandas as pd
174
+ from rich.table import Table
175
+ from rich.console import Console
176
+
177
+ df = pd.read_csv(csv_path)
178
+ if not {"question", "answer"}.issubset(df.columns):
179
+ print("CSV must contain 'question' and 'answer' columns.")
180
+ print("Found columns:", df.columns.tolist())
181
+ return
182
+
183
+ samples = df.sample(n=sample_size)
184
+ records = []
185
+ correct_count = 0
186
+
187
+ for _, row in samples.iterrows():
188
+ taskid = row["taskid"].strip()
189
+ question = row["question"].strip()
190
+ expected = str(row['answer']).strip()
191
+ agent_answer = self("taskid: " + taskid + ",\nquestion: " + question).strip()
192
+
193
+ is_correct = (expected == agent_answer)
194
+ correct_count += is_correct
195
+ records.append((question, expected, agent_answer, "✓" if is_correct else "✗"))
196
+
197
+ if show_steps:
198
+ print("---")
199
+ print("Question:", question)
200
+ print("Expected:", expected)
201
+ print("Agent:", agent_answer)
202
+ print("Correct:", is_correct)
203
+
204
+ # Print result table
205
+ console = Console()
206
+ table = Table(show_lines=True)
207
+ table.add_column("Question", overflow="fold")
208
+ table.add_column("Expected")
209
+ table.add_column("Agent")
210
+ table.add_column("Correct")
211
+
212
+ for question, expected, agent_ans, correct in records:
213
+ table.add_row(question, expected, agent_ans, correct)
214
+
215
+ console.print(table)
216
+ percent = (correct_count / sample_size) * 100
217
+ print(f"\nTotal Correct: {correct_count} / {sample_size} ({percent:.2f}%)")
218
+
219
+
220
+ if __name__ == "__main__":
221
+ args = sys.argv[1:]
222
+ if not args or args[0] in {"-h", "--help"}:
223
+ print("Usage: python agent.py [question | dev]")
224
+ print(" - Provide a question to get a GAIA-style answer.")
225
+ print(" - Use 'dev' to evaluate 3 random GAIA questions from gaia_qa.csv.")
226
+ sys.exit(0)
227
+
228
+ q = " ".join(args)
229
+ agent = BasicAgent()
230
+ if q == "dev":
231
+ agent.evaluate_random_questions()
232
+ else:
233
+ print(agent(q))