from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel, load_tool, tool import datetime import requests import pytz import yaml import os import nltk from bs4 import BeautifulSoup from nltk.tokenize import sent_tokenize from tools.final_answer import FinalAnswerTool from Gradio_UI import GradioUI # Descargar tokenizer de NLTK si no está disponible nltk.download("punkt") # Configurar el modelo de resumen desde Hugging Face en SmolAgents model = HfApiModel( max_tokens=2096, temperature=0.5, model_id="facebook/bart-large-cnn", # Modelo de resumen custom_role_conversions=None, ) @tool def save_scraped_data_as_markdown(scraped_data: dict, filename: str = None) -> str: """Convierte el contenido scrapeado en un archivo Markdown mejor estructurado. Mejoras: - Resumen automático del contenido con NLP. - Uso de encabezados, listas y negritas en Markdown. - Guardado con timestamp para evitar sobrescribir archivos. Args: scraped_data: Diccionario con la URL y los datos extraídos. filename: Nombre del archivo de salida (si no se da, se genera con timestamp). Returns: Mensaje de confirmación o error. """ try: url = scraped_data.get("url", "Desconocido") content_list = scraped_data.get("scraped_data", []) if not content_list: return "No hay datos para guardar en Markdown." # Tokenizar en oraciones tokenized_sentences = [sent_tokenize(text) for text in content_list] formatted_content = "\n\n".join([" ".join(sentences) for sentences in tokenized_sentences]) # Hacer resumen del contenido (limitamos a 1024 tokens por si el texto es muy largo) if len(formatted_content.split()) > 100: summarized_text = model.query( prompt=f"Resume el siguiente texto:\n\n{formatted_content[:1024]}", max_length=150, min_length=50, ) else: summarized_text = formatted_content # Mejorar la estructura Markdown markdown_content = f"# Contenido extraído de {url}\n\n" markdown_content += f"## Resumen\n\n> {summarized_text}\n\n" markdown_content += "## Contenido Completo\n\n" for paragraph in formatted_content.split("\n\n"): if len(paragraph.split()) > 10: # Si el párrafo es largo, lo tratamos como sección markdown_content += f"### {paragraph[:50]}...\n\n{paragraph}\n\n" else: markdown_content += f"- **{paragraph}**\n\n" # Generar nombre con timestamp si no se proporciona if not filename: timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"scraped_{timestamp}.md" # Guardar en un archivo Markdown with open(filename, "w", encoding="utf-8") as file: file.write(markdown_content) return f"El contenido scrapeado se ha guardado en {filename}" except Exception as e: return f"Error al generar el archivo Markdown: {str(e)}" @tool def scrape_webpage(url: str, tag: str = "p", class_name: str = None) -> dict: """Extrae contenido de una página web según una etiqueta HTML y clase opcional. Args: url: URL de la página a scrapear. tag: Etiqueta HTML a extraer (por defecto
). class_name: Clase CSS opcional para filtrar resultados. Returns: Un diccionario con el contenido extraído. """ try: headers = {"User-Agent": "Mozilla/5.0"} response = requests.get(url, headers=headers) response.raise_for_status() soup = BeautifulSoup(response.text, "html.parser") if class_name: elements = soup.find_all(tag, class_=class_name) else: elements = soup.find_all(tag) extracted_data = [element.get_text(strip=True) for element in elements] return {"url": url, "scraped_data": extracted_data[:20]} # Limita a 20 resultados except requests.exceptions.RequestException as e: return {"error": f"Error al acceder a la URL: {str(e)}"} except Exception as e: return {"error": f"Error inesperado: {str(e)}"} @tool def extract_metadata_from_url(url: str) -> dict: """Extrae todos los metadatos de una página web. Args: url: La URL de la página web a analizar. Returns: Un diccionario con los metadatos encontrados. """ try: headers = {"User-Agent": "Mozilla/5.0"} response = requests.get(url, headers=headers) response.raise_for_status() soup = BeautifulSoup(response.text, "html.parser") metadata = {} for meta in soup.find_all("meta"): if "name" in meta.attrs and "content" in meta.attrs: metadata[meta["name"]] = meta["content"] elif "property" in meta.attrs and "content" in meta.attrs: metadata[meta["property"]] = meta["content"] return metadata if metadata else {"error": "No se encontraron metadatos en la página."} except requests.exceptions.RequestException as e: return {"error": f"Error al acceder a la URL: {str(e)}"} @tool def get_current_time_in_timezone(timezone: str) -> str: """Devuelve la hora actual en una zona horaria específica. Args: timezone: Una cadena que representa una zona horaria válida (ej. 'America/New_York'). Returns: La hora local actual en la zona horaria especificada. """ try: tz = pytz.timezone(timezone) local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") return f"La hora local actual en {timezone} es: {local_time}" except Exception as e: return f"Error obteniendo la hora para la zona horaria '{timezone}': {str(e)}" final_answer = FinalAnswerTool() # Import tool from Hub image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True) with open("prompts.yaml", "r") as stream: prompt_templates = yaml.safe_load(stream) agent = CodeAgent( model=model, tools=[ final_answer, extract_metadata_from_url, scrape_webpage, save_scraped_data_as_markdown, # Se añade la nueva herramienta al agente ], max_steps=6, verbosity_level=1, grammar=None, planning_interval=None, name=None, description=None, prompt_templates=prompt_templates, ) GradioUI(agent).launch()