회귀를 가리는 불안정한 평가(flaky evals)를 격리하기
요약
AI 모델의 회귀(regression)를 감지하기 위해 기존 평가 시스템의 불안정성을 개선하는 하네스 구축 방법을 제시합니다. 특히 모델 실패, 평가기 실패, 인프라 실패 세 가지 클래스를 분리하여 격리 레인을 추가함으로써 실제 회귀가 노이즈에 가려지는 것을 방지해야 합니다.
핵심 포인트
- 모델/평가기/인프라 실패를 분리하는 하네스 구축이 필수적입니다.
- 의존 범위(envelope)를 인코딩하는 작은 케이스 세트로 테스트해야 합니다.
- JSON 평가기는 엄격하게 설계하여 형식 변경을 놓치지 않도록 해야 합니다.
- 단순 점수 기록을 넘어 상태, 종료 사유 등 전체 엔벨로프를 기록해야 합니다.
사일런트한 회귀는 예외를 발생시키며 자신을 알리는 법이 드뭅니다. 그것은 마크다운 펜스로 감싸진 출력, 토큰 제한으로 잘린 완성본, 또는 이전의 평가기(grader)가 여전히 통과시켰음에도 타입이 변경된 JSON 필드 안에 있는 녹색 실행 결과처럼 보입니다. 가장 빠른 해결책은 더 큰 모델이 아니라 세 가지 실패 클래스—모델이 실패했는지, 평가기가 실패했는지, 아니면 인프라가 실패했는지—를 분리하는 하네스(harness)입니다. 아래의 하네스는 격리 레인(quarantine lane)을 추가하여 불안정한 케이스들이 실제 회귀를 노이즈가 섞인 통과율 뒤에 숨기는 것을 막아줍니다.
황금 케이스는 분위기가 아니라 계약이다
실제로 의존하는 범위(envelope)를 인코딩하는 작은 케이스 세트부터 시작해야 합니다. 즉, 유효한 JSON, 필수 필드, 거부 경계, 그리고 완료 이유가 포함되어야 합니다. 각 케이스는 하나의 계약을 테스트해야 하는데, 한 번에 다섯 가지를 확인하는 케이스는 추적할 수 없는 실패를 가져옵니다. 이 케이스들을 JSONL 형식으로 저장하여 모든 변경 사항이 풀 리퀘스트(pull request)에서 검토 가능하게 하고, 프롬프트는 리뷰어가 무엇이 바뀌었는지 볼 수 있을 만큼 짧게 유지해야 합니다.
아래의 생성기는 세 가지 형식 예시를 작성합니다. 이들을 벤치마크로 취급하기보다는 자체 도메인 케이스로 대체하세요.
import json
cases = [
{'id': 'invoice_json', 'prompt': 'Return only a JSON object with invoice_id (string) and total_cents (integer). Input: INV-7 totals 1299 cents.', 'grader': 'json_fields', 'expect': {'required': ['invoice_id', 'total_cents'], 'types': {'invoice_id': 'str', 'total_cents': 'int'}}},
...
평가기들이 엄격하다는 점에 주목하세요. JSON 평가기는 전체 문자열에 json.loads를 호출하며 코드 펜스(code fences)를 제거하거나 첫 번째 중괄호를 검색하지 않습니다. 이 엄격함이 핵심입니다: 관대한 평가기는 프로덕션에서 문제가 되는 형식 변경을 통과하는 점수로 변환시켜 버립니다. 만약 귀하의 제품이 실제로 펜스가 있는 JSON을 허용한다면, 그 형식에 대한 별도의 케이스를 추가하고, 수정 사항을 평가기 안에 숨기기보다는 독립적으로 실패하게 하도록 해야 합니다.
점수뿐만 아니라 범위를 기록하라
각 반복(repeat)은 상태, 종료 사유(finish reason), 지연 시간(latency), 프롬프트 해시(prompt hash), 출력 해시(output hash), 그리고 원본 응답을 저장합니다. 이 엔벨로프(envelope)가 중요한 이유는 가시적인 텍스트는 그럴듯해 보여도 길이 제한으로 인한 종료(length finish)는 실패이기 때문이며, 제공업체 오류(provider error)를 모델 실패로 계산해서는 안 되기 때문입니다. 또한 레저(ledger)에는 요청된 모델과 엔드포인트 호스트가 기록되므로, 비교 과정이 두 개의 다른 모델 스탬프를 조용히 넘어서 수행될 수 없습니다.
러너 (The runner)
다음 내용을 quarantine_harness.py로 저장하세요. 이 파일은 Python 표준 라이브러리와 OpenAI와 호환되는 채팅 완료(chat completions) 엔드포인트만을 사용하므로, 프레임워크를 설치할 필요 없이 어떤 제공업체에도 연결할 수 있습니다. 무료 모델 액세스 및 무료 서버 옵션은 코드에 내장된 가정이 아니라 설정 선택 사항입니다.
#!/usr/bin/env python3
# quarantine_harness.py
import argparse, hashlib, json, math, os, sqlite3, sys, time, urllib.error, urllib.parse, urllib.request
...
동일한 케이스 파일과 동일한 반복 횟수(repeat count)를 사용하여 기준선(baseline)과 후보 레이블(candidate label)을 실행하세요. 기준선은 프롬프트, 그레이더(grader), 또는 모델 스탬프를 변경하기 전에 기록되어야 합니다.
export OPENAI_BASE_URL='https://your-provider.example/v1'
export OPENAI_API_KEY='...'
python quarantine_harness.py record --cases cases.jsonl --label baseline --model $MODEL --repeats 5
...
비교 단계는 의도적으로 보수적입니다. 유효하지 않은 반복은 제외하고, 반복 전반에 걸쳐 의견 불일치가 있는 모든 케이스를 격리(quarantine)로 표시하며, 현재 패스율이 최소 효과(minimum effect)만큼 떨어지고 Wilson 구간(Wilson intervals)이 겹치지 않을 때만 회귀(regression)를 호출합니다. 이것은 유의성 검정(significance test)이라기보다는 분류(triage) 규칙이며, 노이즈가 많은 무료 엔드포인트가 모든 풀 리퀘스트(pull request)를 차단하는 것을 방지하도록 설계되었습니다.
| 신호 (Signal) | 결정 (Decision) | 게이트의 역할 (What the gate does) |
|---|---|---|
| 3개 미만의 유효 반복 | 결론 보류 (inconclusive) | 차단하지 않음; 재실행 요청 |
| ... |
무료 티어(free tier)의 적용 범위
MonkeyCode는 무료 모델 접근과 무료 서버 옵션을 제공하는 오픈소스 프로젝트로, 이 워크플로우의 저가형 끝단에 속합니다. 공개 고지: 본 기사는 MonkeyCode의 제품 홍보의 일환으로 작성되었습니다. 빠르고 낮은 비용으로 신호를 받고 싶은 풀 리퀘스트 스모크 레인(pull-request smoke lane)에는 무료 모델 접근을 사용하고, 릴리스 게이트(release gate)를 위해서는 고정된 모델을 유지하십시오. 무료 서버 옵션은 단일 Python 프로세스를 실행하고 SQLite 원장을 영속화할 수 있는 경우에 예약 러너(scheduled runner)로 사용하십시오. 의존하기 전에 영속성, 비밀 처리, 아웃바운드 네트워크 접근을 확인하십시오.
본 기사는 의도적으로 토큰 할당량, 모델 이름 또는 하드웨어 사양을 출력하지 않습니다. 이러한 세부 정보는 가장 먼저 변경되기 때문에, 오래된 할당량은 할당량이 없는 것보다 더 나쁩니다. 이 하네스(harness)는 환경 변수에서 기본 URL과 모델을 읽어오므로, 제공되는 혜택이 변경되어도 본 기사는 잘못되지 않습니다.
사일런트 회귀를 포착하는 워크플로우
모든 풀 리퀘스트에 대해 세 번 반복하여 무료 스모크 레인을 실행한 다음, 동일한 케이스에 대해 다섯 번 이상 반복하여 고정된 나이틀리 레인(nightly lane)을 실행하십시오. 어떤 케이스가 격리 구역(quarantine)에 진입하면, 그것을 삭제하지 말고 그레이더를 완화하지 마십시오. 원시 출력을 검사하고 해당 케이스가 명세가 부족한 것인지 모델이 실제로 불안정한지 결정하십시오. 출력 결과가 잘린 JSON 객체를 보여주면, 이를 모델 또는 토큰 제한 실패로 간주하고; 유효하지만 필드가 누락된 객체를 보여주면, 계약(contract) 실패로 간주하고; 그레이더 자체에서 오류가 발생했다면, 인프라 실패로 간주하고 프롬프트에 손대기 전에 하네스를 수정하십시오.
유용한 격리 검토에는 기록이 남아 있어야 합니다. 원시 응답, 완료 사유(finish reason), 프롬프트 해시, 그레이더 사유는 두 번째 사람이 모델을 다시 실행하지 않고도 결정을 재현하기에 충분해야 합니다. 이는 또한 골든 케이스(golden case)를 변경하는 최소한의 증거이기도 한데, 케이스를 변경하여 빨간 실패(red run)를 녹색으로 만드는 것이 팀들이 회귀를 감지하는 능력을 잃게 만드는 방식입니다.
제한 사항 및 사용해서는 안 되는 경우
Wilson 간격은 독립적인 반복을 가정하며, 온도(temperature), 제공업체 캐싱(provider caching) 또는 공유 시드(shared seed)가 이 가정을 위반할 수 있습니다. 5번의 반복은 벤치마크가 아닌 분류 샘플일 뿐이며, 이 하네스(harness)는 안전성, 정확성 또는 품질을 증명할 수 없습니다. 이는 단지 작성한 케이스와 그레이더(grader) 하에서 회귀를 가시화할 뿐입니다. 잘못된 그레이더는 자신감 있는 통과(pass) 결과를 산출할 것입니다. 안정적인 엔드포인트, 원본 출력 저장소, 또는 그레이더 변경에 대한 명명된 소유자(named owner)가 없는 팀은 아직 이것을 차단하는 CI 게이트(CI gate)에 넣어서는 안 됩니다. 프롬프트와 출력을 저장하는 것이 데이터 정책을 위반한다면, 작성 시점에 비식별화하고 첫 실행 전에 비식별화 경로를 테스트해야 합니다.
아무것도 커버하지 않는 1,000개의 케이스로 시작하기보다, 범위를 포괄하는 열 개의 케이스부터 시작하십시오. 보호하는 계약(contract)과 잡아낼 실패 사례(failure)를 명시할 수 있을 때만 케이스를 추가하십시오. 격리 레인(quarantine lane)은 첫 주 동안 느리게 느껴질 것이며, 이는 소음을 행동으로 바꾸는 것을 거부하기 때문입니다. 이것이 바로 조용한 회귀가 대안일 때 원하는 동작 방식입니다.
중요한 습관은 더 많은 평가를 실행하는 것이 아니라, 시끄러운 실행을 회귀라고 부르는 것을 거부하고 관대한 통과(pass)를 성공이라고 부르는 것을 거부하는 것입니다. 저비용의 스모크 레인(smoke lane)을 원한다면, 이 하네스를 MonkeyCode의 무료 모델 액세스에 연결하고, 기준선(baseline)을 고정하며, 격리 레인이 그 이름을 얻도록 내버려 두십시오.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기