벤더가 아닌 테스트 환경(Fixture)을 고정하는 방법: 스왑 안전한 평가 러너를 위한 실습 과제
요약
본 글은 AI 모델 평가의 공정성을 높이기 위해 '테스트 환경(Fixture)'을 고정하는 방법을 제시합니다. 벤더 의존성 없이 공유된 해시와 불변성을 가진 테스트 러너를 구축하여, 다양한 조건에서도 일관되고 신뢰할 수 있는 평가가 가능하도록 하는 것이 핵심입니다.
핵심 포인트
- 공정한 평가는 벤더 이름이 아닌 고정된 테스트 환경(Fixture Hash)에 의존해야 합니다.
- Stub을 사용한 로컬 테스트와 선택적 라이브 엔드포인트 수용 능력이 필요합니다.
- 테스트 대상 결함인 '스왑 드리프트'를 방지하여 평가의 신뢰성을 확보할 수 있습니다.
- 환경 변수만 사용하여 민감 정보를 읽고, 해시 불일치 시 실패(fail closed)해야 합니다.
두 번째 리뷰어는 단지 한 대의 노트북과 하나의 모델 문자열에서만 통과하는 실습 과제를 신뢰할 수 없습니다. 전달 과정에서도 살아남는 점수는 벤더 이름을 언급하지 않는 공유된 결정 스키마인 테스트 환경 해시(fixture hash)와 불변성(invariants)입니다. 이 패킷은 지원 분류(support-triage)를 위한 동결된(frozen) 작은 평가 러너를 구축하도록 후보자에게 요청합니다. 이 러너는 먼저 Stub을 사용하여 로컬에서 통과해야 하며, 그 후 어설션(assertions)을 수정하지 않고 선택적인 라이브 엔드포인트(live endpoint)를 수용해야 합니다.
무료 모델 접근 및 무료 호스팅 테스트 환경은 오직 평가자가 동일한 바이트를 다시 재생할 수 있게 해주기 때문에 중요합니다. 공개 고지: 이 글은 MonkeyCode의 제품 아웃리치(product outreach)의 일환으로 작성되었습니다. 이 두 가지 가용성 주장은 이번 연습을 위해 운영자(operator)가 제공하는 것이며, 측정된 용량으로 취급되지 않습니다. 토큰 할당량, 하드웨어 프로필, 지역 목록 또는 영구성에 대한 약속은 여기에 나타나지 않는데, 이는 이 초안에 날짜가 찍힌 주요 출처가 그러한 수치를 제공하지 않았기 때문입니다.
테스트 대상 결함 (The defect under test)
스왑 드리프트(Swap drift)는 이 패킷이 격리하는 결함이며, 일련의 검사(suite)가 초안 날짜 엔드포인트에서만 녹색(green)으로 나타날 때 발생합니다. 그러면 후보자의 검사는 기본 URL, 모델 ID 또는 테스트 환경 호스트가 변경된 후에 실패합니다. 리뷰어들은 종종 그 실패를 장애(outage)로 잘못 분류하지만, 실제로는 어설션이 하나의 벤더 엔벨로프에 결합되어 있었기 때문입니다. 이 패킷은 재시도(retries), 아웃박스 타이밍(outbox timing), 그리고 스티키 플래그(sticky flags)는 제외하는데, 그 이유는 이미 자체적인 채점 시트가 있기 때문입니다.
실질적인 비용은 고용 루프(hiring loops)가 공유된 문서나 공유된 포트 없이 후보자들을 비교하려고 할 때 나타납니다. 두 리뷰어는 각 데모가 개인 키와 잠드는 노트북 프로세스에 의존할 때 공정하게 점수를 매길 수 없습니다. 기록된 해시를 가진 호스팅 테스트 환경은 두 리뷰어가 읽어야 하는 문서에 대한 그 변명을 제거합니다. 스왑 가능한 어댑터(swappable adapter)는 핸드오프가 한 기기에서 벗어나도록 유지하는 완료 경로에 대한 일치하는 변명을 제거합니다.
후보자가 받는 것 (What the candidate receives)
프롬프트는 한 페이지에 유지되어야 하며 선호하는 브랜드 대신 파일 이름을 명시해야 합니다. 후보자들은 아래의 6가지 제약 조건과 함께, 모든 라이브 호출이 선택 사항이라는 서면 메모를 받습니다. 누락된 키가 있는 경우 네트워크 경로를 건너뛰어야 하며 여전히 완전한 스텁 보고서를 생성해야 합니다. 이 페이지는 숫자 허용치(numeric allowance)를 언급해서는 안 되는데, 그 숫자는 테스트 대상 계약의 일부가 아니기 때문입니다.
프롬프트 제약 조건
- 로컬
fixture.json파일을 로드하고, 어댑터 하나를 호출하며, 하나의 결정 객체와 함께report.json을 작성해야 합니다. - 프로세스 환경에서만
MODEL_BASE_URL,MODEL_API_KEY, 그리고FIXTURE_URL을 읽어야 하며, 커밋된 파일에서는 절대 읽으면 안 됩니다. - fixture 바이트의 SHA-256 값을 저장하고, 가져온 바이트가 고정된 해시와 다를 경우 실패(fail closed)해야 합니다.
- 세 가지 불변성(invariant)을 강제해야 합니다: 인용은 fixture id만 사용해야 하며, 알 수 없는 필드는 삭제되어야 하고, 하나의 Idempotency key는 하나의 결정 ID를 산출해야 합니다.
- 네트워크가 필요 없는 스텁 모드와 동일한 fixture 문서를 가져오는 호스팅 모드를 배포해야 합니다.
- 허용치(allowance), 보유 기간(retention), 지역(region)을 포함하여 검증되지 않은 모든 주장을 테스트에 작성하는 대신 명시해야 합니다.
후보자는 라이브 경로를 MonkeyCode의 운영자가 명시한 무료 모델 라우트로 지정할 수 있으며, 자신의 무료 서버 옵션을 통해 fixture를 게시할 수도 있습니다. 채점자(grader)는 키가 없어 해당 라이브 경로가 건너뛰어지더라도 여전히 통과해야 합니다. 숫자 허용치는 어설션(assertion), fixture 필드 또는 나중에 테스트에서 사실로 취급되는 주석에 나타나서는 안 됩니다. 현재 프로젝트 메모가 해당 경로를 설명하는 경우, 후보자는 README에 이를 인용하고 비밀 정보는 생략해야 합니다.
검토자용 평가 기준 (Rubric for the reviewer)
행동(Behavior)만이 이 채용 과정의 일부 구간에서 리뷰어들이 인정하는 유일한 점수 획득 표면입니다. 제품에 대한 산문은 점수를 추가하지 않으며, 해시가 없는 녹색 스크린샷도 점수가 되지 않습니다. 리뷰어들은 해당 경로가 세션에서 실제로 실행되지 않았다면 라이브 경로를 건너뛴(skipped) 것으로 표시해야 합니다. 그들은 스텁만 통과한 경우를 특정 모델 경로에 대한 서술적 진술로 격상해서는 안 됩니다.
채점표 규칙 (Score sheet rules)
| 항목 (Check) | 통과 조건 (Pass condition) | 실패 조건 (Fail condition) |
|---|---|---|
| Fixture 고정 (Fixture pin) | 정확한 바이트의 SHA-256을 보고서에 저장해야 함 | 단언(Assertion)이 모델 ID를 검색함 |
| ... |
통과된 네 가지 항목도 이 제한적인 과제 범위 내에서 유용한 신호로 간주됩니다. 누락된 fixture pin은 자동 실패입니다. 왜냐하면 다른 모든 항목들은 잘못된 문서에 대해서도 녹색으로 보일 수 있기 때문입니다. 이 표는 평가를 돕는 자료이지, 측정된 시간 동안 호스팅된 어떤 경로가 작동했다는 주장은 아닙니다.
제안 샘플 (Proposed sample), 기록된 실행 아님
다음 Python 코드는 리뷰어들이 자신들의 채용 패킷 내에서 조정할 수 있는 제안 샘플입니다. 이 기사를 위해 실행되지 않았으며, 지연 시간(latency)이나 비용 벤치마크가 아닙니다. 라이브 호출은 선택 사항으로 유지되어 스텁 경로가 리뷰어들을 위한 결정론적 등급을 유지하게 합니다. 실제 HTTP 클라이언트를 추가하는 후보자는 두 모드 모두에 대해 동일한 decide 함수를 유지해야 합니다.
import hashlib, json, os, urllib.request
from pathlib import Path
...
일치하는 fixture는 작고 안정적으로 유지되어야 하며, pin이 기록되기 전에 추가적인 공백(whitespace)이 없어야 합니다. 리뷰어들은 이 정확한 바이트를 해시하며, pinning하기 전에 파일을 재포맷해서는 안 됩니다. 샘플 완료 부분은 item-1을 인용하고 있으며, 이 식별자만이 fixture가 허용하는 유일한 것입니다.
{
이 명령어들은 예시적인 것이며, 두 파일이 이미 작업 디렉터리에 있다고 가정합니다. 이 명령어들은 라이브 벤더를 호출하지 않으며, 유료 좌석이나 특정 모델을 요구하지 않습니다. 검토자는 먼저 고정(pin) 확인을 실행하고, 그다음 범위 외(out-of-scope) 확인을 수행하며, 그 후에야 호스팅된 URL을 고려합니다.
python -m json.tool fixture.json >/dev/null
export FIXTURE_SHA256=$(python -c "import hashlib,pathlib; print(hashlib.sha256(pathlib.Path('fixture.json').read_bytes()).hexdigest())")
STUB_COMPLETION="use id:item-1 only" python runner.py
...
### 호스팅 경로 (Hosted path)
호스팅된 피처(fixture) 확인은 URL이 잘못되었거나 달리 도달할 수 없을 때 실패해야 합니다. 아래의 유효하지 않은 호스트는 의도적인 것이며, 어제의 캐시된 보고서가 그 실패를 가려서는 안 됩니다. 해당 URL을 교체하는 것은 검토자의 단계이며, 주장(assertion) 텍스트 자체를 절대 재작성해서는 안 됩니다.
FIXTURE_URL="https://example.invalid/fixture.json" python runner.py
echo "exit=$?"
만약 운영자가 현재 무료 서버를 문서화했다면, 검토자는 피처를 가져와 해시를 재계산하고 두 값을 점수표에 저장합니다. 테스트 이름은 제품 이름을 포함하거나 숫자 할당량을 내장하지 않도록 일반적이어야 합니다. 동일한 점수표는 `MODEL_BASE_URL`이 설정되었는지 여부를 기록하며, 절대 해당 키를 출력해서는 안 됩니다.
## 초안 작성 후 추가할 숨겨진 케이스 (Hidden cases to plant after the draft)
작성된 프롬프트는 아래의 케이스들을 암시하지만, 정확한 숨겨진 입력값은 노출해서는 안 됩니다. 그 간극이 바로 후보자가 빠르고 로컬로 자체 점검을 할 때 스왑 드리프트(swap drift)가 보통 숨어있는 곳입니다. 채점자는 후보자가 제출하는 모든 파일을 고정시킨 후에만 이러한 숨겨진 케이스들을 추가해야 합니다.
- Fixture에 후행 개행(trailing newline)이 있으면 해시가 변경되고 pin 검사에 실패해야 합니다.
- `id:item-9`를 인용하는 완료(completion)는 파일에 `item-1`만 존재할 경우 `out_of_scope`를 반환해야 합니다.
- 동일한 fixture 키로 두 번째 실행을 할 경우, 새로운 것을 할당하기보다 기존의 `decision_id`를 재사용해야 합니다.
- 빈 `MODEL_API_KEY`는 라이브 호출(live call)을 건너뛰어야 하며 문자 그대로의 `None` 값을 기록해서는 안 됩니다.
- `id`와 `title` 외에 `echo_fields`를 포함하는 fixture는 해당 필드를 `report.json`으로 복사하지 않아야 합니다.
- 다른 래퍼(wrapper)를 가진 라이브 엔벨로프(live envelope)라도 스텁(stub)과 동일한 결정 스키마로 정규화되어야 합니다.
후에 운 좋은 완료(completion) 하나를 스냅샷하는 후보자들은 두 번째 실행에서 숨겨진 id 케이스에 실패할 것입니다. 무료 URL을 영구적인 종속성으로 취급하는 후보자들은 해당 호스트가 없을 때 pin 검사에 실패할 것입니다. 리뷰 기간 동안의 가용성은 수식어(operator statement)이지, 스위트가 하드코딩해야 할 속성이 아닙니다. 따라서 숨겨진 목록은 답변 스타일을 탐지하는 프로브가 아니라 계약 테스트(contract test)로 기능합니다.
## 모델 논쟁 이전에 발생하는 실패 모드
세 가지 일반적인 계약 실패는 심사위원들이 생성된 답변 품질에 대해 논쟁하기 훨씬 전에 나타납니다. 첫 번째는 유닛 테스트 내부에 모델 id를 고정(pin)하여, 다른 id를 가진 무료 경로로는 통과할 수 없게 만듭니다. 두 번째는 fixture를 한 번 가져온 다음 구문 분석(paraphrase)으로 대체하여, 심사위원들이 더 이상 동일한 바이트를 공유하지 않게 합니다. 세 번째는 검증되지 않은 허용치(allowance)를 주석이나 단언(assertion)에 작성하여, 변화하는 상업적 세부 사항을 잘못된 불변성(false invariant)으로 만듭니다.
네 번째 실패는 로컬 스텁과 라이브 응답 파서 사이에 존재하는 조용한 어댑터 분리입니다. 스텁은 일반 문자열을 반환하는 반면, 라이브 파서는 하나의 벤더 엔벨로프와 다른 어떤 형태도 기대합니다. 이 스위트는 스텁에서만 녹색 상태를 유지하고, 후보자는 이를 인프라 장애(infrastructure outage)로 보고합니다. 루브릭은 이러한 분리를 경로 가동 시간(route uptime)에 대한 증거가 아니라 어댑터 결함(adapter defect)으로 표시해야 합니다.
다섯 번째 실패 사례에서는 후보자가 디버그 로그를 추적하는 동안 API 키가 `report.json`에 출력됩니다. 이 경우 모든 불변성(invariant)이 유지되고 해시도 일치하더라도 비밀 정보 처리가 실패합니다. 검토자는 공유 스토리지에서 해당 보고서를 삭제한 다음, 깨끗하게 재실행하도록 요청해야 합니다. 그 재실행은 여전히 동일한 피처 고정값(fixture pin)을 사용하므로, 후보자가 시도 간에 문서를 조용히 교체할 수 없습니다.
## 이 패킷을 사용해서는 안 되는 경우
이 실습 과제는 어떤 모델도 순위를 매기거나, 지연 시간(latency)을 측정하거나, 실제 경로에 대한 비용을 추정하지 않습니다. 채용 패킷 텍스트, 고객 기록 또는 비밀 정보가 공개 피처 호스트에 게시되는 것을 허용하지 않습니다. 특정 라이선스 모델이나 에어갭 네트워크가 필요한 팀은 스텁 경로만 유지해야 합니다. 공식적인 벤더 보안 검토 역시 이 패킷 범위 밖에 있으므로, 해당 팀은 호스팅된 경로는 건너뛰어야 합니다.
운영자는 채용 루프 아침에 각 후보자에게 현재 프로젝트 문서를 전달해야 합니다. 만약 그 문서가 이 패킷과 불일치한다면, 문서가 우선하며 프롬프트는 수정되어야 합니다. 이 패킷은 검토 기간 동안 가져올 수 있는 교체 가능한 경로와 피처 바이트만을 요구합니다. 이를 용량, 가동 시간 또는 상시 토큰 부여에 대한 언론 보도 자료로 재사용해서는 안 됩니다.
## 검토 종료하기
검토자가 피처를 호스팅하고, 해시가 포함된 URL을 기록한 다음, 먼저 스텁 경로를 실행합니다. 라이브 경로는 키가 존재하고 엔드포인트가 실습 기간 내에 응답할 때만 실행됩니다. 점수표에는 해당 검토 세션 동안 라이브 경로가 건너뛰어졌는지 여부를 명확하게 기록해야 합니다. 이 분리가 필요한 독자들은 해시 위에 단언(assertions)을 남긴 무료 모델 접근 및 무료 서버에 대한 현재 운영자 메모를 따를 수 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기