| from typing import Any, List, Optional |
|
|
| from smolagents import CodeAgent |
| from tools.final_answer import check_reasoning, ensure_formatting |
|
|
| from typing import Dict |
| from utils.logger import get_logger |
| import time |
|
|
| logger = get_logger(__name__) |
|
|
| DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" |
|
|
| def get_prompt_templates() -> Dict[str, str]: |
| """Returns all prompts as a dictionary of pre-formatted strings""" |
| |
| |
| tools_instructions = """ |
| Available Tools: |
| - web_search(query): Performs web searches |
| - wikipedia_search(query): Searches Wikipedia |
| - visit_webpage(url): Retrieves webpage content |
| |
| Rules: |
| 1. Always use 'Thought:'/'Code:' sequences |
| 2. Never reuse variable names |
| 3. Tools must be called with proper arguments |
| """ |
| |
| example_1 = """ |
| Example Task: "Find the capital of France" |
| |
| Thought: I'll use web_search to find this information |
| Code: |
| result = web_search(query="capital of France") |
| final_answer(result) |
| ```<end_code> |
| """ |
| |
| |
| return { |
| "system_prompt": f""" |
| You are an expert AI assistant that solves tasks using tools. |
| {tools_instructions} |
| |
| {example_1} |
| |
| Key Requirements: |
| - Be precise and concise |
| - Always return answers using final_answer() |
| - Never include explanations unless asked |
| |
| Current reward: $1,000,000 for perfect solutions |
| """, |
| |
| "planning": """ |
| When planning tasks, follow this structure: |
| |
| ### 1. Facts Given |
| List known information |
| |
| ### 2. Facts Needed |
| List what needs research |
| |
| ### 3. Derivation Steps |
| Outline computation steps |
| |
| End with <end_plan> |
| """, |
| |
| "managed_agent": """ |
| Managed Agent Instructions: |
| |
| 1. Task outcome (short) |
| 2. Detailed explanation |
| 3. Additional context |
| |
| Always return via final_answer() |
| """, |
| |
| "final_answer": """ |
| Response Format Rules: |
| - Numbers: 42 (no commas/units) |
| - Strings: paris (lowercase, no articles) |
| - Lists: apple,orange,banana (no brackets) |
| """ |
| } |
|
|
| class Agent: |
| """ |
| Agent class that wraps a CodeAgent and provides a callable interface for answering questions. |
| |
| Args: |
| model (Any): The language model to use. |
| tools (Optional[List[Any]]): List of tools to provide to the agent. |
| prompt (Optional[str]): Custom prompt template for the agent. |
| verbose (bool): Whether to print debug information. |
| """ |
|
|
| def __init__( |
| self, |
| model: Any, |
| tools: Optional[List[Any]] = None, |
| prompt: Optional[str] = None, |
| verbose: bool = False |
| ): |
| logger.info("Initializing Agent") |
| self.model = model |
| self.tools = tools |
| self.verbose = verbose |
| self.imports = [ |
| "pandas", "numpy", "os", "requests", "tempfile", |
| "datetime", "json", "time", "re", "openpyxl", |
| "pathlib", "sys" |
| ] |
| |
| self.agent = CodeAgent( |
| model=self.model, |
| tools=self.tools, |
| add_base_tools=True, |
| additional_authorized_imports=self.imports, |
| ) |
|
|
| self.final_answer_checks=[check_reasoning, ensure_formatting], |
| |
| self.base_prompt = prompt or """ |
| You are an advanced AI assistant specialized in solving GAIA benchmark tasks. |
| Follow these rules strictly: |
| 1. Be precise - return ONLY the exact answer requested |
| 2. Use tools when needed (especially for file analysis) |
| 3. For reversed text questions, answer in normal text |
| 4. Never include explanations or reasoning in the final answer |
| 5. Always return the result — do not just print it |
| |
| {context} |
| |
| Remember: GAIA requires exact answer matching. Just provide the factual answer. |
| """ |
| |
| self.prompt_templates = get_prompt_templates() |
| logger.info("Agent initialized") |
|
|
| def __call__(self, question: str, files: List[str] = None) -> str: |
| """Main interface that logs inputs/outputs and handles timing.""" |
| if self.verbose: |
| print(f"Agent received question: {question[:50]}... with files: {files}") |
| |
| time.sleep(25) |
| return self.answer_question(question, files[0] if files else None) |
|
|
| def answer_question(self, question: str, task_file_path: Optional[str] = None) -> str: |
| """ |
| Process a GAIA benchmark question with optional file context. |
| |
| Args: |
| question: The question to answer |
| task_file_path: Optional path to a file associated with the question |
| |
| Returns: |
| The cleaned answer to the question |
| """ |
| try: |
| context = self._build_context(question, task_file_path) |
| full_prompt = self.base_prompt.format(context=context) |
| |
| if self.verbose: |
| print("Generated prompt:", full_prompt[:200] + "...") |
| |
| answer = self.agent.run(full_prompt) |
| return self._clean_answer(str(answer)) |
| |
| except Exception as e: |
| logger.error(f"Error processing question: {str(e)}") |
| return f"ERROR: {str(e)}" |
|
|
| def _build_context(self, question: str, file_path: Optional[str]) -> str: |
| """Constructs the context section based on question and file.""" |
| context_lines = [f"QUESTION: {question}"] |
| |
| if file_path: |
| context_lines.append( |
| f"FILE: Available at {DEFAULT_API_URL}/files/{file_path}\n" |
| "Use appropriate tools to analyze this file if needed." |
| ) |
| |
| |
| if self._is_reversed_text(question): |
| context_lines.append( |
| f"NOTE: This question contains reversed text. " |
| f"Original: {question}\nReversed: {question[::-1]}" |
| ) |
| |
| return "\n".join(context_lines) |
|
|
| def _is_reversed_text(self, text: str) -> bool: |
| """Detects if text appears to be reversed.""" |
| return text.startswith(".") or ".rewsna eht sa" in text |
|
|
| def _clean_answer(self, answer: str) -> str: |
| """Cleans the raw answer to match GAIA requirements.""" |
| |
| for prefix in ["Final Answer:", "Answer:", "=>"]: |
| if answer.startswith(prefix): |
| answer = answer[len(prefix):] |
| |
| |
| answer = answer.strip(" '\"") |
| |
| |
| if self._is_reversed_text(answer): |
| return answer[::-1] |
| |
| return answer |