¡Hola, agente en entrenamiento! Emma aquí, de vuelta de otra inmersión nocturna en el fascinante, a veces frustrante, mundo de los agentes de IA. Si eres algo como yo, probablemente has pasado los últimos meses escuchando susurros (o gritos a pleno pulmón) sobre los agentes de IA y has pensado, “Está bien, esto suena genial, pero también… ¿qué es realmente, y cómo hago para que haga algo por mí?”
Bueno, estás en el lugar correcto. Hoy no solo vamos a hablar sobre lo que *son* los agentes de IA en teoría. Vamos a arremangarnos y abordar una pregunta que ha estado molestando a muchos de ustedes (y a mí, hasta hace poco): ¿Cómo construyo un agente de IA simple que realmente pueda navegar por internet y responder preguntas específicas?
Olvida la exageración por un minuto. Vamos a ser prácticos. Vamos a por “realmente puedo hacer que esto funcione en mi laptop.” Porque, seamos honestos, ver para creer, ¿verdad?
Mis Propios Errores (y Avances) con Bots de Navegación
Cuando empecé a experimentar con la idea de un agente que pudiera utilizar internet, tenía grandes visiones. Quería que investigara temas complejos, comparara precios en una docena de sitios y, básicamente, fuera mi asistente digital personal. Lo que conseguí, inicialmente, fue una cantidad de errores, algunos scrapers web rotos y un profundo sentido de insuficiencia. Mis primeros intentos fueron como tratar de enseñar a un niño pequeño a operar una maquinaria compleja: mucha entusiasmo, cero comprensión de la mecánica subyacente.
¿El mayor obstáculo? Hacer que un modelo de IA interactúe con internet de manera significativa y controlada. Es una cosa preguntar a ChatGPT una pregunta que ya conoce. Es otra muy diferente pedirle que busque nueva información, la interprete y luego actúe en consecuencia. Ahí es donde realmente entra en juego la parte de “agente”.
Después de muchos ensayos y errores, me di cuenta de que la clave no era construir una entidad super compleja y omnisciente desde el principio. Era comenzar pequeño, con una tarea muy específica, y construir a partir de ahí. Y eso es exactamente lo que vamos a hacer hoy. Vamos a crear un agente de navegador básico que pueda ir a un sitio web específico, extraer información y reportar de vuelta.
¿Qué Es Un Agente de IA de Navegador?
Antes de sumergirnos en el código, aclaremos rápidamente. Un agente de IA, en su esencia, es un programa de IA diseñado para realizar una tarea específica de manera autónoma. Observa su entorno (en nuestro caso, internet), toma decisiones basadas en sus objetivos y luego actúa. Un agente de IA *de navegador* utiliza específicamente capacidades de navegación web como parte de su herramienta.
Piénsalo así: le dices a tu agente, “Encuentra el clima actual en Londres.” En lugar de simplemente tirar de su conocimiento interno, un agente de navegador abriría (idealmente) un navegador web, navegaría hacia un sitio de clima, encontraría la información y te la devolvería. Es la diferencia entre preguntar a un bibliotecario una pregunta cuya respuesta ya conocen, y pedirles que encuentren un libro, lean un capítulo específico y luego lo resuman para ti.
Para nuestro agente simple, utilizaremos algunas bibliotecas comunes de Python que hacen que todo este proceso sea mucho menos intimidante.
Las Herramientas que Necesitaremos
No necesitas una supercomputadora para esto. Solo tu configuración habitual de desarrollo. Esto es lo que vamos a usar:
- Python: Nuestro lenguaje de programación favorito. (Estoy usando 3.9, pero 3.8+ debería estar bien.)
requests: Una biblioteca fantástica para hacer solicitudes HTTP (es decir, obtener páginas web).BeautifulSoup(bs4): Este es nuestro mago de análisis web. Nos ayuda a navegar y extraer datos de HTML.- Una API de Modelo de Lenguaje Grande (LLM): Necesitaremos acceso a un LLM como los modelos GPT de OpenAI o Claude de Anthropic. Para este tutorial, asumiré que tienes una clave de API de OpenAI.
Primero lo primero, preparemos nuestro entorno. Si no tienes estas bibliotecas instaladas, abre tu terminal o línea de comandos y ejecuta:
pip install requests beautifulsoup4 openai
Y asegúrate de tener tu clave de API de OpenAI a mano. Necesitarás configurarla como una variable de entorno o pasarlo directamente en tu script. Para simplificar, lo mostraré directamente, pero para producción, las variables de entorno son siempre mejor.
Nuestra Misión: Encontrar un Artículo Específico en un Blog
Establezcamos un objetivo concreto y alcanzable. La misión de nuestro agente será:
- Ir a un blog específico (usaremos una URL de blog de prueba por ahora, o incluso agent101.net si te sientes aventurero).
- Encontrar un título de publicación de blog que contenga una palabra clave específica (por ejemplo, “principiante”).
- Extraer la URL de esa publicación del blog.
- Resumir el contenido de esa publicación del blog utilizando un LLM.
Este es un excelente punto de partida porque combina la interacción web con la comprensión de LLM. Es como pedirle a tu asistente digital que “encuentre el artículo más reciente sobre agentes de IA para principiantes en agent101.net y me diga de qué trata.”
Paso 1: Obteniendo la Página Web
El primer paso para cualquier agente de navegador es “ver” la página web. Usaremos la biblioteca requests para esto. Hagamos un script simple:
import requests
def fetch_webpage(url):
try:
response = requests.get(url)
response.raise_for_status() # Lanza un HTTPError para respuestas malas (4xx o 5xx)
return response.text
except requests.exceptions.RequestException as e:
print(f"Error al obtener la URL {url}: {e}")
return None
# Usaremos una URL de marcador de posición por ahora.
# ¡Reemplaza con una URL real de blog que tenga artículos si deseas probar!
blog_url = "https://www.example-blog.com/articles" # ¡Usa una URL real aquí!
html_content = fetch_webpage(blog_url)
if html_content:
print("Contenido de la página web obtenido con éxito.")
# Procesaremos este contenido en el siguiente paso
else:
print("Error al obtener la página web.")
Una nota rápida sobre URLs: Para que esto funcione, necesitarás una URL real de blog. Si no tienes una a mano, puedes usar un sitio como `http://quotes.toscrape.com/` para probar la extracción simple, pero para publicaciones de blogs, encuentra un blog real. Incluso este mismo sitio, agent101.net, podría funcionar si adaptas los selectores CSS más tarde!
Paso 2: Analizando el HTML y Encontrando Enlaces a Artículos
Ahora que tenemos el HTML sin procesar, es como tener un libro gigante sin índice. BeautifulSoup es nuestro indexador. Necesitamos decirle cómo encontrar los títulos de los artículos y sus enlaces correspondientes.
Esta es la parte más complicada porque la estructura HTML de cada sitio web es diferente. Necesitarás “inspeccionar” el elemento en el sitio web de destino. Haz clic derecho en un título de artículo en el blog que elegiste y selecciona “Inspeccionar” (o “Inspeccionar Elemento”). Busca patrones comunes como etiquetas <h2> para títulos, o etiquetas <a> para enlaces, a menudo anidadas dentro de un <div> con un nombre de clase específico.
Para demostración, asumamos una estructura común de blog donde los títulos de los artículos están en etiquetas <h2>, y sus enlaces están en una etiqueta <a> directamente dentro o precediéndolas.
from bs4 import BeautifulSoup
def find_article_links(html_content, keyword):
soup = BeautifulSoup(html_content, 'html.parser')
article_data = []
# Este es un ejemplo genérico. Tendrás que ajustar los selectores según el sitio web objetivo.
# Patrones comunes: div con clase 'article-item', h2 con clase 'article-title', etc.
# Supongamos que los artículos están en elementos 'div' con clase 'post'
# y los títulos están en etiquetas 'h2' dentro de esos divs, con un enlace dentro.
# Ejemplo: Busca todas las etiquetas que podrían contener títulos de artículos
# Luego verifica si están enlazados.
# Un enfoque más general podría ser encontrar contenedores primero:
article_containers = soup.find_all('div', class_='article-card') # ¡Ajusta esta clase!
if not article_containers:
# Recurso alternativo o intenta otro selector si el primero no funciona
article_containers = soup.find_all('article') # Otra etiqueta común
for container in article_containers:
title_tag = container.find('h2') # O 'h3', 'a', etc.
link_tag = container.find('a', href=True) # Encuentra el primer enlace dentro del contenedor
if title_tag and link_tag:
title = title_tag.get_text(strip=True)
href = link_tag['href']
# Asegúrate de que la URL sea absoluta si es relativa
if href.startswith('/'):
full_url = requests.compat.urljoin(blog_url, href)
else:
full_url = href
if keyword.lower() in title.lower():
article_data.append({'title': title, 'url': full_url})
return article_data
# ... (código previo de fetch_webpage) ...
if html_content:
search_keyword = "beginner" # ¿Qué artículo estamos buscando?
found_articles = find_article_links(html_content, search_keyword)
if found_articles:
print(f"\nArtículos encontrados que contienen '{search_keyword}':")
for article in found_articles:
print(f"- Título: {article['title']}\n URL: {article['url']}")
# Para este ejemplo, tomemos solo el primero encontrado
target_article = found_articles[0]
else:
print(f"\nNo se encontraron artículos que contengan '{search_keyword}'.")
target_article = None
else:
target_article = None
Personalización Crucial: Las líneas como soup.find_all('div', class_='article-card') y container.find('h2') son marcadores de posición. Debes adaptar esto según la estructura HTML real del blog que estás apuntando. Aquí es donde inspeccionar la página web se vuelve esencial. Mi mejor consejo es comenzar amplio (por ejemplo, `soup.find_all(‘a’)` para obtener todos los enlaces) y luego reducirlo con clases o IDs.
Paso 3: Extracción de Contenido del Artículo Objetivo
Una vez que tenemos la URL de nuestro artículo objetivo, necesitamos obtener su contenido y luego extraer el texto del cuerpo principal. Esto suele ser más fácil que analizar una página de índice, ya que la mayoría del contenido de artículos vive dentro de un <div> de contenido principal o una etiqueta <article>.
# ... (código previo) ...
import openai
import os
# Establece tu clave de API de OpenAI
# Mejor práctica: os.environ.get("OPENAI_API_KEY")
# Para este ejemplo, asignación directa:
openai.api_key = "YOUR_OPENAI_API_KEY" # ¡REEMPLAZA ESTO CON TU CLAVE REAL!
def extract_article_text(article_url):
article_html = fetch_webpage(article_url)
if not article_html:
return None
soup = BeautifulSoup(article_html, 'html.parser')
# Esto depende en gran medida de la estructura del sitio web.
# Patrones comunes: encontrar un div con clase 'entry-content', 'article-body', 'main-content'
# O simplemente las etiquetas dentro de la etiqueta del artículo principal.
# Intentemos encontrar áreas de contenido comunes.
content_div = soup.find('div', class_='entry-content') # Común para WordPress
if not content_div:
content_div = soup.find('article') # Otra buena etiqueta general
if not content_div:
# Como último recurso, simplemente obtener todas las etiquetas de párrafo
paragraphs = soup.find_all('p')
return "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
# Si encontramos un div/artículo de contenido específico, extraemos todo el texto del párrafo de él
paragraphs = content_div.find_all('p')
article_text = "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
return article_text
if target_article:
print(f"\nObteniendo contenido para: {target_article['title']}")
article_text_content = extract_article_text(target_article['url'])
if article_text_content:
# print(article_text_content[:500]) # Imprimir los primeros 500 caracteres para verificar
print("\nTexto del artículo extraído con éxito. Pasando a la resumición.")
else:
print("No se pudo extraer el texto del artículo.")
article_text_content = "" # Asegúrate de que no sea None para el siguiente paso
else:
article_text_content = ""
Una vez más, la línea soup.find('div', class_='entry-content') es tu objetivo principal para la personalización. ¡Usa el inspector de tu navegador!
Paso 4: Resumiendo con un LLM
Finalmente, ¡llega el momento que trae la inteligencia artificial! Alimentaremos el texto extraído a un LLM y le pediremos que lo resuma. Aquí es donde la “inteligencia” de nuestro agente realmente brilla.
# ... (código previo) ...
def summarize_text_with_llm(text):
if not text:
return "No se proporcionó texto para resumir."
# Truncar el texto si es demasiado largo para la ventana de contexto del modelo
# GPT-3.5-turbo tiene una ventana de contexto de 16k, pero es buena práctica mantenerlo razonable.
# Para un principiante, apuntar a 4000-8000 tokens es seguro.
# Aproximadamente 1 token = 4 caracteres para texto en inglés.
max_tokens_for_input = 12000 # Ajustar según la capacidad y consideraciones de costo de tu modelo
if len(text) > max_tokens_for_input * 4: # Estimación cruda de caracteres
text = text[:max_tokens_for_input * 4]
print(f"Advertencia: Texto truncado a ~{max_tokens_for_input} tokens para el procesamiento del LLM.")
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo", # O "gpt-4", "claude-3-opus-20240229", etc.
messages=[
{"role": "system", "content": "Eres un asistente útil que resume publicaciones de blog de manera concisa."},
{"role": "user", "content": f"Por favor, resume la siguiente publicación de blog para mí:\n\n{text}"}
],
temperature=0.7, # Controla la aleatoriedad. Más bajo para resúmenes más centrados.
max_tokens=500 # Max tokens para el resumen en sí
)
return response.choices[0].message.content.strip()
except openai.APIError as e:
print(f"Error de API de OpenAI: {e}")
return "Falló debido a un error de API."
except Exception as e:
print(f"Ocurrió un error inesperado durante la resumición: {e}")
return "Falló debido a un error inesperado."
if article_text_content:
print("\nSolicitando resumición LLM...")
summary = summarize_text_with_llm(article_text_content)
print("\n--- Resumen del Artículo ---")
print(summary)
else:
print("\nNo se puede resumir: No hay contenido de artículo disponible.")
¡Y ahí lo tienes! Un agente de IA básico pero funcional que puede navegar, extraer y resumir. Esto no es solo teoría; es un código tangible que puedes ejecutar.
Reuniéndolo Todo (Script Completo)
Aquí está el script completo para facilitar la copia y prueba. ¡Recuerda reemplazar las URLs de marcador de posición y tu clave de API de OpenAI!
import requests
from bs4 import BeautifulSoup
import openai
import os
# --- Configuración ---
# Reemplaza con la URL real de tu blog. ¡Asegúrate de que tenga artículos!
TARGET_BLOG_URL = "https://www.example-blog.com/articles"
SEARCH_KEYWORD = "beginner" # La palabra clave para encontrar en los títulos de los artículos
OPENAI_API_KEY = "YOUR_OPENAI_API_KEY" # ¡REEMPLAZA ESTO! O usa os.environ.get("OPENAI_API_KEY")
# Establecer la clave API de OpenAI
openai.api_key = OPENAI_API_KEY
# --- Funciones Auxiliares ---
def fetch_webpage(url):
"""Obtiene el contenido HTML de una URL dada."""
try:
response = requests.get(url, timeout=10) # Se agregó un tiempo de espera
response.raise_for_status()
return response.text
except requests.exceptions.RequestException as e:
print(f"Error al obtener la URL {url}: {e}")
return None
def find_article_links(html_content, base_url, keyword):
"""Analiza HTML para encontrar títulos de artículos y URLs que coincidan con una palabra clave."""
soup = BeautifulSoup(html_content, 'html.parser')
article_data = []
# --- PUNTO DE PERSONALIZACIÓN 1: Ajusta estos selectores para tu blog objetivo ---
# Inspecciona la página del blog para encontrar las etiquetas HTML y clases correctas para contenedores de artículos, títulos y enlaces.
article_containers = soup.find_all('div', class_='article-card') # Común para tarjetas/previsualizaciones de artículos
if not article_containers:
article_containers = soup.find_all('article') # Otro tag común para artículos individuales
if not article_containers:
print("Advertencia: No se pudieron encontrar las etiquetas de contenedor de artículo comunes. Intentando búsqueda más amplia.")
# Respaldo: buscar solo todos los enlaces que podrían ser enlaces de artículos
all_links = soup.find_all('a', href=True)
for link in all_links:
title_text = link.get_text(strip=True)
if keyword.lower() in title_text.lower():
href = link['href']
full_url = requests.compat.urljoin(base_url, href)
article_data.append({'title': title_text, 'url': full_url})
return article_data # Retornar temprano si solo se encontraron enlaces amplios
for container in article_containers:
# Buscar el título y el enlace dentro de cada contenedor
title_tag = container.find(['h2', 'h3', 'a']) # Los títulos a menudo están en h2/h3 o vinculados directamente
link_tag = container.find('a', href=True)
if title_tag and link_tag:
title = title_tag.get_text(strip=True)
href = link_tag['href']
# Construir la URL completa si es relativa
full_url = requests.compat.urljoin(base_url, href)
if keyword.lower() in title.lower():
article_data.append({'title': title, 'url': full_url})
return article_data
def extract_article_text(article_url):
"""Obtiene una página de artículo y extrae su contenido textual principal."""
article_html = fetch_webpage(article_url)
if not article_html:
return None
soup = BeautifulSoup(article_html, 'html.parser')
# --- PUNTO DE PERSONALIZACIÓN 2: Ajusta estos selectores para el contenido principal del artículo ---
# Busca el área de contenido principal del artículo (por ejemplo, div con clase 'entry-content', 'article-body')
content_area = soup.find('div', class_='entry-content')
if not content_area:
content_area = soup.find('article', class_='main-article-content') # Otro patrón común
if not content_area:
content_area = soup.find('div', id='content') # Otro ID común
if content_area:
paragraphs = content_area.find_all('p')
return "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
else:
# Respaldo: obtener todas las etiquetas de párrafo si no se encuentra un área de contenido específica
print(f"Advertencia: No se encontró un área de contenido específica para {article_url}. Extrayendo todos los párrafos.")
paragraphs = soup.find_all('p')
return "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
def summarize_text_with_llm(text):
"""Usa un LLM para el texto proporcionado."""
if not text or len(text.strip()) < 50: # Longitud mínima de texto para intentar resumir
return "No hay suficiente contenido para resumir."
# Truncamiento simple para mantenerse dentro de los límites típicos de la ventana de contexto
# Considera usar un contador de tokens adecuado para más precisión si es necesario
max_chars_for_llm = 40000 # Aproximadamente 10k tokens para GPT-3.5-turbo 16k contexto
if len(text) > max_chars_for_llm:
print(f"Advertencia: Texto truncado de {len(text)} a {max_chars_for_llm} caracteres para LLM.")
text = text[:max_chars_for_llm]
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo", # Puedes usar "gpt-4" si tienes acceso y deseas mejor calidad
messages=[
{"role": "system", "content": "Eres un asistente útil que resume publicaciones de blog de manera concisa y clara."},
{"role": "user", "content": f"Por favor, proporciona un resumen conciso de la siguiente publicación del blog, destacando los puntos clave:\n\n{text}"}
],
temperature=0.6, # Un poco menos aleatorio para resúmenes
max_tokens=600 # Máximo de tokens para el resumen generado
)
return response.choices[0].message.content.strip()
except openai.APIError as e:
print(f"Error de API de OpenAI: {e}")
return f"Falló debido a un error de API: {e}"
except Exception as e:
print(f"Ocurrió un error inesperado durante el resumen: {e}")
return f"Falló debido a un error inesperado: {e}"
# --- Lógica Principal del Agente ---
def run_browser_agent():
print(f"Iniciando el agente del navegador para {TARGET_BLOG_URL} para encontrar artículos sobre '{SEARCH_KEYWORD}'...")
# Paso 1: Obtener la página principal del blog
blog_html = fetch_webpage(TARGET_BLOG_URL)
if not blog_html:
print("El agente no pudo obtener la página principal del blog. Saliendo.")
return
# Paso 2: Encontrar artículos que coincidan con la palabra clave
found_articles = find_article_links(blog_html, TARGET_BLOG_URL, SEARCH_KEYWORD)
if not found_articles:
print(f"No se encontraron artículos que contengan '{SEARCH_KEYWORD}' en {TARGET_BLOG_URL}. Agente finalizado.")
return
print(f"\nSe encontraron {len(found_articles)} artículos potenciales que contienen '{SEARCH_KEYWORD}':")
for i, article in enumerate(found_articles):
print(f"{i+1}. Título: {article['title']}\n URL: {article['url']}")
# Para este ejemplo, procesemos el primer artículo encontrado
target_article = found_articles[0]
print(f"\nProcesando el primer artículo encontrado: '{target_article['title']}' en {target_article['url']}")
# Paso 3: Extraer contenido del artículo objetivo
article_content = extract_article_text(target_article['url'])
if not article_content:
print(f"El agente no pudo extraer contenido de {target_article['url']}. No se puede resumir.")
return
print(f"\nContenido del artículo extraído (primeros 200 caracteres): {article_content[:200]}...")
# Paso 4: Resumir el contenido usando LLM
print("\nSolicitando a LLM el artículo...")
summary = summarize_text_with_llm(article_content)
print("\n--- Informe Final del Agente ---")
print(f"Título del Artículo: {target_article['title']}")
print(f"URL del Artículo: {target_article['url']}")
print("\nResumen:")
print(summary)
print("\n--- Tarea del Agente Completada ---")
if __name__ == "__main__":
run_browser_agent()
Puntos Clave Accionables para Tu Propio Viaje de Agente
Este pequeño proyecto es solo la punta del iceberg, pero te muestra la mecánica central. Aquí’s lo que aprendí y lo que deberías tener en cuenta:
- Comienza Pequeño, Piensa Específico: No intentes construir Skynet el primer día. Elige una tarea muy estrecha y alcanzable para tu agente. “Encuentra una receta de lasaña vegana” es mejor que “cocina la cena por mí.”
- El Web Scraping es el Viejo Oeste: Cada sitio web es diferente. PASARÁS tiempo inspeccionando elementos en tu navegador para obtener los selectores CSS o XPath correctos. Este es el trabajo arduo, pero es esencial. Los sitios web cambian, así que tu agente puede necesitar ajustes ocasionales.
- El Manejo de Errores es Tu Amigo: Las cosas saldrán mal. Los sitios web estarán caídos, tu internet se caerá, las llamadas a la API fallarán. Agrega
try-exceptbloques para manejar estos problemas de manera elegante. - Los LLM son Inteligentes, pero Necesitan Orientación: La calidad de la salida de tu LLM depende en gran medida de tu solicitud. Sé claro, conciso y dile exactamente lo que esperas. Además, ten en cuenta los límites de la ventana de contexto y los costos.
- Esta es una Fundación: Hemos construido un agente de un solo turno. Los verdaderos agentes a menudo involucran múltiples pasos, lógica condicional (por ejemplo, “si encuentro esto, entonces haz aquello”) y memoria. Pero ahora tienes los bloques de construcción.
Mi esperanza es que ver este agente simple cobrar vida desmitifique un poco el concepto de “agente AI”. No es magia; es solo automatización inteligente construida sobre herramientas existentes. Ve adelante, experimenta y construye tus propios pequeños ayudantes digitales.
Feliz codificación,
Emma Walsh, agent101.net
Artículos Relacionados
- Cómo los Agentes de AI Dominar Múltiples Idiomas Sin Problemas
- Hice Que Mi Agente AI Fuera Útil (Así es Como)
- Construí un Agente AI en 2026: Mi Opinión Honesta
🕒 Published: