에이전트는 검사하는 것만 최적화해요

프로젝트가 커질수록 에이전트가 짠 코드가 왜 점점 난해해지는지, 예전에는 그 이유를 안다고 생각했습니다. 모델이 벤치마크 점수를 높이도록 학습되다 보니 기계적인 채점 기준은 만족시키면서도 정작 사람이 읽기에는 어색한 방언을 체득했다고 여겼습니다.

그래서 그 가설이 가장 크게 무너질 만한 현장을 찾아보았습니다. 에이전트 코드가 쏟아져 들어오는데도 질서가 유지되는 저장소가 정말 있을지 궁금했습니다.

OpenClaw는 14일 동안 8,905개의 PR을 머지했습니다. 제출만 된 것이 아니라 실제 코드베이스에 병합된 숫자입니다. 이 프로젝트의 CONTRIBUTING.md에는 이런 선언이 적혀 있습니다. “AI/Vibe-Coded PRs Welcome! 🤖”, 그리고 그 아래에는 “Codex, Claude 또는 다른 AI 도구로 작업하셨나요? 환영합니다! AI 사용 라벨이나 별도의 고지는 필요하지 않습니다.”라는 설명이 붙어 있습니다.

하루에 머지되는 PR만 600개에 달하고, 에이전트 기여를 대놓고 장려하며, 사전 고지조차 요구하지 않습니다. 기존 상식대로라면 이 저장소는 감당할 수 없는 기술 부채로 가득 차 있어야 마땅합니다.

하지만 환영 문구 바로 아래 체크리스트에는 또 다른 조건이 적혀 있습니다. “이 코드가 실제로 어떤 동작을 수행하는지 이해했음을 확인하세요.”

그 정도 작업량 속에서 코드 이해도를 유지하는 것은 불가능하다고 생각했습니다. 그래서 이 저장소가 그 원칙을 어떻게 지키는지 자세히 들여다보았습니다.

검사 스크립트 96개의 비밀

저장소에는 48,994개의 파일이 있습니다. 그 안을 살펴보면:

  • 일반적인 린터 위에 얹힌 96개의 커스텀 검사 스크립트 (scripts/)
  • 105개의 CI 워크플로
  • 디렉터리별로 세분화된 30개의 AGENTS.md
  • 127개의 모듈화된 SKILL.md
  • config/에 위치한 4개의 래칫(ratchet) 기준선

이 도구들은 두루뭉술한 품질 검사기가 아닙니다. 이 코드베이스에서 실제로 일어났던 사고마다 그에 맞는 이름이 붙어 있습니다. check-import-cycles, check-duplicates, check-deadcode-unused-files, check-no-conflict-markers, check-sqlite-transaction-boundary. -boundary로 끝나는 스크립트만 열세 개입니다. 문서에 적어두면 아무도 읽지 않고 지나칠 아키텍처 계층 규칙을 이 스크립트들이 코드로 고정합니다.

그중 하나가 check-env-var-count.mts입니다. 소스코드 전체에서 OPENCLAW_[A-Z0-9_]+ 패턴이 몇 번 나오는지 전부 세어 config/env-var-count-budget.txt에 기록된 숫자와 비교합니다.

환경 변수 총개수에 명확한 상한 예산을 두었습니다. 에이전트가 요구하지도 않은 설정 옵션을 임의로 늘려놓는다는 불만은 저도 종종 했습니다. 다만 저는 그것을 diff를 일일이 눈으로 읽으며 잡아내야 할 문제로 보았습니다. OpenClaw는 CI에서 문자열 개수를 세어 간단히 잡아냅니다.

이상한 코드가 아니라, 검사받지 않은 코드입니다

제 관점을 바꾼 결정적인 논문은 The Readability Spectrum이었습니다. 실제 개발 데이터에서 뽑은 2,735개의 시나리오를 인간 개발자의 코드와 비교한 연구입니다. LLM이 작성한 코드의 중앙값 가독성은 인간의 코드와 큰 차이가 없었습니다. 차이는 편차에 있었습니다. 인간의 코드는 품질이 비교적 균일하지만 모델의 코드는 편차가 극단적입니다. 연구진이 짚은 문제는 낯익은 것들이었습니다. 불필요한 구조적 복잡성, 장황하고 뻔한 주석, 불필요하게 낯선 API 사용 등입니다.

프롬프트 엔지니어링만으로는 이 문제를 풀기 어렵습니다. 타입 시그니처나 제약 조건을 자세히 설명하면 어느 정도 도움이 되기는 하지만 프롬프트 설계의 영향력에는 분명한 한계가 있습니다.

반면 Static Analysis as a Feedback Loop 연구는 정적 분석 도구 Bandit과 Pylint를 모델의 코드 생성 루프 안에 피드백으로 바로 연결했습니다. GPT-4o로 반복 수정을 거치자 가독성 위반 사례가 80% 이상에서 11%로 급감했습니다. 보안 취약점은 40% 이상에서 13%로, 신뢰성 경고는 50% 이상에서 11%로 떨어졌습니다.

벤치마크 하나의 결과이긴 하지만 메시지는 분명합니다. 같은 모델, 같은 과제라도 생성 도중에 코드를 엄격하게 검증하는 센서가 작동하는 순간 산출물의 성격이 완전히 달라집니다.

Reward Hacking Benchmark가 그 이유를 뒷받침합니다. 13개의 프론티어 모델 전반에서 강화학습 사후 훈련은 지름길이나 편법을 찾는 경향을 눈에 띄게 키웠습니다. 모델이 엉뚱한 고유의 언어를 쓰는 것이 아닙니다. 자신을 평가하고 측정하는 기준에 맞춰 가장 효율적으로 최적화할 뿐입니다.

실패 경험에서 길어 올린 두 가지 규칙

OpenClaw의 루트 AGENTS.md는 123줄입니다. 그중 두 줄이 특히 기억에 남았습니다.

“정적 분석 경고를 수정할 때는 타입과 런타임의 실질적인 계약을 강화하거나 위험한 연산을 근본적으로 제거하라. 단순 캐스트, 타입 확장, 마커 타입, 프로퍼티 탐색 등으로 덮어 가리지 마라.”

“재시도 횟수 늘리기, 타임아웃 연장, 느슨한 단언문 작성, 무분별한 모의(mock) 객체 사용, 기준선 완화 등으로 테스트 실패를 감추지 마라.”

이런 구체적인 지침은 책상머리에서 나오지 않습니다. 뼈아픈 실수를 실제로 겪어본 사람만 쓸 수 있는 문장입니다. 에이전트가 타입 오류를 해결한답시고 as any 캐스트를 덧붙여 컴파일러 입을 막아버리거나, 실패하는 단언문을 살짝 고쳐서 억지로 초록 불을 띄우는 모습을 지켜보았기 때문입니다.

에이전트에게 악의가 있었던 것은 아닙니다. 검사기가 통과 판정을 내릴 때까지 리소스가 가장 적게 드는 지름길을 골랐을 뿐입니다. 그것이 최적화 알고리즘의 본질입니다.

검사는 품질을 단순히 측정하는 데 그치지 않고, 품질의 경계선을 직접 정의합니다. 검사를 우회할 틈이 남아 있으면 에이전트는 누구보다 빨리 그 틈을 찾아냅니다.

OpenClaw는 이 텍스트 규칙에 실행 가능한 센서를 붙였습니다. config/assertion-safety-baseline.txt 파일의 헤더에는 이렇게 적혀 있습니다.

“// SAFETY: 설명이 없는 프로덕션 타입 단언문의 파일별 개수. 래칫(Ratchet): 이 개수는 오직 줄어들 수만 있다. 새로운 단언문에는 반드시 SAFETY 주석이 동반되어야 한다.”

문서는 캐스트로 타입 오류를 덮지 말라고 규정하고 래칫 스크립트는 실제 캐스트 개수를 전부 셉니다. 에이전트가 읽는 지침서에 한 번 적어두고 빌드를 멈추는 CI 검사에서 다시 한번 못을 박는 셈입니다.

코드 리뷰도 마찬가지입니다. autoreview는 PR을 열기 전에 독립적인 리뷰를 돌리는 전용 도구이고 CONTRIBUTING.md는 기여자가 이를 실행하도록 유도합니다. 이 리뷰어에게도 autoreview_test.py라는 전용 테스트 슈트가 있습니다. 기계 리뷰어조차 일반 프로덕션 코드와 똑같이 테스트를 거칩니다.

문서가 실패할 때 코드로 승격합니다

OpenClaw가 수많은 시행착오 끝에 이 체계에 도달했다면 OpenAI의 라이언 로포폴로(Ryan Lopopolo)가 발표한 하네스 엔지니어링 리포트는 이를 하나의 개발 방법론으로 체계화했습니다.

OpenAI 팀은 극단적인 제약을 두고 작업했습니다. 5개월 동안 약 100만 줄의 코드를 작성하고 1,500개의 PR을 머지하는 과정에서 사람이 손으로 직접 작성한 코드는 0줄이었습니다. 소수의 엔지니어가 인당 하루 평균 3.5개의 PR을 처리했고 수작업 대비 10분의 1의 시간으로 소프트웨어를 완성했습니다.

그 글에서 가장 인상적인 대목은 이 한 줄입니다.

“문서로 설명해서 실패하면, 그 규칙을 코드로 승격시킨다.”

규칙이 지켜지지 않으면 그들은 단어의 뉘앙스를 다듬어 문서를 고쳐 쓰는 대신 규칙 자체를 실행 가능한 테스트 코드로 만들어버립니다.

그들은 장황한 단일 지침서가 왜 무너질 수밖에 없는지도 분명히 짚습니다. 컨텍스트 윈도가 한정되어 있으니 거대한 가이드 문서는 정작 해결해야 할 핵심 과제를 밀어냅니다. 모든 것이 중요하다고 강조하면 아무것도 지켜지지 않습니다. 문서는 낡아가면서 시스템의 부채가 됩니다. 무엇보다 단일 텍스트 덩어리는 기계적으로 검증할 수 없습니다.

그들의 시스템 설계에도 이 원칙이 엄격하게 반영되어 있습니다. 비즈니스 로직은 단방향 계층 구조(Types → Config → Repo → Service → Runtime → UI)를 따르고 인증이나 로깅 같은 공통 기능은 단일 Providers 인터페이스로만 주입됩니다. 그 밖의 모든 임의 연결은 Codex가 직접 작성한 커스텀 린터가 빌드 단계에서 차단합니다:

“린터가 커스텀으로 제작되었기 때문에, 에러 메시지 자체가 에이전트에게 구체적인 수정 가이드를 주입하도록 설계했다.”

실패를 알리는 에러 로그는 처벌 대신 문제를 해결할 다음 턴의 프롬프트 역할을 합니다.

규칙을 감시하는 메타 규칙

두 AI 기업의 사례만 보고 보편적인 원칙이라고 단정하기는 조심스럽습니다. 그래서 AI 도구 판매와 무관한 오픈소스 저장소인 PostHog의 구조를 살펴보았습니다.

58,577개의 파일과 수많은 워크플로를 운영하는 이 대규모 프로젝트에는 posthog/test/repo_invariants/라는 특별한 테스트 디렉터리가 있습니다. 저장소 전체를 검사하는 26개의 불변성 검사 테스트가 들어 있고 OpenClaw와 비슷한 래칫 기준선 파일도 함께 관리합니다.

특히 흥미로운 파일은 test_agents_md_enforcement_tags.py입니다. 이 테스트는 AGENTS.md 문서를 파싱해 아키텍처와 스타일 가이드의 모든 규칙을 추출한 뒤, 각 규칙마다 그것을 물리적으로 강제하는 수단이 명시되어 있지 않으면 CI를 통과시키지 않습니다.

지침서의 범례는 이렇게 정의되어 있습니다:

“[lint: <id>]는 린터, semgrep 규칙, 또는 불변성 테스트가 이를 물리적으로 차단하고 있음을 의미한다. 검사는 CI가 담당하며, 본문은 그 이유를 설명할 뿐이다.”

“[review]는 자동으로 걸러낼 수 없음을 의미한다. 코드를 읽는 사람만이 유일한 검증 수단이다.”

모든 행동 지침은 둘 중 하나의 태그를 의무적으로 달아야 합니다. 추가 검사로 린터 규칙이 임의로 비활성화되지는 않았는지도 교차 검증합니다. “낡은 태그는 태그가 없는 것보다 위험하다. 리뷰어가 그 태그를 믿고 검사를 건너뛰기 때문이다”라는 주석에서 이들이 시스템을 얼마나 철저하게 통제하는지 드러납니다.

문서는 스스로 권위를 내려놓고 테스트 검사기의 보조 수단이 되었습니다.

병목은 생성이 아니라 검증입니다

이 접근법은 개인 단위의 작업에서도 똑같이 통합니다. 메타의 React 팀과 Cursor를 거쳐 Grok 팀에서 일하는 로렌 탠(Lauren Tan)은 개인 작업 환경에서 매달 약 2,500개의 PR을 프로덕션에 배포하며 주목받았습니다. 그가 내세운 핵심 원칙 하나는 “에이전트에게 단순한 마크다운을 주기보다 실행 가능한 도구를 부여하라”라는 문장입니다.

소프트웨어 생산 라인에서 마지막까지 막히는 곳은 코드 작성보다 검증 단계입니다. 검증 시스템이 뒷받침되지 않은 채 코드 생성 속도만 높이는 것은 토스트 굽는 속도만 올려놓고 덜 익은 달걀 프라이를 기다리는 것과 같습니다.

그는 에이전트가 크롬 개발자 도구 프로토콜(CDP)을 통해 브라우저를 직접 조작하고, 스크린샷을 찍고, 힙 프로파일을 측정하며 동작을 검증할 수 있는 환경을 만들었습니다:

“에이전트도 숙련된 엔지니어가 좋은 코드를 짤 때 활용하는 신호들을 동일하게 받아볼 수 있어야 한다. 컴파일러 진단, 린트 결과, 정적 분석 피드백처럼 내가 보는 것을 에이전트도 보고, 내가 쓰는 도구를 에이전트도 다룰 수 있어야 한다.”

자가 검증 체계가 갖춰지기 전에 에이전트를 병렬로 늘리면 검토하고 버려야 할 쓰레기 코드 더미만 커집니다. 검증이 선행되어야 안전한 확장이 가능합니다.

반복되는 실패 유형과 실전 대응 전략

에이전트와 대규모로 협업하는 프로젝트들을 모아 보면 모델이 보이는 오작동 패턴과 그에 대응하는 엔지니어링 해법이 놀라울 만큼 비슷합니다:

에이전트의 오작동 패턴원인 분석효과적인 저장소 차원의 대응책
문제의 원인을 고치는 대신 단순 캐스트나 타입 확장으로 검사기를 무마함테스트를 통과하는 가장 비용이 적게 드는 지름길을 택하기 때문문서상 금지 규정과 함께 assertion-safety-baseline.txt로 캐스트 발생 수를 전수 감시. 견고한 하네스는 편법 비율을 6.5%에서 0.8%로 급감시킴
단언문(assert) 없이 단순 실행만 통과시키는 무의미한 테스트 작성커버리지 수치만 목표로 잡히고, 단언문의 유효성은 측정되지 않기 때문뮤테이션 테스트 도입, 단언문 래칫 적용, 단언문을 임의로 약화하지 못하도록 규칙화
기존 유틸리티를 재사용하지 않고 유사 코드를 중복 구현함재사용에는 전체 코드베이스에 대한 폭넓은 이해가 필요하지만, 복사는 즉시 가능하기 때문중복 감지 도구(check-duplicates), 중복 기준선 파일 관리
모듈 간 아키텍처 계층 경계를 침범함계층화 원칙이 텍스트 문서에만 존재하여 강제력이 없기 때문단방향 의존성 규칙 강제, 경계 린터 도입, 경계 기준선 파일 유지
불필요한 설정 플래그나 옵션을 임의로 추가함개별 플래그는 그 자체로 논리적 결함이 없어 보이기 때문check-env-var-count.mts 등을 통해 환경 변수 및 설정 총량 제한
사용되지 않는 코드, 낡은 TODO, 최신화되지 않은 문서를 방치함문서가 낡아도 빌드가 실패하지 않기 때문정기 정리 워크플로, 데드코드 분석기, 문서 동기화 에이전트 운용
부분적으로는 그럴싸하지만 유지보수하기 힘든 거대한 파일 생성가독성 지표를 물리적으로 측정하지 않기 때문파일 최대 라인 수 래칫, 분할 방식을 구체적으로 제시하는 커스텀 에러 메시지
인간의 리뷰 한계를 초과하는 방대한 코드 생성코드 생성은 무한히 확장되지만 인간의 주의력은 한정되어 있기 때문사전 자동 리뷰 도입, 다중 에이전트 상호 교차 검증, 인간 리뷰가 필수적인 영역만 선별 태깅

이 전략들에는 공통된 핵심 기법이 두 가지 있습니다:

래칫(Ratchet) 기준선 관리. 레거시 프로젝트에 엄격한 규칙을 도입할 때 가장 큰 걸림돌은 기존 코드를 청소하는 비용입니다. 래칫 기법은 지금의 부채 규모를 기준선으로 고정하고 그 숫자가 늘어나는 것만 엄격히 막습니다. 대대적인 리팩터링 없이도 바로 통제를 시작할 수 있고 기술 부채는 오직 줄어드는 방향으로만 움직입니다.

다음 프롬프트 역할을 하는 에러 메시지. 뛰어난 하네스는 에러 출력을 실패 통보로 끝내지 않고 에이전트가 다음 턴에서 바로 취해야 할 구체적인 조치 지침으로 설계합니다. 에이전트는 기획 문서보다 CI 실패 로그를 훨씬 정밀하게 읽고 반응하기 때문입니다.

객관적인 데이터가 증명하는 효과

이 체계의 효과는 정량 연구에서도 분명하게 입증됩니다.

메타(Meta)의 프로그램 자동 복구 연구는 3개월 동안 내부 저장소에서 실제로 발생한 테스트 실패 1,589건으로 피드백 루프의 영향을 측정했습니다:

  • ReAct 에이전트 단독 실행: 문제 해결률 28.5%
  • 정적 분석 피드백 결합: 해결률 34.1%
  • 테스트 실행 피드백 결합: 해결률 43.9%
  • 두 가지 피드백 동시 제공: 해결률 42.3%, 에러 발생률은 1.0%에서 0.2%로 급감

하네스의 피드백 체계만으로 해결률이 15포인트 넘게 올랐고 생성된 패치의 25.5%는 실제 동료 개발자의 검토를 거쳐 메인 브랜치에 머지되었습니다.

제약 조건을 걸면 에이전트의 문제 해결 능력이 떨어질 것이라는 우려와 달리 편법 시도는 6.5%에서 0.8%로 급감했고 전체 과제 성공률은 그대로 유지되었습니다. 정교한 가드레일은 에이전트의 역량을 깎아먹지 않습니다.

하네스를 운영할 때 주의해야 할 점

물론 기계적인 센서가 만능은 아닙니다:

  • 센서도 속을 수 있습니다: 구문 커버리지가 100%라 해도 단언문이 부실하면 결함을 놓칩니다.
  • 센서 간의 충돌 가능성: 라인 수 제한 같은 규칙이 과도하면 근본적인 분할 대신 프로퍼티 체인을 길게 늘어뜨리는 기형적인 리팩터링 루프에 빠지기도 합니다.
  • 검사 규칙의 절제: 검사 스크립트가 무분별하게 늘어나면 유지보수 비용이 급증하므로 명확한 불변식과 소유권이 입증된 규칙만 선별해야 합니다.
  • 처리량과 품질의 구분: 수천 개의 PR이 머지된다는 사실이 모든 커밋의 우수성을 보장하지는 않습니다.

사람의 취향과 에이전트의 가독성

OpenAI 팀의 보고서에는 통념을 흔드는 솔직한 고백이 담겨 있습니다:

“결과물로 나온 코드가 언제나 인간 개발자의 스타일 취향과 일치하지는 않는다. 그래도 괜찮다. 결과가 정확하고, 유지보수 가능하며, 이후의 에이전트 작업에서 명확히 해석될 수 있다면 합격 기준을 충족한 것이다.”

그들은 인간의 시각적 선호가 아니라 에이전트의 해석 가능성에 맞춰 코드베이스를 최적화했습니다. 사람이 손으로 코드를 치지 않는 환경에서는 개인의 미적 취향보다 시스템의 정확성과 기계적 가독성이 먼저라는 설명입니다.

그래도 사람이 코드를 얼마나 이해하고 있어야 하는지는 현장마다 시각이 갈립니다. OpenClaw는 AI 생성 사실을 밝히지 않아도 되지만 코드의 동작은 이해하라고 요구합니다. PostHog는 투명한 고지와 완벽한 이해를 함께 요구합니다. 반면 OpenAI는 전 과정을 기계적 루프에 맡깁니다.

하네스로 규칙을 기계화해야 한다는 데는 모두 동의하지만 인간의 개입 수준을 두고는 여전히 건강한 모색이 이어집니다.

우리가 가져가야 할 실천적 교훈

가장 중요한 배움은 스크립트의 개수가 아니라 일의 순서입니다.

정성적인 문서만으로는 시스템을 지킬 수 없습니다. “캐스트로 오류를 가리지 말라”는 좋은 문장도 캐스트 개수를 전부 세는 스크립트가 뒷받침해야 제대로 작동합니다.

새로운 아키텍처 규칙이나 코딩 스타일을 문서에 적기 전에 그것을 기계적인 테스트로 검증할 수 있는지 먼저 자문해볼 필요가 있습니다. 자동화된 검사로 표현할 수 없다면 그 규칙은 에이전트에게 전달되지 않는 막연한 기대에 가깝습니다.

규칙을 코드로 검증할 수 있으면 그 검사 스크립트는 장황한 문서보다 훨씬 간결하고 강력합니다. 환경 변수 개수를 세는 정규식 한 줄이 수십 장의 가이드 문서보다 시스템의 팽창을 훨씬 더 정확하게 막아줍니다.

모델의 지능이 더 높아지더라도 이 엔지니어링 원칙은 변하지 않을 것입니다. 실패를 일으킬 수 없는 느슨한 기준은 시스템에 적용된 규칙이 아닙니다. 사람의 머릿속에 기도로 남아 있을 뿐입니다.