| import os |
| import sys |
| import requests |
| import json |
| import traceback |
| from datetime import datetime |
| import re |
| import markdownify |
| import wikipediaapi |
| import trafilatura |
| import gradio as gr |
| import pandas as pd |
| import time |
|
|
| from smolagents import CodeAgent, LiteLLMModel, tool, DuckDuckGoSearchTool, FinalAnswerTool |
|
|
|
|
| @tool |
| def get_wikipedia_article(search_query: str) -> str: |
| """ |
| Searches for a Wikipedia article by its exact title or a close topic and returns its full, clean content. |
| This is the preferred tool for questions that likely have a Wikipedia page. |
| |
| Args: |
| search_query (str): The title or main subject to search for on Wikipedia (e.g., "Mercedes Sosa"). |
| """ |
| print(f"--- Herramienta: Buscando en Wikipedia: '{search_query}' ---") |
| try: |
| wiki_wiki = wikipediaapi.Wikipedia( |
| language='en', |
| user_agent='GAIA-Agent-Course-Test/1.0 (tu_email@ejemplo.com)' |
| ) |
| page = wiki_wiki.page(search_query) |
| if page.exists(): |
| print(f"--- Herramienta: Página de Wikipedia encontrada: '{page.title}' ---") |
| return page.text |
| else: |
| return f"Error: La página de Wikipedia para '{search_query}' no fue encontrada." |
| except Exception as e: |
| return f"An error occurred while fetching from Wikipedia: {e}" |
|
|
| @tool |
| def visit_and_get_html(url: str) -> str: |
| """ |
| Visits a webpage using robust headers and returns its raw HTML content for further processing. |
| Args: |
| url (str): The URL of the webpage to visit. |
| """ |
| print(f"--- Herramienta: Visitando URL {url} para obtener HTML ---") |
| try: |
| headers = { |
| 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36', |
| 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8', |
| 'Accept-Language': 'en-US,en;q=0.9', |
| 'Referer': 'https://www.google.com/', |
| } |
| response = requests.get(url, headers=headers, timeout=20) |
| response.raise_for_status() |
| return response.text |
| except requests.exceptions.RequestException as e: |
| error_message = f"Error fetching the webpage {url}: {e}" |
| print(error_message) |
| return error_message |
| |
| @tool |
| def extract_main_content(html_content: str) -> str: |
| """ |
| Extracts the main article text from raw HTML content, removing boilerplate like menus, ads, and footers. |
| Args: |
| html_content (str): The raw HTML of a webpage. |
| """ |
| print("--- Herramienta: Extrayendo contenido principal del HTML con Trafilatura... ---") |
| if not html_content or not isinstance(html_content, str) or "Error" in html_content: |
| return "Error: No valid HTML content provided to extract." |
| return trafilatura.extract(html_content) |
|
|
|
|
| GEMINI_API_KEY = os.environ.get("GEMINI_API_KEY") |
| os.environ["GEMINI_API_KEY"] = GEMINI_API_KEY |
| GEMINI_MODEL_ID = "gemini/gemini-1.5-flash-latest" |
|
|
| llm_provider = None |
| agente_principal = None |
|
|
| if GEMINI_API_KEY: |
| try: |
| llm_provider = LiteLLMModel(model_id=GEMINI_MODEL_ID, temperature=0.1, max_tokens=8192) |
| print(f"LLM ({GEMINI_MODEL_ID}) configurado para usar la API de Google a través de LiteLLM.") |
|
|
| agente_principal = CodeAgent( |
| model=llm_provider, |
| name="AgenteGAIA", |
| description="Soy un agente autónomo diseñado para resolver preguntas del benchmark GAIA. Utilizo mis herramientas para investigar y encontrar la respuesta correcta.", |
| tools=[ |
| get_wikipedia_article, |
| DuckDuckGoSearchTool(), |
| visit_and_get_html, |
| extract_main_content, |
| FinalAnswerTool() |
| ], |
| additional_authorized_imports=["re"], |
| verbosity_level=2, |
| max_steps=15 |
| ) |
| print("AgenteGAIA Principal inicializado.") |
| except Exception as e: |
| print(f"CRITICAL ERROR: No se pudo inicializar el agente o el LLM: {e}") |
| traceback.print_exc() |
| else: |
| print("CRITICAL ERROR: Clave API 'GEMINI_API_KEY' no se encontró en los Secrets del Space.") |
|
|
|
|
| DEFAULT_API_URL = "https://agents-course-unit4-scoring.hf.space" |
|
|
| def run_and_submit_all(profile: gr.OAuthProfile | None): |
| """ |
| Fetches all questions, runs `agente_principal` on them, submits the answers. |
| """ |
|
|
| if not profile: |
| return "Por favor, inicia sesión con tu perfil de Hugging Face.", pd.DataFrame() |
|
|
| if not agente_principal: |
| return "Error: El agente principal no está inicializado. Revisa los logs y la API Key.", pd.DataFrame() |
|
|
| username = profile.username |
| space_id = os.getenv("SPACE_ID") |
| code_link = f"https://huggingface.co/spaces/{space_id}" if space_id else "No se pudo obtener el link al Space." |
| |
| print(f"Iniciando evaluación para el usuario: {username}") |
|
|
| |
| questions_url = f"{DEFAULT_API_URL}/questions" |
| try: |
| response = requests.get(questions_url) |
| response.raise_for_status() |
| all_questions = response.json() |
| print(f"Se obtuvieron {len(all_questions)} preguntas del examen.") |
| except Exception as e: |
| return f"Error al obtener las preguntas: {e}", pd.DataFrame() |
|
|
| |
| all_answers = [] |
| results_for_display = [] |
| |
| |
| gaia_rules_prompt = "1. Answer Format: Numbers must be plain digits. Strings must omit articles.\n2. Response Content: ONLY the final answer, without prefixes or explanations." |
| main_task_prompt_template = """ |
| Your mission is to accurately answer the question "{question_text}" using your available tools and a robust, multi-step reasoning process. |
| |
| **YOUR OVERALL PLAN:** |
| 1. **STRATEGY - WIKIPEDIA FIRST:** Check if the question mentions 'Wikipedia' or is about a topic likely to have a detailed Wikipedia page. If so, your first attempt should be to use your `get_wikipedia_article` tool with a relevant search query. |
| 2. **STRATEGY - WEB SEARCH FALLBACK:** If the Wikipedia tool fails or does not provide enough information, your code must then use the `DuckDuckGoSearchTool` to find alternative sources. |
| 3. **INFORMATION EXTRACTION:** |
| - If you have content from Wikipedia, analyze it directly. |
| - If you have a URL from web search, you MUST use the following sequence: First, call `visit_and_get_html` with the URL. Second, call `extract_main_content` on the resulting HTML to get clean text. |
| 4. **SYNTHESIZE & VERIFY:** Analyze the clean text you have obtained. Generate Python code (using string methods or the `re` module) to find the specific information needed. If you can, visit a second source to verify your finding. |
| 5. **FINAL ANSWER:** Once confident in your answer, call your `final_answer` tool with ONLY the final, clean value, formatted perfectly according to the GAIA rules below. |
| |
| **GAIA RULES FOR FINAL ANSWER:** |
| {gaia_rules_prompt} |
| |
| **INSTRUCTION FOR YOUR CURRENT ACTION:** |
| Begin with STEP 1. Follow the plan meticulously, starting with the "Wikipedia-First" approach. Generate your thoughts and the Python code for the NEXT logical step. |
| """ |
|
|
| for i, question in enumerate(all_questions): |
| task_id = question['task_id'] |
| question_text = question['question'] |
| print(f"\n--- Procesando Pregunta {i+1}/{len(all_questions)} (ID: {task_id}) ---") |
| |
| current_prompt = main_task_prompt_template.format(question_text=question_text, gaia_rules_prompt=gaia_rules_prompt) |
| |
| agent_answer = None |
| try: |
| agent_answer = agente_principal.run(current_prompt) |
| print(f"Respuesta del Agente: {agent_answer}") |
| except Exception as e: |
| agent_answer = f"Error al ejecutar el agente: {e}" |
| print(agent_answer) |
| traceback.print_exc() |
|
|
| submission_answer = {"task_id": task_id, "submitted_answer": agent_answer} |
| all_answers.append(submission_answer) |
| results_for_display.append({"Question": question_text, "Agent's Answer": agent_answer}) |
|
|
| print("--- Pausando por 5 segundos para no exceder el límite de la API... ---") |
| time.sleep(5) |
|
|
| submission_file = "submission.jsonl" |
| with open(submission_file, 'w') as f: |
| for answer in all_answers: |
| f.write(json.dumps(answer) + '\n') |
| |
| print(f"Archivo de envío '{submission_file}' creado con {len(all_answers)} respuestas.") |
|
|
| try: |
| payload = {"username": username, "agent_code": code_link} |
| with open(submission_file, 'rb') as f: |
| files = {'answers': (submission_file, f, 'application/jsonl')} |
| submit_url = f"{DEFAULT_API_URL}/submit" |
| response = requests.post(submit_url, data=payload, files=files) |
| response.raise_for_status() |
| |
| submission_result = response.json() |
| print("¡Resultados enviados exitosamente!") |
| return f"¡Éxito! Tu puntaje final es: {submission_result.get('score', 'N/A')}", pd.DataFrame(results_for_display) |
| except Exception as e: |
| error_msg = f"Error al enviar los resultados: {e}" |
| print(error_msg) |
| return error_msg, pd.DataFrame(results_for_display) |
|
|
|
|
| with gr.Blocks(theme=gr.themes.Soft()) as demo: |
| gr.Markdown("# AZUFR3 AGENT DAEMON - Ecosistema de Agentes para el Examen Final de GAIA") |
| gr.Markdown("Inicia sesión con tu perfil de Hugging Face y haz clic en el botón para ejecutar tu agente en las 20 preguntas del examen y enviar tus resultados. La ejecución puede tardar varios minutos.") |
| |
| login_button = gr.LoginButton() |
| run_button = gr.Button("Ejecutar Evaluación y Enviar Todas las Respuestas", variant="primary") |
| |
| status_output = gr.Textbox(label="Estado de la Ejecución / Resultado del Envío", lines=5, interactive=False) |
| results_table = gr.DataFrame(label="Preguntas y Respuestas del Agente", wrap=True, column_widths=["50%", "50%"]) |
|
|
| run_button.click( |
| fn=run_and_submit_all, |
| outputs=[status_output, results_table] |
| ) |
|
|
| if __name__ == "__main__": |
| demo.launch() |
|
|