
Emma Foster
Machine Learning Engineer

detect, get_captcha_info, atau solve_on_page dari CapSolver Core pada batas pemulihan deterministik.Harness pemulihan browser AI adalah lapisan runtime yang menjaga tugas browser tetap terkendali saat status halaman berubah secara tidak terduga. Model dapat memutuskan langkah bisnis berikutnya, tetapi harness harus memiliki konteks Playwright, kebijakan host yang disetujui, titik pemeriksaan navigasi, klasifikasi halaman, pemulihan tantangan yang didukung, batas ulang, jejak, screenshot, dan penyelesaian. CapSolver cocok di lapisan ini sebagai kemampuan pemulihan deterministik: capsolver-core dapat mendetekeksi tantangan yang didukung, membaca parameter, menyelesaikannya, dan mengisi hasilnya kembali ke halaman yang sama. Harness kemudian memverifikasi bahwa status aplikasi yang diharapkan kembali sebelum memungkinkan agen melanjutkan. Panduan ini membangun model kebijakan, mesin keadaan, manajer konteks asinkron, fungsi pemulihan, perekam artefak, span OpenTelemetry, tes, dan kontrol produksi untuk otomatisasi browser yang dapat diandalkan.
Harness bukanlah model dan bukan hanya driver browser. Ini adalah plane kontrol di antara keduanya.
Tujuan bisnis dari agen
↓
Harness pemulihan browser
├─ kebijakan target
├─ konteks Playwright
├─ klasifikasi status
├─ penyimpanan titik pemeriksaan
├─ pemulihan CapSolver
├─ anggaran ulang
├─ jejak + artefak
└─ pembersihan
↓
Tindakan halaman yang disetujui atau tinjauan operator
Dokumentasi fixture Playwright menekankan fixture halaman dan konteks browser yang terisolasi, setup dan teardown yang dapat digunakan kembali, komposabilitas, dan lampiran debug otomatis. Sifat-sifat ini langsung terjemahkan ke dalam harness produksi.
Dokumentasi SDK Core CapSolver mendefinisikan empat tahap browser yang berguna: detect, get_captcha_info, solve, dan solve_on_page.
Model mungkin memutuskan untuk membuka halaman produk yang diketahui atau membaca status publik. Harness memutuskan apakah host yang diminta diizinkan, apakah halaman saat ini diharapkan, apakah pemulihan didukung, dan apakah anggaran ulang tersisa.
| Keputusan | Pemilik | Alasan |
|---|---|---|
| Langkah bisnis berikutnya | Agen atau alur kerja | Membutuhkan konteks tugas |
| Izin host dan jalur | Kebijakan harness | Harus deterministik |
| Klasifikasi status halaman | Klasifikator harness | Harus menggunakan bukti DOM/jaringan yang tepercaya |
| Panggilan pemulihan tantangan | Harness | Membutuhkan rahasia dan objek browser |
| Penanganan token/kuki | Harness | Data runtime sensitif |
| Lanjutkan vs tinjauan | Mesin keadaan harness | Memaksa pemulihan terbatas |
| Pengiriman akhir | Manusia atau layanan khusus | Tindakan berdampak tinggi |
Panduan Agen AI CapSolver menjelaskan pembagian kerja yang sama: model menangani pemikiran, sementara lapisan CapSolver melakukan pekerjaan tantangan yang didukung.
Mulai dengan kebijakan sempit untuk host yang disetujui, jalur, tindakan, dan anggaran.
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("Hanya target HTTPS yang diperbolehkan")
if parsed.hostname not in self.allowed_hosts:
raise PermissionError("Host di luar kebijakan yang disetujui")
if not parsed.path.startswith(self.allowed_path_prefixes):
raise PermissionError("Jalur di luar kebijakan yang disetujui")
Gunakan kebijakan khusus tenant. Jangan pertahankan satu daftar izin global untuk pelanggan atau proyek yang tidak terkait.
FAQ AI dan Otomasi CapSolver memberikan konteks integrasi, sementara FAQ Web-Scraping CapSolver menutupi alur kerja data publik yang bertanggung jawab.
Harness pemulihan harus menggunakan state eksplisit daripada loop "coba lagi" tanpa batas.
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"
Transisi yang diizinkan dapat direpresentasikan sebagai data:
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,
},
}
Validasi setiap transisi. Ini membuat loop terlihat dan dapat diuji.
Titik pemeriksaan mencatat metadata yang aman yang diperlukan untuk menentukan apakah alur kerja dilanjutkan dengan benar.
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(),
)
Jangan menyimpan state penyimpanan, cookie, kata sandi, token, atau nilai formulir lengkap dalam titik pemeriksaan.
Gunakan bukti DOM yang tepercaya, judul, URL, dan selektor yang diharapkan. Jangan pernah meminta model untuk menebak status halaman dari gambar layar saja.
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
Gunakan marker khusus target dan fixture terkontrol. Set marker adalah heuristik routing, bukan entitlemen akses.
FAQ Pemecahan CAPTCHA CapSolver menjelaskan alur kerja tantangan yang didukung, dan FAQ Kesalahan CapSolver membantu mengklasifikasikan kegagalan.
SDK Core resmi menyarankan menggunakan manajer konteks asinkronnya sehingga koneksi HTTP dapat digunakan kembali dan dilepaskan dengan benar.
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",
)
Jangan membuat klien baru untuk setiap pemeriksaan DOM. Pertahankan satu klien untuk siklus hidup harness dan tutup saat penyelesaian.
Gunakan detect dan get_captcha_info untuk diagnostik, lalu solve_on_page untuk alur browser all-in-one.
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": "anggaran pemulihan habis",
}
detected = await cap.detect(page)
if not detected:
return {
"success": False,
"state": BrowserState.REVIEW_REQUIRED,
"reason": "tidak ada tantangan yang didukung terdeteksi",
}
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,
}
Pertahankan objek page asli. Tujuan solve_on_page adalah mendeteksi, menyelesaikan, dan mengisi kembali dalam sesi browser yang sama.
Respons alat yang berhasil tidak membuktikan bahwa halaman aplikasi yang diharapkan kembali.
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
Setelah pemulihan, klasifikasikan halaman lagi. Jika tantangan masih ada atau selektor yang diharapkan tidak ada, hentikan dan minta tinjauan.
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": "halaman yang diharapkan tidak kembali setelah pemulihan",
}
return {
"success": True,
"state": BrowserState.EXPECTED_PAGE,
"reason": "halaman dipulihkan dan diverifikasi",
}
Gunakan manajer konteks asinkron untuk memastikan pembersihan.
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()
Model atau alur kerja menerima metode terkontrol, bukan akses browser yang tidak terbatas.
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": "anggaran navigasi habis",
}
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": "status halaman tidak dikenal",
}
Agen dapat meminta safe_navigate, tetapi harness memiliki kebijakan dan jalur pemulihan.
< a href="https://opentelemetry.io/blog/2026/genai-observability/" rel="nofollow">Guia Observability GenAI dari OpenTelemetry menjelaskan jejak untuk operasi model dan alat. Ia juga mencatat bahwa konten lengkap prompt dan alat dapat mengandung data sensitif. Default ke span hanya metadata.
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
Jangan sertakan token, kuki, kunci API, kredensial proxy, status penyimpanan, konten prompt, atau HTML halaman penuh ke span.
Screenshot dan HTML dapat berisi data pribadi atau rahasia. Tangkap hanya ketika kebijakan memungkinkan, redaksi di mana-mana, dan simpan referensi sementara.
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(),
}
Gunakan batas penyimpanan dan kontrol akses. Hindari mengambil screenshot halaman penuh ketika hanya keadaan tingkat atas yang diperlukan.
Artikel blog CapSolver browser automation berisi pola implementasi terkait, dan panduan ekstensi Chrome CapSolver dapat membantu tim memeriksa parameter widget yang didukung selama pengembangan.
Gunakan konteks browser yang terisolasi dan halaman yang dikontrol. Fixture Playwright menyediakan setup dan teardown yang dapat digunakan kembali.
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"] == "budget pemulihan habis"
Buat fixture untuk tidak ada tantangan, tantangan yang didukung, interstitial tidak dikenal, pengisian kembali yang berhasil, kegagalan penyelesaian, loop tantangan pasca-pemulihan, dan selektor yang diperlukan hilang.
| Metrik | Tujuan |
|---|---|
| Tingkat halaman yang diharapkan | Mengukur navigasi normal yang berhasil |
| Tingkat kemunculan tantangan | Menunjukkan gesekan sumber oleh host yang disetujui |
| Tingkat keberhasilan pemulihan | Mengukur hasil pemulihan yang didukung |
| Tingkat loop tantangan | Mendeteksi keadaan interstitial yang berulang |
| Tingkat halaman tidak dikenal | Menemukan perubahan tata letak, otentikasi, atau kebijakan |
| Latensi pemulihan P95 | Melacak penundaan yang terlihat pengguna |
| Tingkat ulasan operator | Mengukur volume alur kerja yang tidak terselesaikan |
| Tingkat penangkapan artefak | Mendeteksi pencatatan kegagalan berlebihan |
Pecah metrik berdasarkan kebijakan target, rute, versi browser, jenis tantangan, dan versi harness. Jangan menandai kegagalan pemulihan tantangan sebagai kegagalan tugas bisnis tanpa mempertahankan kedua dimensi.
Kode Bonus: Gunakan kode WEBS di Dashboard CapSolver untuk mendapatkan bonus tambahan 5% pada setiap recharge.
| Pendekatan | Kepemilikan browser | Kontrol pemulihan | Penggunaan terbaik |
|---|---|---|---|
| Akses browser agen langsung | Runtime agen | Bergantung pada prompt | Hanya prototipe risiko rendah |
| Tindakan khusus kerangka kerja | Kerangka kerja agen | Wrapper alat | Integrasi cepat |
| Harness pemulihan khusus | Lapisan kontrol independen | Mesin keadaan deterministik | Keandalan dan tata kelola produksi |
| Pemulihan hanya manusia | Operator | Manual | Alur kerja yang tidak didukung atau berisiko tinggi |
Harness khusus membutuhkan lebih banyak insinyur, tetapi menciptakan satu lapisan kebijakan dan observabilitas yang dapat melayani berbagai kerangka kerja agen.
Halaman produk CapSolver mencantumkan kategori solusi yang didukung, sementara blog AI CapSolver menutupi contoh kerangka kerja agen yang dapat memanggil tindakan harness.
Gunakan harness pemulihan browser hanya pada sistem yang Anda miliki, uji, atau memiliki izin eksplisit untuk otomasi. Solusi tantangan yang sukses tidak memberikan izin untuk mengakses konten pribadi, mengabaikan batas otentikasi, melebihi batas laju, atau melakukan transaksi. Pertahankan harness terbatas, baca saja secara default, dan dapat diaudit. Alihkan ketidakpastian ke orang alih-alih memperluas izin secara dinamis.
Harness pemulihan browser AI mengubah penanganan tantangan menjadi kemampuan runtime yang terkendali. Ia memiliki konteks browser, memvalidasi target, mengklasifikasikan keadaan halaman, memanggil CapSolver Core di batas deterministik, memverifikasi halaman yang diharapkan, mencatat telemetri yang direduksi, dan berhenti setelah upaya yang dibatasi. Kerangka kerja agen dapat menggunakan harness tanpa mendapatkan akses langsung ke rahasia atau kontrol browser yang tidak terbatas.
Mulai dengan CapSolver, implementasikan mesin keadaan terhadap aplikasi staging yang disetujui, dan tambahkan fixture terisolasi dan penghalang keandalan sebelum produksi.
Tidak. Ia adalah lapisan runtime dan kebijakan independen yang dapat dipanggil oleh kerangka kerja agen. Harness memiliki keadaan browser, pemulihan, titik pemeriksaan, telemetri, dan pembersihan.
solve_on_page?solve_on_page menggabungkan deteksi, ekstraksi parameter, penyelesaian, dan pengisian kembali DOM pada halaman Playwright yang sama, yang membuatnya cocok untuk batas pemulihan browser yang dikontrol.
Lebih baik menggunakan tindakan harness yang sempit seperti safe_navigate dan read_public_page. Akses halaman mentah membuat lebih sulit untuk menerapkan kebijakan target, navigasi, dan pemulihan.
Gunakan satu upaya secara default. Tantangan berulang atau keadaan halaman tidak dikenal seharusnya dialihkan ke ulasan operator alih-alih menciptakan loop yang tidak terkendali.
Simpan metadata seperti host target, versi harness, transisi keadaan, latensi, kesalahan yang dinormalisasi, dan referensi artefak. Jangan simpan token penyelesaian, kuki, kunci API, kredensial proxy, status penyimpanan, atau konten halaman pribadi.
Bangun kerangka uji CAPTCHA untuk panggilan alat agen AI dengan skema CapSolver, fixture, penilai jejak, asersi, dataset regresi, dan gate CI.

Pelajari cara menyelesaikan Cloudflare Turnstile di agen LlamaIndex dengan CapSolver, skema FunctionTool, penanganan token yang aman, pengulangan, dan pemulihan browser.
