
Aloísio Vítor
Image Processing Expert

detect, get_captcha_info o solve_on_page de CapSolver Core en límites de recuperación deterministas.Un arnés de recuperación de navegador de IA es la capa de ejecución que mantiene controlado una tarea de navegador cuando los cambios de estado de página son inesperados. El modelo puede decidir qué paso de negocio sigue, pero el arnés debe poseer el contexto de Playwright, la política de host aprobado, los puntos de control de navegación, la clasificación de página, la recuperación de desafíos admitidos, los límites de reintentos, los registros, las capturas de pantalla y la desinstalación. CapSolver encaja en esta capa como una capacidad de recuperación determinista: capsolver-core puede detectar desafíos admitidos, leer parámetros, resolverlos y llenar el resultado de vuelta en la misma página. Luego, el arnés verifica que el estado de aplicación esperado se haya devuelto antes de permitir que el agente continúe. Esta guía construye el modelo de política, la máquina de estados, el administrador de contexto asincrónico, la función de recuperación, el registrador de artefactos, las trazas de OpenTelemetry, las pruebas y los controles de producción para automatización de navegador autorizada confiable.
El arnés no es el modelo ni solo el controlador de navegador. Es el plano de control entre ellos.
Objetivo comercial del agente
↓
Arnés de recuperación de navegador
├─ política de destino
├─ contexto de Playwright
├─ clasificador de estado
├─ almacén de puntos de control
├─ recuperación de CapSolver
├─ presupuesto de reintentos
├─ trazas + artefactos
└─ limpieza
↓
Acción de página aprobada u revisión del operador
La documentación de fixture de Playwright enfatiza fixtures aislados de página y contexto de navegador, configuración y limpieza reutilizables, componibilidad y adjuntos de depuración automáticos. Estas propiedades se traducen directamente en un arnés de producción.
La documentación del SDK de CapSolver Core define cuatro etapas de navegador útiles: detect, get_captcha_info, solve y solve_on_page.
El modelo puede decidir abrir una página de producto conocida o leer un estado público. El arnés decide si el host solicitado es permitido, si la página actual es esperada, si la recuperación es admitida y si el presupuesto de reintentos permanece.
| Decisión | Propietario | Razón |
|---|---|---|
| Paso de negocio siguiente | Agente o flujo | Requiere contexto de tarea |
| Permisos de host y ruta | Política del arnés | Debe ser determinista |
| Clasificación de estado de página | Clasificador del arnés | Debe usar evidencia confiable de DOM/red |
| Llamada de recuperación de desafío | Arnés | Requiere secretos y objeto de navegador |
| Manejo de tokens/cookies | Arnés | Datos de ejecución sensibles |
| Continuar vs revisión | Máquina de estados del arnés | Enfuerza recuperación limitada |
| Envío final | Humano o servicio dedicado | Acción de alto impacto |
La guía de agentes de IA de CapSolver explica la misma división de trabajo: el modelo maneja el razonamiento, mientras que las capas de CapSolver realizan el trabajo de desafío admitido.
Comience con una política estrecha para hosts aprobados, rutas, acciones y presupuestos.
from dataclasses import dataclass, field
from urllib.parse import urlparse
@dataclass(frozen=True)
class TargetPolicy:
allowed_hosts: set[str]
allowed_path_prefixes: tuple[str, ...]
max_navigations: int = 20
max_recovery_attempts: int = 1
capture_screenshots: bool = True
capture_html: bool = False
allow_form_submission: bool = False
def validate_url(self, url: str) -> None:
parsed = urlparse(url)
if parsed.scheme != "https":
raise PermissionError("Solo se permiten destinos HTTPS")
if parsed.hostname not in self.allowed_hosts:
raise PermissionError("El host está fuera de la política aprobada")
if not parsed.path.startswith(self.allowed_path_prefixes):
raise PermissionError("La ruta está fuera de la política aprobada")
Use políticas específicas de inquilino. No mantenga una lista de permitidos global para clientes o proyectos no relacionados.
La FAQ de IA y automatización de CapSolver proporciona contexto de integración, mientras que la FAQ de scraping web de CapSolver cubre flujos de datos públicos responsables.
Un arnés de recuperación debe usar estados explícitos en lugar de un bucle "intentar de nuevo" sin límites.
from enum import Enum
class BrowserState(str, Enum):
EXPECTED_PAGE = "expected_page"
SUPPORTED_CHALLENGE = "supported_challenge"
UNKNOWN_PAGE = "unknown_page"
RECOVERING = "recovering"
RECOVERED = "recovered"
REVIEW_REQUIRED = "review_required"
FAILED = "failed"
Las transiciones permitidas se pueden representar como datos:
ALLOWED_TRANSITIONS = {
BrowserState.EXPECTED_PAGE: {
BrowserState.EXPECTED_PAGE,
BrowserState.SUPPORTED_CHALLENGE,
BrowserState.UNKNOWN_PAGE,
},
BrowserState.SUPPORTED_CHALLENGE: {
BrowserState.RECOVERING,
BrowserState.REVIEW_REQUIRED,
},
BrowserState.RECOVERING: {
BrowserState.RECOVERED,
BrowserState.REVIEW_REQUIRED,
BrowserState.FAILED,
},
BrowserState.RECOVERED: {
BrowserState.EXPECTED_PAGE,
BrowserState.REVIEW_REQUIRED,
},
}
Valide cada transición. Esto hace visibles los bucles y los hace probables.
Un punto de control registra metadatos seguros necesarios para determinar si el flujo se reanudó correctamente.
from dataclasses import dataclass
from datetime import datetime, timezone
@dataclass
class BrowserCheckpoint:
url: str
title: str
expected_selector: str | None
navigation_index: int
recovery_attempts: int
observed_at: str
async def checkpoint(page, expected_selector, nav_index, attempts):
return BrowserCheckpoint(
url=page.url,
title=await page.title(),
expected_selector=expected_selector,
navigation_index=nav_index,
recovery_attempts=attempts,
observed_at=datetime.now(timezone.utc).isoformat(),
)
No almacene estado de almacenamiento, cookies, contraseñas, tokens o valores completos de formulario en el punto de control.
Use evidencia confiable de DOM, título, URL y selectores esperados. Nunca pida al modelo que infiera el estado de la página a partir de una imagen sola.
async def classify_page(page, expected_selector: str) -> BrowserState:
if await page.locator(expected_selector).count():
return BrowserState.EXPECTED_PAGE
title = (await page.title()).strip().lower()
html = (await page.content()).lower()
challenge_markers = (
"just a moment...",
"challenge-platform",
"cf-chl-",
)
if any(marker in title or marker in html for marker in challenge_markers):
return BrowserState.SUPPORTED_CHALLENGE
return BrowserState.UNKNOWN_PAGE
Use marcadores específicos del destino y fixtures controlados. Un conjunto de marcadores es una heurística de enrutamiento, no una autorización de acceso.
La FAQ de resolución de CAPTCHA de CapSolver explica flujos de desafío admitidos, y la FAQ de errores de CapSolver ayuda a clasificar fallas.
El SDK Core oficial recomienda usar su administrador de contexto asincrónico para que las conexiones HTTP se reutilicen y se liberen correctamente.
import os
from capsolver_core import create_capsolver
def create_recovery_client():
return create_capsolver(
api_key=os.environ["CAPSOLVER_API_KEY"],
default_timeout=120,
polling_interval=5,
request_timeout_ms=30000,
source="ai-browser-recovery-harness",
version="1.0.0",
)
No cree un cliente nuevo para cada verificación de DOM. Mantenga un cliente para el ciclo de vida del arnés y cierre el cliente durante la limpieza.
Use detect y get_captcha_info para diagnósticos, luego solve_on_page para el flujo completo del navegador.
from capsolver_core import SolveOnPageOptions
async def recover_supported_challenge(
cap,
page,
policy: TargetPolicy,
recovery_attempts: int,
) -> dict:
policy.validate_url(page.url)
if recovery_attempts >= policy.max_recovery_attempts:
return {
"success": False,
"state": BrowserState.REVIEW_REQUIRED,
"reason": "presupuesto de recuperación agotado",
}
detected = await cap.detect(page)
if not detected:
return {
"success": False,
"state": BrowserState.REVIEW_REQUIRED,
"reason": "no se detectó ningún desafío admitido",
}
infos = await cap.get_captcha_info(page)
results = await cap.solve_on_page(
page,
options=SolveOnPageOptions(
autofill=True,
throw_on_error=False,
timeout=120,
polling_interval=5,
),
)
errors = [item.error for item in results if item.error]
filled = bool(results) and all(item.filled for item in results)
return {
"success": filled and not errors,
"state": (
BrowserState.RECOVERED
if filled and not errors
else BrowserState.REVIEW_REQUIRED
),
"detected_count": len(detected),
"info_count": len(infos),
"result_count": len(results),
"errors": errors,
}
Mantenga el objeto page original. El punto de solve_on_page es detectar, resolver y llenar dentro de la sesión de navegador existente.
Una respuesta de herramienta exitosa no prueba que la página de aplicación esperada se haya devuelto.
async def verify_recovery(
page,
expected_selector: str,
timeout_ms: int = 15000,
) -> bool:
try:
await page.locator(expected_selector).wait_for(
state="visible",
timeout=timeout_ms,
)
return True
except Exception:
return False
Después de la recuperación, clasifique la página nuevamente. Si el desafío persiste o el selector esperado está ausente, deténgase y solicite revisión.
async def recover_and_verify(cap, page, policy, expected_selector, attempts):
result = await recover_supported_challenge(
cap=cap,
page=page,
policy=policy,
recovery_attempts=attempts,
)
if not result["success"]:
return result
if not await verify_recovery(page, expected_selector):
return {
"success": False,
"state": BrowserState.REVIEW_REQUIRED,
"reason": "la página esperada no se devolvió después de la recuperación",
}
return {
"success": True,
"state": BrowserState.EXPECTED_PAGE,
"reason": "página recuperada y verificada",
}
Use un administrador de contexto asincrónico para garantizar la limpieza.
from contextlib import asynccontextmanager
from playwright.async_api import async_playwright
@dataclass
class BrowserHarness:
policy: TargetPolicy
playwright: object
browser: object
context: object
page: object
capsolver: object
navigation_count: int = 0
recovery_attempts: int = 0
@asynccontextmanager
async def browser_recovery_harness(policy: TargetPolicy):
async with async_playwright() as playwright:
browser = await playwright.chromium.launch(headless=True)
context = await browser.new_context()
page = await context.new_page()
async with create_recovery_client() as cap:
harness = BrowserHarness(
policy=policy,
playwright=playwright,
browser=browser,
context=context,
page=page,
capsolver=cap,
)
try:
yield harness
finally:
await context.close()
await browser.close()
El modelo o flujo recibe métodos controlados, no acceso sin restricciones al navegador.
async def safe_navigate(
harness: BrowserHarness,
url: str,
expected_selector: str,
) -> dict:
harness.policy.validate_url(url)
if harness.navigation_count >= harness.policy.max_navigations:
return {
"success": False,
"state": BrowserState.REVIEW_REQUIRED,
"reason": "presupuesto de navegación agotado",
}
harness.navigation_count += 1
await harness.page.goto(url, wait_until="domcontentloaded")
state = await classify_page(harness.page, expected_selector)
if state == BrowserState.EXPECTED_PAGE:
return {"success": True, "state": state}
if state == BrowserState.SUPPORTED_CHALLENGE:
result = await recover_and_verify(
cap=harness.capsolver,
page=harness.page,
policy=harness.policy,
expected_selector=expected_selector,
attempts=harness.recovery_attempts,
)
harness.recovery_attempts += 1
return result
return {
"success": False,
"state": BrowserState.REVIEW_REQUIRED,
"reason": "estado de página desconocido",
}
El agente puede solicitar safe_navigate, pero el arnés posee la política y el camino de recuperación.
La guía de observabilidad de GenAI de OpenTelemetry describe trazas para operaciones de modelo y herramienta. También menciona que el contenido completo de la solicitud y la herramienta puede contener datos sensibles. Predomine en trazas con solo metadatos.
from opentelemetry import trace
tracer = trace.get_tracer("capsolver.browser_harness")
async def traced_safe_navigate(harness, url, expected_selector):
with tracer.start_as_current_span("browser.safe_navigate") as span:
span.set_attribute("browser.target_host", url.split("/")[2])
span.set_attribute("browser.navigation_index", harness.navigation_count + 1)
span.set_attribute("browser.recovery_attempts", harness.recovery_attempts)
result = await safe_navigate(harness, url, expected_selector)
span.set_attribute("browser.outcome", str(result.get("state")))
span.set_attribute("browser.success", bool(result.get("success")))
return result
No se deben adjuntar tokens, cookies, claves de API, credenciales de proxy, estado de almacenamiento, contenido de la solicitud o HTML de toda la página a los spans.
Las capturas de pantalla y el HTML pueden contener datos personales o confidenciales. Captúrelas solo cuando la política lo permita, redáctelas donde sea posible y almacene referencias de corta duración.
from pathlib import Path
import secrets
async def capture_failure_artifacts(harness, directory: Path) -> dict:
artifact_id = secrets.token_hex(12)
screenshot = directory / f"{artifact_id}.png"
await harness.page.screenshot(
path=str(screenshot),
full_page=False,
)
return {
"artifact_id": artifact_id,
"screenshot_path": str(screenshot),
"url": harness.page.url,
"title": await harness.page.title(),
}
Use límites de retención y controles de acceso. Evite capturar capturas de pantalla completas cuando solo se necesite el estado de nivel superior.
El blog de automatización del navegador de CapSolver contiene patrones de implementación relacionados, y la guía del complemento de Chrome de CapSolver puede ayudar a los equipos a inspeccionar los parámetros de widget admitidos durante el desarrollo.
Use contextos de navegador aislados y páginas controladas. Las fixtures de Playwright proporcionan configuración y limpieza reutilizables.
import pytest
@pytest.mark.asyncio
async def test_unknown_host_is_rejected():
policy = TargetPolicy(
allowed_hosts={"staging.example.com"},
allowed_path_prefixes=("/qa/",),
)
with pytest.raises(PermissionError):
policy.validate_url("https://other.example.net/qa/test")
@pytest.mark.asyncio
async def test_recovery_budget_is_bounded(fake_cap, fake_page):
policy = TargetPolicy(
allowed_hosts={"staging.example.com"},
allowed_path_prefixes=("/qa/",),
max_recovery_attempts=1,
)
result = await recover_supported_challenge(
cap=fake_cap,
page=fake_page,
policy=policy,
recovery_attempts=1,
)
assert result["state"] == BrowserState.REVIEW_REQUIRED
assert result["reason"] == "recovery budget exhausted"
Cree fixtures para sin desafío, desafío admitido, intersticial desconocido, relleno exitoso, fallo en la resolución, bucle de desafío posterior a la recuperación y selector esperado faltante.
| Métrica | Propósito |
|---|---|
| Tasa de página esperada | Mide la navegación normal exitosa |
| Tasa de detección de desafío | Muestra la fricción de la fuente por host aprobado |
| Tasa de éxito de recuperación | Mide los resultados de recuperación admitidos |
| Tasa de bucle de desafío | Detecta estado intersticial repetido |
| Tasa de página desconocida | Detecta cambios en el diseño, autenticación o política |
| Latencia de recuperación P95 | Rastrea el retraso visible para el usuario |
| Tasa de revisión por operador | Mide el volumen de flujos de trabajo no resueltos |
| Tasa de captura de artefactos | Detecta el registro excesivo de errores |
Divida las métricas por política de destino, ruta, versión del navegador, tipo de desafío y versión del harness. Nunca etiquete un fracaso de recuperación de desafío como fracaso de tarea empresarial sin preservar ambas dimensiones.
Código de bonificación: Use el código WEBS en CapSolver Dashboard para obtener un 5% adicional en cada recarga.
| Enfoque | Propiedad del navegador | Control de recuperación | Mejor uso |
|---|---|---|---|
| Acceso directo al navegador del agente | Entorno de ejecución del agente | Dependiente de la solicitud | Únicamente para prototipos de bajo riesgo |
| Acción específica del framework | Marco del agente | Envoltura de herramienta | Integración rápida |
| Harness de recuperación dedicado | Capa de control independiente | Máquina de estado determinista | Confianza en producción y gobernanza |
| Recuperación exclusiva de humanos | Operador | Manual | No admitido o flujos de trabajo de alto riesgo |
Un harness dedicado requiere más ingeniería, pero crea una capa de política y observabilidad que puede servir a múltiples marcos de agente.
La página de productos de CapSolver enumera las categorías de soluciones admitidas, mientras que el blog de IA de CapSolver cubre ejemplos de marcos de agente que pueden llamar a una acción de harness.
Use el harness de recuperación del navegador solo en sistemas que posea, pruebe o tenga autorización explícita para automatizar. Una solución exitosa de desafío no otorga permiso para acceder a contenido privado, ignorar límites de autenticación, exceder límites de tasa o realizar transacciones. Mantenga el harness con alcance limitado, de solo lectura por defecto y auditables. Dirija la incertidumbre a una persona en lugar de expandir permisos dinámicamente.
Un harness de recuperación de navegador de IA convierte el manejo de desafíos en una capacidad de ejecución controlada. Posee el contexto del navegador, valida los objetivos, clasifica el estado de la página, invoca a CapSolver Core en un límite determinista, verifica la página esperada, registra telemetría redactada y se detiene después de un intento limitado. Los marcos de agentes pueden usar el harness sin obtener acceso directo a secretos o control sin restricciones del navegador.
Comience con CapSolver, implemente la máquina de estados contra una aplicación de prueba, y agregue fixtures aislados y puertas de confiabilidad antes de producción.
No. Es una capa de ejecución y política independiente que un marco de agente puede llamar. El harness posee el estado del navegador, la recuperación, puntos de verificación, telemetría y limpieza.
solve_on_page?solve_on_page combina la detección, extracción de parámetros, resolución y relleno de DOM en la misma página de Playwright, lo que lo hace adecuado para un límite de recuperación de navegador controlado.
Prefiera acciones de harness estrechas como safe_navigate y read_public_page. El acceso a la página sin procesar hace más difícil aplicar políticas de objetivo, navegación y recuperación.
Use uno por defecto. Los desafíos repetidos o el estado de página desconocido deben dirigirse a la revisión del operador en lugar de crear un bucle no controlado.
Almacene metadatos como host de destino, versión del harness, transiciones de estado, latencia, errores normalizados y referencias de artefactos. No almacene tokens de solución, cookies, claves de API, credenciales de proxy, estado de almacenamiento o contenido de página privada.
Construir un marco de evaluación CAPTCHA para llamadas a herramientas de agente de IA con esquemas de CapSolver, fixtures, evaluadores de trazas, aserciones, conjuntos de datos de regresión y puertas de CI.

Aprende a resolver Cloudflare Turnstile en agentes de LlamaIndex con CapSolver, esquemas de FunctionTool, manejo seguro de tokens, reintentos y recuperación del navegador.
