세 번째는 하나도 못 메웠다 — Qwen3.8-27B 4bit, Terminal-Bench 89개 pass@3 46, 그리고 ERR 13건의 정체
Qwen3.8-27B MLX 4bit(17GB) 3회차. 2회차 실패 43개를 같은 조건으로 다시 돌렸는데 복구 0, pass@3 46/89 그대로입니다. 30개는 1·2회차와 똑같이 실패했고 13개는 모델이 한 턴도 못 돌고 ERR로 유실됐습니다. 서버 탓인 줄 알았던 ERR의 진짜 원인과 pass@k 포화 이야기.

요약
2회차 글에서 Qwen3.8-27B 4bit(17GB)은 재시도 한 번으로 35→46을 만들었습니다. 같은 방식으로 2회차 실패 43개를 완전히 동일한 조건(terminus-2, ctx 120K, temperature 0.3, reasoning xhigh, 타임아웃 1.0×)으로 한 번 더 돌렸습니다. 16시간 26분이 걸렸습니다.
결과: pass@3 = 46 / 89 (51.7%) — 복구 0. 2회차와 같은 숫자입니다.
43개 내역은 PASS 0, FAIL 6, TO 24, ERR 13입니다. ERR는 처음 나온 상태입니다. 1·2회차 143개 시행에서 한 건도 없었습니다.
| pass@k | 27B 4bit (17GB) | Flash Next stock (100GB) | Uncensored 4bit |
|---|---|---|---|
| pass@1 | 35 (39.3%) | 45 | 41 |
| pass@2 | 46 (51.7%) | 53 | — |
| pass@3 | 46 (51.7%) | — | — |
| pass@5 | — | 64 | 59 |
2회차 글 끝에 “다음 재시도의 기대 복구율은 빠르게 떨어진다”고 썼는데, 떨어지는 정도가 아니라 0이었습니다. 다만 43개 중 13개는 모델이 시도조차 못 했으므로 “세 번 줘도 못 푼 것”은 정확히는 30개입니다. 그 13개 얘기는 아래에서 합니다.
30개는 왜 세 번째도 똑같이 실패했나
정상적으로 채점된 30개(FAIL 6 · TO 24)를 1·2회차 궤적과 나란히 놓고 보면 결론은 단순합니다. 같은 함정에 같은 방식으로 빠집니다. temperature 0.3에서는 관찰이 같으면 응답이 거의 같고, 태스크 초반은 관찰이 매번 같습니다. 분기가 생길 여지가 있는 태스크는 이미 2회차에서 갈렸고, 남은 것은 갈리지 않는 것들입니다.
| 실패 유형 | 태스크 | 3회차 궤적 |
|---|---|---|
| heredoc 함정 | regex-chess | 605턴 중 589턴이 동일 명령 연속 — “터미널이 heredoc에 갇혔다, Ctrl+C를 보내자”를 60분 내내 |
| 〃 | feal-differential-cryptanalysis | 115턴, 동일 턴 96회 연속 |
| 〃 | feal-linear-cryptanalysis | 89턴, Ctrl+Z로 빠져나오려다 30분 |
| 130K 컨텍스트 wedge | path-tracing-reverse | 319턴, ctx 130K 도달 후 “Technical difficulties” 재시도 252회 |
| 〃 | video-processing | 287턴, 동일 |
| 〃 | make-mips-interpreter | 119턴, heredoc 함정으로 시작해 wedge로 끝남 |
| 반복 루프 | build-pov-ray | 193턴, 렌더링 “진행 중” 확인 74회 연속, 200분 |
| 최적화 함정 | winning-avg-corewars | 75턴, ctx 103K. 이길 만한 워리어를 찾고도 제출 대신 60분 내내 튜닝 |
| “verified” 후 오답 | mteb-retrieve / sanitize-git-repo / sparql-university / bn-fit-modify | 3~7분 만에 “검증 완료” 선언 후 채점 FAIL. 세 번 다 같은 답 |
FAIL 6개가 특히 그렇습니다. mteb-retrieve는 3회 모두 9턴 안팎, 3~4분에 “task completed successfully”로 끝났고 세 번 다 틀렸습니다. 자기 검증을 통과한 오답은 재시도로 고쳐지지 않습니다. 검증 기준 자체가 틀렸기 때문에 몇 번을 돌려도 같은 검증을 같은 답이 통과합니다.
TO 24개는 3회차에서만 860분을 썼습니다. 전체 실행시간의 87%입니다. 그중 heredoc·wedge·반복 루프로 분류되는 8개가 470분입니다. 2회차 글에서 “하네스에 반복 감지를 넣지 않으면 매 회차 같은 시간을 태운다”고 썼는데, 그대로 됐습니다.
ERR 13건의 정체 — 서버 탓이 아니었다
처음엔 서버를 의심했습니다. mlx-serve가 1회차부터 74시간을 쉬지 않고 돌고 있었고, ERR 메시지가 Command timed out after 120 seconds였으니 “장기 가동으로 첫 응답이 느려져 하네스의 명령 타임아웃에 걸렸다”는 설명이 그럴듯했습니다. 그래서 4회차 전에 서버를 재기동했습니다.
트레이스백을 끝까지 읽고 나서야 틀렸다는 것을 알았습니다. 타임아웃이 난 곳은 모델 호출이 아니라 harbor terminus-2의 에이전트 준비 단계였습니다.
trial.py: _prepare() → _setup_agent()
terminus_2.py: setup() → self._session.start()
tmux_session.py: _attempt_tmux_installation() → _install_recording_tools()
docker.py: _compose_exec(...) → RuntimeError: Command timed out after 120 seconds
terminus-2는 태스크 컨테이너에 들어가 tmux와 asciinema를 apt-get install로 깔고 나서 모델을 부릅니다. 이 설치 명령에 120초 한도가 걸려 있는데, ERR 13건 모두 여기서 죽었습니다. agent_execution은 None — 모델은 한 토큰도 생성하지 않았습니다. result.json의 소요시간이 13건 모두 0.0분인 이유입니다.
증거는 setup 소요시간 분포에 있습니다. 3회차 43개 중 정상 30개의 에이전트 준비 시간은 중앙값 11초(최소 1.4초)인데, ERR 13개는 전부 정확히 120.47초입니다. 같은 태스크가 1·2회차에서는 18초 만에 준비를 마쳤습니다. 컨테이너 이미지가 같으니 apt 캐시 상태도 같습니다. 달라진 것은 그 시각의 네트워크뿐입니다.
발생 시각도 그렇습니다. 13건은 배치 27, 9월 11일 15:53에서 23:10 사이에 몰려 있고 배치 1·8·9에서는 한 건도 없습니다. 배치 2에서는 configure-git-webserver(15:53)·db-wal-recovery(15:56)·crack-7z-hash(15:58) 세 태스크가 23분 간격으로 연달아 죽었습니다. 각각 120초씩 apt 미러 응답을 기다리다 포기한 간격입니다. 그 7시간 동안 컨테이너 안에서 나가는 apt 요청이 간헐적으로 막혔고, 모델 서버는 그동안 멀쩡히 다른 태스크를 처리하고 있었습니다.
harbor 0.22.0 코드에는 이 단계가 “best-effort”라고 적혀 있습니다. 설치가 240초 예산을 넘기면 경고만 찍고 녹화 없이 진행한다는 보호막이 있는데, 그 보호막은 asyncio.TimeoutError만 잡습니다. 정작 120초 안쪽의 개별 명령 타임아웃은 RuntimeError로 올라와서 보호막을 그냥 통과해 시행 전체를 죽입니다. 녹화 도구 설치 실패로 모델 평가 하나가 통째로 날아가는 구조입니다.
문제는 이 13개 중 7개가 복구 후보였다는 점입니다. configure-git-webserver·crack-7z-hash·db-wal-recovery·dna-insert·fix-ocaml-gc·mailman·polyglot-rust-c — 다른 모델이 어느 회차에서든 통과했던 태스크입니다. 30개 포화는 모델의 한계지만, 이 7개는 시도 자체가 유실됐습니다. pass@3 46이라는 숫자는 그래서 상한이 아니라 하한입니다.
시간은 어디로 갔나
| 그룹 | 개수 | 합계 |
|---|---|---|
| TO | 24 | 860분 |
| FAIL | 6 | 50분 |
| ERR | 13 | 26분 (각 120초 대기) |
| 배치 오버헤드 | — | 50분 |
16시간 26분 중 14시간 20분이 타임아웃 24개입니다. 2회차와 같은 구조입니다. 실패 태스크는 제한시간을 끝까지 씁니다. ERR 13개가 정상 실행됐다면 대략 4~5시간이 더 붙었을 것입니다.
네 번째로
pass@1 35 → pass@2 46 → pass@3 46. 곡선이 눕는 것을 확인했고, 원인도 셋으로 갈렸습니다.
- 30개는 재시도로 안 풀립니다. heredoc 탈출, 컨텍스트 wedge, 반복 감지, “제출하고 끝내라”는 지시 — 전부 하네스나 프롬프트 몫입니다.
- 13개는 평가가 안 됐습니다. 모델 점수가 아니라 인프라 결측입니다.
- 그중 7개는 다른 모델이 푼 것이라 실제로 복구될 수 있습니다.
4회차는 43개 전체를 다시 돌립니다. 서버는 이미 재기동했으니(오진이었지만 나쁠 것은 없습니다) ERR가 다시 나오면 네트워크가 원인이라는 것이 확인되고, 안 나오면 유실됐던 7개가 진짜 풀리는지 볼 수 있습니다. 그것이 이 시리즈에서 27B의 pass@k 곡선이 어디서 멈추는지 정직하게 재는 마지막 조각입니다.
한 가지 배운 것이 있습니다. 벤치마크 ERR는 메시지만 보고 원인을 정하면 안 됩니다. “120초 타임아웃”이라는 문장만으로 서버를 재기동했는데, 트레이스백 40줄 아래에 답이 있었습니다.