AI 에이전트 벤치마크를 뜯어보기
Table of Contents
새 모델이 나올 때마다 발표 자료에 벤치마크 점수가 딸려 온다. SWE-Bench, Terminal-Bench, OSWorld, Humanity’s Last Exam, GDPval… 대개는 “코딩 잘함”, “컴퓨터 잘 씀”, “추론 잘함” 정도로 읽고 넘기게 된다.
그런데 이 점수가 실제로 어떻게 계산되는지는 의외로 잘 안 들여다보게 된다. SWE-Bench 70%가 GitHub 이슈에 정답 코드를 맞힌 비율인지, GUI 평가라면 실제로 마우스를 움직이는지, 터미널은 진짜로 쓰는지 — 확인해보기 전엔 그냥 넘어갔던 것들이다.
다섯 개를 논문이랑 실제 harness 코드까지 확인해보니, 문제만 다른 게 아니라 평가 방식 자체가 딴판이었다. 어떤 건 Docker에서 저장소를 통째로 돌리고, 어떤 건 가상머신을 부팅해서 데스크톱을 조작하고, 어떤 건 사람이 붙어서 결과물을 채점한다. 같은 “벤치마크"인데 안을 열어보면 거의 다른 물건이다. 근거가 되는 논문·저장소는 각 벤치마크 이름에 링크로 걸어뒀다.
일단 재려는 게 다 다르다
처음엔 다 비슷한 시험인 줄 알았는데, 논문을 보니 애초에 측정하려는 능력이 달랐다.
| 벤치마크 | 재는 것 | 채점 방식 |
|---|---|---|
| Humanity’s Last Exam | 전문가 수준 학술 추론 | 정답 대조 |
| SWE-Bench | 실제 GitHub 이슈 해결 | 테스트 실행 |
| Terminal-Bench | CLI 작업 자동 수행 | 테스트 실행 |
| OSWorld | GUI로 컴퓨터 조작 | 상태 검사 |
| GDPval | 실무 산출물 생성 | 사람이 비교 |
HLE는 문제만 맞히면 된다. 근데 SWE-Bench는 정답을 맞히는 게 아니라 버그를 실제로 고쳐야 하고, OSWorld는 아예 컴퓨터를 직접 만질 줄 알아야 한다. 재는 대상이 이렇게 다르니 채점이 같을 수가 없다. 하나씩 보자.
SWE-Bench
코딩 에이전트 평가라길래, LeetCode처럼 문제 풀고 정답이랑 비교하는 거겠거니 했다. 모델이 짠 코드랑 사람이 짠 정답 코드를 diff 떠서 얼마나 겹치나 보는 식.
근데 실제로는 코드를 비교하는 부분이 아예 없다. 이게 이 벤치마크의 핵심이다.
먼저 데이터셋부터 좀 독특하다. django, sympy, scikit-learn, matplotlib 같은 인기 Python 저장소 12개에서 PR을 9만 개쯤 긁어와서, 3단계로 거른 뒤 2,294개를 남긴다. 거르는 기준이 재밌는데, PR이 실제 이슈를 해결하면서 저장소의 테스트 파일을 건드려야 하고, 거기에 더해 실제로 환경을 깔고 돌렸을 때 테스트 하나 이상이 실패에서 통과로 바뀌어야 한다. 데이터를 만드는 단계부터 이미 “코드"가 아니라 “테스트 실행"이 기준인 셈이다.
문제 하나(인스턴스)는 PR 적용 직전 커밋(base_commit), 원본 PR의 코드 변경분(정답 패치), 테스트 변경분(채점용), 그리고 두 종류의 테스트 목록을 들고 있다. 패치 전엔 실패하다 후엔 통과해야 하는 FAIL_TO_PASS가 이슈가 실제로 풀렸는지 보는 용도고, 전후 다 통과해야 하는 PASS_TO_PASS가 기존 기능이 안 깨졌는지 보는 회귀 방지용이다. 모델한테는 이슈 텍스트랑 base_commit 상태의 코드베이스만 주고, 정답 패치랑 테스트는 숨긴다.
base_commit으로 되돌리는 이유는 단순하다. 지금 main에는 그 버그가 이미 고쳐져 있을 수 있다. 이슈가 올라오던 그 순간으로 돌려놔야 모델이 실제 버그를 마주하는 상황이 만들어진다.
채점 흐름은 대충 이렇게 흘러간다.
flowchart TD
A[이슈 + base_commit 코드베이스] --> B[에이전트가 패치 생성]
B --> C[인스턴스 전용 Docker 컨테이너]
C --> D[모델 패치 적용]
D --> E[테스트 파일을 base_commit으로 강제 리셋]
E --> F[채점용 test_patch 주입]
F --> G[pytest 실행]
G --> H{FAIL_TO_PASS + PASS_TO_PASS 전부 통과?}
H -->|Yes| I[resolved]
H -->|No| J[미해결]
harness에는 눈에 띄는 방어가 하나 있다. 테스트를 돌리기 직전에 테스트 파일을 원본 상태로 강제로 되돌린다. 모델이 테스트를 자기한테 유리하게 고쳐서 통과하는 걸 막으려는 거다. 그러고 나서야 채점용 테스트를 덮어씌운다.
핵심은 처음 예상과 정반대인 지점이다. 정답 패치랑 모델 패치를 코드로 비교하지 않는다. 정답 패치는 데이터셋 만들 때 테스트 목록 뽑는 데만 쓰였고, 채점 순간엔 등장하지 않는다. 논문 표현을 그대로 옮기면 “패치가 적용되고 이 테스트들이 전부 통과하면 이슈를 해결한 것으로 본다"가 끝이다.
판정도 단순하다. FAIL_TO_PASS를 전부 통과하고 PASS_TO_PASS도 전부 통과하면 해결, 하나라도 빠지면 미해결. 부분 점수는 없다. 그러니까 모델이 짠 코드가 사람 패치랑 완전히 딴판이어도 테스트만 다 통과하면 성공이다. “코드를 잘 생성하는가"가 아니라 “버그를 실제로 해결하는가"를 보는 거다. Pass@1이나 % Resolved도 이 맥락에서 봐야 한다. 테스트를 몇 % 통과했냐가 아니라, 인스턴스 단위로 전부 통과한 비율이다.
Docker를 인스턴스마다 따로 만드는 이유도 여기 있다. 저장소 12개가 의존성이랑 Python 버전이 다 다르고, 같은 저장소도 시점마다 다르다. numpy 특정 버전에서만 재현되는 테스트가 수두룩해서, 환경이 조금만 어긋나도 테스트가 flaky해지고 채점이 오염된다. 그래서 base 이미지 → repo+버전별 이미지 → 인스턴스별 이미지 이렇게 계층으로 쌓아서 결정론적으로 재현되게 만들어 둔다.
참고로 요즘 자주 보이는 Verified랑 Pro는 원본이랑 좀 다르다. Verified는 원본 2,294개 중 사람이 검증한 500개인데, 원본을 뜯어보니 38%가 이슈 설명이 부실하고 61%가 정당한 해답을 부당하게 떨어뜨리는 테스트를 갖고 있었다고 한다. 즉 원본이 모델을 오히려 과소평가하고 있었다는 얘기라, 채점 로직은 그대로 두고 데이터 품질만 걸러냈다(GPT-4o가 16% → 33%로 뛴다). Pro는 Scale AI가 만든 별개 물건인데, 여러 파일에 걸친 긴 작업 + 학습에 안 들어갔을 상업 코드베이스로 오염을 막아서 점수가 확 떨어진다.
Terminal-Bench
이건 모델이 bash 명령을 문자열로 뱉으면 그걸 실행해서 결과만 확인하는 명령 생성기라고 예상하기 쉽다. 실제 구현은 모델이 tmux 세션 화면에 직접 키를 두드리고, 그 화면을 다시 읽으면서 작업하는 방식이다.
태스크 하나가 디렉토리 하나다. 지시문이 담긴 task.yaml, 초기 환경을 만드는 Dockerfile, 테스트 스크립트, 그리고 레퍼런스 정답으로 구성된다. Dockerfile이 좀 웃긴 게, 태스크 상황을 일부러 만들어낸다. 예를 들어 broken-python 태스크는 베이스 이미지에서 pip 관련 패키지를 통째로 지워버리고 “내 파이썬에서 pip가 안 돼요"라는 이슈를 던진다. (모든 태스크 파일 첫 줄엔 학습 데이터 오염 방지용 canary 문자열이 항상 박혀 있다.)
핵심은 harness가 컨테이너를 계속 켜두고 그 안 tmux 세션을 통해 조작한다는 거다. 에이전트랑 환경 사이 인터페이스가 딱 두 개다. send-keys로 키를 밀어넣고, capture-pane으로 화면을 캡처한다. 그러니까 모델은 화면에 실제로 타이핑을 하는 거고, Enter도 개행 문자를 명시적으로 보내야 실행된다. tmux가 컨테이너에 없으면 아예 에러 뱉고 멈춘다.
레퍼런스 에이전트도 폐루프로 돈다. LLM이 키 입력 배치를 생성하면 tmux로 실행하고, 그 화면 출력을 다시 다음 입력으로 넣는다. 스스로 “완료"를 선언하거나 정해진 에피소드(기본 50회)를 소진할 때까지 반복한다. 명령을 생성만 하는 게 아니라 실행하고, 화면 읽고, 다음 판단하고를 반복하는 거다. 프롬프트에도 “less, vim, git diff 같은 인터랙티브 세션을 다룰 때는 키 입력을 직접 보내라"고 적혀 있다. vim 같은 TUI까지 키로 조작해야 하는 진짜 터미널인 거다.
채점은 SWE-Bench랑 똑같이 테스트 통과 여부다. 에이전트가 끝내면 테스트 스크립트를 컨테이너에 복사해서 돌리고 pytest 결과를 파싱한다. 전부 통과여야 성공, 하나라도 실패면 그 태스크는 실패다. broken-python 테스트는 실제로 pip install이 되는지 확인한다. 정답이랑 diff를 뜨는 게 아니라 상태를 실측한다.
그럼 레퍼런스 정답은 왜 있냐면 채점용이 아니라 검증용이다. Oracle 에이전트가 정답 명령을 그대로 실행했을 때 테스트가 진짜 통과하는지, 즉 애초에 풀 수 있는 문제인지 확인하는 상한선 역할이다. (2.0부터는 공식 harness가 Harbor로 바뀌어 클라우드 샌드박스를 오케스트레이션한다는데, 컨테이너 + 테스트 기반이라는 뼈대는 그대로다.)
OSWorld
GUI 평가라고 하면 스크린샷 주고 클릭할 좌표를 찍게 하는 정도로 짐작하기 쉽다. 실제로는 진짜 가상머신을 부팅한다.
DesktopEnv가 OpenAI Gym 인터페이스를 구현하는데, 그 안에서 VMware(기본), VirtualBox, Docker, AWS 같은 provider로 실제 VM을 띄운다. 기본 OS는 Ubuntu고 LibreOffice, Chrome, VLC, GIMP, VS Code 같은 진짜 앱을 조작한다. VM 안엔 HTTP 서버가 떠 있고, 호스트 컨트롤러가 거기 REST로 요청을 보내서 스크린샷을 받고 명령을 실행시킨다.
모델이 받는 관측도 골라 쓸 수 있다. 순수 스크린샷, accessibility tree(UI 구조 XML), 둘 다, 아니면 요소마다 번호 붙인 스크린샷. 기본은 a11y tree다.
액션 방식이 특히 특이하다. 좌표만 찍는 게 아니라 pyautogui 파이썬 코드를 문자열로 생성하고, 그게 VM 안에서 실제로 돈다. pyautogui.click(...)이나 pyautogui.write(...) 같은 코드를 모델이 뱉으면, 컨트롤러가 그걸 VM 서버로 보내서 게스트에서 그대로 실행한다. 한글이나 shift 입력 정확도 때문에 몽키패치까지 넣어둔 걸 보면 실제 입력에 꽤 공을 들였다. 루프는 스크린샷 → 코드 생성 → 실행 → 잠깐 대기 → 새 스크린샷을 반복하고, 기본 15스텝 안에 끝내거나 모델이 완료를 선언하면 종료된다.
채점은 OCR로 화면 글자를 읽는 방식이 아니다. VM의 실제 파일이나 앱 상태를 가져와서 정답이랑 비교한다. 태스크마다 evaluator가 선언적으로 박혀 있다. 예를 들어 “빈 셀을 위 셀 값으로 채워라"라는 LibreOffice Calc 태스크는, 채점 직전 저장을 강제로 시킨 뒤 VM에 저장된 실제 xlsx를 내려받아 정답 xlsx와 셀 단위로 비교한다. 화면 캡처 텍스트가 아니라 진짜 저장된 파일끼리 비교하는 거다. Chrome은 CDP로 열린 탭을, VLC는 HTTP 인터페이스로 재생 상태를 읽는 식으로 앱마다 실제 상태를 조회한다. 재밌는 건 아예 “수행 불가능한” 태스크도 섞여 있어서, 이건 에이전트가 마지막에 “안 됨"을 선언해야만 점수를 받는다. 안 되는 걸 안 된다고 판단하는 것까지 본다.
태스크는 369개고, 논문 기준 사람은 72% 푸는데 당시 최고 모델이 12%였다. 어렵긴 어렵다.
Humanity’s Last Exam
이건 좀 결이 다르다. MMLU 같은 기존 벤치마크가 포화돼서(프런티어 모델이 90%를 넘겨버리니 변별이 안 된다) 만든 거다. Center for AI Safety랑 Scale AI가 전 세계 전문가 1,000명쯤 모아서, 인간 지식의 최전선에 있는 극악한 학술 문제를 만들었다. 스스로를 “이런 종류의 마지막 폐쇄형 학술 시험"이라 부른다.
공개 2,500문항에 비공개 holdout 500문항, 과목이 100개가 넘는다. 객관식이 4분의 1쯤, 나머지는 단답형이고 이미지 문제도 13%쯤 섞여 있다. 답이 명확한 폐쇄형이라 자동 채점인데, 순수 문자열 매칭이 아니라 LLM 채점기가 의미가 같은지를 판정한다(표기 차이 흡수용).
여기서 흥미로운 건 정답률만 안 본다는 거다. 모델한테 답이랑 같이 0~100%로 자기 확신도를 내게 하고, 그걸로 calibration error를 잰다. 핵심 발견이, 지금 모델들은 정확도도 낮은데 체계적으로 과신한다는 거다. 틀리면서 “확실하다"고 한다. 정답 맞히는 능력이랑 별개로, 자기가 모른다는 걸 아는지를 같이 보는 셈이다.
GDPval
앞의 것들이 “문제 풀이"라면 이건 “실무"다. OpenAI가 미국 GDP 상위 9개 산업의 44개 직업에서, 평균 경력 14년짜리 현업 전문가가 만든 실무 과제 1,320개(공개는 220개)로 구성했다. 산출물이 코드가 아니라 문서, 슬라이드, 스프레드시트, 다이어그램에 PDF, 영상, CAD 파일까지 간다.
채점이 다른 것들과 결정적으로 다르다. 테스트도 정답 대조도 아니고 사람이 직접 비교한다. 과제마다 그 직무 전문가가 라벨 가린 두 산출물(하나는 사람, 하나는 모델)을 보고 어느 쪽이 나은지 고른다. 점수는 0, 0.5, 1(사람 우세 / 무승부 / 모델 우세). 과제당 채점자가 평균 다섯 명이고 한 건 비교에 한 시간 넘게 걸린다. 헤드라인은 사람 대비 승률이다.
여기서 헷갈리기 쉬운 게 하나 있다. 발표에 종종 나오는 “GDPval-AA”는 OpenAI 원본이 아니다. AA는 Artificial Analysis라는 제3자 기관이고, 얘네가 GDPval 데이터셋을 재현한 버전이다. 결정적 차이가 채점 주체인데, 원본은 사람이 비교하지만 AA 버전은 LLM judge가 비교하고 그 결과를 Elo로 환산한다. 자동화된 버전이라 자주 인용되지만, 원본이랑 채점 방식이 다르다는 건 알고 봐야 한다.
다섯 개를 놓고 보면, 코딩 계열은 정답 코드를 아예 안 보고 테스트가 돌아가는지만 본다. 사람 코드랑 완전히 달라도 테스트만 통과하면 그만이다. 반대로 GUI나 실무로 가면 통과 여부를 테스트로 못 박기 어려우니, 최종 상태를 검사하거나 사람이 직접 비교하는 쪽으로 넘어간다. 재는 대상이 달라지면 채점 방식도 따라간다. “SWE-Bench 70%” 같은 숫자 하나 뒤에 이만한 harness가 통째로 돌아가고 있는 셈이다.