Ir al contenido principal

Tutorial de GLM-5-Turbo: crea un agente de navegador en tiempo real

Aprende a crear un agente de IA en el navegador que busque en la web, extraiga datos de vuelos en vivo y devuelva recomendaciones claras con GLM-5-Turbo.
Actualizado 17 sept 2026  · 9 min leer

Explora con IA

ChatGPTClaudePerplexity

GLM-5-Turbo está diseñado específicamente para flujos de trabajo de tipo agente, donde el modelo coordina ejecuciones en varios pasos. Está optimizado para comprender instrucciones complejas, invocar herramientas con fiabilidad y producir salidas estructuradas que puedan consumir otros sistemas. 

En este tutorial, vamos a crear un agente de navegador en tiempo real que navega por la web, extrae información relevante y genera un resumen claro para el usuario. Al terminar, tendrás una arquitectura reutilizable que podrás extender más allá de los vuelos a cualquier tarea real de búsqueda y extracción.

Puedes ver nuestro proyecto con GLM-5 Turbo en acción en el siguiente vídeo:

¿Qué es GLM-5-Turbo?

GLM-5-Turbo está pensado para admitir flujos de trabajo de agentes en escenarios reales. Ofrece varios modos de razonamiento, lo que permite equilibrar entre respuestas más rápidas o una reflexión más profunda según la tarea. 

Para aplicaciones interactivas, también admite salida en streaming, entregando las respuestas token a token para una experiencia de usuario más fluida.

Una de sus principales fortalezas es la invocación de funciones, que permite al modelo llamar a herramientas externas con fiabilidad. El modelo también admite salidas estructuradas como JSON, lo que facilita la integración con sistemas posteriores y capas de interfaz.

Para flujos de trabajo largos, GLM-5-Turbo incluye almacenamiento en caché de contexto, lo que ayuda a mantener el rendimiento en interacciones extensas sin volver a procesar la misma información una y otra vez. También admite la integración con sistemas externos mediante MCP (Model Context Protocol), permitiendo al modelo conectarse con herramientas y fuentes de datos adicionales.

Creado para flujos de trabajo con agentes

GLM-5-Turbo está profundamente optimizado para entornos tipo agente, especialmente en escenarios como OpenClaw, donde las tareas implican múltiples pasos, herramientas y cadenas de ejecución largas.

Esta optimización mejora varias áreas clave:

  • Fiabilidad al invocar herramientas: garantiza que el modelo llame de forma consistente a las funciones adecuadas.
  • Descomposición de instrucciones: permite dividir tareas complejas en pasos más pequeños.
  • Estabilidad en cadenas largas de ejecución: esencial para agentes de múltiples pasos.

En la práctica, GLM-5-Turbo es lo suficientemente rápido para sistemas interactivos y, a la vez, fiable en escenarios con múltiples pasos y llamadas a herramientas, justo lo que necesitamos para crear agentes de navegador.

Proyecto de ejemplo con GLM-5-Turbo: crea un agente de navegador en tiempo real

En este tutorial, crearemos una aplicación de Streamlit que funciona como un asistente ligero de viajes. El usuario introduce una consulta como «Encuentra los vuelos más baratos de SFO a JFK de viernes a lunes». El sistema hará lo siguiente:

  • Buscar en la web páginas de resultados de vuelos relevantes
  • Abrir e inspeccionar esas páginas
  • Extraer información útil como precio, duración y escalas
  • Resumir las mejores opciones en un formato claro y fácil de leer

A alto nivel, el sistema consta de cuatro componentes que trabajan en conjunto.

  • El primero es la capa LLM (llm_client.py), que envía solicitudes de chat-completion a Z.AI u OpenRouter. 
  • La segunda es la capa de herramientas de navegador (browser_tools.py), que expone acciones como buscar, abrir páginas y extraer contenido. 
  • La tercera es el bucle del agente (agent.py), que conecta el modelo y las herramientas en un flujo de trabajo de múltiples pasos. 
  • Por último, la interfaz de Streamlit (app.py) ofrece la capa de interacción y visualización.

Código fuente completo: https://github.com/AashiDutt/GLM5_TURBO

Paso 1: requisitos previos

Antes de crear la aplicación, asegúrate de tener lo siguiente:

  • Python 3.9+
  • Una clave de API de GLM-5-Turbo (Z.AI o OpenRouter)
  • Playwright instalado para la automatización del navegador

Instala las librerías necesarias:

pip install streamlit pandas python-dotenv requests playwright
playwright install chromium

El archivo app.py usa Streamlit, pandas, dotenv y módulos propios. Mientras que browser_tools.py utiliza requests y Playwright. Las llamadas a la API se realizan en llm_client.py con requests.

Ahora, ejecuta el siguiente comando para crear un archivo .env donde guardaremos la(s) clave(s) de API:

cp .env.example .env

Elige tu endpoint de API preferido y añade la clave de API:

.env
PROVIDER=zai
# Direct Z.AI API
ZAI_API_KEY=Your Z.AI API key
ZAI_BASE_URL=https://api.z.ai/api/paas/v4/chat/completions
ZAI_MODEL=glm-5-turbo
# OpenRouter
OPENROUTER_API_KEY=Your OpenRouter API key
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1/chat/completions
OPENROUTER_MODEL=z-ai/glm-5-turbo
# Browser
HEADLESS=true
MAX_STEPS=10

Del mismo modo, el navegador de Playwright se lanza desde este constructor en browser_tools.py:

class BrowserTools:
    def __init__(self, headless: bool = True) -> None:
        self.playwright = sync_playwright().start()
        self.browser = self.playwright.chromium.launch(headless=headless)
        self.context = self.browser.new_context(viewport={"width": 1440, "height": 1100})
        self.page = self.context.new_page()

Este bloque inicializa un entorno de navegador totalmente controlado con Playwright, que el agente utiliza para interactuar con la web. 

Arranca el motor de Playwright, lanza un navegador Chromium, crea una sesión aislada y abre una página nueva donde se realizan todas las acciones. 

Con esta configuración, el agente puede navegar por sitios reales, observar contenido en vivo y tomar decisiones en función de lo que ve.

Paso 2: configurar el cliente LLM

A continuación configuraremos nuestro LLMClient, que admite tanto Z.AI directo como OpenRouter con la misma interfaz. El endpoint específico del proveedor se selecciona en el constructor. Como los precios son comparables, puedes usar cualquiera de las dos APIs en este demo según tu preferencia.

Esta es la configuración clave:

class LLMClient:
    def __init__(
        self,
        provider: str,
        api_key: str,
        model: str,
        app_name: str = "GLM-5 Browser Agent Demo",
        app_url: str = "http://localhost:8501",
    ) -> None:
        self.provider = provider.lower().strip()
        self.api_key = api_key
        self.model = model
        if self.provider == "zai":
            self.url = os.getenv("ZAI_BASE_URL", "https://api.z.ai/api/paas/v4/chat/completions")
        elif self.provider == "openrouter":
            self.url = os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1/chat/completions")

Este inicializador configura el cliente LLM seleccionando el endpoint adecuado (Z.AI u OpenRouter) manteniendo una interfaz coherente. 

Almacena la clave de API, el modelo y la URL del endpoint, de modo que el resto del sistema pueda llamar al modelo sin preocuparse por diferencias entre proveedores.

Luego, create_chat_completion() construye el payload, adjunta herramientas cuando es necesario y analiza cualquier llamada a herramientas devuelta. 

payload: Dict[str, Any] = {
    "model": self.model,
    "messages": messages,
    "temperature": temperature,
    "max_tokens": max_tokens,
    "stream": False,
}
if tools:
    payload["tools"] = tools
    payload["tool_choice"] = "auto"

El payload anterior define la solicitud enviada al modelo, incluyendo el contexto de la conversación y los parámetros de generación. Al adjuntar tools y habilitar tool_choice="auto", el modelo puede decidir cuándo invocar funciones externas.

Paso 3: definir las herramientas de navegador

En agent.py definimos un esquema de herramientas que el modelo puede llamar. Incluye search_web, open_url, open_search_result, extract_page, extract_flight_cards, click, type_text y wait_for_text.

Aquí tienes un ejemplo de definición de herramienta:

{
    "type": "function",
    "function": {
        "name": "search_web",
        "description": "Search the public web for flight result pages or travel aggregators.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "max_results": {"type": "integer", "default": 8},
            },
            "required": ["query"],
        },
    },
}

Este esquema indica al modelo qué hace la herramienta, qué entradas espera y cómo llamarla. Al definir herramientas así, permitimos que el modelo decida dinámicamente cuándo buscar en la web en lugar de fijar el flujo por código.

En la parte de ejecución, esas herramientas están implementadas en browser_tools.py. Para este demo, usamos el endpoint HTML de DuckDuckGo para almacenar los enlaces de resultados actuales y navegar después.

def search_web(self, query: str, max_results: int = 8) -> Dict[str, Any]:
    url = f"https://html.duckduckgo.com/html/?q={quote_plus(query)}"
    headers = {"User-Agent": "Mozilla/5.0"}
    resp = requests.get(url, headers=headers, timeout=30)
    resp.raise_for_status()
    html = resp.text

La función search_web() anterior realiza una búsqueda web ligera obteniendo resultados HTML en bruto y analizándolos, evitando la necesidad de una API de búsqueda de pago. También almacena internamente los resultados para que el agente pueda navegar por ellos en pasos posteriores.

Ahora que hemos definido cómo puede interactuar el agente con la web, el siguiente paso es ver cómo usa estas herramientas para extraer información de vuelos mediante búsqueda web.

Paso 4: extracción de información de vuelos

Una vez que el modelo encuentra una fuente prometedora, necesita inspeccionarla. Podemos usar las herramientas del navegador (del paso 3) para extraer instantáneas de la página.

Paso 4.1: abrir y extraer páginas

Usaremos la herramienta open_url() para cargar una página web y devolver una instantánea con el título, la URL y el texto visible.

def open_url(self, url: str) -> Dict[str, Any]:
    self.page.goto(url, wait_until="domcontentloaded", timeout=45000)
    self.page.wait_for_timeout(1200)
    return self._page_snapshot()

Para una extracción más amplia, usaremos la herramienta extract_page(), que devuelve la instantánea y un conjunto de enlaces recopilados de la página actual.

def extract_page(self, max_chars: int = 7000, link_limit: int = 20) -> Dict[str, Any]:
    snapshot = self._page_snapshot(max_chars=max_chars)
    snapshot["links"] = self._collect_links(limit=link_limit)
    self.current_links = snapshot["links"]
    return snapshot

Esto es útil porque el modelo no siempre necesita hacer clic a ciegas. A veces puede inspeccionar primero el texto de la página actual y los enlaces disponibles, y luego decidir el siguiente paso.

Paso 4.2: extracción heurística de vuelos

Una parte clave del demo es la función extract_flight_cards(), que toma el texto visible de la página y usa patrones regex para detectar precios, horas, duraciones y escalas.

Aquí está la lógica central:

money_pattern = re.compile(r"(?:\$|USD\s?)(\d{2,5})")
time_pattern = re.compile(r"\b\d{1,2}:\d{2}\s?(?:AM|PM|am|pm)?\b")
duration_pattern = re.compile(r"\b\d{1,2}\s?h\s?\d{0,2}\s?m?\b|\b\d{1,2}\s?hr\s?\d{0,2}\s?min?\b", re.I)
stop_pattern = re.compile(r"nonstop|1 stop|2 stops|3 stops", re.I)

Estos patrones regex actúan como detectores de señales frecuentes relacionadas con vuelos. money_pattern captura precios en formatos como $299 o USD 299, mientras que time_pattern identifica horas de salida y llegada. 

Del mismo modo, duration_pattern extrae la duración del viaje, y stop_pattern detecta si un vuelo es directo o tiene escalas. Puedes crear patrones similares para cualquier caso de uso.

Una vez definidas estas señales, la función recorre pequeños fragmentos de texto y busca combinaciones significativas. Cuando encuentra una coincidencia, construye una tarjeta de vuelo estructurada:

card = {
    "snippet": joined[:350],
    "price_usd": int(price_match.group(1)),
    "times": times[:2],
    "duration": duration.group(0) if duration else None,
    "stops": stops.group(0) if stops else None,
}

El bloque anterior convierte texto en bruto en una representación estructurada de una opción de vuelo. Extrae el precio, limita las horas a las más relevantes y gestiona campos ausentes. Así conserva el contexto original, útil para depurar.

Con la extracción lista, el agente ya puede convertir contenido no estructurado en datos utilizables. A continuación veremos cómo estos datos fluyen por el bucle del agente para impulsar decisiones en varios pasos.

Paso 5: bucle del agente

El comportamiento real del agente está en la clase BrowserAgent (dentro de agent.py). Esta clase envía el prompt y el esquema de herramientas al modelo, comprueba si el modelo quiere llamar a una herramienta, ejecuta esa herramienta, añade el resultado de vuelta a la conversación y continúa hasta producir una respuesta final.

Este es el bucle principal:

for step in range(1, self.max_steps + 1):
    assistant = self.llm.create_chat_completion(
        messages=messages,
        tools=TOOLS,
        temperature=0.2,
        max_tokens=2200,
        enable_thinking=False,
    )
    if assistant.tool_calls:
        ...
        result = self.tool_runner(tc.name, tc.arguments)
        ...
        continue
    final_content = assistant.content.strip()
    if final_content:
        yield {"type": "final", "step": step, "content": final_content}
        return

Este bucle es el corazón del demo. 

En cada paso, el modelo recibe el estado actual de la conversación y decide qué hacer a continuación. Si devuelve llamadas a herramientas, se ejecutan con tool_runner y los resultados se reinyectan en el bucle para la siguiente iteración. 

Este proceso continúa hasta que el modelo genera una respuesta final en lugar de llamar a una herramienta.

El bucle también impone un límite máximo de pasos para evitar ejecuciones infinitas y mantener el proceso controlado y eficiente.

Paso 6: conectar las herramientas con la app

En app.py, asignamos los nombres de herramientas que pide el modelo a los métodos reales del navegador. Ese mapeo se implementa con la función make_tool_runner():

def make_tool_runner(browser: BrowserTools):
    def run(name: str, args: Dict[str, Any]):
        if name == "search_web":
            return browser.search_web(**args)
        if name == "open_url":
            return browser.open_url(**args)
        if name == "open_search_result":
            return browser.open_search_result(**args)
        if name == "extract_page":
            return browser.extract_page(**args)
        if name == "extract_flight_cards":
            return browser.extract_flight_cards(**args)

Esta función actúa como un despachador entre el modelo y el navegador. El modelo solo devuelve el nombre de la herramienta y los argumentos, y run() lo traduce en una llamada al método correspondiente de BrowserTools. 

Ahora que el agente puede llamar y ejecutar herramientas, el siguiente paso es construir la interfaz de usuario que conecte todas las piezas.

Paso 7: construir la interfaz en Streamlit

La interfaz de Streamlit convierte el agente de navegador en una aplicación interactiva. Recoge los detalles del viaje, lanza el agente, muestra los pasos intermedios en streaming y, por último, presenta un resumen junto con una tabla estructurada con las mejores opciones de vuelo. Todo este flujo de UI está implementado en app.py.

st.set_page_config(page_title="GLM-5 Browser Agent", page_icon="", layout="wide")
st.title("GLM-5-Turbo Real-Time Browser Agent")
st.subheader("Trip Request")
origin = st.text_input("Origin", value="SFO")
destination = st.text_input("Destination", value="JFK")
depart_date = st.date_input("Depart date")
return_date = st.date_input("Return date")
adults = st.number_input("Adults", min_value=1, max_value=9, value=1)
natural_request = st.text_area(
    "Optional natural language request",
    value="Find the cheapest reasonable flight options and summarize the best ones.",
    height=120,
)
user_query = (
    f"Find flight options from {origin} to {destination} for {adults} adult(s). "
    f"Departure date: {depart_date}. Return date: {return_date}. "
    f"User preference: {natural_request}"
)
run = st.button("Run browser agent", type="primary", use_container_width=True)
if run:
    if not api_key:
        st.error("Please enter an API key in the sidebar.")
        st.stop()
    llm = LLMClient(provider=provider, api_key=api_key, model=model)
    event_box = st.container(border=True)
    result_box = st.container(border=True)
    all_events = []
    with managed_browser(headless=headless) as browser:
        agent = BrowserAgent(llm=llm, tool_runner=make_tool_runner(browser), max_steps=max_steps)
for event in agent.stream(user_query):
    all_events.append(event)
    with event_box:
        if event["type"] == "status":
            st.write(f"**Step {event['step']}** — {event['content']}")
        elif event["type"] == "tool_call":
            with st.expander(f"Step {event['step']} • Tool call: {event['tool']}", expanded=False):
                st.json(event["arguments"])
        elif event["type"] == "tool_result":
            with st.expander(f"Step {event['step']} • Tool result: {event['tool']}", expanded=False):
                st.json(event["result"])
        elif event["type"] == "final":
            st.success("Agent finished.")
final_event = next((e for e in reversed(all_events) if e["type"] == "final"), None)
final_text = final_event["content"] if final_event else ""
with result_box:
    st.subheader("Final Answer")
    parsed = parse_final_json(final_text)
    if parsed:
        st.markdown(render_final_markdown(parsed))
        options_df = build_options_dataframe(parsed)
        if not options_df.empty:
            st.subheader("Top options table")
            st.dataframe(
                options_df,
                use_container_width=True,
                column_config={
                    "Link": st.column_config.LinkColumn("Link", display_text="Open source"),
                },
            )
    else:
        st.markdown(final_text)

Así es como la interfaz de Streamlit une todos los componentes:

  • Las llamadas st.set_page_config() y st.title() definen la maquetación general y el encabezado de la app, mientras que los widgets de entrada como st.text_input(), st.date_input(), st.number_input() y st.text_area() recogen los detalles del viaje. 
  • Cuando el usuario hace clic en el botón Run browser agent, la app primero comprueba si hay una clave de API, luego inicializa LLMClient. Crea contenedores separados para los registros intermedios del agente y los resultados finales, y abre una sesión de navegador Playwright gestionada. Dentro de esa sesión, BrowserAgent empieza a emitir eventos en streaming, y la UI muestra cada actualización de estado, llamada a herramienta y resultado de herramienta.
  • Cuando el agente termina, la app extrae el evento final, analiza el JSON devuelto y lo renderiza. El resumen se muestra con render_final_markdown(), mientras que build_options_dataframe() convierte las opciones de vuelo estructuradas en una tabla con enlaces a las fuentes.

Con este paso listo, puedes guardar todo como app.py y lanzar la app con:

streamlit run app.py

Este proyecto funciona bien como demo real porque usa resultados de búsqueda en el navegador y llamadas a herramientas reales. Aun así, sigue siendo un prototipo. Los sitios de vuelos son dinámicos, las páginas pueden tener mucho ruido y algunas plataformas usan protecciones anti-bots. 

La lógica de extracción actual es intencionadamente heurística, lo que mantiene el sistema simple y fácil de entender.

Y, sobre todo, esta configuración no se limita a vuelos. Con unos pocos ajustes —principalmente actualizar los prompts y la lógica de extracción— puedes reutilizar la misma arquitectura para crear agentes de compras, investigación, agregación de noticias o cualquier tarea que requiera navegar y resumir información en vivo.

Conclusión

En este tutorial, construimos un agente de navegador en tiempo real con GLM-5-Turbo, Playwright y Streamlit. El modelo actúa como planificador, las herramientas de navegador como capa de ejecución y la UI transforma los resultados estructurados en un formato amigable.

La idea clave es que el valor no proviene solo del modelo. Nace de combinar el modelo con una capa de herramientas controlada y un bucle de agente. 

Hay varias formas de extender este proyecto: mejorar la extracción con parsers específicos por sitio, ampliar el sistema a hoteles o itinerarios, o integrar preferencias del usuario como vuelos directos o aerolíneas preferidas. Con esto, puedes convertir este demo en una extensión de navegador completa.

Preguntas frecuentes sobre GLM-5-Turbo

¿Necesito una GPU para ejecutar este proyecto con GLM-5-Turbo?

No. No necesitas una GPU para este proyecto porque el modelo se accede vía API (Z.AI u OpenRouter). Todo el cómputo pesado ocurre en la infraestructura del proveedor; tu máquina local solo gestiona la UI y la automatización del navegador.

¿Por qué usar un agente de navegador en lugar de una API de vuelos?

Las APIs son más fiables y estructuradas, pero se limitan a datos predefinidos y a menudo requieren pago. Un agente de navegador, en cambio, puede:

  • explorar múltiples fuentes de forma dinámica
  • adaptarse a consultas diferentes
  • funcionar sin APIs integradas previamente

Sin embargo, para producción, lo ideal suele ser un enfoque híbrido (agente y APIs).

¿Por qué el agente a veces falla en ciertos sitios web?

Muchos sitios de viajes utilizan:

  • renderizado dinámico con JavaScript
  • protecciones anti-bots
  • diseños que cambian con frecuencia

Como este demo se basa en automatización genérica del navegador y extracción heurística, puede que no funcione de forma consistente en todos los sitios.

¿Por qué evitamos devolver JSON como salida final?

JSON es útil internamente para depurar, procesar de forma estructurada, clasificar y filtrar. Pero los usuarios finales prefieren resúmenes legibles. Por eso el sistema convierte las salidas estructuradas en un formato fácil de entender.


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Soy experta Google Developers en ML (Gen AI), triple experta en Kaggle y embajadora de Women Techmakers, con más de tres años de experiencia en el sector tecnológico. Cofundé una startup de salud en 2020 y actualmente curso un máster en informática en Georgia Tech, con especialización en aprendizaje automático.

Temas
Inteligencia Artificial
Agentes de IA

Cursos destacados de DataCamp

Curso

Sistemas multiagente con LangGraph

2 h 45 min
8.9K
Crea potentes sistemas multiagente aplicando patrones de diseño agentico emergentes en el marco LangGraph.
Ver detallesRight Arrow
Empezar Curso
Ver másRight Arrow
Relacionado
An AI juggles tasks

blog

Cinco proyectos que puedes crear con modelos de IA generativa (con ejemplos)

Aprende a utilizar modelos de IA generativa para crear un editor de imágenes, un chatbot similar a ChatGPT con pocos recursos y una aplicación clasificadora de aprobación de préstamos y a automatizar interacciones PDF y un asistente de voz con GPT.
Abid Ali Awan's photo

Abid Ali Awan

10 min

Tutorial

Tutorial FLAN-T5: Guía y puesta a punto

Una guía completa para afinar un modelo FLAN-T5 para una tarea de respuesta a preguntas utilizando la biblioteca de transformadores, y ejecutando la inferencia optmizada en un escenario del mundo real.
Zoumana Keita 's photo

Zoumana Keita

15 min

Tutorial

Guía para principiantes sobre la ingeniería de avisos ChatGPT

Descubra cómo conseguir que ChatGPT le proporcione los resultados que desea dándole las entradas que necesita.
Matt Crabtree's photo

Matt Crabtree

6 min

Tutorial

Visión GPT-4: Guía completa para principiantes

Este tutorial le presentará todo lo que necesita saber sobre GPT-4 Vision, desde cómo acceder a él hasta ejemplos prácticos del mundo real y sus limitaciones.
Arunn Thevapalan's photo

Arunn Thevapalan

12 min

Tutorial

Cómo ejecutar Stable Diffusion:

Explora la IA generativa con nuestro tutorial introductorio sobre Stable Diffusion. Aprende a ejecutar el modelo de aprendizaje profundo en línea y localmente para generar imágenes detalladas.
Kurtis Pykes 's photo

Kurtis Pykes

7 min

Tutorial

Construir agentes LangChain para automatizar tareas en Python

Un tutorial completo sobre la construcción de agentes LangChain multiherramienta para automatizar tareas en Python utilizando LLMs y modelos de chat utilizando OpenAI.
Ver MásVer Más