
검증자가 침묵할 때: 리뷰어의 충돌(Crash)이 반증(Refutation)이 아닌 이유
요약
멀티 에이전트 루프에서 검증기(Verifier)가 API 속도 제한 등으로 인해 침묵할 때, 이를 '반증'으로 오인하여 잘못된 결론을 내리는 실패 모드를 분석합니다. '침묵은 판결이 아니다'라는 원칙을 바탕으로 에이전트 시스템의 신뢰성을 확보하는 방법을 다룹니다.
핵심 포인트
- 검증기의 API 오류나 침묵을 '반증'으로 잘못 처리하는 'fail-closed' 위험성 경고
- 에이전트 루프의 핵심 구성 요소인 추론기, 검증기, 개선기의 역할 정의
- 검증 단계에서 발생하는 보이지 않는 실패 모드를 포착하고 방지하는 설계 필요성
- 잘못된 결과를 생성한 실행을 복구하고 신뢰할 수 있는 검증 게이트를 구축하는 가이드
나는 어려운 질문에 대해 멀티 에이전트 연구 에이전트(multi-agent research agent)를 실행했고, 그것은 깨끗하고 자신감 넘치는 판결을 내놓았다: "적대적 검증(adversarial verification)에 의해 25개 주장 모두가 반증됨. 연구 결과 불충분."
그 25개 주장 중 하나도 빠짐없이 모두 사실이었다. 몇몇은 내가 다른 탭에서 바로 찾아볼 수 있는 실제의 최신 논문들을 인용하고 있었다. 실제로 아무것도 확인되지 않은 상태였다.
검증자(verifier)가 연구 내용에 대해 동의하지 않은 것이 아니었다. 그것은 충돌(crashed) 상태였다. 모든 검증 호출(verification call)이 일시적인 API 속도 제한(rate-limiting)에 걸려 아무것도 반환하지 않았고, 루프(loop)는 "아무것도 반환되지 않음"을 "반증됨(refuted)"으로 점수 매긴 것이다. _증거에 의한 반증_과 _판결되지 않음(never adjudicated)_을 구분하지 못하는 '실패 시 폐쇄(fail-closed)' 방식의 게이트는 당신에게 완전한 자신감을 가지고 거짓말을 할 것이며, 그 거짓말을 성실함으로 포장할 것이다.
이 가이드는 그러한 실패 모드(failure mode)에 관한 것이다: 왜 이런 일이 발생하는지, 방지하도록 설계하지 않으면 왜 이것이 기본(default) 결과가 되는지, 자신의 루프에서 이를 어떻게 포착하는지, 이를 방지하는 게이트는 무엇인지, 그리고 이미 "모든 것이 거짓이다"라는 잘못된 결과를 생성한 실행을 어떻게 복구하는지에 대해 다룬다.
대상: 자신의 작업을 스스로 점검하는 에이전트 루프를 구축하는 모든 사람 — 연구 에이전트(research agents), 리뷰 단계가 포함된 코드 에이전트(code agents), "생성(generate) → 검증(verify) → 유지 또는 폐기(keep or discard)" 사이클을 가진 모든 것. 특정 프레임워크는 필요하지 않다. 당신은 한 가지 아이디어를 진지하게 받아들여야 한다: 침묵은 판결이 아니다.
1. 자기 점검 루프의 구조 (The anatomy of a self-checking loop)
거의 모든 진지한 에이전트 루프는 그것을 무엇이라 부르든 동일한 세 가지 역할을 가진다. LLM 추론에 관한 reasoning-frontiers 서베이(arXiv:2504.09037)는 어휘를 다음과 같이 정의한다:
- 추론기 (Reasoner): 후보 답변(정책, policy)을 생성한다.
- 하나 이상의 검증기 (Verifiers): 이를 평가한다 (판사 / 보상 신호, reward signal).
- 개선기 (Refiner): 검증기 피드백을 바탕으로 수정한다.
세 가지 모두 다른 역할을 수행하는 동일한 모델일 수 있으며, 이것이 바로 "자기 개선(self-refinement)"의 본질이다. 나에게 거짓말을 했던 연구 에이전트는 이것의 팬아웃(fan-out) 버전이었다:
scope ──► search ──► fetch ──► extract claims ──► VERIFY (claim당 3인 투표 패널) ──► synthesize
│
└─ 각 claim은 생존을 위해 정족수(quorum)의 투표가 필요함
검증(verify) 단계는 하중을 견디는 핵심 단계(load-bearing stage)이다. 루프가 무엇을 유지할 만큼 충분히 참인지 결정하는 지점이다. 그리고 이곳은 모두가 잘못 이해하는 단계이기도 한데, 그 이유는 실패가 눈에 보이지 않기 때문이다. 고장 난 추론기(Reasoner)는 명백한 쓰레기(garbage)를 만들어내지만, 고장 난 **검증기(Verifier)**는 깔끔하고 그럴듯하며 틀린 판결을 내놓는다.
2. 버그: 3상태 세계에서의 2상태 게이트(two-state gate)
나를 실망시켰던 루프에 탑재되었던 게이트는 다음과 같다. 각 claim은 세 개의 독립적인 검증기 투표를 받았으며, notRefuted(반박되지 않음)–refuted(반박됨)로 집계되었다. 생존 규칙은 겉보기에는 합리적이었다:
claim은 **최소 2개의 유효한 투표(valid votes)**를 얻고, 반박(refutations)이 2개 미만일 경우 생존한다.
이 규칙을 주의 깊게 읽어보라. 이 규칙은 투표가 _존재한다_는 것을 은연중에 가정하고 있다. 속도 제한(rate-limiting)으로 인해 검증 단계가 충돌(crash)했을 때, 모든 투표는 빈 값으로 돌아왔다. 각 claim의 점수는 **0–0**이었다. 즉, 반박은 0개였지만, 유효한 투표 또한 0개였다. 생존 규칙에 따르면 "반박이 2개 미만인가? 예. 최소 2개의 유효한 투표가 있는가? 아니오." — 따라서 해당 claim은 생존하지 못했다.
여기까지는 맞다. 검증되지 않은 claim의 통과를 거부하는 것은 좋은 설계이다. 버그는 한 단계 위인 보고(reporting) 단계에 있었다. 생존하지 못한 claim들은 말 그대로 refuted[]라고 이름 붙여진 버킷(bucket)에 던져졌고, 요약본은 해당 버킷을 _"25개 claim 모두 반박됨, 연구 결과 불확실함."_이라고 렌더링했다.
게이트가 3상태의 세계를 2개의 상태로 붕괴시킨 것이다:
세계에는 세 가지 결과가 존재하며, 당신은 이를 반드시 구분해야 한다:
| 결과 (Outcome) | 의미 (Meaning) | 알려주는 것 (What it tells you) |
|---|---|---|
| CONFIRMED | 유효 투표의 정족수(quorum of valid votes), 반증 임계값(refute threshold) 미만 | 유지하세요 (keep it) |
| ... |
이 전체 가이드가 의존하는 단 하나의 규칙: 투표의 부재는 투표가 아닙니다. _반증됨(Refuted)_은 증거가 도달한 평결이며, _기권함(abstained)_은 증거가 결코 도달하지 않은 평결입니다. 이 둘을 혼동하는 게이트는 모든 일시적인 장애를 거짓 음성(false negatives)의 벽으로 변환합니다 — 그리고 그것이 _닫힘 실패(fails closed)_하기 때문에, 무언가를 하면서 책임감 있는 것처럼 보입니다.
3. 이것이 예외 사례가 아니라 기본값인 이유
당신은 이것이 이국적인 버그라고 생각할 수 있습니다. 그렇지 않습니다 — 이는 당신이 명시적으로 막지 않는 한 발생하는 현상이며, 세 가지 복합적인 이유 때문입니다.
닫힘 실패(Fail-closed)는 올바른 본능이지만, 그것이 함정입니다. 당신은 검증할 수 없는 주장을 통과시키기를 거부해야 합니다. 안전을 중시하는 모든 엔지니어는 불확실성에 대해 거절하도록 게이트를 만듭니다. 문제는
이것은 연구를 통해 계속해서 드러나고 있는 일반적인 진실의 구체적이고 고약한 사례입니다. 즉, 에이전트(agents)는 좋은 답을 생성하는(producing) 데는 훨씬 능숙하지만, 그것을 인식하는(recognizing) 데는 훨씬 서투르다는 점입니다. 검증(verification) 단계는 루프(loop) 내에서 가장 취약한 연결 고리이므로, 가장 덜 신경 써야 할 부분이 아니라 가장 방어적인 설계가 필요한 부분입니다.
4. 연구 결과: 병목 구간은 생성이 아니라 검증이다
버그에서 한 걸음 물러나, 왜 검증(verify) 단계가 루프의 생사를 결정짓는 지점인지 살펴보십시오.
검증 격차 (The verification gap). 일반적인 LLM 에이전트에 대한 2026년 벤치마크는 이 실패에 이름을 붙였습니다 (arXiv:2602.18998). 여러 개의 후보 답안을 샘플링할 때, _정답_은 이미 당신의 집합 안에 있는 경우가 많습니다. 모델이 그것을 _생성(generated)_했기 때문에 pass@K 값은 높게 나타납니다. 하지만 모델이 그것을 신뢰성 있게 선택하지 못하기 때문에, 실제로 실현되는 정확도는 이미 보유하고 있는 답안에 비해 훨씬 뒤처지게 됩니다. 생성이 판단을 앞지르는 것입니다. 이것이
이 두 가지 발견을 종합하면 설계 결론은 매우 냉혹합니다. 단일 모델에 의존하여 조용히 스스로를 판단하는 루프는, 측정 가능한 결과로 볼 때 해당 작업에서 가장 성능이 떨어지는 시스템의 일부를 기반으로 구축되는 것입니다. 이는 더 많은 자기 성찰 (self-reflection)로 해결할 수 없습니다. 이를 해결하려면 _더 많고, 더 독립적인 검증 (verification)_이 필요합니다. 이것이 바로 검증자가 조용히 0으로 떨어지는(drop to zero) 것이 매우 파괴적인 이유입니다. 그것은 모델의 사각지대를 보완해주던 유일한 요소를 제거하며, 그 과정이 보이지 않게 이루어집니다.
검증은 그 자체로 하나의 스케일링 축 (scaling axis)입니다. 검증 격차 (verification gap)에 대한 최첨단 대응책은 단일 모델의 영리함을 높이는 것이 아니라, 검증자의 수를 확장 (scale)하는 것입니다. Multi-Agent Verification (arXiv:2502.20379)은 기성 모델들을 투표를 수행하는 독립적인 "측면 검증자 (Aspect Verifiers)"로 사용하여 별도의 훈련 없이도 작동하며, _약체-강체 일반화 (weak-to-strong generalization)_를 보여줍니다. 즉, 더 약한 검증자 패널이 심지어 더 강력한 생성기 (generator)의 성능까지 향상시킬 수 있다는 것입니다. Rubric-grounded test-time verification (DeepVerifier, arXiv:2601.15808)은 동일한 아이디어를 연구 에이전트 (research agents)로 확장하여 미세 조정 (fine-tuning) 없이 즉시 사용(plug-and-play)할 수 있게 합니다. 이러한 시스템의 핵심 목적은 판결을 강건하게 (robust) 만드는 것입니다. 집단적으로 조용히 기권하는 패널은 이와 정반대입니다. 그것은 누구에게도 알리지 않은 채 조용히 정족수 (quorum) 미만으로 떨어진 정족수와 같습니다.
5. 해결책: 세 가지 상태를 중심으로 게이트를 설계하라
문제를 정의하고 나면 해결책은 명확합니다. 기권 (ABSTAINED)을 일급 객체 상태 (first-class state)로 만들고, 결코 통과 (pass)나 실패 (fail) 중 하나로 위장하게 두지 마십시오.
이를 구현하기 위한 세 가지 규칙이 있습니다.
규칙 1 — 순 점수 (net score)가 아니라 유효한 (valid) 투표를 기준으로 분류하십시오. 검증자가 실제로 실행되어 파싱 가능한 판단을 반환했을 때만 그 투표가 판결에 반영됩니다. notRefuted 대 refuted를 집계하기 전에, 유효한 투표가 총 몇 개인지 먼저 세십시오. 만약 유효한 투표가 정족수 미만이라면, 결과는 즉시 ABSTAINED입니다. 더 이상 반증 (refute) 비교 단계로 넘어가지 않습니다.
for each claim:
valid = 실제로 실행되고 파싱된 투표
refuted = "refuted"라고 말하는 유효한 투표
...
§2의 3표 패널의 경우, QUORUM = 2이고 REFUTE_THRESHOLD = 2입니다. 이는 깨진 게이트와 완전히 동일한 임계값입니다. 숫자는 아무것도 바뀌지 않았습니다. 단지 검사(check)의 순서와 유효 투표 수에 대한 **키잉(keying)**만 변경되었습니다.
깨진 게이트에서 바뀐 점은 첫 번째 분기(branch)가 먼저 실행된다는 것과, 이 분기가 반증 횟수가 아닌 _유효 투표 수_를 기준으로 한다는 것입니다. 0–0 클레임은 반증되었다고 보고될 수 없습니다. 왜냐하면 반증 분기가 평가되기 전에 이미 ABSTAINED(기권) 상태에서 종료되기 때문입니다.
규칙 2 — 오류(error), 시간 초과(timeout), 또는 null 값은 기권이며, 기권은 재시도되고 깨끗하게 계산되지 않습니다. 예외를 발생시키거나, 시간 초과되거나, 아무것도 반환하는 검증자 호출은 PENDING 기권입니다. 이를 백오프(backoff)와 함께 최대 _N_번까지 재실행해야 합니다. 결정적으로, 기권은 절대 조용히 통과(pass)로 또는 실패(fail)로 간주되어서는 안 됩니다. (이는 좋은 코드 리뷰 게이트가 필요로 하는 것과 같은 원칙입니다. 오류를 내는 검토자 서브에이전트가 있다고 해서 차이점(diff)을 _승인_한 것은 아닙니다. '리뷰가 충돌했다'를 '리뷰가 깨끗하다'로 간주하는 것은 다른 옷을 입은 것과 같은 버그입니다.)
규칙 3 — 검증자가 실행되었는지 확인하지 않고 '결론을 내릴 수 없음(inconclusive)'으로 기록해서는 안 됩니다. 이것은 보고 계층(reporting-layer)의 규칙이며, 실제로 저를 구한 규칙입니다. 요약이 _
reclassify(run_output):
for each claim in run_output.refuted_bucket:
valid = parse_vote_tally(claim)
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기