Ir al contenido principal

Guía de uso del ordenador Gemini 2.5 con proyecto de demostración: Crear un agente de búsqueda de empleo

Aprende a utilizar Gemini 2.5 Computer Use para crear un agente de búsqueda de empleo basado en inteligencia artificial. Este tutorial práctico te guía a través de la automatización del navegador con Playwright y Streamlit, sin necesidad de API.
Actualizado 11 oct 2025  · 8 min de lectura

El equipo de Gemini ha presentado recientemente presentó Gemini 2.5 Computer Use, un modelo especializado que puede ver una pantalla en directo y actuar en ella haciendo clic, escribiendo, desplazándose y navegando por la web como un operador humano.

En esta guía, omitiremos los puntos de referencia abstractos y crearemos algo práctico. Una aplicación Streamlit que utiliza Computer Use para controlar un navegador real, buscar ofertas de empleo en Google, aplicar un filtro y exportar los resultados a CSV sin necesidad de API de búsqueda de terceros.

En este tutorial, aprenderás a:

  • Configura el bucle de uso del ordenador Gemini 2.5.
  • Ejecuta el agente con Playwright (Chromium), utilizando una lista de permitidos y la confirmación humana opcional para los pasos arriesgados.
  • Crea una interfaz de usuario Streamlit con registros de acciones en directo y capturas de pantalla después de cada turno.
  • Recopila los resultados orgánicos y descarga un archivo CSV.

Al final, tendrás un agente de búsqueda de empleo que seleccionará ofertas de trabajo adecuadas para ti.

Imagen de demostración

Si deseas obtener más información sobre Gemini 2.5, te recomiendo que consultes nuestro tutorial de Gemini 2.5 Pro, que cubre características, pruebas, acceso, benchmarks y más.

¿Qué es el uso informático Gemini 2.5?

Gemini 2.5 Computer Use es un modelo y una herramienta especializados (vista previa) de la API de Gemini que te permite crear agentes de control de navegador. En lugar de llamar a API específicas del sitio, el modelo funciona a partir de capturas de pantalla. Puede ver lo que hay en la página y luego actuar emitiendo acciones de interfaz de usuario como navigate, click_at, scroll_document, etc.

El código del cliente recibe esas acciones propuestas, las ejecuta y envía una nueva combinación de captura de pantalla y URL para que el modelo pueda decidir el siguiente paso. 

Cómo funciona el ciclo de uso del ordenador

bucle de agente

Fuente: Documentación de Gemini

Así es como funciona el bucle del agente anterior de principio a fin:

  1. Enviar una solicitud: Comienza enviando una solicitud que incluya la herramienta Uso del ordenador, una indicación del objetivo y una captura de pantalla inicial de la interfaz de usuario.
  2. Obtener una respuesta: El modelo responde con una narración y una o varias llamadas a funciones (como click_at o type_text_at) y una decisión de seguridad.
  3. Ejecución: A continuación, ejecuta las acciones propuestas, es decir, si la acción está marcada como regular/permitida, podemos ejecutarla; si requiere confirmación, se le solicita al usuario y solo se procede con su aprobación explícita.
  4. Estado de retorno: Después de ejecutar la acción, devolvemos el estado al modelo enviando un objeto « FunctionResponse » que contiene una captura de pantalla nueva y la URL actual.
  5. Repite: Repetimos este ciclo « see–decide–act–observe » hasta que se complete la tarea, se produzca un error o el usuario o el modelo decidan detenerlo.

Crea un agente de búsqueda de empleo con Gemini 2.5 Computer Use y Streamlit.

En esta sección, crearemos un agente de búsqueda de trabajos impulsado por Streamlitque maneja un navegador real usando Gemini 2.5 Computer Use y Playwright, y luego exporta los resultados a CSV.

Así es como funciona:

  • El usuario introduce una lista de palabras clave relacionadas con el trabajo en la barra lateral de Streamlit.
  • El agente le indica a Gemini que abra Google, ejecute cada consulta y aplique un filtro de tiempo.
  • El agente se detiene en la primera página de resultados orgánicos y muestra capturas de pantalla en directo junto con registros de acciones en cada turno.
  • A continuación, extrae los títulos, enlaces y fragmentos de la página 1 y los agrega a todas las palabras clave.
  • Por último, muestra una tabla con los resultados junto con un archivo CSV descargable.

En segundo plano, la aplicación aplica una lista de dominios permitidos para garantizar una navegación segura, admite la confirmación humana opcional para pasos arriesgados y utiliza el bucle de uso del ordenador para facilitar la depuración.

Antes de empezar: 

Algunos casos de uso responsable y políticas del sitio incluyen:

  • Ámbito de la automatización: Esta demostración solo automatiza la búsqueda en Google (incluida en la lista de permitidos) y recopila los resultados orgánicos de la página 1.
  • Respetar las condiciones del sitio: No automatices sitios con condiciones de servicio restrictivas y nunca intentes eludir los CAPTCHA. Si aparece un CAPTCHA, complétalo manualmente y deja que el modelo continúe.
  • Seguridad con intervención humana: Si Gemini propone una acción arriesgada (como una interacción CAPTCHA o compras), la aplicación se detiene de forma predeterminada. Solo se continuará si habilitas explícitamente la confirmación automática.

Paso 1: Requisitos previos

  • Python 3.10+
  • Una clave API de Gemini de Google AI Studio
  • macOS / Windows / Linux con Playwright Chromium instalado

Paso 1.1: Importaciones 

En primer lugar, asegúrate de tener instaladas las siguientes importaciones:

python -m venv .venv && source .venv/bin/activate
pip install streamlit google-genai playwright python-dotenv
playwright install chromium

Los comandos anteriores configuran un entorno virtual e instalan todas las dependencias básicas necesarias para compilar la aplicación, a saber Streamlit para la interfaz de usuario, google-genai para llamar a la API de Gemini y Playwright para la automatización del navegador y python-dotenv para cargar variables de entorno y Chromium para Playwright.

Paso 1.2: Configuración de la clave API de Gemini 

Ahora que ya tenemos las dependencias instaladas, lo siguiente es configurar la clave API de Gemini desde AI Studio. 

  • Inicia sesión en AI Studio y busca «Obtener clave API».
  • Haz clic en Crear clave API
  • Selecciona un proyecto de Google Cloud existente o crea uno con la facturación habilitada.

Crear clave API

  • Simplemente haz clic en enlace «Añadir una cuenta de facturación» y añade los datos de tu tarjeta para verificarla.
  • Por último, copia la clave API de AI Studio y guárdala.

Ahora, crea un archivo .env en la carpeta de tu proyecto y añade ahí tu clave API:

GOOGLE_API_KEY=YOUR_REAL_KEY
ALLOWED_HOSTS=google.com

El paquete python-dotenv cargará GOOGLE_API_KEY en tiempo de ejecución para que la aplicación pueda llamar al modelo Gemini 2.5 Computer Use.

Paso 2: Configuración del modelo

Ahora, preparémonos para el tiempo de ejecución, que incluye importaciones, constantes, ID de modelo y autenticación. Esto garantiza que la aplicación se comunique con Gemini y que la navegación se mantenga de forma segura dentro de un dominio incluido en la lista de permitidos.

import os, io, time, csv, base64, urllib.parse
from typing import List, Dict, Tuple
import streamlit as st
from dotenv import load_dotenv
from playwright.sync_api import sync_playwright
from google import genai
from google.genai import types
from google.genai.types import Content, Part
W, H = 1440, 900
MODEL = "gemini-2.5-computer-use-preview-10-2025"  
load_dotenv()
API_KEY = os.getenv("GOOGLE_API_KEY")
if not API_KEY:
    st.stop()
ALLOWED_HOSTS = {h.strip().lower() for h in os.getenv("ALLOWED_HOSTS", "google.com").split(",") if h.strip()}
client = genai.Client(api_key=API_KEY)

Empezamos importando las bibliotecas principales, como Streamlit, Playwright, google-genai y pequeñas utilidades (dotenv, os, io, csv, time, urllib) para la configuración y la E/S.

Una ventana gráfica fija (W, H = 1440 × 900) mantiene la coherencia de las capturas de pantalla y la asignación de coordenadas para el uso del ordenador, y el modelo de vista previa se establece en gemini-2.5-computer-use-preview-10-2025

La clave API se carga con load_dotenv() y os.getenv("GOOGLE_API_KEY"). La navegación se configura mediante una lista de permitidos (por defecto, google.com) por motivos de seguridad. 

Por último, genai.Client(api_key=API_KEY) inicializa el SDK de Gemini para que la aplicación pueda ejecutar el bucle del agente y mostrar los resultados en Streamlit.

Paso 3: Creación de funciones auxiliares

Antes de pasar al núcleo de la aplicación, necesitamos configurar algunas funciones auxiliares. Ayudáis con las comprobaciones de seguridad, coordináis la conversión, ejecutáis acciones para las llamadas de función de Gemini, extraéis datos de SERP y exportáis los resultados en formato CSV. 

Paso 3.1: Seguridad y ejecución de acciones

Empezamos añadiendo un asistente de backend que permite un control seguro del navegador. Esto incluye una función de verificación de la lista de dominios permitidos para restringir la navegación, una función de conversión de coordenadas para asignar la parilla 0-999 del modelo a la ventana gráfica fija y una función de distribución de acciones que interpreta las llamadas a la función Computer Use y las ejecuta a través de Playwright.

def host_allowed(url: str) -> bool:
    try:
        netloc = urllib.parse.urlparse(url).netloc.lower()
        return any(netloc.endswith(allowed) for allowed in ALLOWED_HOSTS)
    except Exception:
        return False
def denorm(v: int, size: int) -> int:
    return int(v/1000*size)
def exec_calls(candidate, page, viewport, *, approve_all=False) -> List[Tuple[str, Dict]]:
    W, H = viewport
    results = []
    for part in candidate.content.parts:
        fc = getattr(part, "function_call", None)
        if not fc:
            continue
        name, args = fc.name, (fc.args or {})
        sd = args.get("safety_decision")
        if sd and sd.get("decision") == "require_confirmation":
            reason = sd.get("explanation", "Model flagged a risky action.")
            log_box.warning(f"[SAFETY requires confirmation] {reason}")
            if not approve_all:
                st.stop()
            results.append((name, {"safety_acknowledgement": "true"}))
        if name == "navigate":
            target = args.get("url", "")
            if target and not host_allowed(target):
                log_box.error(f"[BLOCKED] Non-allowlisted host: {target}")
                results.append((name, {"error": "blocked_by_allowlist"}))
                continue
        try:
            if name == "open_web_browser":
                pass
            elif name == "navigate":
                page.goto(args["url"], timeout=30000)
            elif name == "search":
                page.goto("https://www.google.com", timeout=30000)
            elif name == "click_at":
                page.mouse.click(denorm(args["x"], W), denorm(args["y"], H))
            elif name == "hover_at":
                page.mouse.move(denorm(args["x"], W), denorm(args["y"], H))
            elif name == "type_text_at":
                x, y = denorm(args["x"], W), denorm(args["y"], H)
                page.mouse.click(x, y)
                if args.get("clear_before_typing", True):
                    page.keyboard.press("Meta+A"); page.keyboard.press("Backspace")
                page.keyboard.type(args["text"])
                if args.get("press_enter", True):
                    page.keyboard.press("Enter")
            elif name == "scroll_document":
                page.mouse.wheel(0, 800 if args["direction"] == "down" else -800)
            elif name == "key_combination":
                page.keyboard.press(args["keys"])
            page.wait_for_load_state("networkidle", timeout=10000)
            results.append((name, {}))
            time.sleep(0.6)
        except Exception as e:
            results.append((name, {"error": str(e)}))
    return results

Veamos cómo encaja cada función en el proceso:

  • host_allowed() función: Esta función analiza la URL de destino, normaliza el nombre de host a minúsculas y comprueba si termina con algún dominio de ALLOWED_HOSTS incluido en el archivo .env
  • denorm() función: Esta utilidad convierte las coordenadas del puntero en unidades de píxeles reales para la ventana gráfica actual. Es fundamental para realizar clics, desplazamientos y escrituras precisos, especialmente cuando cambias el tamaño de la pantalla o trabajas sin monitor.
  • exec_calls() función: Por último, la función execute calls analiza la respuesta del modelo en busca de llamadas a funciones y envía cada una de ellas a Playwright. Bloquea las llamadas de navegación a hosts que no están en la lista de permitidos, ejecuta las acciones compatibles, espera a que la red esté inactiva para estabilizar la página y registra el resultado de cada acción (incluidos los errores). Estos resultados se utilizan en el siguiente paso para construir un FunctionResponses encia con el modelo.

En conjunto, estas funciones hacen cumplir los límites, mantienen la precisión de las operaciones matemáticas con punteros y registran resultados granulares.

Paso 3.2: Comentarios posteriores a la acción y recopilación de datos

Después de ejecutar acciones, el agente necesita retroalimentación para decidir el siguiente paso y devolver resultados útiles. Este paso proporciona tres funciones auxiliares, que son las siguientes:

def fr_from(page, results):
    shot = page.screenshot(type="png")
    url = page.url
    frs = []
    for name, result in results:
        frs.append(types.FunctionResponse(
            name=name,
            response={"url": url, **result},
            parts=[types.FunctionResponsePart(
                inline_data=types.FunctionResponseBlob(mime_type="image/png", data=shot)
            )]
        ))
    return frs, shot
def scrape_google_serp(page, max_items=10):
    items = []
    anchors = page.locator('div#search a:has(h3)')
    count = min(anchors.count(), max_items)
    for i in range(count):
        a = anchors.nth(i)
        title = a.locator('h3').inner_text()
        link = a.get_attribute('href')
        snippet = ""
        snips = page.locator('div#search .VwiC3b')
        if snips.count() > i:
            snippet = snips.nth(i).inner_text()
        items.append({"title": title, "link": link, "snippet": snippet})
    return items
def to_csv_download(rows: List[Dict], name="results.csv"):
    if not rows:
        return None
    out = io.StringIO()
    writer = csv.DictWriter(out, fieldnames=["keyword", "title", "link", "snippet"])
    writer.writeheader(); writer.writerows(rows)
    b = out.getvalue().encode("utf-8")
    href = f"data:text/csv;base64,{base64.b64encode(b).decode()}"
    st.download_button("Download CSV", data=b, file_name=name, mime="text/csv")

Veamos cómo funciona cada función auxiliar post-action:

  • fr_from() función: Esto captura una nueva captura de pantalla PNG y la URL actual, y luego crea un FunctionResponse para cada acción ejecutada. Esto incorpora un contexto visual para la siguiente ronda de inferencias y conserva un registro auditable de lo que ha sucedido.
  • scrape_google_serp() función: A continuación, extraemos títulos, enlaces y fragmentos de la primera página de resultados de Google utilizando selectores resilientes como a:has(h3) para títulos/enlaces y .VwiC3b para fragmentos. El rascador captura los resultados en max_items y devuelve filas limpias y estructuradas, listas para su análisis o exportación.
  • to_csv_download() función: Esta función genera un CSV en memoria con encabezados coherentes y lo expone a través de la función « download_button() » (Generar CSV) de Streamlit.

Ahora que ya tenemos todas las funciones auxiliares en su sitio, podemos crear una aplicación Streamlit en torno a ellas.

Paso 4: Creación de la aplicación Streamlit

Este paso conecta la interfaz de usuario de Streamlit con el bucle del agente de uso del ordenador. Renderiza la interfaz, gestiona la forma en que el usuario interactúa con el agente y garantiza que el resultado se ajuste a tus necesidades.

st.set_page_config(page_title="Jobs Search with Gemini Computer Use", layout="wide")
st.title("Jobs Search with Gemini 2.5 Computer Use")
with st.sidebar:
    st.markdown("**Search inputs**")
    default_kw = "data scientist remote\nml engineer \ngenai research india"
    kw_text = st.text_area("Job keywords", value=default_kw, height=120)
    query_mode = st.selectbox("Query style", ["<kw> jobs", 'site:linkedin.com/jobs "<kw>"'], index=0)
    use_past_week = st.checkbox('Ask agent to set "Past week" filter', value=True)
    turns = st.slider("Max agent turns per keyword", 3, 12, 6)
    auto_confirm = st.checkbox("Auto-approve risky actions", value=False, help="If model requests confirmation (e.g., CAPTCHA), auto-approve instead of pausing.")
    run_btn = st.button("Run search")
log_col, shot_col = st.columns([0.45, 0.55])
log_box = log_col.container(height=520)
shot_box = shot_col.container(height=520)
table_box = st.container()
if run_btn:
    keywords = [k.strip() for k in kw_text.splitlines() if k.strip()]
    all_rows = []
    log_box.write("Initializing browser...")
    pw = sync_playwright().start()
    browser = pw.chromium.launch(headless=False)
    ctx = browser.new_context(viewport={"width": W, "height": H})
    page = ctx.new_page()
    try:
        for kw in keywords:
            log_box.subheader(f"▶ {kw}")
            page.goto("https://www.google.com", timeout=30000)
            initial_shot = page.screenshot(type="png")
            base_query = f'{kw} jobs' if query_mode == "<kw> jobs" else f'site:linkedin.com/jobs "{kw}"'
            goal = (
                f'Search Google for "{base_query}". '
                f'{"Open Tools and set time filter to Past week. " if use_past_week else ""}'
                'Stop when first-page results are fully visible; do NOT open the Jobs panel.'
            )
            contents = [Content(role="user", parts=[Part(text=goal), Part.from_bytes(data=initial_shot, mime_type="image/png")])]
            cfg = types.GenerateContentConfig(
                tools=[types.Tool(computer_use=types.ComputerUse(
                    environment=types.Environment.ENVIRONMENT_BROWSER
                ))]
            )
            for turn in range(turns):
                log_box.caption(f"Turn {turn+1}: thinking…")
                resp = client.models.generate_content(model=MODEL, contents=contents, config=cfg)
                cand = resp.candidates[0]
                contents.append(cand.content)
                narr = " ".join([p.text for p in cand.content.parts if getattr(p, "text", None)])
                if narr:
                    log_box.info(narr[:400])
                fcs = [p.function_call for p in cand.content.parts if getattr(p, "function_call", None)]
                if not fcs:
                    log_box.success("Agent stopped proposing actions.")
                    break
                for fc in fcs:
                    log_box.write(f"→ {fc.name} {fc.args or {}}")
                results = exec_calls(cand, page, (W, H), approve_all=auto_confirm)
                frs, shot = fr_from(page, results)
                contents.append(Content(role="user", parts=[Part(function_response=fr) for fr in frs]))
                shot_box.image(shot, caption=page.url, width='stretch')
            rows = scrape_google_serp(page)
            for r in rows:
                r["keyword"] = kw
            all_rows.extend(rows)
            log_box.success(f"{kw}: collected {len(rows)} results")
        if all_rows:
            table_box.dataframe(all_rows, width='stretch')
            to_csv_download(all_rows, name="jobs_google_results.csv")
        else:
            st.warning("No rows collected. Try fewer keywords or fewer turns.")
    finally:
        browser.close()
        pw.stop()

Veamos en detalle cómo funciona este proceso:

  • La aplicación comienza configurando la página con un diseño amplio y estableciendo un título claro para que los usuarios sepan inmediatamente qué hace la herramienta. 
  • En la barra lateral, los usuarios proporcionan palabras clave relacionadas con el trabajo, eligen el estilo de consulta, un filtro de tiempo, el número máximo de turnos del agente por palabra clave y activan o desactivan la confirmación automática para acciones de riesgo. A continuación, el botón« » (Ejecutar búsqueda) activa el flujo de trabajo.
  • El área principal se divide en dos columnas, que incluyen un contenedor «registro» a la izquierda que transmite actualizaciones de texto y un contenedor «captura de pantalla» a la derecha que muestra imágenes en directo de cada turno. Debajo hay un contenedor separado para la tabla de resultados finales.
  • Cuando el usuario hace clic en Ejecutar búsqueda, la interfaz escribe mensajes de progreso en el panel de registro, actualiza el panel de capturas de pantalla a medida que hay nuevas imágenes disponibles y, tras procesar todas las palabras clave, muestra los resultados agregados en forma de DataFrame.

Para ejecutar esta aplicación, simplemente ejecuta el siguiente comando bash en tu terminal:

python -m streamlit run app.py

Consejo: Inicia siempre Streamlit desde tu venv para evitar problemas de «ModuleNotFoundError».

Conclusión

Este tutorial muestra cómo crear un agente visual de búsqueda de empleo utilizando Gemini 2.5 Computer Use para controlar un navegador, aplicar filtros y exportar resultados utilizando una interfaz de usuario Streamlit. 

Desde aquí, puedes ampliar el flujo de trabajo añadiendo paginación para recorrer más páginas, ampliar las capacidades del rastreador con fechas de publicación, ubicaciones y nombres de empresas, o incluir en la lista blanca una bolsa de trabajo sandbox. 

Si deseas obtener más información sobre el uso de ordenadores y la IA agencial, no te pierdas el curso curso Introducción a los agentes de IA.


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Soy una Google Developers Expert en ML(Gen AI), una Kaggle 3x Expert y una Women Techmakers Ambassador con más de 3 años de experiencia en tecnología. Cofundé una startup de tecnología sanitaria en 2020 y estoy cursando un máster en informática en Georgia Tech, especializándome en aprendizaje automático.

Temas

Los mejores cursos de DataCamp

Curso

Building AI Agents with Google ADK

1 h
3.3K
Build a customer-support assistant step-by-step with Google’s Agent Development Kit (ADK).
Ver detallesRight Arrow
Comienza el 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 de DeepSeek-Coder-V2: Ejemplos, instalación, puntos de referencia

DeepSeek-Coder-V2 es un modelo de lenguaje de código de código abierto que rivaliza con el rendimiento de GPT-4, Gemini 1.5 Pro, Claude 3 Opus, Llama 3 70B o Codestral.
Dimitri Didmanidze's photo

Dimitri Didmanidze

cursor ai code editor

Tutorial

Cursor AI: Una guía con 10 ejemplos prácticos

Aprende a instalar Cursor AI en Windows, macOS y Linux, y descubre cómo utilizarlo a través de 10 casos de uso diferentes.

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

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

Tutorial

Cómo ajustar GPT 3.5: Liberar todo el potencial de la IA

Explore GPT-3.5 Turbo y descubra el potencial transformador del ajuste fino. Aprenda a personalizar este modelo de lenguaje avanzado para aplicaciones especializadas, mejore su rendimiento y comprenda los costes asociados, la seguridad y las consideraciones de privacidad.
Moez Ali's photo

Moez Ali

Ver másVer más