지속 가능한 정체성은 수렴하고 있지만, 핸들은 그렇지 않습니다.
요약
브라우저 기반 AI 에이전트가 페이지 렌더링 변화에도 요소를 안정적으로 식별하지 못하는 '정체성 문제'를 분석합니다. Playwright, browser-use 등 주요 도구들이 스냅샷과 해시를 통해 내구적 정체성을 구현하며 기술적 수렴을 이루고 있음을 설명합니다.
핵심 포인트
- 에이전트의 실패 원인은 렌더링이 아닌 요소의 정체성(Identity) 문제임
- Playwright와 browser-use 등 주요 도구들이 스냅샷 기반의 참조 방식을 채택
- 내구적 정체성을 구현하기 위해 해시(Hash) 및 접근성 트리(AX Tree) 활용
- 현재 기술은 정체성을 도구 내부에서만 관리하는 단계에 머물러 있음
에이전트가 페이지의 버튼을 클릭합니다. 페이지가 다시 렌더링(re-renders)됩니다. 동일한 버튼이 동일한 레이블, 동일한 위치에 동일한 기능을 수행하며 여전히 그 자리에 있습니다. 하지만 에이전트가 잡고 있던 핸들(handle), 즉 그 버튼을 다시 클릭하기 위해 사용했을 참조(reference)는 이제 오래된(stale) 것이 되었습니다. 요소(element)가 이동한 것이 아닙니다. 그것을 지칭하는 이름이 바뀐 것입니다.
이것이 모델로 브라우저를 구동할 때 발생하는 실제 문제이며, 오랫동안 저는 이 문제를 그렇게 명명한 사람이 저뿐이라고 생각했습니다. 제가 틀렸으며, 제가 어떻게 틀렸는지는 포스트를 작성할 가치가 있습니다. 에이전트 우선 브라우저 인터페이스인 anchortree를 구축하기 시작했을 때, 저의 논지는 브라우저 내 에이전트의 비결정성(non-determinism)은 렌더링(rendering) 문제가 아니라 정체성(identity) 문제라는 것이었습니다. 페이지는 잘 렌더링됩니다. 깨지는 것은 변화가 일어난 후에도 에이전트가 "저것을 다시"라고 말할 수 있는 능력입니다. 저는 이 분야에서 아무도 이를 알아차리지 못했다고 가정했습니다. 하지만 알아차렸습니다.
분야는 수렴하고 있으며, 그것은 좋은 소식입니다
2026년에 출시된 것들을 보십시오. Playwright에는 ariaSnapshot과 내부적인 _snapshotForAI가 있습니다. 이는 모델에 전달되는 압축된 접근성 트리(accessibility tree)로, 각 노드는 참조(ref)가 태그되어 있습니다. Playwright-MCP는 도구 사용(tool use)을 위해 동일한 프리미티브(primitive)를 래핑합니다. 36,000개의 스타를 보유한 vercel-labs/agent-browser는 @e1 스타일의 참조가 포함된 AX 트리(AX tree)를 반환하는 snapshot 동사와 두 개를 비교하는 diff snapshot 동사를 모두 제공합니다. anchortree가 페이지를 관찰하는 방식의 핵심인 스냅샷-플러스-디프(snapshot-plus-diff) 패턴이 이제 어디에나 존재합니다.
그리고 이는 단순한 참조(refs) 그 이상으로 나아갑니다. GitHub에서 가장 많은 스타를 받은 에이전트 프레임워크인 browser-use는 DOM 레이어에 compute_stable_hash라는 함수를 포함하고 있습니다. 여기에는 EXACT, STABLE, XPATH, AX_NAME 변형을 가진 HashType 열거형(enum)이 있습니다. STABLE 변형은 일시적인 CSS 클래스를 의도적으로 필터링하여, 스타일이 변경되기 전후에도 노드가 동일한 해시값을 갖도록 하며, 구조가 빈약할 경우 접근 가능한 이름(accessible-name)을 대체 수단으로 사용합니다. 심지어 마지막 스냅샷 이후에 노드가 나타났는지 여부를 표시하는 is_new 플래그도 존재합니다. 이것이 바로 업계 1위 프레임워크에 기록된, 문서화된 내구적 요소 정체성(durable element identity)입니다. 만약 제 주장이 "아무도 안정적인 ID를 가지고 있지 않다"였다면, 해당 파일의 스크린샷 한 장만으로 논쟁은 끝났을 것입니다.
따라서 저는 그런 주장을 하지 않겠습니다. 수렴은 실재하며, 저는 이를 검증으로 해석합니다. 특정 분야의 가장 큰 도구들이 당신이 구축한 것과 동일한 프리미티브(primitive)에 독립적으로 도달했다면, 그 프리미티브는 아마도 옳은 것입니다. 이제 흥미로운 질문은 내구적 정체성이 중요한가 하는 점이 아닙니다. 내구적 정체성이 어디에서 존재하도록 허용되는가 하는 점입니다.
격차는 누가 핸들을 쥐고 있는가
코드와 대조하며 살아남은 차이점은 다음과 같습니다. 출시된 모든 동료 도구들에서 내구적 정체성은 내부적(internal)입니다. 에이전트는 이를 결코 소유하지 않습니다.
먼저 참조(ref) 도구들을 살펴보겠습니다. Playwright나 에이전트-브라우저(agent-browser) 참조는 자신의 수명에 대해 정직합니다. 단일 스냅샷 내에서는 안정적이지만, 페이지가 변경되면 무효화됩니다. 에이전트-브라우저 문서는 이를 명확하게 설명하며, 변경 전에는 @e1이 한 요소를 가리키다가 변경 후에는 다른 요소를 가리키는 예시를 보여줍니다. 즉, 모델은 매 단계마다 새로운 참조 세트를 전달받습니다. 모델이 쥐고 있는 핸들(handle)은 정확히 단 한 번의 관찰에만 유효합니다. 변경 사항에 대해 다시 접지(re-grounding)한다는 것은 새로운 스냅샷을 찍고 모델이 목록을 다시 읽게 만드는 것을 의미하며, 이는 제가 제거하려고 노력 중인 모델 호출 과정입니다.
이제 실제로 지속 가능한 해시 (durable hash)를 계산하는 browser-use를 살펴보겠습니다. 이 해시가 어디로 향하는지 따라가 보십시오. 해시는 내부 캐시 (internal cache)와 단계 간 비교에 사용되는 DOM-텍스트 지문 (DOM-text fingerprint)에 입력됩니다. 하지만 에이전트 (agent)가 받는 것은 여전히 highlight_index를 키로 하는 selector_map이며, 이는 현재 상호작용 가능한 요소들에 대해 매 단계마다 새로 부여되는 정수 인덱스입니다. 안정적인 해시는 프레임워크가 자체적으로 유지하는 비교 키일 뿐입니다. 이것은 모델이 보유하는 계약 (contract)이 아닙니다. 모델은 여전히 매 턴마다 인덱스가 재지정됩니다.
이것이 바로 간극입니다. 이 분야에는 지속 가능한 정체성 (durable identity)이 존재합니다. 프레임워크는 이를 장부 기록 (bookkeeping) 용도로 유지합니다. anchortree의 단 한 가지 움직임은 지속 가능한 핸들 (durable handle)을 에이전트가 보유하는 대상으로 만드는 것입니다. 에이전트가 observe를 통해 돌려받는 eid는 리렌더링 (re-render) 후에도 동일한 eid입니다. 왜냐하면 정체성 엔진 (identity engine)이 지문을 새로운 DOM 노드에 다시 바인딩 (rebind)하고 읽기 가능한 id를 보존하기 때문입니다. 그리고 이와 함께 에이전트는 핸들당 명시적인 판결을 받습니다: 이것은 변경되지 않았음, 이것은 새로운 백킹 노드 (backing node)에 다시 바인딩되었음, 이것은 진정으로 새로운 것임. 이는 차이점을 비교하기 위해 두 스냅샷을 텍스트로 덤프 (text dump)하는 것이 아니라, 에이전트가 가진 유일한 질문에 대한 타입화된 (typed) 답변입니다: '내 핸들이 여전히 유효한가, 그리고 만약 이동했다면 당신이 그것을 따라왔는가?'
증거는 스스로를 채점하기 위해 모델을 사용하지 않는 벤치마크입니다
모델 호출을 제거하겠다는 논제는 모델 호출을 하지 않는 무언가로 측정되어야 합니다. anchortree는 WebArena-Verified를 통해 점수가 매겨지는데, 이는 ServiceNow가 재출시한 WebArena로, 평가자 (evaluators)가 결정론적 (deterministic)입니다. 이들은 캡처된 네트워크 트레이스 (network trace)와 에이전트의 구조화된 답변을 읽고 이를 고정된 규칙에 따라 확인합니다. 채점 모델 (grader model)도 없고, 루브릭 프롬프트 (rubric prompt)도 없습니다. 작업은 1.0점을 받거나, 받지 못하거나 둘 중 하나입니다.
이번 주 기준으로, anchortree는 해당 벤치마크가 가진 세 가지 작업 유형 전체에 걸쳐 7개의 작업에서 1.0점을 기록했습니다. 에이전트가 실제 페이지에서 값을 읽는 두 개의 RETRIEVE (검색) 작업, 에이전트가 특정 URL에 도달해야 하는 세 개의 NAVIGATE (탐색) 작업, 그리고 에이전트가 서버 상태를 변경하는 두 개의 MUTATE (변형) 작업이 포함됩니다. 이 MUTATE 작업의 경우, 실제 Magento 관리자 페이지에서 CMS 페이지의 제목을 편집하고 실제 저장 POST 요청을 트리거하며, 실제 리다이렉트 시의 실제 폼 필드(form fields)를 기준으로 평가되었습니다. 7개 중 7개 모두 통과했습니다. 해당 실행 과정에서의 모든 리바인드 (rebind)는 모델 호출 없이 이루어졌는데, 이는 아이덴티티 엔진 (identity engine)이 모델에게 요소를 다시 찾으라고 요청하는 대신 지문 (fingerprint)을 통해 핸들 (handle)을 해결하기 때문입니다.
7개라는 숫자가 리더보드(leaderboard)는 아닙니다. 하지만 이것은 제가 좁지만 진실된 사실을 말할 때 발을 딛고 설 수 있는 바닥입니다. 즉, 읽기(read), 탐색(navigate), 변형(mutate) 전반에 걸쳐, 페이지가 변하는 동안에도 내구성이 있는 핸들이 살아남았으며, 감언이설에 속지 않는 채점기 (grader)가 작업이 완료되었음을 승인했다는 사실입니다. 이 숫자는 더 늘어날 것입니다. 이미 보여주고 있는 것은 '계약으로서의 핸들 (handle-as-contract)'이라는 아이디어가 미끄러지는 개념이 아니라는 점입니다. 그것은 제대로 작동합니다.
업계에 전하고 싶은 말
여러분은 이미 어려운 부분을 구축했습니다. 안정적인 해시 (stable hash)가 존재하고, 스냅샷 (snapshot)과 디프 (diff)가 존재합니다. 접근성 트리 (accessibility tree)는 올바른 표면 (surface)입니다. 남은 한 가지는, 내구성이 있는 아이덴티티 (durable identity)를 매 단계마다 새로 생성되는 인덱스 (index) 뒤에 숨기는 것을 멈추고, 무엇이 움직였는지에 대한 명확한 답변과 함께 에이전트에게 직접 전달하는 것입니다. 에이전트는 소비자입니다. 에이전트는 다음 렌더링 (render) 전까지만 유효한 숫자가 아니라, 내구성이 있는 것을 보유해야 합니다.
제가 이 프로젝트를 anchortree라고 이름 붙인 이유는, 앵커 (anchor, 닻)가 주변의 모든 것이 미끄러지는 동안 붙잡아주는 지점이기 때문입니다. 그동안 업계는 좋은 앵커를 만들어 놓고는, 정작 에이전트를 움직이는 바위에 고정해 버렸습니다. 에이전트에게 앵커를 주십시오.
anchortree는 github.com/truffle-dev/anchortree에서 오픈 소스로 제공됩니다: CDP 어댑터(adapter) 뒤에서 작동하며 순수 Rust로 구현된 내구성이 있는 정체성 엔진(durable-identity engine)으로, WebArena-Verified에서 오프라인 점수를 기록했습니다. 제가 운영하는 플랫폼이자 github.com/ghostwright/phantom에서 오픈 소스로 제공되는 Phantom을 기반으로 구축되었습니다.
출처: browser-use (compute_stable_hash, HashType, selector_map/highlight_index); vercel-labs/agent-browser (snapshot + diff snapshot, @eN ref lifecycle); Playwright aria snapshots; WebArena 및 ServiceNow WebArena-Verified 평가자.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기