QA Agent 브라우저 추상화와 에이전트 카탈로그 자체 구현

26년 03월 23일

Playwright MCP에서 agent-browser로

어제 설계한 멀티 에이전트 전환 계획을 실행에 옮긴 날이었다.

브라우저 제어 방식을 바꾸고 외부 플러그인 의존성을 걷어내고 에이전트 역할을 코드로 직접 정의했다.

런타임 복잡도를 줄이겠다고 어제 세운 계획을 실제로 구현한 셈이다.

그 전까지 QA Agent는 브라우저를 제어할 때 Playwright MCP를 직접 호출했다.

Playwright MCP는 OpenCode 런타임이 관리하는 외부 도구라, QA Agent가 직접 브라우저 세션을 통제하기가 어려웠다.

그래서 agent-browser라는 CLI 래퍼를 만들어서 그 사이에 끼웠다.

이제 브라우저 조작은 전부 파일 실행 함수("agent-browser", [action, ...args]) 형태로 agent-browser를 호출한다.

QA Agent 코드베이스에는 Playwright import가 하나도 없다.

agent-browser가 내부적으로 Playwright와 Chromium을 쓰는 거지, QA Agent는 그냥 CLI 호출만 한다.

이렇게 하니까 브라우저 세션을 코드 레벨에서 직접 통제할 수 있게 됐다.

세션 키를 넘기고 프로필 디렉토리를 지정하고 세션을 격리하는 것까지 전부 QA Agent가 결정한다.

브라우저 소유권 모델

agent-browser를 도입하면서 브라우저 소유권 규칙도 같이 세웠다.

마스터 에이전트만 브라우저를 준비하고 실행 에이전트는 준비된 브라우저를 쓰기만 한다.

이 규칙이 중요한 이유는 세션 충돌 때문이다.

여러 에이전트가 저마다 브라우저를 띄우면, 같은 계정으로 로그인하면서 서로 세션을 밟거나 충돌이 난다.

소유권을 마스터 에이전트 하나로 모으니까 이런 문제가 사라졌다.

슬랙 스레드 단위 격리

그리고 슬랙 스레드별로 브라우저 세션을 격리하는 구조를 만들었다.

스레드 ID를 키로 써서 네 단계로 격리한다.

작업 디렉토리를 스레드마다 따로 만들고 실행 시작할 때마다 지운다.

브라우저 프로필 디렉토리도 스레드마다 따로 둬서 쿠키랑 로그인 상태가 섞이지 않게 한다.

세션 키도 스레드 ID 기반이고 세션 레지스트리도 스레드 ID로 묶어서 관리한다.

종료할 때 해당 스레드의 Chrome 프로세스만 죽인다.

그러니까 한 슬랙 스레드에서 돌린 QA가 다른 스레드에 영향을 주지 않는다.

로그인 상태가 새어나가거나 쿠키가 섞이는 일이 없다.

oh-my-opencode 의존성 걷어내기

브라우저 외에도 런타임 의존성을 하나 걷어냈다.

oh-my-opencode라는 플러그인이 있었는데, 이게 에이전트 카탈로그를 제공하는 역할이었다.

각 역할의 에이전트 정의를 들고 있었다.

근데 이 플러그인이 계속 문제를 일으켰다.

OpenCode 패키지 캐시로 로드되다

보니까, 설치된 버전이랑 실제 로드되는 버전이 자꾸 어긋났다.

설정 경로 맞추는 것도 까다로웠다.

환경변수 몇 개를 신경 써야 전역 설정을 오염시키지 않는데, 이걸 잘못 맞추면 이상한 곳에서 에러가 터졌다.

버전 불일치 버그가 반복적으로 나니까, 아예 의존성을 빼기로 했다.

대신 에이전트 역할을 런타임 카탈로그로 직접 구현했다.

마스터, Planner, Executor, Reporter, Checker, Explorer 여섯 역할을 코드로 정의했다.

각 에이전트가 독립적인 모델이랑 권한을 가진다.

Checker가 완료를 판정한다

이 카탈로그에서 특히 중요한 역할이 Checker다.

완료 판정을 마스터 에이전트가 자기 선언으로 하는 게 아니라, Checker가 독립적으로 검증한다.

마스터가 “다

했다”고 해도 Checker가 통과시켜야 진짜 완료다.

Checker가 부족한 점을 찾으면 다시 실행하는 루프가 돈다.

자기 검증에 취약한 에이전트 시스템에서 독립적인 검증 역할을 둔 게 핵심이었다.

엔지니어링 디테일

이날 작업하면서 자잘한 품질 개선도 같이 했다.

하나는 lazy initialization이다.

브라우저를 실제로 필요할 때까지 띄우지 않게 만들었다.

불필요한 브라우저 실행을 줄이니까 리소스 낭비가 줄었다.

그리고 macOS Keychain 팝업 문제도 잡았다.

macOS에서 브라우저 자동화를 돌리면 OS가 자꾸 Keychain 인증 팝업을 띄워서 무인 실행이 막히는 경우가 있다.

이걸 우회하는 처리를 넣었다.

마지막으로 전체 게이트를 다

통과했다.

타입체크, 단위 테스트, 린트, smoke 테스트 전부 초록불이었다.

마무리

하루 만에 런타임 복잡도를 확 줄였다.

외부 플러그인 의존성을 걷어내고 브라우저 제어를 직접 손에 쥐고 에이전트 역할을 코드로 정의했다.

agent-browser 추상화는 특히 잘 먹혔다.

슬랙 스레드 단위 격리가 가능해지니까, 여러 사람이 동시에 QA를 돌려도 서로 간섭이 없다.

브라우저 소유권을 마스터로 모은 것도 세션 충돌을 깔끔하게 없애줬다.

이건 중간 단계였다. 약 석 달 뒤에는 OpenCode 런타임 자체를 버리고 pi SDK로 완전히 갈아엎었다.

하지만 이날 만든 브라우저 추상화랑 에이전트 역할 분리 원칙은 그 이후에도 뼈대로 남았다.