Microsoft가 공개한 1,500줄짜리 미니멀 웹 에이전트 하네스. Online-Mind2Web 86.7%, Odysseys 60.1%로 오픈소스 최고 성능을 기록했다. 어떻게 이게 가능했는지, 코드 한 줄까지 파고든다.
결국 무엇인가?
대부분의 웹 에이전트는 매 스텝마다 모델에게 페이지 상태를 보여주고 "다음 클릭은 어디?"를 물어본다. Webwright는 그 루프를 버린다. 대신 모델에게 터미널을 쥐어주고, "Playwright 스크립트를 짜서 실행해라"고 시킨다. 영구 상태로 남는 건 브라우저 세션이 아니라 로컬 폴더의 코드와 로그다.
그 결과: 코어 에이전트 루프 약 450줄, Playwright 환경 약 570줄, CLI 약 150줄. 전체 1,500줄로 Online-Mind2Web 벤치마크에서 GPT-5.4 기준 86.7% 달성. 오픈소스 하네스 중 1위다.
한 줄로 줄이면 — Webwright는 "사람 엔지니어가 브라우저 자동화 스크립트를 짤 때처럼" LLM이 일하게 만드는 가장 작은 단위의 하네스다. 그 이상도 이하도 아니다. 멀티 에이전트도, 그래프 엔진도, 플러그인 레이어도, 숨겨진 오케스트레이션도 없다. 그냥 터미널, 브라우저, 모델 세 개뿐.
트렌딩 이유와 경쟁 제품 대비 장점.
2026년 5월 기준 GitHub 별은 13개로 적지만, TrendShift 상위에 오른 이유는 벤치마크 수치와 Microsoft 출시라는 시그널 두 가지다. AutoGen·Magentic-One 같은 거대 프레임워크를 만든 같은 회사가, 정반대 철학으로 "1,500줄이면 충분하다"고 선언한 셈이다.
OpenAI의 Operator나 Claude Computer Use는 스크린샷을 보고 "여기를 클릭해라 (x=423, y=812)"를 예측한다. 이걸 코드형 액션(code-as-action)으로 바꾼 게 Webwright의 핵심 차이다.
같은 GPT-5.4라도 좌표 예측 베이스라인은 Odysseys에서 33.5%, Webwright(코드-액션)는 60.1% — 26.6 포인트 차이. 모델은 그대로인데 하네스만 바꿔서 두 배 가까이 올린 것이다.
대부분의 웹 에이전트는 브라우저 세션을 영구 상태로 잡고 그 위에서 한 스텝씩 조작한다. 세션이 죽으면 처음부터. Webwright는 다르다. 매 실행마다 새 브라우저를 띄우고, 스크립트가 끝나면 닫는다. 진짜 상태는 디스크에 남는 final_script.py·로그·스크린샷이다.
이건 사람 RPA(Robotic Process Automation) 엔지니어가 일하는 방식과 똑같다. 사이트 한 번 살펴보고, 스크립트 짜고, 돌리고, 깨지면 고치고, 결과물은 코드로 남긴다.
LangGraph, AutoGen, CrewAI 같은 프레임워크는 "에이전트 노드", "라우터", "메모리 매니저" 같은 추상화를 100겹 쌓는다. 디버그할 때 어디서 멈춰야 할지도 모른다. Webwright는 httpx, pydantic, playwright, typer 네 개로 끝. 외부 의존성도 단순하다.
요리에 비유하면 — 기존 웹 에이전트는 "한 입씩 떠먹여주는 베이비 푸드"고, Webwright는 "주방을 통째로 맡기고 레시피만 시키는 방식"이다. 모델이 약했던 GPT-3.5 시절에는 떠먹여줘야 했지만, GPT-5.4·Claude Opus 4.7쯤 되면 주방을 맡겨야 더 잘한다.
벤치마크 수치 정리:
GPT-5.4 + Webwright = 86.7% (오픈소스 1위). Claude Opus 4.7 + Webwright = 84.7%인데, 어려운 split에서는 오히려 80.5% vs GPT-5.4의 76.6%로 역전. 모델별 강점이 갈린다.
Webwright + GPT-5.4 = 60.1%. 직전 SOTA(Opus 4.6 비전 기반 접근 + 영구 브라우저) 44.5% 대비 +15.6 포인트. 같은 GPT-5.4라도 코드-액션이 좌표 예측보다 무조건 좋다는 게 증명됐다.
Qwen-3.5-9B(9B짜리 오픈소스 모델)도 사전 제작된 5개 이상의 CLI 도구가 주어지면 Online-Mind2Web을 풀어낸다. 즉 Webwright는 한 번 짠 스크립트를 재사용 가능한 CLI 도구로 패키징해서, 다음 번에는 작은 모델로도 같은 일을 시킬 수 있다.
백엔드·자동화 환경·도구까지 한 장으로.
pyproject.toml을 직접 까서 보면 의존성이 놀랍도록 짧다. 그래서 한눈에 그릴 수 있다.
python -m webwright.run.cli가 깔끔한 --task / --start-url 인자를 받게 해주는 게 이놈 역할. Click 기반인데 type hint로 옵션을 자동 추출.{{ workspace_dir }}, {{ task }}, {{ start_url }} 같은 토큰이 런타임에 채워진다.models/openai_model.py · anthropic_model.py · openrouter_model.py. 셋 다 base.py의 Model 추상 클래스를 상속한다. 핵심 메서드는 query(messages) 하나. 메시지 리스트를 받아서 JSON 응답(thought + bash_command + done)을 돌려준다.
OpenRouter를 통하면 Claude·Gemini·Llama 등을 같은 인터페이스로 쓸 수 있다. 백엔드 추가도 ~150줄짜리 파일 하나 만들면 끝.
environments/local_workspace.py가 메인. Bash 명령 한 줄을 받아서 서브프로세스로 실행하고, 표준출력·표준에러·종료 코드를 dict로 묶어 반환한다. 그 안에서 Playwright 스크립트를 heredoc(python - <<'PY' ... PY) 형태로 실행할 수도 있다.
local_browser.py는 별도 Playwright 환경. persistent_browser.yaml로 영구 브라우저 모드도 옵션으로 제공.
agents/default.py의 DefaultAgent가 전부. run() → step() → query() → execute_actions() 네 메서드가 한 사이클. 종료 조건은 done=true가 들어오거나 step_limit(기본 100)을 넘기는 것.
흥미로운 디테일: _compact_history() 메서드가 있다. 매 N 스텝마다 대화 히스토리를 LLM에게 요약시켜 토큰을 절약하는 자동 컨텍스트 압축 기능. 긴 호라이즌 태스크에 필수다.
두 개뿐. tools/image_qa.py = 저장된 스크린샷을 OpenAI Vision으로 질문/검증. tools/self_reflection.py = 최종 실행이 끝난 후 모든 스크린샷+로그를 보고 "성공/실패" 판정.
Claude Code 스킬 버전에서는 이 도구들이 빠진다. Claude가 PNG를 네이티브로 읽을 수 있어서 직접 검증하면 되기 때문.
config/base.yaml(공통) + model_openai.yaml 또는 model_claude.yaml(모델별)을 -c 플래그로 스택. persistent_browser.yaml, local_browser.yaml, task_showcase.yaml 등 시나리오별 오버레이도 있다. recursive_merge로 dict 깊이 병합.
외부 인프라는 거의 없다. 선택사항으로 Browserbase(브라우저 클라우드)에 연결할 수 있고, 모델 API 키만 cred.sh에 export하면 끝. 도커도, 쿠버네티스도, 메시지 큐도 없다. 1인 개발자 PC에서 그대로 돌아간다.
BROWSER_MODE=browserbase로 설정하면 로컬 Chromium 대신 클라우드 세션에 CDP(Chrome DevTools Protocol)로 연결한다. 캡차 우회·IP 분산 등이 필요할 때 쓴다.루프의 한 사이클을 코드 레벨로 해부한다.
핵심을 한 그림으로:
┌──────────────────────────────────────────────────────────────┐
│ Webwright Agent Loop (한 스텝) │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ 1) query(messages) │
│ │ messages │ ──────────────────────────┐ │
│ │ list │ │ │
│ └──────────┘ ▼ │
│ ▲ ┌──────────────┐ │
│ │ │ Model │ │
│ │ 5) add_messages(...) │ (OpenAI / │ │
│ │ │ Anthropic / │ │
│ │ │ OpenRouter) │ │
│ │ └──────┬───────┘ │
│ │ │ │
│ │ │ 2) JSON 응답 │
│ │ ▼ │
│ │ { │
│ │ "thought": "...", │
│ │ "bash_command": "...", │
│ │ "done": false, │
│ │ "final_response": "" │
│ │ } │
│ │ │ │
│ │ │ 3) execute │
│ │ ▼ │
│ │ ┌────────────────────────┐ │
│ │ │ Environment │ │
│ │ │ (local_workspace) │ │
│ │ │ │ │
│ │ │ subprocess.run(bash) │ │
│ │ │ │ │ │
│ │ │ ▼ │ │
│ │ │ ┌──────────────┐ │ │
│ │ │ │ Playwright │ │ │
│ │ │ │ heredoc │ │ │
│ │ │ │ browser │ │ │
│ │ │ │ page │ │ │
│ │ │ │ screenshot │ │ │
│ │ │ └──────────────┘ │ │
│ │ └──────────┬─────────────┘ │
│ │ │ │
│ │ │ 4) Observation: │
│ │ │ stdout/stderr/ │
│ │ │ returncode │
│ └───────────────────────────────┘ │
│ │
│ ⇣ 반복 (step_limit=100 까지) │
│ ⇣ summary_every_n_steps=20 마다 자동 컨텍스트 압축 │
│ ⇣ done=true 가 들어오면 self_reflection 게이트 통과 후 종료│
│ │
└──────────────────────────────────────────────────────────────┘
모델이 매 턴 반환해야 하는 응답은 {thought, bash_command, done, final_response} 네 필드를 가진 JSON 객체 하나. 마크다운도, 코드 펜스도, 산문도 금지. 이 강제 덕분에 파싱이 단순해진다.
만약 모델이 형식을 어기면 FormatError가 발생하고, format_error_template으로 "제대로 된 JSON으로 다시 응답해라"를 모델에게 돌려준다. n_format_errors에 카운트가 쌓이고 추적 가능.
모델이 클릭 좌표를 예측하는 게 아니라 Playwright 스크립트를 짠다. page.get_by_role("button", name="필터 적용").click() 같이 의미적 셀렉터를 쓰면, 사이트 디자인이 살짝 바뀌어도 깨지지 않는다.
또한 한 번 짠 스크립트는 로컬 함수로 추상화 가능. "날짜 선택 5스텝"이 함수 하나가 되고, 다른 날짜로 재사용 가능. 좌표 예측은 이게 불가능하다.
모델이 done=true를 외쳐도 바로 끝나지 않는다. _self_reflection_gate_error()가 검사한다 — 최신 final_runs/run_<id>/ 폴더에 self_reflect_result.json이 있고 predicted_label == 1인가? 아니면 done을 false로 되돌리고, "왜 안되는지" 에러 메시지를 모델에게 돌려준다.
이게 "성공처럼 보이지만 실제로는 실패"를 막는 안전벨트다. 모델이 자기 자신을 자기 손으로 검증한 후에만 종료할 수 있다.
매 실행마다 새 브라우저를 띄우지만, workspace_dir 안의 plan.md · self_reflect_config.json · final_runs/run_001/... · final_runs/run_002/...는 디스크에 남는다. 실행 실패 시 다음 시도(run_002)에서 이전 시도의 스크린샷·로그를 참고할 수 있다.
2-stage 검증이 핵심: ①각 스크린샷을 critical point 리스트와 매칭 → Score 1-5 + Reasoning, ②모든 reasoning + action 로그를 종합해 "success / failure" 판정.
코드 곳곳에 "Always avoid taking full page screenshot, use viewport 1280x1800"라고 못 박혀 있다. page.screenshot(full_page=True)는 사이트가 길수록 메모리 폭주 + 다음 image_qa 호출에서 토큰 폭발. 반드시 뷰포트 크기로 캡처한다.
레포에 뭐가 어디 있는지.
Webwright/
├── pyproject.toml # 패키지 메타 + 9개 의존성
├── README.md # 18KB, 자세한 설명
├── .claude-plugin/ # Claude Code 플러그인 메타
├── .codex-plugin/ # Codex(OpenAI 에이전트) 플러그인 메타
│
├── assets/
│ ├── webwright_logo.svg
│ ├── demo.mp4 # 5.5MB 데모 영상
│ ├── om2w_autoeval_step100.png # Online-Mind2Web 결과 그래프
│ ├── odysseys_eval_step100.png # Odysseys 결과 그래프
│ └── task_showcase/ # 6개 실제 태스크 사례 (gallery)
│ ├── app.py # Flask 갤러리 앱
│ ├── tasks/
│ │ ├── austin_apartments/{task.json, report.json}
│ │ ├── driving_weather/...
│ │ ├── nz_au_legal_jobs/...
│ │ ├── ophelia_pittsburgh/...
│ │ ├── pokemon_tcg/...
│ │ └── slickdeals/...
│ └── templates/{dashboard.html, task.html}
│
├── skills/webwright/ # Claude Code 스킬로 패키징된 버전
│ ├── SKILL.md # description + workflow
│ ├── commands/
│ │ ├── run.md # /webwright:run 슬래시 커맨드
│ │ └── craft.md # /webwright:craft (CLI 도구 생성)
│ └── reference/
│ ├── workflow.md # plan→explore→final→verify 흐름
│ ├── playwright_patterns.md# heredoc 패턴, ARIA 스냅샷 레시피
│ └── cli_tool_mode.md # 재사용 가능한 CLI 생성 규약
│
├── src/webwright/ # 실제 패키지 본체
│ ├── __init__.py
│ ├── exceptions.py # FormatError, InterruptAgentFlow, LimitsExceeded
│ │
│ ├── agents/
│ │ └── default.py # ~450줄, DefaultAgent 본체 ★
│ │
│ ├── environments/
│ │ ├── local_workspace.py # Bash 실행 환경
│ │ └── local_browser.py # Playwright 환경
│ │
│ ├── models/
│ │ ├── base.py # Model 추상 클래스
│ │ ├── openai_model.py # GPT-5.4 등 OpenAI 백엔드
│ │ ├── anthropic_model.py # Claude Opus 4.7 등
│ │ └── openrouter_model.py # OpenRouter 통합 백엔드
│ │
│ ├── tools/
│ │ ├── image_qa.py # 스크린샷 질의응답
│ │ ├── self_reflection.py # 최종 성공 판정
│ │ └── persistent_local_browser.py
│ │
│ ├── config/
│ │ ├── base.yaml # 공통 설정 (system_template 포함!) ★
│ │ ├── model_openai.yaml # OpenAI 오버레이
│ │ ├── model_claude.yaml # Anthropic 오버레이
│ │ ├── model_openrouter.yaml
│ │ ├── local_browser.yaml
│ │ ├── persistent_browser.yaml
│ │ ├── crafted_cli.yaml
│ │ └── task_showcase.yaml
│ │
│ ├── run/
│ │ ├── cli.py # webwright CLI 엔트리포인트
│ │ └── run_command.sh
│ │
│ └── utils/
│ ├── logging.py
│ ├── runtime.py
│ └── serialize.py # recursive_merge 등
│
└── tests/
├── conftest.py
└── unit/
├── test_doctor.py
└── test_tool_model_routing.py
주목할 위치:
① src/webwright/config/base.yaml (system_template만 약 220줄, 에이전트의 "헌법" 같은 곳) → ② src/webwright/agents/default.py (메인 루프) → ③ src/webwright/run/cli.py (어떻게 시작되는지) → ④ skills/webwright/SKILL.md (Claude Code용 어댑테이션).
이 레포에서 무엇을 배울 수 있는가.
Webwright의 모든 브라우저 동작은 bash 한 줄 안에 들어가는 Python 스크립트다. 이 패턴은 LLM이 텍스트 한 덩어리만 출력할 때 매우 유용하다.
python - <<'PY'
import asyncio
from playwright.async_api import async_playwright
async def main():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
ctx = await browser.new_context(viewport={"width": 1280, "height": 1800})
page = await ctx.new_page()
await page.goto("https://www.google.com/flights")
await page.get_by_role("button", name="Search").click()
snapshot = await page.locator("body").aria_snapshot()
print(snapshot)
await browser.close()
asyncio.run(main())
PY
여기서 핵심은 get_by_role() + aria_snapshot() 조합. CSS 셀렉터(div.btn-primary:nth-child(3))는 사이트가 바뀌면 깨지지만, ARIA role/name은 접근성 표준이라 안정적이다.
system_template이 명령한다 — "응답은 strict JSON 객체 하나. 마크다운 금지, 코드 펜스 금지". 이건 LLM에게 매우 익숙하지 않은 제약인데, Webwright는 그걸 강하게 밀어붙인다. 결과적으로 모든 응답을 json.loads() 한 번으로 파싱 가능.
LLM에게 "자유롭게 써라"라고 하면 시인이 되고, "이 양식대로만 채워라"라고 하면 회계사가 된다. Webwright는 회계사를 원한다. 예측 가능성이 곧 디버깅 가능성이기 때문에.
-c base.yaml -c model_claude.yaml -c persistent_browser.yaml처럼 여러 YAML을 깊이 병합해서 최종 설정을 만든다. utils/serialize.py의 recursive_merge가 핵심. dict이면 키별로 재귀적 병합, list이면 덮어쓰기, UNSET sentinel로 "이 값은 안 건드림"을 표현.
매 20스텝(설정값)마다 자동으로 트리거되는 _compact_history(). 시스템 메시지를 보존한 채, 그 외 모든 메시지를 LLM에게 "요약해라"고 시켜서 한 줄 user 메시지로 치환한다. 100스텝짜리 long-horizon에서 토큰 폭발을 막는 트릭.
# default.py 의 _compact_history()
summary_request = self.model.format_message(
role="user",
content=self.config.summary_user_prompt,
)
response = self.model.query(self.messages + [summary_request])
self.messages = [system_message, summary_message] # 압축!
models/base.py의 Model 추상 클래스는 4개 메서드만 요구한다 — query(), format_message(), format_observation_messages(), get_template_vars(). 새 백엔드 추가는 이걸 구현하면 끝. OpenAI든, Gemini든, 자체 호스팅 vLLM이든.
이게 Webwright의 진짜 비밀 무기다. 모델은 plan.md(critical points 체크리스트)를 먼저 만들고, 그 다음 final_script.py를 작성하고, 실행 후 self_reflection이 "각 critical point에 대응되는 스크린샷이 정말 그 조건을 만족하는가?"를 2-stage로 검증한다.
실제 self_reflection 프롬프트는 모델에게 "엄격한 평가자" 역할을 부여한다. 응답 마지막 라인이 정확히 Status: success 또는 Status: failure여야 하고, 그게 아니면 자동으로 failure.
pip install -e . → playwright install chromium → OpenAI 키 export → python -m webwright.run.cli -c base.yaml -c model_openai.yaml -t "찾아라 ...". outputs/<task_id>_<timestamp>/에 trajectory.json + 스크린샷이 쌓이는 걸 직접 본다.
/plugin marketplace add microsoft/Webwright 후 /plugin install webwright@webwright 실행. Claude Code를 재시작하면 /webwright:run, /webwright:craft 슬래시 명령이 활성화된다. 모델 API 키 추가 불필요 — Claude Code 구독으로 모든 게 돌아간다.
실제로 돌리려면 뭐가 필요한가.
Python 3.10+ (3.11 권장, async 성능 차이). Playwright 호환 Chromium(또는 Firefox — Claude Code 스킬은 Firefox 사용). RAM 8GB+ (Chromium이 사이트당 1-2GB 잡아먹음). 디스크 5GB+ (브라우저 + 스크린샷 누적).
OPENAI_API_KEY (GPT-5.4 권장, 가장 강함) 또는 ANTHROPIC_API_KEY (Claude Opus 4.7, 어려운 split에서 더 강함) 또는 OPENROUTER_API_KEY (다양한 모델 시도용). 한 번 풀 태스크에 $0.50~$2 정도 소비. 100스텝 한도까지 가면 더 들 수 있음.
로컬 머신이 약하거나 사이트가 IP 차단을 강하게 하면 BROWSERBASE_API_KEY + BROWSERBASE_PROJECT_ID 설정 후 browser_mode: browserbase로 클라우드 브라우저 임대. 가격은 분 단위 과금.
Claude Code 구독($20/월)만 있으면 skills/webwright/를 통째로 가져와 LLM API 비용 0원으로 돌릴 수 있다. PNG 검증도 Claude가 직접 함.
Cloudflare·reCAPTCHA가 강한 사이트(은행, 항공권 일부)는 로컬 Chromium에서 막힐 수 있다. 그래서 Webwright 스킬 버전은 Firefox로 기본 변경(ERR_HTTP2_PROTOCOL_ERROR 회피). 그래도 막히면 Browserbase로 넘어가야 한다.
난이도별 5단계.
start_url을 네이버 영화 페이지로, task를 "오늘 박스오피스 1위 영화의 제목과 평점을 알려줘"로 설정. 한 페이지 안에서 끝나는 단순 추출 태스크. 배울 점: ARIA 스냅샷 출력하고 텍스트 파싱하는 기본 흐름.
"쿠팡에서 무선이어폰 10만원 이하, 평점 4.5↑, 무료배송 필터 적용한 상위 3개의 가격과 모델명". 필터 컨트롤 확장 → 체크박스 클릭 → 적용 확인 → 결과 캡처. 배울 점: 필터가 드로어/아코디언 뒤에 숨어있을 때 어떻게 다루는지.
"부킹닷컴에서 2026-08-10 ~ 2026-08-13, 성인 2명, 강남 위치, 4성 이상, 50만원 이하 호텔 중 평점 가장 높은 곳"의 예약 페이지 진입까지(예약 버튼 클릭 직전). 날짜 피커, 다중 필터, 정렬, 다음 페이지 진입. 배울 점: long horizon에서 self_reflection 게이트가 어떻게 작동하는지 직접 체감.
/webwright:craft로 "구글 플라이트에서 [출발지] → [도착지] [날짜] 최저가 이코노미 검색"을 짠 후, final_script.py --origin ICN --destination LAX --depart-date 2026-09-01로 파라미터를 바꿔 재실행. 배울 점: 한 번 짠 자동화를 재사용 가능한 함수로 추상화하는 패턴.
models/base.py를 상속해 models/local_vllm_model.py를 만들고, 로컬 호스팅된 Qwen2.5-72B나 Llama-3-70B를 백엔드로 붙인다. config/model_qwen.yaml도 작성. 그 다음 동일한 태스크를 GPT-5.4와 Qwen으로 각각 돌려서 trajectory.json 비교. 배울 점: Webwright의 추상화가 정말 모델-아그노스틱(model-agnostic)인지 검증 + 작은 모델의 한계 파악.
4주 코스로 정리.
Playwright 공식 문서의 "Locators" 챕터를 정독. get_by_role, get_by_text, get_by_label, get_by_placeholder의 차이를 손에 익힌다. page.wait_for_selector, page.wait_for_load_state("networkidle") 등 대기 전략도 학습. 매일 한 사이트씩 자동화 스크립트 짜기.
httpx + asyncio로 OpenAI API 콜을 직접 짜본다(SDK 안 쓰고). 스트리밍, 재시도, 백오프, 토큰 카운팅을 손으로 구현. webwright/models/openai_model.py를 따라 만들어보고, 자기만의 "JSON 응답 강제" 패턴을 시도. 참고: tiktoken 또는 anthropic-tokenizer로 토큰 계산.
SWE-agent/mini-swe-agent(Webwright의 영감 소스)와 Webwright의 default.py를 나란히 놓고 비교. ReAct 패턴(Reason + Act), Reflexion(자기 회고), Plan-and-Solve 같은 학술 패턴이 코드에서 어떻게 등장하는지 매핑. 읽을 논문: ReAct (Yao et al., 2022), Reflexion (Shinn et al., 2023).
Online-Mind2Web · WebArena · VisualWebArena · Odysseys 벤치마크 페이퍼를 읽고, 각각이 무엇을 측정하는지 이해. 작은 부분집합(10~20 태스크)을 직접 받아서 Webwright로 돌려본다. trajectory.json을 분석해서 "어떤 종류의 태스크에서 실패하는지" 패턴 분석. 최종적으로 자기만의 critic 모델을 self_reflection에 끼워본다.
한 번 더 정리.
- [ ] CP1: ... 형식으로 기록. 각 CP는 스크린샷 한 장 또는 로그 한 줄로 독립적으로 검증 가능해야 한다.Status: success 또는 Status: failure를 출력하는 2-stage 평가. predicted_label==1일 때만 done 허용.python - <<'PY' ... PY 형태로 stdin에 코드 블록을 흘려넣어 실행. LLM이 한 번에 출력해야 하는 "bash_command 한 문자열"에 멀티라인 Python을 담는 트릭.page.locator("body").aria_snapshot()로 추출. 픽셀 좌표 대신 "이 페이지에 어떤 의미적 요소가 있는지"를 텍스트로 보여준다.summary_every_n_steps마다 트리거. 100스텝짜리 호라이즌에서 토큰 폭주 방지.utils/serialize.py에 정의. -c base.yaml -c model_claude.yaml처럼 여러 YAML을 합칠 때 핵심.base.yaml에 정의. FormatError 예외와 짝.더 깊이 파고들 자료.
GitHub: github.com/microsoft/Webwright
Microsoft Research 블로그: Webwright: A terminal is all you need for web agents
웹사이트 & 데모 비디오: microsoft.github.io/Webwright
Webwright가 "design inspiration"이라고 명시한 레포. 미니멀 에이전트 루프의 원형. github.com/SWE-agent/mini-swe-agent
playwright.dev/python — 특히 "Locators", "Auto-waiting", "Accessibility" 챕터 추천.
Mind2Web: 실사이트 대상의 GUI 에이전트 벤치마크. Online 변형은 실시간 사이트에서 평가. Mind2Web 홈
Odysseys: 200개의 long-horizon 태스크 벤치마크. Webwright 블로그 글에 자세한 소개.
ReAct (Yao et al., 2022) — Reasoning + Acting을 교차로 하는 에이전트 패턴의 시작.
Reflexion (Shinn et al., 2023) — 에이전트의 자기 회고를 통한 성능 향상.
WebArena (Zhou et al., 2023) — 웹 에이전트 벤치마크의 표준.
Code as Policies (Liang et al., 2022) — 코드를 액션 공간으로 쓰는 초기 아이디어.
pip install -e . → playwright install chromium → OpenAI 키 export → README에 있는 SEA→JFK 항공권 검색 명령 그대로 실행. outputs/ 폴더에 trajectory.json과 스크린샷이 쌓이는 걸 본다.skills/webwright를 ~/.claude/skills/webwright에 심볼릭 링크하고 Claude Code 재시작. /webwright:run으로 자연어 한 줄에 자동화 시작. API 키 없이도 동작한다는 게 진짜 매력.self_reflect_config.json의 4개 프롬프트를 자기 도메인에 맞게 다시 짠다. 예: 쇼핑 가격 비교 태스크 전용 critic, 데이터 추출 태스크 전용 critic. Webwright는 이런 prompt-engineering 친화적 설계가 돋보인다.