> ## Documentation Index
> Fetch the complete documentation index at: https://developers.insuranceboosters.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Paso Agente IA: automatizar portal

> Un agente de IA sigue tu guion en un navegador en la nube: login, portales y descargas.

El paso `browser.script` ejecuta un **guion de navegador** con un agente de IA. A diferencia del [Agente IA: investigar la web](/automatizaciones/paso-navegador) —que decide solo cómo completar una tarea—, aquí tú escribes los pasos y el agente los sigue en orden, interpretando cada instrucción sobre la página real.

Úsalo para procesos repetitivos sobre un portal conocido: iniciar sesión, navegar a una sección, descargar un reporte, extraer datos.

## Campos del paso

| Campo       | Requerido | Descripción                                                                          |
| ----------- | --------- | ------------------------------------------------------------------------------------ |
| `actionKey` | Sí        | Debe ser `browser.script`.                                                           |
| `code`      | Sí        | JavaScript que dirige al agente con el objeto `browser`.                             |
| `input`     | No        | Variables disponibles como `input.*` dentro del código. Acepta plantillas `{{...}}`. |
| `timeoutMs` | No        | Límite de tiempo del paso.                                                           |
| `capture`   | No        | Copia campos del output a `context`.                                                 |

## El objeto `browser`

Dentro de `code`, el objeto `browser` abre un navegador en la nube y le da el control a tu guion:

```javascript theme={null}
const result = await browser.run(async (page) => {
  await page.goto(input.portalUrl);

  // Instrucciones en lenguaje natural: el agente de IA las interpreta
  // sobre la página real, sin selectores CSS. Una acción por llamada.
  await page.act("escribe %usuario% en el campo de usuario", {
    variables: { usuario: input.usuario },
  });
  await page.act("escribe %password% en el campo de contraseña", {
    variables: { password: input.password },
  });
  await page.act("haz clic en el botón 'Iniciar sesión'");

  await page.act("haz clic en la sección 'Cobranza' del menú");
  return await page.extract(
    "extrae el total de primas cobradas del mes (número) y la fecha de corte (AAAA-MM-DD)"
  );
});

output = { ...result.data, sessionId: result.sessionId };
```

* `browser.run(fn)` crea la sesión, ejecuta tu función con la página y **siempre cierra la sesión** al terminar.
* `page.act(instruccion, opciones)` ejecuta una acción descrita en lenguaje natural. Con `opciones.variables`, los valores sensibles (contraseñas, números de cuenta) se sustituyen como `%nombre%` sin pasar por el modelo de IA ni quedar en logs.
* `page.extract(instruccion)` devuelve datos estructurados de la página.
* `page.observe(instruccion)` devuelve las acciones disponibles sin ejecutarlas; puedes pasar una directamente a `page.act`.
* `page.goto(url)` navega directo, sin IA.
* `browser.run` resuelve a `{ data, sessionId }`: `data` es lo que devuelve tu función y `sessionId` identifica la sesión para depuración.

Solo puede haber **una sesión de navegador abierta a la vez** por ejecución.

<Tip>
  Mantén todo el recorrido relacionado dentro de una sola llamada a `browser.run`. Así conservas el estado de la sesión —incluidos login, cookies y pestañas— hasta terminar el guion.
</Tip>

## Ejemplo completo: reporte de cobranza

```json theme={null}
{
  "actionKey": "browser.script",
  "name": "Descargar cobranza",
  "input": {
    "portalUrl": "https://portal.aseguradora.mx",
    "usuario": "{{context.portalUsuario}}",
    "password": "{{context.portalPassword}}",
    "periodo": "{{trigger.body.periodo}}"
  },
  "code": "const result = await browser.run(async (page) => { await page.goto(input.portalUrl); await page.act('escribe %usuario% en el campo de usuario', { variables: { usuario: input.usuario } }); await page.act('escribe %password% en el campo de contraseña', { variables: { password: input.password } }); await page.act(\"haz clic en el botón 'Iniciar sesión'\"); await page.act(\"haz clic en la sección 'Cobranza' del menú\"); await page.act(`haz clic en el botón de descarga del reporte del periodo ${input.periodo}`); return await page.extract('extrae el nombre del reporte descargado y el periodo (AAAA-MM)'); }); output = { ...result.data, sessionId: result.sessionId };"
}
```

<Warning>
  No escribas credenciales directamente en `code` ni en la definición del paso. Pásalas por `input` con plantillas (`{{context.*}}` o `{{trigger.body.*}}`) y úsalas con `variables` dentro de `act`.
</Warning>

## Buenas prácticas para `act`, `extract` y `observe`

Los guiones confiables siguen estas reglas. Un guion con instrucciones vagas o combinadas falla de formas difíciles de depurar.

### Una acción por `act`

Cada llamada debe describir **una sola acción atómica**:

```javascript theme={null}
// Bien: acciones individuales y específicas
await page.act("haz clic en el botón 'Iniciar sesión'");
await page.act("escribe %usuario% en el campo de correo", {
  variables: { usuario: input.usuario },
});

// Mal: varias acciones combinadas
await page.act("llena el formulario de login y entra al panel de cobranza");
```

### Describe el elemento por tipo y texto, no por color

```javascript theme={null}
// Bien
await page.act("haz clic en el botón 'Exportar' en la parte superior de la tabla");
await page.act("selecciona 'Enero 2026' en el menú desplegable de periodo");

// Mal
await page.act("haz clic en el botón azul");
await page.act("elige enero");
```

Usa el verbo correcto: *haz clic* para botones y enlaces, *escribe* para campos de texto, *selecciona* para menús desplegables, *marca/desmarca* para casillas.

### Navega con `goto`, no con instrucciones

Si conoces la URL, `page.goto(url)` es más rápido y estable que pedirle al agente que navegue:

```javascript theme={null}
// Bien
await page.goto("https://portal.ejemplo.com/reportes/cobranza");

// Mal: gasta pasos del agente en algo determinista
await page.act("ve a la sección de reportes y luego a cobranza");
```

### Datos sensibles siempre en `variables`

Los valores de `variables` se sustituyen localmente como `%nombre%`: no pasan por el modelo de IA ni quedan en logs. Nunca interpoles contraseñas directamente en el texto de la instrucción.

### Pide campos concretos en `extract`

Nombra exactamente qué campos quieres y en qué formato:

```javascript theme={null}
// Bien
const datos = await page.extract(
  "extrae el número de póliza (texto), la prima total (número sin símbolo de moneda) y la fecha de corte (formato AAAA-MM-DD)"
);

// Mal
const datos = await page.extract("saca los datos del reporte");
```

Usa `extract` solo para **leer** y `act` solo para **interactuar**; no mezcles ambos en una instrucción.

### Verifica antes de actuar con `observe`

En páginas que cambian de estado, `observe` devuelve las acciones disponibles sin ejecutarlas. Puedes inspeccionar el resultado y pasarlo directo a `act`, que lo reproduce sin volver a consultar al modelo:

```javascript theme={null}
const acciones = await page.observe("encuentra el botón 'Descargar reporte'");
if (acciones.length === 0) {
  throw new Error("El reporte no está disponible todavía");
}
await page.act(acciones[0]);
```

También puedes buscar varios candidatos en una sola consulta y elegir el correcto. Como `description` es texto generado, normalízalo antes de compararlo:

```javascript theme={null}
const descargas = await page.observe(
  "encuentra todos los botones de descarga de la tabla e identifica el periodo de cada reporte"
);

const periodo = String(input.periodo).toLowerCase();
const descarga = descargas.find((accion) =>
  accion.description.toLowerCase().includes(periodo)
);

if (!descarga) {
  throw new Error(`No hay un reporte disponible para ${input.periodo}`);
}

// Reproduce la acción observada sin otra inferencia.
await page.act(descarga);
```

Vuelve a llamar `observe` después de navegar, enviar un formulario o actualizar filtros. Las acciones observadas representan el estado de la página en ese momento y pueden dejar de ser válidas cuando cambia el contenido.

### Trabaja con pestañas nuevas

Si un clic abre y enfoca otra pestaña, las siguientes llamadas a `act`, `observe` y `extract` trabajan sobre esa pestaña automáticamente:

```javascript theme={null}
await page.act("haz clic en el enlace 'Ver detalle', que abre una pestaña nueva");

const detalle = await page.extract(
  "extrae el número de póliza (texto) y el estatus actual (texto)"
);
```

El objeto de la página original no cambia cuando se abre otra pestaña. Si necesitas alternar entre ambas, conserva una referencia y cambia la pestaña activa de forma explícita:

```javascript theme={null}
const context = page.stagehand.browser.context;
const original = await context.activePage();

await page.act("haz clic en el enlace 'Ver detalle', que abre una pestaña nueva");
const detalle = await context.activePage();

if (detalle === original) {
  throw new Error("El detalle no abrió una pestaña nueva");
}

const datos = await page.extract(
  "extrae el número de póliza (texto) y el estatus actual (texto)"
);

await context.setActivePage(original);
await page.act("haz clic en el botón 'Continuar' del formulario");
```

Cada locator pertenece a la pestaña donde fue creado. No reutilices un locator de la pestaña original sobre la nueva. Si el sitio abre una pestaña en segundo plano sin enfocarla, cambia la pestaña activa antes de usar `act`, `observe` o `extract`.

### Reduce tiempo y costo

Cada operación interpretada por IA añade latencia y consumo. Sigue este orden de preferencia:

1. Usa `goto` cuando ya conoces la URL.
2. Usa una sola llamada a `observe` para localizar varios candidatos visibles y luego pasa la acción elegida a `act`.
3. Agrupa en un solo `extract` todos los campos que necesitas del mismo estado de la página.
4. Usa `act` solo para interacciones y conserva una acción atómica por llamada.
5. Valida el resultado antes de continuar para detener pronto un guion incorrecto.

Por ejemplo, una extracción conjunta evita releer la misma página varias veces:

```javascript theme={null}
// Mejor: una lectura con todos los campos necesarios.
const reporte = await page.extract(
  "extrae el folio (texto), el periodo (AAAA-MM), el total (número sin símbolo de moneda) y si está conciliado (booleano)"
);

// Evita una llamada separada para folio, otra para periodo y otra para total.
```

En páginas estables puedes acortar esperas por operación. No uses límites agresivos en portales lentos o con contenido dinámico:

```javascript theme={null}
await page.goto(input.portalUrl, {
  waitUntil: "domcontentloaded",
  timeout: 15000,
});

await page.act("haz clic en el botón 'Consultar'", {
  timeout: 5000,
});
```

Para páginas grandes, limita el análisis a la sección relevante y excluye zonas repetitivas. Esto reduce el contenido que debe procesarse:

```javascript theme={null}
const formulario = page.page.locator("#consulta-poliza");

await page.act("haz clic en el botón 'Consultar'", {
  locator: formulario,
  ignoreLocators: [
    page.page.locator("nav"),
    page.page.locator(".cookie-banner"),
  ],
});
```

Usa este patrón solo cuando los selectores sean estables. Si el portal cambia con frecuencia, una instrucción descriptiva sin locator suele ser más mantenible.

### Valida el resultado antes de devolverlo

```javascript theme={null}
const datos = await page.extract("extrae el total de primas (número)");
if (typeof datos.total !== "number") {
  throw new Error("No se encontró el total en la página");
}
output = { total: datos.total };
```

Un paso que falla con un error claro es más útil que un output incompleto que rompe los pasos siguientes.

## ¿Guion o agente autónomo?

| Situación                                                        | Paso recomendado                                       |
| ---------------------------------------------------------------- | ------------------------------------------------------ |
| Portal conocido, mismos pasos cada vez (login, reportes, RPA)    | `browser.script` (esta página)                         |
| Investigar en fuentes públicas, sitios variados, tareas abiertas | [`browser.use`](/automatizaciones/paso-navegador)      |
| Solo transformar datos, sin navegador                            | [`code.javascript`](/automatizaciones/paso-javascript) |

`browser.script` es más rápido, más barato y más predecible cuando el camino es fijo. `browser.use` gana cuando el agente tiene que decidir por sí mismo dónde buscar.

## Salida del paso

Lo que asignes a `output` se guarda como resultado del paso y queda disponible para los siguientes con `{{steps.N.output.*}}`, igual que en `code.javascript`.

## Errores frecuentes

| Situación                                      | Qué hacer                                                                                                                                                      |
| ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| El login falla                                 | Verifica credenciales y si el portal pide CAPTCHA o un código MFA; estos flujos pueden requerir configuración adicional.                                       |
| Una acción no encuentra el elemento            | Describe el tipo, texto y ubicación del elemento ("el botón 'Exportar' en la parte superior derecha") o navega con `page.goto` directo a la URL de la sección. |
| Una acción se ejecuta en la pestaña equivocada | Confirma cuál pestaña está activa y usa `context.setActivePage(...)` antes de continuar.                                                                       |
| El guion tarda demasiado                       | Reemplaza navegación interpretada por `goto`, agrupa lecturas relacionadas en un `extract` y reutiliza acciones devueltas por `observe`.                       |
| El paso falla al abrir el navegador            | Usa una sola llamada a `browser.run` por ejecución. Si el error persiste, contacta a soporte.                                                                  |

## Relacionado

* [Agente IA: investigar la web](/automatizaciones/paso-navegador)
* [Tipos de paso](/automatizaciones/tipos-de-paso)
* [Variables y contexto](/automatizaciones/variables-y-contexto)
