ZeroDOM

라이브 웹 페이지를 AI 브라우저 에이전트용 토큰 최적화 상호작용 그래프로 파싱합니다 — Playwright의 ARIA 스냅샷 모드보다 토큰을 71% 적게 사용합니다. 모든 CSS 선택자는 속성에서 추정되는 것이 아니라 반환되기 전에 실제 문서 전체에서의 고유성이 확인됩니다.

문서

Every tool here answers the same question: what can this page do, addressed precisely enough that an agent can act on it without guessing. An agent working from raw HTML either burns its context window re-reading a 30,000-token page every turn, or gets a hand-rolled subset of the DOM that misses the one button it needed. ZeroDOM sits between the two: a deterministic pass over the page that keeps everything an agent can click or fill, drops everything it can't, and hands back a graph small enough to reread on every single action without it mattering.

Nothing about that pass touches a model. Parsing is lxml (Python) or linkedom (TypeScript) over HTML you already have — from a Playwright page, from a raw string, from a file — and it runs the same way every time. The only thing that ever reaches an LLM is the text you choose to send it: graph.to_compact_text(), a few hundred tokens instead of the page. Start with the quickstart if you already know Playwright, or install first if you're setting up from scratch.

Measured across 111 live sites — static pages, SPAs, web components, shadow DOM, iframes, dashboards, commerce, and login walls — 10,756 audited nodes resolved to exactly one live element 99.00% of the time, with 0.03% ambiguous and zero invalid selectors. Median token savings against raw HTML: 98.9%.

How it fits together

The selector never leaves your process. A model only ever sees a node id like [03] — the CSS path that actually addresses the element lives in selector_map(), on your side, and gets resolved right before the click. On real pages that selector is often longer than the label describing it; keeping it out of the prompt is most of where the token savings above come from.

1

Live page

a Playwright Page, or HTML you already have

2

ZeroDOMParser

one DFS pass — prune, collect, label

3

InteractionGraph

compact text or JSON, sent to the model

4

Model picks a node

e.g. click 03 — never a selector

5

selector_map()[node]

resolved on your side, not the model's

6

Act

click/fill via Playwright, then re-read