문서

Laya를 브라우저 에이전트 의사결정 head로 파인튜닝하기

Laya가 제로샷으로 못 하는 결정군을 위해 Laya를 전문화하는, 완전히 재현 가능한 실습 예제: browser-use/jev-ultrafast를 위한 다음 브라우저 동작(연산 + 대상 요소) 고르기. 이 저장소의 /v1/systemone 요청 형식은 Agent.predict(state, questions)와 같습니다. 아래의 모든 것은 유료 API 없이 하나의 RTX 4070 Ti SUPER(16 GB)에서 실행되었으며, 가중치, 코드, 실행별 결과는 huggingface.co/cklxx/laya-browser에 있습니다.

결과

typed-decisions, 제로샷 파인튜닝
홀드아웃 페이지에서 요소 top-1(2,734개 의사결정, 각 약 45개 후보) 0.10(무작위) 0.66(421M) / 0.63(322M)
연산 정확도(CLICK / TYPE_TEXT / SELECT / DONE) 0.54 0.88~0.89
실제 브라우저 작업 16개, 각 3회 실행 0 % 62 %(322M), 50 %(421M)
단계당 지연 시간(질문 3개, 후보 30~65개) 50~200 ms 41~50 ms(421M), 17~23 ms(322M)

라이브 스위트는 쌍봉형입니다. 10개 작업은 3/3 통과하고(카테고리 / 탭 / 페이지 이동, 체크박스, <select>, 일부 사이트의 검색 + 제출) 6개는 3/3 실패합니다(입력 후 제안 고르기 흐름, 먼저 스크롤해야 하는 페이지네이션, Google Flights). 라이브 사이트에서 실행 간 분산이 두 백본 사이의 격차보다 크므로, 둘을 동등하게 취급하고 지연 시간으로 고르십시오.

체크포인트는 평범한 Laya 체크포인트 디렉터리입니다:

agent = laya.load("laya-browser/v10s")                       # after huggingface-cli download cklxx/laya-browser
agent.cfg["head_max_len"] = agent.cfg["head_max_len_train"]  # 768; the config records the input format too

파이프라인

모든 단계는 Hub 저장소의 code/finetune/에 있는 스크립트이며, run_v10.sh / run_v10s.sh가 그것을 처음부터 끝까지 실행합니다.

  1. 크롤링: jev의 DOM 리더로 실제 페이지 421개(Wikipedia, GitHub, HN, arXiv, HF, 데모 상점, 폼이 많은 테스트 사이트)를 크롤링하고, 요소 표와 페이지 텍스트를 보관합니다.
  2. 목표 역생성(5,244개): 요소 하나를 답으로 고르고, 로컬 Qwen3-8B에게 사용자가 그것을 필요로 하며 말할 목표를 쓰게 합니다. 교사가 무엇을 풀어야 할 필요가 없으므로 레이블이 깨끗합니다.
  3. 실제 DONE 상태(700개): 브라우저에서 클릭을 실행하고, 도착 페이지를 이력과 함께 DONE 사례로 기록합니다.
  4. 2단계 부정 사례(659개): 그 도착 페이지들에서 이력을 유지한 채 새 목표를 만들어, “이력이 있음”이 DONE을 예측하지 못하게 합니다.
  5. Mind2Web (osunlp/Mind2Web, 7,296단계): 후보를 요소 표로 다시 렌더링하고, 이력은 action_reprs에서 가져오며, 입력된 값은 필드의 현재 값으로 표시합니다.
  6. 온폴리시 교정(DAgger, 177개): 현재 모델로 실제 작업을 실행하고, 각 단계에서 로컬 LLM에 물어보고, 그 판정을 모델 자체의 상태와 함께 보관합니다.
  7. 빌드 → 학습 → 캘리브레이션 → 평가: Laya의 RLCD 레시피(골드 분포 소프트 타깃 + 노이즈 있는 로짓 정책 그래디언트 + 소프트 CE), 단일 GPU, 그래디언트 체크포인팅 없음, 4 에포크(421M에 약 2시간, 322M에 약 1시간), 사후 온도, 평가용 홀드아웃 페이지 / 웹사이트.

가장 중요했던 것: 입력 형식

jev의 state를 그대로 전달하면(페이지 텍스트 + 전체 요소 표를 state 안에 JSON으로) 1,024토큰 창이 표의 대부분을 잘라내므로, 모델이 골라야 할 후보를 아예 보지 못하는 일이 잦습니다. 요소를 state 밖으로 빼내 옵션 목록에 넣는 것(전체 레이블 + 역할 + 현재 값, head_max_len 512 → 768; state는 제목 / URL / 이력 / 텍스트 1.2~1.5k자를 유지)이 어떤 데이터 변경보다도 값졌습니다. 같은 데이터에서 Mind2Web 클릭 top-1이 0.44 → 0.51, 라이브 스위트가 6/16 → 10/16이 되었습니다.

통하지 않은 것(반복하지 않도록)

  • 템플릿화된 DONE 목표(“X라는 제목의 페이지를 열고, 열리면 멈춰라”)는 문구를 누출합니다. 모델이 멈출 때 ⇒ DONE을 학습합니다. DONE 표본은 실행된 동작 이후의 실제 도착 페이지여야 합니다.
  • 모든 DONE 표본이 정확히 하나의 사전 동작을 갖고 모든 클릭 표본이 갖지 않으면, 모델이 이력이 있으면 ⇒ DONE을 학습합니다. 작업 중간의 부정 사례를 추가하십시오.
  • Mind2Web만 쓰면 DONE / TYPE_TEXT를 죽입니다(DONE이 없고 CLICK이 지배적). 희귀 연산의 가중치를 다시 주십시오(DONE ×4, TYPE_TEXT / SELECT ×3).
  • 페이지 텍스트를 3,000자로 자르는 것은 아무것도 아끼지 못하고(head가 시퀀스를 지배) top-1을 0.04 잃었습니다.
  • 가변 길이 배치에 torch.compile을 쓰면 형상마다 다시 컴파일합니다: 6배 느려집니다. 그래디언트 체크포인팅을 끄는 것이 진짜 공짜 이득이었습니다(1.25배).
  • 신뢰도로 게이팅해 로컬 8B나 27B LLM으로 에스컬레이션하면 결과가 더 나빠졌습니다. 이 페이지들에서는 파인튜닝된 322M 모델이 더 나은 결정자입니다(300토큰 사고 예산을 가진 27B: 단계당 4.7초에 0.861 연산 정확도 / 0.603 top-1, 대 21 ms에 0.890 / 0.623). 더 나아가려면 더 강한 교사가 필요합니다.
  • jev의 DOM 리더는 설계상 비밀번호 필드를 숨기고 접힌 메뉴를 결코 보지 못합니다. 일부 “실패”는 모델이 아니라 프레임워크입니다.

재현

huggingface-cli download cklxx/laya-browser --local-dir laya-browser
cd laya-browser/code && uv sync --extra fast
uv run python verify.py v10s            # downloads the checkpoint, answers one recorded browser step

그 저장소의 code/finetune/README.md에 첫 시도부터 최종까지의 모든 중간 수치가 있고, results/에 위 표 뒤에 있는 실행별 스위트 JSON이 있습니다.