에이전트는 이제 사람을 대신하여 작업을 수행하는 소프트웨어의 사용자입니다. 에이전트를 구축하든, 에이전트가 사용하는 소프트웨어(API, CLI, MCP 서버, SDK)를 구축하든 관계없이, evals는 그 작업이 얼마나 잘 완료되는지 측정할 수 있는 방법을 제공합니다.
에이전트는 아마도 Highway 101의 출시 영상이나 빌보드를 놓쳤을 수 있지만, 제품을 찾고 사용하는 과정에서는 여전히 인간과 동일한 퍼널(funnel)을 거칩니다. 즉, [Discovery] -> [Selection] -> [Access] -> [Success]입니다.
이 퍼널의 어느 단계에 대해서든 evals를 구축할 수 있습니다. 여기서는 에이전트가 사용자의 작업을 올바르게 완료하는지 여부인 최종 단계, 즉 [Success]에 초점을 맞추겠습니다.
이는 들리는 것보다 확인하기 더 어렵습니다. 에이전트는 사용자가 아이스 라떼를 요청했음에도 불구하고 "성공적인" 오트 라떼 주문을 할 수 있습니다. 요청 자체는 작동했기 때문에 Observability 스택의 로그는 정상으로 보입니다. 그리고 LLM 출력은 실행마다 달라지기 때문에, 한 번의 좋은 수동 테스트만으로는 수정 사항이 제대로 작동했는지 알 수 없습니다. 변경 전과 후에 동일한 작업을 반복하고 그 결과를 평가해야 합니다.
바로 여기서 evals가 역할을 할 수 있습니다. 저희는 다음과 같은 방식으로 이를 구축합니다.
테스트가 "작동했나요?"라고 묻는다면, evals는 "얼마나 자주 작동하며, 얼마나 잘 작동하나요?"라고 묻습니다. 사용자가 에이전트에게 작업을 부여하고, 그 결과를 평가한 다음, 반복된 시도에 걸쳐 결과를 집계합니다.
저희는 에이전트 소프트웨어 테스트를 일반적으로 필요한 LLM 호출 수에 따라 그룹화합니다: 결정론적 테스트(deterministic tests, 0회), 단일 턴 evals (one-turn evals, 1회), 그리고 E2E evals (2회 이상, 전체 작업 과정). 이는 이들을 구축하는 노력, 실행하는 데 걸리는 시간, 그리고 실행 비용에 대한 훌륭한 대리 지표가 되어 왔습니다. 실용적인 테스트 피라미드(the practical test pyramid)에서처럼, 빠르고 저렴한 테스트들이 테스트 스위트의 대부분을 차지해야 합니다.
피라미드를 사용하려면, 포착하고 싶은 문제부터 시작하여 그것을 포착할 수 있는 가장 낮은 계층부터 선택합니다. [IMG:N]의 경우, 이것이 최상단입니다. 원턴(one-turn) 평가는 에이전트가 처음 실행하는 명령어와 같은 단일 결정을 확인합니다. 하지만 전체 단계 순서에 따라 달라집니다 (예: 카페 검색 → 사용자 기준 필터링 → 라테 주문하기). 각 과정은 자체적으로는 합리적으로 보일 수 있지만, 에이전트는 여전히 잘못된 결과를 얻을 수 있습니다. Success__Success
E2E 평가(End-to-End eval)는 에이전트가 환경 내에서 전체 작업을 수행하도록 한 다음, 최종 결과가 사용자의 요구 사항을 충족하는지 확인합니다. 이미 몇 가지를 보셨을 것입니다. 가장 인기 있는 코딩 벤치마크인 SWE-bench와 Terminal-Bench는 E2E 평가의 예시입니다.
만약 당신이 샌프란시스코에서 커피를 주문하는 API인 SF Beans를 소유하고 있다고 가정해 봅시다. 에이전트에게 서비스에 접근할 수 있는 가장 좋은 방법 중 하나는 CLI(Command Line Interface)를 통해 제공하는 것이므로, 당신은 .sfbeans를 배포하게 됩니다.
고객 중 한 명으로부터 온 전형적인 요청입니다:
[IMG:N]
오전 8시 30분에 알게 될 내용은 다음과 같습니다:sfbeans
[IMG:N]
피라미드의 각 계층에서 :sfbeans에 대해 테스트할 수 있는 내용은 다음과 같습니다.
저희는 Harbor를 사용하여 커피 평가 시스템을 구축할 것입니다. Harbor는 Terminal-Bench 개발자들이 만든 오픈 소스 프레임워크로, 에이전트를 컨테이너 내에서 실행하고, 결과를 확인하며, 그들의 행동을 기록합니다.Harbor
사용하게 될 용어들은 다음과 같습니다:
모든 것에 대해 E2E 평가를 구축하는 것은 실용적이지 않습니다. 기존 소프트웨어의 경우 100% 테스트 커버리지(test coverage)를 목표로 할 수 있습니다. 하지만 에이전트의 경우, 다뤄야 하는 표면은 LLM이 제품으로 수행할 수 있는 모든 것이며, 심지어 그 경계가 어디까지인지 파악하는 것조차 어렵습니다 (참고: mightthe jagged frontier).
사용자가 중요하게 생각하는 하나의 결과부터 시작하여, 몇 가지 작업으로 평가할 수 있을 만큼 충분히 좁게 범위를 좁힙니다. 일반적으로 그 목표는 세 곳 중 한 곳에서 발견됩니다:
예를 들어, 당신의 목표는 다음과 같을 수 있습니다: [IMG:N]
저희는 :sfbeans에 대해 세 군데 모두를 살펴보았습니다.
그것이 우리의 목표가 됩니다:
E2E 에이전트 평가에 가장 많이 사용하는 지표는 다음과 같습니다:
성공률(Success)
효율성(Efficiency) (성공적인 시도에서만 측정)
우리의 목표는 모든 시도에서 주문을 정확하게 처리하는 것입니다. 3단계와 4단계에서 평가를 구축하고 실행하면, 아이스 라테 주문에 대한 Claude Code + Opus 5.5의 다섯 번의 시도는 다음과 같을 수 있습니다: anthropic/claude-opus-5.5AI Gateway
이 수치들이 의미하는 바는 무엇일까요:
Opus가 작동할까요? 99%의 pass@5는 '예'라고 말합니다. 8%의 pass^5는 매주 평일에 이 음료를 주문하는 사용자가 1년 중 92%의 주에는 적어도 한 번은 잘못된 커피를 받게 된다고 말합니다. 두 수치 모두 맞습니다. 단지 서로 다른 질문에 답할 뿐입니다: pass@5는 에이전트가 주문을 정확하게 처리했는지 묻고, pass^5는 매번 정확하게 처리하는지를 묻습니다.
어떤 지표가 가장 중요한지는 실패가 사용자에게 어떤 비용을 초래하는지에 따라 다릅니다. 아침 커피를 주문하는 사람은 재시도가 불가능합니다. 만약 에이전트가 뜨거운 라테를 주문하거나, 기차가 떠난 후에 준비되는 음료를 주문한다면, 그들은 잘못된 커피에 돈을 지불했고 짜증 난 상태로 하루를 시작하게 됩니다. 그들은 매일 아침 정확한 것이 필요하므로 pass^5에 최적화해야 합니다.
다른 제품들은 실패한 시도가 저렴하고 중요해지기 전에 포착되기 때문에 더 관대합니다: 전체 평균은 자주 실패하는 작업을 숨길 수 있으므로 항상 작업별(per task) 결과를 검사해야 합니다. 그리고 단 5번의 시도만으로는, 100%는 '아직 실패가 발견되지 않았다'는 의미이지 '절대로 실패하지 않는다'는 의미가 아닙니다.
1단계에서 우리는 평가의 목표를 설정했습니다: 에이전트가 사용자가 요청한 정확한 음료를 주문하는 빈도를 높이는 것입니다. 하지만 에이전트는 목표(goal)를 시도할 수 없습니다. 에이전트에게는 작업(task)이 필요합니다. 즉, 사용자들이 실제로 요청하는 것에서 추출된 하나의 구체적인 요청입니다.
실행하면서 무언가를 배울 수 있을 만큼 충분히 어려운 작업을 선택하세요. 좋은 세트는 일반적인 경우(common case), 에이전트를 당황하게 만드는 엣지 케이스(edge cases), 그리고 행동하지 않는 것이 올바른 움직임인 요청을 최소 하나 포함해야 합니다.
각 작업에 대해 다음 부분들을 정의하세요:
원하는 목표에 따라 환경의 모든 측면을 가짜로 만들거나 현실적으로 만들 수 있습니다. 이메일 리서치 에이전트를 위해 환경을 정의한 OpenPipe 팀의 사례를 참고할 수 있습니다.has a nice writeup
우리는 4개의 작업을 작성했으며, 각각은 .tasks/ 폴더 내의 Harbor 작업 폴더입니다.
여기에 추가적인 검사가 하나 더 있습니다. 이 검증기(verifier)는 에이전트가 쓸 수 없는 명령어 로그를 읽어, 에이전트가 포기하기 전에 검색을 시도했는지 확인합니다. 우리는 이를 어려운 방법으로 배웠습니다 (Step 5 참조).impossible-request
여기가 :iced-latte-mission입니다.
이는 에이전트에게 사용자 요청과 CLI 이름을 제공합니다. 실제 운영 환경에서는 에이전트가 스킬(skill), 문서(docs) 또는 MCP 서버를 통해 도구를 찾습니다. 여기서 명시적으로 이름 지어주는 것은 평가의 초점을 **지침(instruction)**에 맞추고, [빈칸]을 건너뛰게 합니다.instructionsfbeansDiscovery__Selection__Success
또한 오전 8시 30분이라는 시간도 지정합니다. 오직 고정된 클럭만 사용하며, 샌드박스의 시스템 시계는 여전히 실제 시간을 표시합니다:sfbeans
이 환경은 부분적으로 실제이고 부분적으로 가짜입니다. 에이전트는 실제 CLI를 사용하지만, 시딩된 카페와 고정된 시간을 대상으로 하므로 모든 시도가 같은 아침에 시작됩니다:environmentsfbeans
데모를 간단하게 유지하기 위해, 라이브 데이터는 에이전트의 환경 내부에 있습니다. 실제 평가에서는 시드 데이터와 검증기 코드를 보호하고, 에이전트가 CLI를 통해서만 주문 기록을 업데이트할 수 있도록 해야 합니다. 에이전트는 예를 들어 재고를 수정하거나 직접 작성하는 방식으로 통과하기 위해 시도할 수도 있습니다.cafes.json``orders.jsonreward hack
이 커피 주문에 대해, 검증기는 최종 주문을 사용자의 요구 사항과 대조하여 확인합니다. Harbor는 에이전트가 완료된 후 실행되어, 에이전트가 작성한 점수(score)를 읽습니다:verifiertest.sh``/logs/verifier/reward.json
실제 검사는 .py 파일에 있습니다. 이 코드는 각 제약 조건을 개별적으로 점수화한 다음, 모든 조건이 통과할 때만 1로 설정합니다. Harbor는 이를 트라이얼의 주요 점수로 사용합니다:check_orders.py``reward``reward
솔루션(solution)은 알려진 좋은 답(known-good answer)입니다. 이 작업이 작동하는지 증명하기 위해 실제 에이전트보다 먼저 실행합니다:
마지막으로, 이 모든 것을 하나로 연결합니다. 타임아웃을 설정하고, 검증기(verifier)를 에이전트의 샌드박스(실행되는 환경)에서 실행하며, Step 5에서 우리가 검사할 수 있도록 주문들을 아티팩트로 저장합니다: task.toml``orders.json
느린 CI에 대한 보편적인 불만을 공유한다면, evals를 기다리는 것을 싫어할 것입니다. 모든 시도는 에이전트가 단계별로 LLM 호출을 하는 데 의존하며, 한 번의 실행은 각 변형(variant)과 작업마다 여러 번 반복합니다.
우리가 상위 단계에서 구축한 Harbor는 속도를 크게 높여줍니다. 이는 각 시도를 자체 샌드박스에서 동시에 실행하며, 모든 시도마다 다시 빌드할 필요가 없도록 빌드된 작업 환경을 캐싱합니다. 모델 호출을 통해 라우팅하면 여러 모델 제공업체에 걸쳐 하나의 키와 예산을 가질 수 있습니다.Vercel integrationVercel SandboxAI Gateway
두 개의 에이전트와 [빈칸], 각각 두 가지 모델을 사용하여 네 개의 변형을 만듭니다.Claude CodeCodex
그런 다음 Harbor를 설치하고 Vercel 프로젝트에 연결한 후 작업을 실행합니다:
[IMG:N]
이 실행은 Hobby 플랜에서 가능합니다. 10개의 동시 샌드박스가 Hobby 제한이며, 각 시도는 45분 세션 한도보다 훨씬 적습니다. Pro에서는 최대 10,000개까지 늘릴 수 있습니다 (n_concurrent_trials)Sandbox limits
예시를 간단하게 유지하기 위해, 우리는 에이전트가 읽을 수 있는 환경 변수(env var)로 [빈칸]을 각 샌드박스에 전달합니다. 데모를 넘어 실제 사용 시에는 단기 토큰을 사용하고, AI Gateway와 같이 각 시도를 필요한 호스트로만 제한하는 규칙을 설정하기 위해 Harbor의 Vercel 통합 기능을 사용하십시오.OIDC tokenSandbox firewall
내부적으로는 한 단계 더 나아가, API 키를 에이전트의 환경 외부로 유지하면서 나가는 요청에 자격 증명(credentials)을 첨부하는 자격 증명 중개(credentials brokering) 기능을 사용합니다.
이제 핵심 내용입니다: 트레이스 다이빙(trace-diving). 점수는 에이전트가 얼마나 자주 실패하는지 알려주지만, 트레이스는 왜 실패했는지 알려줍니다. 성공 사례도 읽어봐야 합니다. 왜냐하면 '성공'했다는 것은 검증자(verifier)가 만족했다는 것만을 의미하기 때문입니다.
4단계 실행 결과는 .에 80개의 트라이얼 폴더를 남깁니다. 각 폴더에는 해당 트라이얼의 결과, 에이전트의 궤적(trajectory), 검증자의 점수, 그리고 아티팩트로 저장되었기 때문에 에이전트가 주문한 순서가 포함되어 있습니다:jobs/sfbeans-baseline/``task.toml
다음 명령어를 실행하여 폴더들을 탐색할 수 있습니다. 네 가지 변형 모델이 보고할 수 있는 내용은 다음과 같습니다 (숫자는 예시입니다): harbor view jobs
이것이 우리의 기준선(baseline)입니다. 2단계에서 선택한 지표(metric)인 pass^5의 경우, 가장 좋은 변형 모델조차도 신뢰하기 어렵습니다. 하지만 점수는 무엇을 수정해야 하는지 알려주지 않습니다. 이를 위해서는 실패한 트라이얼들을 열어봐야 합니다.
2단계의 트라이얼 2를 살펴보겠습니다. Claude Code + Opus 5.5가 24th St Beans에서 $9.27에 주문했습니다. 검증자는 이 에이전트가 어떤 제약 조건(constraint)을 위반했는지 알려줍니다:
올바른 음료, 제시간에, 예산을 초과했음, 그리고 저장된 기록은 $9.27 총액을 확인시켜 줍니다. 이제 내용을 읽어봅니다. 에이전트는 Mission 지역에서 라테를 검색하고 메뉴판에서 $7.50을 보고 곧바로 .으로 이동합니다. 아무것도 없습니다. 이는 메뉴 가격을 최종 가격으로 간주했기 때문입니다.orders.json``agent/trajectory.json``order``quote
같은 변형 모델의 트라이얼 1은 세 곳의 카페를 인용하고, 24th St Beans는 제외하며, Valencia Grind에서 $7.42에 주문합니다. 따라서 CLI(Command Line Interface)는 주문을 올바르게 처리할 수 있지만, 에이전트는 사용자 돈을 쓰기 전에 총액을 신뢰성 있게 확인하지 못합니다.
그런 다음 우리는 다른 실패한 트라이얼 전반에서 그 패턴을 찾고, 만약 그것이 계속 나타난다면, 수정해야 할 구체적인 문제가 있다는 뜻입니다. 총액을 놓치기 어렵게 만들거나, 예산을 초과하는 것은 거부하도록 플래그를 추가할 수 있습니다. 이것은
대규모 실행(larger runs)의 경우, 모든 트라이얼을 수동으로 검사하는 것은 느려지므로, 이를 자동화합니다.
최대 40개의 리더 에이전트(reader agents)가 fast 모델과 같은 빠른 모델을 병렬로 실행하여 트라이얼 스크립트를 읽고 그 결과를 요약합니다. 더 스마트한 코디네이터 에이전트(coordinator agent)가 이 요약본들을 검토하고, 필요할 때 리더들에게 추가 컨텍스트를 위해 다시 보내며, 최종 보고서를 작성합니다. analysis.md 파일은 코디네이터에게 무엇을 찾아야 하는지 알려주어, 실행 전반에 걸쳐 워크플로우(workflow)를 재사용할 수 있게 합니다.
모델 호출(Model calls)은 AI Gateway를 통해 이루어집니다. 트라이얼 아티팩트(Trial artifacts)는 분석 기간 동안 유지되며, 트레이스(traces)는 장기 저장을 위해 Vercel Drives, Vercel Blob으로 전송됩니다.
첫 번째 평가 실행(eval run)은 기준선(baseline)을 제공합니다. 이제 이를 바탕으로 조치를 취하세요.
트레이스가 가리키는 수정 사항(fix), 예를 들어 플래그(flag)를 선택하고 동일한 작업을 다시 실행한 다음 기준선과 비교합니다. 이 과정에서 새로운 실패가 발생하지 않으면서 목표로 한 실패율을 줄이는지 확인하고, 그 결과를 사용하여 변경 사항이 배포 준비가 되었는지 결정합니다.--max-total