
“다 했습니다”
AI 에이전트에게 527개 함수의 테스트를 작성하라고 시켰다. 에이전트는 작업을 마치고 보고했다.
“완료했습니다.”
실제로 테스트가 작성된 함수: 40개.
거짓말을 한 게 아니다. 40개를 하고 나서 “충분히 했다"고 판단한 것이다. 어려운 함수를 만나면 스킵하고, 몇 개 더 하다가 “나머지도 비슷한 패턴이니 됐다"고 결론 내린다.
LLM은 생성은 잘한다. 하지만 완료 여부 판단은 신뢰할 수 없다.
래칫
래칫 렌치는 톱니가 한 방향으로만 걸린다. 돌리면 앞으로 가고, 놓으면 멈추지만 되돌아가지 않는다.
Ratchet Pattern은 이 메커니즘을 에이전트 제어에 적용한다.
항목 1: 기계적 검증 → PASS → 다음
항목 2: 기계적 검증 → FAIL → 재시도 (피드백 포함)
항목 2: 기계적 검증 → PASS → 다음
...
항목 N: PASS → 완료. 멈춤.
세 가지 규칙:
- 한 번에 하나의 항목만 보여준다.
- 통과해야 다음이 열린다.
- 전부 통과하면 멈춘다.
이 규칙을 CLI로 구현하면, 에이전트는 하나의 명령만 알면 된다. next. 나머지는 기계가 결정한다.
40에서 멈추는 에이전트, 527을 완주하는 래칫
같은 모델. 같은 프로젝트. 같은 527개 함수.
자율 에이전트: 40 / 527 (7.6%) — 에이전트가 "완료" 선언
래칫 CLI: 527 / 527 (100%) — 기계가 "아직 487개 남았다" 선언
차이는 모델의 성능이 아니다. “끝"을 누가 결정하는가다.
자율 에이전트에서는 LLM이 종료를 판단한다. LLM은 낙관적이다. 40개를 하고 “충분하다"고 느낀다. 래칫에서는 기계가 종료를 판단한다. 기계는 느끼지 않는다. 남은 항목이 0이 될 때까지 “아직"이라고 선언한다.
한 문장 정의
확률적 에이전트를 결정론적 상태 기계 안에 넣는다.
| 역할 | 담당 |
|---|---|
| 생성 | LLM |
| 판정 | verifier |
| 진행 관리 | ratchet |
많은 시스템이 생성, 판정, 종료 판단을 전부 LLM에 맡긴다. Ratchet은 그것을 분리한다.
다섯 가지 원칙
1. 종료 조건이 기계적이다
pass/fail. “looks good"이 아니다. go test가 통과하면 PASS. 커버리지가 100%면 PASS. 주관적 판단이 개입할 여지가 없다.
2. PASS는 불변이다
통과한 항목은 다시 열리지 않는다. 되돌리지 않는다. 남은 항목 수는 단조 감소한다.
remaining_work(t+1) ≤ remaining_work(t)
오늘 만든 것을 내일 다시 뜯는 일이 없다. 앞으로만 간다. 이것이 “24시간 에이전트"와의 근본적인 차이다. 종료 조건 없이 돌아가는 에이전트는 오늘 추가한 추상화를 내일 제거하고 모레 다시 추가한다. 래칫은 그런 진동을 허용하지 않는다.
3. LLM은 생성만 한다
코드를 생성하고, 테스트를 작성하고, 수정안을 제시하는 것 — 이것이 LLM의 역할이다. 뭘 수정할지, 통과인지, 다음은 뭔지, 끝났는지 — 이것은 전부 기계가 결정한다. LLM은 planner가 아니라 constrained generator다.
4. 에이전트의 종료 판단권을 박탈한다
“다 했다"를 LLM이 말하면 40에서 멈춘다. “다 했다"를 기계가 말하면 527에서 멈춘다. 래칫의 존재 이유는 이 한 줄로 요약된다.
5. Verifier는 결정론적이어야 한다
아무것이나 verifier가 될 수 있는 것은 아니다.
| 될 수 있다 | 될 수 없다 |
|---|---|
go test | “looks cleaner” |
| coverage 측정 | “seems better” |
| AST validation | “more scalable” |
| schema diff | “clean architecture” |
Verifier의 조건: deterministic, machine-checkable, resumable, localized feedback. 이 네 가지를 충족하지 못하면 래칫의 톱니가 걸리지 않는다.
피드백이 gradient signal이다
래칫이 단순히 “통과/실패"만 돌려주면 LLM은 방향 없이 수정한다. 피드백이 구체적일수록 LLM의 교정이 정확해진다.
약한 피드백: "테스트 실패" → LLM이 방향 없이 수정
중간 피드백: "커버리지 65%" → LLM이 대략적으로 보강
강한 피드백: "line 41, 44, 70 미커버" → LLM이 정확히 그 분기를 커버
실제 프로젝트에서 검증된 숫자:
피드백 없이: 60~70% 커버리지에서 멈춤
피드백과 함께: 100% 달성 (도달 가능한 함수 한정)
같은 모델이다. “line 41 not covered"라는 한 줄이 gradient signal 역할을 한다.
피드백의 해상도가 올라갈수록 LLM의 수정 정확도가 올라가고, 루프 반복 횟수가 줄어들고, 비용이 감소한다.
에이전트는 죽는다. 진행은 살아남는다.
에이전트는 반드시 뻗는다. 토큰 한도, 네트워크 에러, 세션 끊김. 래칫이 진행 상태를 영속 저장하면, 에이전트가 죽어도 다음 에이전트가 이어간다.
에이전트 A: 1~200번 함수 처리 → 죽음
에이전트 B: next → 201번부터 이어감
에이전트 C: next → 401번부터 이어감
에이전트는 일회용이다. 진행은 누적된다.
Verifier를 교체하면 다른 도구가 된다
래칫은 특정 검증기에 종속되지 않는다. 검증기만 바꾸면 다른 도구가 된다.
| 래칫 + 검증기 | 용도 |
|---|---|
래칫 + go test + coverage | 함수 단위 테스트 생성 |
| 래칫 + 구조 규칙 validator | 코드 구조 정리 |
| 래칫 + hurl pass/fail | API 엔드포인트 검증 |
| 래칫 + 명세 교차 검증 | SSOT 정합성 확보 |
| 래칫 + Toulmin verdict | 사용자 정의 규칙 강제 |
패턴은 하나다. 검증기가 도메인을 결정한다.
질문
당신의 에이전트는 몇 개를 완료하고 “다 했습니다"라고 말했는가?
그것은 정말 다 한 것인가?
누가 “끝"을 결정했는가 — 에이전트인가, 기계인가?
관련 글: 모델 IQ보다 피드백 토폴로지 — Ratchet Pattern의 이론적 배경. 왜 피드백 구조가 모델 성능보다 중요한가.