
모델이 똑똑해지면 해결될까?
AI 코딩 도구의 지배적인 서사는 이렇다. 모델이 충분히 좋아지면 코드도 잘 짜고 테스트도 잘 짜고 리팩토링도 알아서 할 것이다. GPT-4에서 안 됐으면 GPT-5에서 될 것이다. Claude가 못 하면 더 큰 Claude가 할 것이다.
정말 그런가?
Claude Opus 4.7에게 filefunc 리팩토링을 시켰다. 사람의 리뷰 없이 1시간 만에 완주했다. validate 통과, pytest 통과, 커버리지 유지. 결과만 보면 “역시 모델이 좋으면 된다"는 서사에 맞는다.
하지만 같은 모델에게 같은 리팩토링을 filefunc 규칙 없이 시키면? validate 없이? 커버리지 피드백 없이? 결과는 완전히 달라진다. Doom loop에 빠진다. 같은 버그를 고치다가 다른 곳을 깨뜨리고, 그걸 고치다가 또 다른 곳을 깨뜨린다.
같은 모델이다. 달라진 건 환경이다.
“다 했습니다” — 에이전트의 조기 종료 본능
같은 모델로 또 하나의 실험을 했다. 527개 함수가 있는 프로젝트에 에이전트를 자율적으로 투입했다. “모든 함수에 테스트를 작성해줘.” 에이전트는 작업을 마치고 보고했다. “완료했습니다.”
실제로 테스트가 작성된 함수: 40개. 527개 중 40개.
에이전트는 거짓말을 한 게 아니다. 40개를 하고 나서 “충분히 했다"고 판단한 것이다. LLM의 기본 성향은 낙관적 조기 종료다. 어려운 함수를 만나면 스킵하고, 몇 개 더 하다가 “나머지도 비슷한 패턴이니 됐다"고 결론 내린다.
CLI 도구로 루프를 강제한 후:
자율 에이전트: 40 / 527 (7.6%) — 에이전트가 "완료" 선언
CLI 루프: 527 / 527 (100%) — 기계가 "아직 487개 남았다" 선언
같은 모델이다. 같은 프로젝트다. 차이는 “끝"을 누가 결정하는가다.
환경이 모델을 만든다
두 실험이 같은 결론을 가리킨다. Opus 4.7이 완주한 건 모델이 똑똑해서가 아니다. specification surface가 machine-checkable했기 때문이다.
filefunc validate → 코드 구조가 규칙을 충족하는가?
pytest → 기존 동작이 보존되는가?
coverage → 어떤 분기가 빠졌는가?
이 세 개가 매 수정마다 즉각 피드백을 돌려줬다. 모델은 이 피드백을 받아서 수정하고, 다시 피드백을 받고, 다시 수정했다. self-correcting loop.
핵심은 여기다:
모델의 IQ보다 피드백 토폴로지가 결과를 결정한다.
LLM은 생성 능력은 강하지만 correctness 보장은 약하다. 하지만 deterministic verifier가 있으면 성능이 급격히 안정화된다. lint, typecheck, test, coverage — 이것들이 모델의 출력을 교정하는 gradient signal이 된다.
“모델이 충분히 좋아지면 해결된다"는 틀린 명제다. 정확하게는 “피드백이 충분히 빠르면 현재 모델로도 해결된다"다.
broad exploration vs local correction
LLM의 강점은 broad exploration이 아니라 local correction이다.
“이 프로젝트의 테스트를 작성해줘” — 이건 broad exploration이다. LLM은 방향을 잃는다.
“line 41이 커버되지 않았다” — 이건 local correction이다. LLM은 정확히 그 줄을 커버하는 테스트를 작성한다.
실제 프로젝트에서 검증된 숫자:
피드백 없이: 60~70% 커버리지에서 멈춤
피드백과 함께: 100% 달성 (도달 가능한 함수 한정)
같은 모델이다. “line 41 not covered"라는 한 줄이 gradient signal 역할을 한다. 이 피드백이 LLM의 수정을 정확한 방향으로 유도한다.
Symbolic Feedback Loop
이 모든 관찰을 관통하는 구조가 하나 있다.
LLM이 생성한다 → 결정론적 도구가 판정한다 → 결과를 LLM에 돌려준다 → 반복
이것을 Symbolic Feedback Loop라고 부른다.
지금 업계의 주류는 LLM Feedback Loop다. AI가 AI를 검증한다. 술 취한 사람이 술 취한 친구에게 “나 취했어?“라고 묻는 구조다. 둘 다 확률적이니까 오류가 누적된다.
Symbolic Feedback Loop는 다르다. pytest는 환각을 안 한다. go test는 안 취한다. 커버리지 측정은 거짓말을 안 한다. 명세 검증은 표류하지 않는다.
이 구조는 correctness를 기계적으로 판정할 수 있는 영역 — 코드, 테스트, 명세, 타입 — 에서 작동한다. API 설계의 우아함이나 UX의 자연스러움은 아직 심볼릭 도구가 판정할 수 없다. 그 경계를 넓히는 것이 다음 과제다. 자연어도 verifiable한 경계 안으로 가져올 길이 존재할 것이라고 믿는다.
모델을 더 똑똑하게 만드는 것보다, 모델에게 돌려줄 피드백을 더 정확하게 만드는 것이 효과적이다.
결정의 위임
결정을 AI에게 위임하면 안 된다는 사실은 자명하다. 다만 인간이 모든 것을 확인하고 결정하는 것은 고된 일이다. 반복적이고 정형적인 어떤 결정은 심볼릭 도구가 인간을 대신해 수행할 수 있다.
“이 테스트가 모든 분기를 커버하는가?” — 사람이 읽어볼 필요 없다. 커버리지 도구가 판정한다. “이 코드가 구조 규칙을 충족하는가?” — 사람이 리뷰할 필요 없다. validate가 판정한다. “아직 처리하지 않은 함수가 남았는가?” — 사람이 세어볼 필요 없다. CLI가 선언한다.
AI에게 위임할 수 없는 결정을 심볼릭 도구에게는 위임할 수 있다. 확률이 아니라 결정론이기 때문이다. 이것이 Symbolic Feedback Loop의 존재 이유다.
기차를 더 빠르게 만드는 것보다, 선로를 까는 것이 중요하다.
많은 사람이 기차를 만들고 있다. 선로를 까는 사람은 아직 거의 없다.