CodeWorlds
Powrót do kolekcji
Przewodnik11 min czytaniaZespół CodeWorlds

Stagehand, automatyzacja przeglądarki z LLM

Stagehand 4.0.2 zamienia zdania na akcje w przeglądarce. Licencja MIT, wersja lokalna bez konta Browserbase, cache serwerowy i realny koszt dwóch rachunków.

Stagehand, automatyzacja przeglądarki z LLM

Stagehand to biblioteka do sterowania przeglądarką, w której akcję opisujesz zdaniem, a model zamienia je na kliknięcia i wpisywanie tekstu. Wersja 4.0.2 z 20 sierpnia 2026 roku jest na licencji MIT i uruchamia się lokalnie, ale cache akcji oraz automatyczny wybór modelu działają wyłącznie na płatnej infrastrukturze Browserbase.

Czym jest Stagehand i co zmieniła wersja 4

Biblioteka daje trzy metody oparte na modelu językowym oraz pełny, deterministyczny sterownik przeglądarki obok nich. act wykonuje pojedynczą akcję opisaną zdaniem, observe zwraca listę elementów, w które da się kliknąć, wraz z gotowymi selektorami, a extract wyciąga ustrukturyzowane dane według schematu Zod albo Pydantic. Obok nich stoi klasa Page z metodami goto, click, type, keyPress, screenshot, snapshot, evaluate, waitForSelector i locator, oraz klasa Locator z click, fill, innerText, selectOption, setInputFiles, nth i first.

Wersja 4 zmieniła fundament. Pakiet 3.7.2 deklarował playwright-core w wersji ^1.55.1 jako zależność opcjonalną typu peer, obok puppeteer-core i patchright-core. Pakiet 4.0.2 nie ma żadnej z nich. Zamiast tego wozi własnego klienta Chrome DevTools Protocol i rozszerzenie do przeglądarki: w paczce leży katalog dist/extension z plikiem service-worker.js ważącym około dwóch megabajtów oraz manifest.json w wersji trzeciej, żądający uprawnień debugger, offscreen, scripting, tabs i dostępu do wszystkich adresów.

To rozróżnienie ma znaczenie praktyczne. Nazwy metod przypominają Playwright, ale to nie jest Playwright, tylko własna implementacja o zbliżonym interfejsie. Wtyczki, reportery, expect z pakietu testowego i cała reszta ekosystemu nie przenoszą się automatycznie. Inferencja modelu dzieje się wewnątrz rozszerzenia, które samo woła api.openai.com, api.anthropic.com, generativelanguage.googleapis.com, api.groq.com albo api.cerebras.ai, zależnie od wybranego dostawcy.

Wersja, licencja i zawartość paczki

Licencję sprawdziłem w trzech miejscach i wynik jest zgodny. Plik LICENSE w repozytorium browserbase/stagehand zawiera tekst MIT z formułą Copyright (c) 2024 Browserbase Inc. Pole license w rejestrze npm dla pakietu @browserbasehq/stagehand ma wartość MIT. Opublikowana paczka stagehand-4.0.2.tgz liczy szesnaście plików, w tym package/LICENSE z tym samym tekstem, a kod jest realny: dist/index.mjs waży 192 kilobajty, deklaracje typów dist/index.d.mts kolejne 244 kilobajty, całość po rozpakowaniu to około 3,3 megabajta. To nie jest atrapa ani pusty placeholder.

Jedna drobna rozbieżność dotyczy pakietu dla Pythona. Wheel stagehand-4.0.2-py3-none-any.whl deklaruje w metadanych License-Expression: MIT, zawiera czterdzieści plików z prawdziwym kodem, ale w katalogu dist-info nie ma pliku z tekstem licencji, tylko METADATA, WHEEL i RECORD. Jeśli Twój audyt zależności wymaga fizycznego pliku licencyjnego w artefakcie, dla wersji pythonowej trzeba go dociągnąć z repozytorium. README dodaje, że nazwa Stagehand jest znakiem towarowym Browserbase, co ogranicza użycie samej marki, ale nie kodu.

Stan projektu wygląda na aktywny. Repozytorium ma około 24 tysięcy gwiazdek, wydania 4.0.0, 4.0.1 i 4.0.2 wyszły kolejno 10, 14 i 20 sierpnia 2026 roku, a gałąź 3.7.2 dostała wydanie tego samego dnia co 4.0.2. Świeżość majora jest tu ryzykiem samym w sobie: piszę o bibliotece, której wersja główna ma dwanaście dni, więc część przykładów w sieci i w odpowiedziach modeli nadal opisuje interfejs z trójki.

Wymagania techniczne bywają zaskoczeniem. Pole engines żąda Node w wersji co najmniej 22.18.0, a exports udostępnia wyłącznie wariant ESM, czyli require nie zadziała. Zależność zod jest przypięta dokładnie do wersji 4.4.3, bez zakresu.

Lokalnie czy w Browserbase

Bibliotekę da się uruchomić bez konta Browserbase i to jest odpowiedź na najważniejsze pytanie o przywiązanie do dostawcy. Fabryka localBrowser.launch() startuje Chrome na Twojej maszynie, a localBrowser.connect({ cdpUrl }) podpina się do już działającej przeglądarki pod wskazanym adresem. Opcje launch obejmują między innymi headless, executablePath, userDataDir, preserveUserDataDir, proxy, viewport, downloadsPath i chromiumSandbox. Klucz do Browserbase nie jest tu potrzebny w żadnym miejscu.

Trzy rzeczy przestają jednak działać poza chmurą dostawcy i wszystkie trzy są istotne. Po pierwsze cache: budowniczy kontekstu w kodzie rozszerzenia zwraca undefined, gdy brakuje apiKey albo sessionId, a lokalna przeglądarka nie ma identyfikatora sesji Browserbase. Opcja cache staje się wtedy martwa i każde wywołanie płaci pełną cenę inferencji. Po drugie Model Gateway, czyli tryb bez pola model, w którym dostawca sam dobiera model do każdego wywołania. Dokumentacja mówi wprost, że wymaga on przeglądarki hostowanej, bo nie ma czego rozliczyć i autoryzować. Po trzecie cała warstwa infrastruktury: proxy, tryb ukrywania automatyzacji, nagrania sesji, wybór regionu i skalowanie równoległych sesji.

Lokalnie musisz więc podać własny klucz do modelu w polu model.apiKey albo dostarczyć własną funkcję generate, która sama gada z dowolnym dostawcą, także z modelem uruchomionym u siebie. Obsługiwane prefiksy nazw modeli to openai/, anthropic/, google/, groq/ i cerebras/, więc konto w OpenAI albo dostęp do Claude załatwia sprawę.

Trzy metody AI i powrót do selektorów

Najprostszy przykład lokalny wygląda tak.

Code
Bash
node --version   # wymagane co najmniej 22.18.0
npm install @browserbasehq/stagehand zod
export OPENAI_API_KEY="sk-..."
Code
TypeScript
import { localBrowser, Stagehand } from '@browserbasehq/stagehand'
import { z } from 'zod/v4'

const browser = await localBrowser.launch({ headless: false })

const stagehand = await Stagehand.create({
  browser,
  model: {
    modelName: 'openai/gpt-5.4-mini',
    apiKey: process.env.OPENAI_API_KEY
  },
  selfHeal: true,
  domSettleTimeoutMs: 3000
})

const page = await browser.context.activePage()
await page.goto('https://news.ycombinator.com')

const { data, metadata } = await stagehand.extract(
  'wypisz tytuł i liczbę punktów pierwszych trzech wpisów',
  z.object({
    items: z.array(z.object({ title: z.string(), points: z.number() }))
  }),
  { screenshot: false }
)

console.log(data.items, metadata.usage.inputTokens, metadata.usage.inferenceTimeMs)
await stagehand.close()
await browser.close()

Ciekawszy jest wzorzec, w którym model pracuje raz, a potem schodzi z drogi. observe zwraca tablicę obiektów z polami selector, description, method i arguments. Selektor możesz zapisać u siebie i przy kolejnych uruchomieniach użyć go bezpośrednio, bez żadnego wywołania modelu.

Code
TypeScript
const { data: actions } = await stagehand.observe('znajdź przycisk logowania')

// wariant deterministyczny: klikamy selektorem, zero tokenów
await page.locator(actions[0].selector).click()

// wariant z modelem, gdy strona się zmienia i selektor przestał pasować
await stagehand.act('kliknij przycisk logowania', {
  timeout: 20000,
  variables: {
    login: { value: process.env.APP_LOGIN!, description: 'nazwa użytkownika' }
  }
})

const metrics = await stagehand.metrics()
console.log(metrics.actPromptTokens, metrics.totalInferenceTimeMs)

Pole variables istnieje po to, żeby nie wklejać haseł do treści instrukcji, która trafia do modelu i do klucza cache. Opcje locator oraz ignoreLocators zawężają obszar strony, na którym model pracuje, co skraca kontekst i obniża rachunek.

Cache po stronie serwera

Mechanizm buforowania istnieje, ale nie jest tym, czym wydaje się na pierwszy rzut oka, co potwierdza dokumentacja cache. Cache nie leży na dysku obok projektu, tylko w usłudze pod adresem api.stagehand.browserbase.com, z osobnymi punktami końcowymi dla regionów us-west-2, us-east-1, eu-central-1 i ap-southeast-1. Żądania idą na trasy /cache/get i /cache/set z nagłówkiem x-bb-api-key, a projekt rozpoznawany jest po identyfikatorze sesji.

Klucz budowany jest z metody, adresu strony, drzewa dostępności pobranego przez CDP oraz przekazanych opcji. Konfiguracja modelu jest z klucza celowo wyłączona, więc zmiana modelu nie unieważnia zapisanych wpisów. Do tego dochodzi próg trafień: pole threshold mówi, ile razy dostawca musi zobaczyć identyczny wynik, zanim zacznie go serwować. Wartość 1 oznacza trafienie już przy drugim uruchomieniu, wartość wyższa opóźnia moment, w którym wynik uznaje się za stabilny.

Każdy wynik act, observe i extract niesie obiekt metadata.cache ze statusem HIT, MISS albo DISABLED. Przy trafieniu dostajesz count, threshold i tokensSaved, przy chybieniu missReason, w tym rozróżnienie na replay_failed, gdy wpis znaleziono, lecz nie dało się go odtworzyć, oraz read_failed, gdy zapytanie do cache poległo. Status DISABLED zobaczysz zawsze wtedy, gdy pracujesz lokalnie.

Code
TypeScript
import { browserbase, Stagehand } from '@browserbasehq/stagehand'

const browser = await browserbase.launch({
  apiKey: process.env.BROWSERBASE_API_KEY!,
  region: 'eu-central-1'
})

const stagehand = await Stagehand.create({
  browser,
  cache: { threshold: 2 }
})

const result = await stagehand.act('kliknij pierwszy wynik wyszukiwania', {
  cache: { threshold: 1 }
})

if (result.metadata.cache.status === 'HIT') {
  console.log('zaoszczędzone tokeny:', result.metadata.cache.tokensSaved?.totalTokens)
} else {
  console.log('powód chybienia:', result.metadata.cache.missReason)
}

Dwa rachunki: infrastruktura i model

Rachunek pierwszy to Browserbase. Plan darmowy daje trzy równoległe przeglądarki, jedną godzinę pracy przeglądarki, trzy uruchomienia agenta, tysiąc wywołań Search i tysiąc Fetch, sesję ograniczoną do piętnastu minut, siedem dni przechowywania danych oraz pięć dolarów w tokenach. Plan Developer kosztuje 20 dolarów miesięcznie i daje 25 równoległych przeglądarek oraz sto godzin, potem 0,12 dolara za godzinę. Plan Startup to 99 dolarów miesięcznie, sto równoległych przeglądarek, pięćset godzin, potem 0,10 dolara za godzinę, oraz trzydzieści dni retencji. Plan Scale ma cenę ustalaną indywidualnie.

Na stronie cennika nie ma przełącznika na rozliczenie roczne ani ceny rocznej, więc dwanaście miesięcy planu Developer to po prostu 240 dolarów, bez zniżki. Znalazłem za to rozjazd w samym cenniku: karta planu Startup podaje dla wywołań Fetch stawkę 1 dolara za tysiąc po wyczerpaniu limitu, a tabela porównawcza niżej na tej samej stronie podaje dla tego samego planu 0,50 dolara za tysiąc. Obie liczby są tam dzisiaj, więc przed policzeniem budżetu warto potwierdzić stawkę u dostawcy.

Rachunek drugi to model. Każde act, observe i extract, które nie trafiło w cache, to wywołanie modelu z drzewem dostępności strony w kontekście. Przy pracy lokalnej płacisz go zawsze i płacisz go osobno, na fakturze dostawcy modelu. Model Gateway łączy oba rachunki w jeden, ale kosztem tego, że przeglądarka musi stać u dostawcy. Zanim policzysz koszt kampanii scrapowania, zbierz metrics() z jednego przebiegu i pomnóż przez liczbę stron.

Kiedy język naturalny pomaga, a kiedy szkodzi

Selektor CSS jest darmowy, natychmiastowy i deterministyczny: albo pasuje, albo nie. Zdanie w języku naturalnym kosztuje wywołanie modelu, trwa setki milisekund lub sekundy i przy kolejnym uruchomieniu może wybrać inny element, bo strona się przesunęła albo model odpowiedział inaczej. To nie jest wada implementacji, tylko właściwość narzędzia.

Do stabilnego zestawu testów regresyjnych Stagehand jest złym wyborem. Test regresyjny ma dawać ten sam wynik przy każdym przebiegu i czerwone światło ma znaczyć błąd w aplikacji, a nie kaprys modelu. Do tego służy Playwright albo Cypress, a warstwę logiki testujesz w Vitest. Stagehand ma sens tam, gdzie strona jest cudza i zmienia się bez ostrzeżenia: scraping serwisów bez API, wypełnianie formularzy w portalach kontrahentów, wyciąganie danych z paneli, których nikt nie wersjonuje.

KryteriumStagehand 4.0.2browser-use 0.13.8Playwright 1.62.1
Sterowaniezdanie plus selektoryzadanie dla agentawyłącznie selektory
Silnikwłasny klient CDP i rozszerzenieChromium przez CDPwłasne sterowniki
LicencjaMITMITApache 2.0
JęzykiTypeScript, Python, GoPythonTypeScript, Python, Java, .NET
Koszt wywołaniatokeny modelutokeny modeluzero
Powtarzalnośćzależna od modelu i cachezależna od modelupełna
Cache akcjiserwerowy, tylko Browserbasebrak wbudowanegonie dotyczy
Praca lokalnatak, bez konta dostawcytaktak

Sensowny układ hybrydowy wygląda tak: observe raz, selektory do repozytorium, act tylko jako ścieżka ratunkowa, gdy zapisany selektor przestanie pasować. Wtedy płacisz za model przy zmianach, a nie przy każdym przebiegu. Porównanie z browser-use sprowadza się do poziomu abstrakcji: tam opisujesz cel całego zadania i oddajesz agentowi kontrolę nad pętlą, tutaj sterujesz krok po kroku i możesz w dowolnym momencie zejść do surowego selektora.

Typowe błędy

Pierwszy błąd to oczekiwanie, że cache: true cokolwiek zrobi przy lokalnej przeglądarce. Nie zrobi, a jedynym sygnałem jest status DISABLED w metadanych, którego nikt nie czyta.

Drugi to wstawianie act do zestawu testów regresyjnych i dziwienie się, że raz na dwadzieścia przebiegów wynik jest inny. Ten sam efekt bierze się z Model Gateway bez przypiętego modelu: skoro dostawca dobiera model per wywołanie, dwa przebiegi mogą użyć różnych modeli.

Trzeci to kopiowanie przykładów z wersji 3, gdzie metody wisiały na obiekcie strony. W wersji 4 act, observe i extract są metodami instancji Stagehand, a strona służy do nawigacji i selektorów.

Czwarty to konfiguracja środowiska: Node starszy niż 22.18.0 albo projekt na CommonJS. Pakiet eksportuje wyłącznie ESM i require po prostu się wywali.

Piąty to hasła wklejane wprost do instrukcji zamiast do variables. Instrukcja idzie do modelu i staje się częścią klucza cache.

Szósty to ignorowanie kosztu kontekstu. Bez locator albo ignoreLocators model dostaje drzewo dostępności całej strony i za nie płacisz przy każdym chybieniu cache.

FAQ

Czy Stagehand działa bez konta Browserbase?

Tak. localBrowser.launch() uruchamia Chrome na Twojej maszynie, a localBrowser.connect({ cdpUrl }) podpina się do już działającej przeglądarki. Potrzebny jest wtedy własny klucz do modelu albo własna funkcja generate. Bez konta dostawcy tracisz cache, Model Gateway, proxy, tryb ukrywania automatyzacji i nagrania sesji.

Czy Stagehand zastąpi Playwrighta w testach?

Nie w testach regresyjnych. Wywołanie modelu nie daje gwarancji powtarzalności, a test, który raz na kilkadziesiąt przebiegów świeci na czerwono bez zmiany w kodzie, przestaje być użyteczny. Stagehand nadaje się do przepływów eksploracyjnych i do stron, których nie kontrolujesz.

Ile kosztuje jedno wywołanie act?

Zależy od modelu i wielkości drzewa dostępności strony, więc nie ma jednej liczby. Zmierz ją u siebie: metadata.usage zwraca inputTokens, outputTokens, reasoningTokens i inferenceTimeMs dla pojedynczego wywołania, a stagehand.metrics() sumuje to samo w rozbiciu na act, extract i observe.

Czym Stagehand różni się od browser-use?

Poziomem kontroli. browser-use dostaje cel i sam prowadzi pętlę agenta. Stagehand wykonuje jedną akcję na wywołanie, zwraca selektory przez observe i pozwala w każdej chwili wrócić do deterministycznego page.locator(). Stagehand ma też oficjalne SDK dla TypeScriptu, Pythona i Go.

Jak kosztowna jest migracja z wersji 3 do 4?

Zmienił się fundament, nie tylko nazwy. Zniknęła zależność od playwright-core, metody AI przeniosły się z obiektu strony na instancję Stagehand, przeglądarkę tworzy się fabryką i przekazuje do Stagehand.create, a wymagany Node podniósł się do 22.18.0. Gałąź 3.7.x nadal dostaje wydania, więc migracja nie musi być natychmiastowa.

Czytaj dalej

Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie