TL;DR
- Anthropic의 권장 사항은 취지상 맞다. 피드백 루프를 가진 모델이 더 나은 결과물을 만든다. 문제는 그 루프를 누가 돌리느냐다. 116회 실행에서 내장
/verify는 24회 중 24회 PASS를 반환했다. 명시된 요구사항을 깨뜨린 변경에서도 마찬가지였다. - 자기 변경을 스스로 확인하는 모델은 같은 사각지대를 공유한다. Haiku 4.5는 같은 모델을 쓰는 모든 setup에서 동일한 unique-field 케이스를 11회 중 11회 놓쳤고, Haiku 자신의
/verify는 틀린 케이스 옆에 체크 표시를 남겼다. - Opus 5.5에서
/verify는 비용이 2.5배($0.96 대 $0.39)였고 결과는 하나도 바꾸지 못했다. 그냥 Opus는 12회 중 12회 정답이었고, 그중 11회는 스스로 테스트 스위트를 실행했다. - 프로젝트 skill은 모델 입장에서 선택 사항이다. 24회 중 10회만 발동했고, Haiku는 6회 중 0회였다. Stop hook은 매번 발동했고 Opus 기준 비용은 약 20% 더 들었다.
- 효과가 있었던 검사는 작성자 바깥에서 왔다. 같은
/verify를 Opus로 실행하면 Haiku의 변경에 대해 3회 중 3회 FAIL을 반환하고 버그를 짚어냈다. 이것을 Stop hook으로 연결하자 Haiku는 3회 중 3회 버그를 고쳤고, 비용은 태스크당 $1.36으로 Opus가 혼자 쓸 때의 $0.76과 비교된다. - 그대로 가져다 쓸 것: 결정적인 부분에는 Stop hook, 판단이 필요한 부분에는 작성자가 아닌 verifier, 명세가 분명한 작업의 Opus에서는
/verify를 쓰지 않기.
What the measurements say
Anthropic의 주장: "If Claude has that feedback loop, it will 2-3x the quality of the final result." s4 블로그는 이를 5단계 도입 워크플로로 정리한다. 가장 자주 반복하는 수동 검사를 고르고, 내장 /verify를 써 보고, 절차를 평이한 영어로 skill에 적고, 그것을 결정적으로 만들고, 마지막으로 CI로 옮긴다. s1 v2.1.215부터 "Claude no longer runs the /verify and /code-review skills on its own; invoke them with /verify or /code-review when you want them." s3
현장 보고는 검증했다고 주장했지만 실제로는 실행되지 않은 경우에 관한 것이다. Issue #96416은 우려 19건 중 5건만 검증하고도 "accept as-is"를 내린 리뷰 판정이다. s6 Issue #97039는 체크리스트가 로드되어 있었는데도 일부만 확인하고 "held up"이라고 주장한 사례다. s7 "build passes"와 "production-ready"는 서로 다른 기준이며, 놓칠 때마다 2-3번의 재프롬프트 비용이 든다. s8
우리 벤치는 에이전트가 한 번도 보지 못한 숨겨진 acceptance test로 모든 "done"을 채점했다. 태스크 실행 112회 + 모델 간 /verify 실행 4회 = 116회, API 환산 지출은 $66.29다. 잘못된 "done"은 112회 중 12회였다. 그중 11회는 setup A부터 E까지 전부에서 T6를 푼 Haiku였고, 1회는 skill setup에서 T6를 푼 Sonnet으로, 자기가 새로 쓴 테스트가 실패하고 있었는데 skill이 발동하지 않았다. s1
내장 /verify는 세 모델에 걸쳐 24회 중 24회 Verdict: PASS라고 했다(23회는 파싱 가능, 1회는 라벨 없는 pass). Haiku의 깨진 T6도 포함된다. Opus에서는 일반 실행의 $0.39 대비 $0.96(2.5배), 75 s 대비 125 s가 들었고 결과는 바뀌지 않았다. s3
5단계 워크플로대로 작성한 프로젝트 skill은 24회 중 10회 호출되었다. Opus 8/12, Sonnet 2/6, Haiku 0/6. Stop hook은 매 실행에서 발동했고, Opus 기준 $0.39 대비 $0.47(+20 %)이었다. s19
사각지대는 하나의 요구사항, T6의 req 4다. 일치하는 여러 문서에 같은 unique 값을 주는 단일 update는 DuplicateKeyError를 던져야 한다. Haiku는 모든 setup(plain, /verify, skill, hook, tests first)에서 11회 중 11회 놓쳤다. Haiku 자신의 /verify는 "update one doc onto another's email"만 확인했고, 한 번의 update가 여러 문서에 걸리는 경우는 시도하지 않았다. 테스트 우선 규칙도 도움이 되지 않았다. 여전히 3/3이 req 4를 놓쳤고, 그중 한 번은 req 3까지 깨뜨렸다. s1
같은 Haiku 변경을 다른 모델이 /verify한 결과: Sonnet은 PASS(1/1), Opus는 FAIL 3/3이었고, 매번 같은 케이스, 즉 한 번의 update가 같은 값을 여러 문서에 쓰는 경우를 짚었다. 검사 1회당 $0.39에서 $0.47이 들었다. Haiku에 Stop hook으로 붙였을 때(setup F), Opus verifier는 3회 중 3회 버그를 고치게 했다. 세 번 모두 수정 중 60턴 상한에 도달했고, verifier를 포함한 T6 1회 평균은 $1.36이었다. Opus가 T6를 혼자 쓴 경우는 $0.76이었고 잘못된 "done"은 0이었다. s19
Measurements
하네스: Claude Code 2.1.283 headless(claude -p), 격리된 config dir, 태스크마다 같은 프롬프트, 60턴 상한. 저장소: msiemens/tinydb @ 18d73a1(Python, 테스트 226개). 기능 요청 T1부터 T6까지 6개, 각각 명시된 요구사항 5개(T5는 6개). 요구사항마다 하나씩인 숨겨진 acceptance test는 에이전트에게 절대 보여 주지 않고 모든 실행을 채점하며, 저장소 자체 스위트도 함께 돌린다. 잘못된 "done"이란 숨겨진 테스트나 저장소 스위트가 실패하는데도 완료를 주장하며 끝난 실행이다.
Setup: A plain(요청만), B /verify(A 다음 두 번째 턴에 내장 /verify), C verify skill(5단계 워크플로대로 작성한, 모델이 호출할 수 있는 프로젝트 skill), D Stop hook(prove-it.py: 스위트가 빨간 동안 중단을 막고, 첫 중단에서는 요구사항마다 증거 한 줄을 요구하며 막는다), E tests first(CLAUDE.md 규칙: 코드 전에 요구사항마다 실패하는 테스트 하나), F Opus verifier hook(변경에 대해 claude -p /verify --model opus 실행, PASS가 아닌 verdict면 차단, 최대 2라운드).
| model | setup | runs | false done | avg cost | avg turns | avg wall |
|---|---|---|---|---|---|---|
| Opus 5.5 | A plain | 12 | 0 | $0.39 | 16.6 | 75 s |
| Opus 5.5 | B /verify | 12 | 0 | $0.96 | 22.3 | 125 s |
| Opus 5.5 | C skill | 12 | 0 | $0.43 | 19.5 | 80 s |
| Opus 5.5 | D hook | 12 | 0 | $0.47 | 19.8 | 94 s |
| Opus 5.5 | E tests first | 2 (T6) | 0 | $0.64 | 18.5 | 129 s |
| Sonnet 5 | A | 7 | 0 | $0.44 | 24.0 | 112 s |
| Sonnet 5 | B | 6 | 0 | $1.18 | 36.3 | 210 s |
| Sonnet 5 | C | 7 | 1 | $0.48 | 25.7 | 136 s |
| Sonnet 5 | D | 6 | 0 | $0.53 | 27.0 | 157 s |
| Haiku 4.5 | A | 8 | 3 | $0.34 | 35.4 | 172 s |
| Haiku 4.5 | B | 6 | 1 | $0.68 | 43.3 | 217 s |
| Haiku 4.5 | C | 6 | 1 | $0.26 | 28.2 | 124 s |
| Haiku 4.5 | D | 8 | 3 | $0.37 | 38.9 | 179 s |
| Haiku 4.5 | E | 3 (T6) | 3 | $0.41 | 38.3 | 186 s |
| Haiku 4.5 | F Opus verifier | 3 (T6) | 0 | $1.36 verifier 포함 | 61 (cap) | 457 s |
| Sonnet 5 | F | 1 (T6) | 0 | $2.30 verifier 포함 | 50 | 512 s |
한계: 저장소는 하나(작고 테스트가 잘 갖춰진 Python 라이브러리), 명세가 분명한 요청 6개, 셀당 반복 1에서 3회, headless 실행. 숨겨진 테스트는 요청에 명시된 것만 검사한다.
Do this Monday
- 모델의 "done"을 읽기 전에, 명시된 요구사항마다 acceptance test를 직접 하나씩 작성한다. 벤치의 숨겨진 테스트는 같은 모델이 한 모든 검사가 놓친 것을 잡아냈다.
- 테스트 스위트를 실행하고 빨간 동안 block decision을 반환하는 Stop hook을 추가한다. 매번 발동한다. skill은 그렇지 않다.
- 태스크의 첫 중단에는 요구사항마다 증거 한 줄을 요구한다(
prove-it.py패턴). 문장이 아니라 명령과 그 출력으로. - 판단이 필요한 검사는 그 변경을 쓰지 않은 모델에게 맡긴다. diff에
claude -p /verify --model opus를 실행하고, PASS가 아니면 차단하고, 2라운드로 제한한다. - 명세가 분명한 요청의 Opus 5.5에서는 습관적으로
/verify를 치지 않는다. 비용이 2.5배였고 12회 실행에서 아무것도 바꾸지 못했다. 테스트가 없는 영역이나 기존 버그 사냥에만 남겨 둔다. - 비용 때문에 Haiku 4.5에 위임한다면 verifier 예산을 잡아 둔다. Opus hook을 붙이면 태스크당 $1.36, Opus가 혼자 쓰면 $0.76이다.
- 실패한 수정의 재시도를 모두 ledger에 기록하고, 같은 시도가 반복되면 루프를 멈춰서, 차단형 hook이 같은 잘못된 패치에 토큰을 태우지 않게 한다.
- 요구사항을 여러 행에 걸친 케이스 관점에서 다시 읽는다. "one update that matches several documents"는 Haiku의 11회 중 11회가 한 번도 시도하지 않은 케이스의 형태다.
Go further
- 5단계 워크플로와 수동 검사에서 CI 게이트까지의 성숙도 사다리. hook이 로컬에서 동작하면 결정적인 부분이 속할 곳은 위쪽 단계(CI와 PR 게이트)다. s1
- Stop hook의 의미 체계: reason이 있는 block decision은 턴을 모델에게 되돌려 보낸다. 직접 게이트를 쓰기 전에 exit code와 JSON contract를 읽어 두자. s19
- Groundtruth는 검사가 통과할 때까지 턴 종료를 거부하는 Stop hook이다. 이 아이디어의 결정적인 버전이며 참고 구현으로 읽기 좋다. s5
- regressionledger는 비용 쪽 이야기다. 같은 실패한 수정을 다시 시도하는 루프를 막는 hook으로, 실행이 60턴 상한에 닿았을 때 우리 Stop hook에 없던 부품이다. s10
Sources
- Building verification loops in Claude Code with skills, Anthropic blog. 읽는 이유: 벤치의 skill setup이 기반으로 삼은 5단계 워크플로와 사다리.
- Building verification loops in Claude Code, official Claude channel. 읽는 이유:
/verify가 처음 실행될 때 무엇을 하는지 3분 안에. 계속 쓸지 정하기 전에. - Claude Code CHANGELOG, GitHub. 읽는 이유: v2.1.215에서
/verify가 사용자 호출 전용이 되었고, 그만큼 실행 빈도가 달라진다. - Boris Cherny: give Claude a way to verify its work, X. 읽는 이유: "2-3x the quality" 주장의 정확한 원문.
- Groundtruth, GitHub. 읽는 이유: 처음부터 쓰는 대신 복사해 올 수 있는, 실제로 동작하는 Stop hook 게이트.
- Issue #96416, GitHub. 읽는 이유: 우려 19건 중 5건만 검증하고도 승인한 리뷰 판정의 날짜가 찍힌 트랜스크립트.
- Issue #97039, GitHub. 읽는 이유: 이틀 뒤 체크리스트가 로드된 상태에서 일어난 같은 실패. 체크리스트가 해결책이 아니라는 뜻이다.
- AI coding agents can verify some of their work now, dev.to. 읽는 이유: "build passes"와 "production-ready" 사이의 간극을 가장 명확하게 설명한 글.
- Saguaro, GitHub. 읽는 이유: 루프 안 리뷰 대 PR 단계 리뷰 논쟁과 댓글에 달린 반론.
- regressionledger, GitHub. 읽는 이유: 차단형 hook이 만드는 재시도 비용 문제와 그것을 제한하는 한 가지 방법.
- SPICE simulation to oscilloscope to verification with Claude Code, personal blog. 읽는 이유: 오라클이 물리적 계측기인 검증 루프.
- Hooks reference, code.claude.com. 읽는 이유: Stop hook이 지켜야 하는 block decision contract.
FAQ
내장 /verify는 버그를 잡아낼까?
이 벤치에서는 아니었다. Opus 5.5, Sonnet 5, Haiku 4.5 전체에서 24회 중 24회 PASS를 반환했고, 명시된 요구사항을 깨뜨린 Haiku의 변경도 포함된다. 유일하게 쓸모 있던 발견은 변경과 무관한, 기존 upstream 버그였다.
왜 더 강한 verifier가 더 약한 작성자에게 도움이 될까?
Haiku 자신의 검사는 이미 생각해 둔 케이스만 시험했다. Opus는 같은 변경과 같은 /verify로 한 번의 update가 여러 문서에 걸리는 경우를 시도했고 3회 중 3회 FAIL이라고 했다. Sonnet은 PASS라고 했다. verifier는 작성자가 보지 못한 케이스를 봐야 한다.
Haiku를 Opus로 검증하는 것과 Opus로 직접 쓰는 것 중 어느 쪽이 더 쌀까?
Opus로 쓰는 편이 싸다. Haiku에 붙인 Opus verifier hook은 T6 1회당 평균 $1.36이었고 매번 60턴 상한에 닿았다. Opus가 T6를 혼자 쓴 경우는 평균 $0.76이었고 잘못된 "done"은 0이었다.
AIDive