원장 task 예제 실행법
이 글을 위해 작성한 합성 원장과 세 프레임워크 adapter다. 실제 모델 성능을 측정하는 실행기는 포함하지 않는다. 예제 ZIP을 내려받아 압축을 푼다. 아래 명령은 최상위 agent-eval-task-notebook 폴더에서 시작한다.
unzip agent-eval-task-notebook.zip
cd agent-eval-task-notebook
Python 3.12 이상과 uv를 사용한다. Harbor 실행에는 로컬 Docker daemon이 추가로 필요하다. 의존성 설치와 Docker image 다운로드는 인터넷을 사용한다. 생성기와 직접 채점은 Python 표준 라이브러리만 사용한다. API 키, 실제 고객 데이터, 유료 모델 호출은 필요 없다.
1. API와 Docker 없이 task를 만든다
python3 rewrite-v2/examples/make_task.py --output /tmp/my-first-ledger
/tmp/my-first-ledger에 13개 파일이 생긴다. 이미 있는 폴더는 덮어쓰지 않는다. 그때는 새 경로를 지정한다. instruction.md, environment/ledger.json, solution/actor.py, tests/fixtures/expected.json, tests/grade.py를 차례로 연다. ZIP의 rewrite-v2/examples/harbor/ledger-task/에도 그림에서 본 13개 파일이 포함돼 있다.
2. 채점기에서 정상과 오답을 구분한다
printf '%s\n' '{"totals":{"EUR":350,"USD":1100},"included":["e1@2","e2@1","e5@1"]}' > /tmp/ledger-answer.json
python3 rewrite-v2/examples/check_answer.py /tmp/ledger-answer.json
예상은 {"reward": 1.0, "reason": "correct"}다. USD를 1300으로 바꾸면 reward: 0.0과 wrong_result, 빈 파일이면 missing_artifact가 나온다. 아직 Harbor trial을 실행한 것은 아니다.
3. Harbor에서 oracle과 오답을 실행한다
uv sync --frozen --project experiments/harbor --python 3.12
docker pull python@sha256:78387bc3881b8273120a12ebe6c1ab22b018ccc2c9adf565ae1ac9b536e184ea
HARBOR_TELEMETRY=0 experiments/harbor/.venv/bin/harbor run \
-p /tmp/my-first-ledger -a oracle -e docker -k 1 -n 1 \
--max-retries 0 --jobs-dir /tmp/ledger-jobs --job-name first-oracle --quiet
first-oracle 아래 trial의 result.json에서 reward 1, verifier/contract.json에서 correct, artifacts/app/submission/output.json에서 제출물을 확인한다. oracle은 작성한 solution/solve.sh를 실행한다. LLM 호출이 아니다.
python3 rewrite-v2/examples/make_task.py --output /tmp/my-wrong-ledger --case wrong_total
HARBOR_TELEMETRY=0 experiments/harbor/.venv/bin/harbor run \
-p /tmp/my-wrong-ledger -a oracle -e docker -k 1 -n 1 \
--max-retries 0 --jobs-dir /tmp/ledger-jobs --job-name first-wrong --quiet
이 task의 기준 풀이는 의도적으로 오답을 쓴다. 정상 완료한 trial의 reward는 0이어야 한다. Docker를 시작하지 못한 오류와 0점을 구분한다. 실행 이름이 이미 있으면 새 --job-name을 사용한다.
agent와 별도 verifier는 예제의 Compose network_mode: none을 사용한다. task.toml의 public은 Harbor native egress sidecar를 요청하지 않는 설정이다. 이 예제의 네트워크 제한은 Compose가 맡으며, 설치 단계까지 오프라인인 구성이나 모든 프로토콜의 보안 감사 결과가 아니다.
4. Prime Verifiers v1: Task.score와 로컬 harness
ZIP 최상위에서 다음 하위 폴더로 이동한다.
cd rewrite-v2/examples/verifiers/ledger_reconciliation_v1
uv sync --frozen
uv run --frozen score_controls.py
uv run --frozen vf-validate ledger-reconciliation-v1 \
--runtime.type subprocess --run.name ledger-gold --no-rich
uv run --frozen run_local_stub.py
직접 score 경로는 total: 26, matches_expectation: 26을, 로컬 harness 실행은 episodes: 26, matched: 26, paid_calls: 0을 보고한다. 같은 task에 13종 제출 응답 대조군을 두 번씩 시험한 것이며 정답 4회와 의도한 0점 22회를 포함한다. 26개 정답을 풀었다는 뜻이 아니다. vf-validate의 실행 이름이 이미 있으면 새 --run.name을 사용한다.
run_local_stub.py는 임의의 로컬 포트에 scripted HTTP 서버를 띄운다. null harness, subprocess runtime, --no-push, loopback endpoint를 명시하고 resolved config를 다시 검사한다. 이름이 null이어도 endpoint를 호출하므로 이 명령을 bare vf-eval로 줄이지 않는다. 실제 추론 모델과 LLM judge는 호출하지 않는다. 이 Python subprocess는 OS 보안 격리가 아니다.
코드와 fixture만 ZIP에 포함했다. 실행 뒤 score-controls.json, local-stub-results.json과 파생 safe JSON이 생기며, 원래 runtime trace는 임시 디렉터리에 기록된다. 글에 사용한 기존 결과는 실행 근거에서 별도로 볼 수 있다.
다음 명령으로 ZIP 최상위로 돌아온다.
cd ../../../..
5. Inspect: MockLLM과 custom scorer
ZIP 최상위에서 실행한다.
mkdir -p experiments/results
uv sync --frozen --project experiments/inspect --python 3.12
experiments/inspect/.venv/bin/python experiments/inspect/run.py
framework_status: success, all_match: true, cases: 26을 확인한다. experiments/results/inspect.json에 결과, experiments/inspect/runtime/에 .eval 로그가 생긴다. 일부러 발생시킨 모델 전송 오류는 sample error이며 오답 점수로 바꾸지 않는다. MockLLM은 정해진 응답을 돌려주며, Python socket guard는 OS sandbox를 대신하지 않는다.
수정해서 자기 task로 만들기
세 표현은 같은 일곱 줄 원장과 독립 정답을 쓴다. 업무를 바꿀 때는 instruction, 입력, 기대 결과, reference, grader와 대조군을 함께 바꾼다. 기존 예제는 실제 LLM의 도구 사용, LLM judge, RL 학습을 실행하지 않는다. 모델을 연결하려면 credential, 도구 권한, 호출 예산과 외부 접근 범위를 별도로 설정해야 한다.
원본 예제의 새 포괄 재사용 라이선스를 부여한 배포는 아니다. 의존성의 upstream 라이선스는 ZIP의 NOTICE.md에 연결했다. 프레임워크 원문, 이미지, 고객 데이터는 ZIP에 복제하지 않았다.