TL;DR

  • 에이전트에게 실제 작업을 맡기기 전에 린터, 타입 검사기, 컴파일러, 포매터, 테스트를 갖춘 검증 하네스를 구축해야 안전하게 맡길 수 있는 작업 범위가 커짐.
  • 에이전트는 다음 코드 조각으로 그럴듯한 답을 추측하며, 파일 작성 여부만 신호로 주어지면 작업이 끝났다고 판단함.
  • 린터, 타입 검사기, 컴파일러, 포매터, 테스트는 서로 다른 오류를 잡으며, 저렴한 검사부터 실행해야 함.
  • 검사 순서를 변경 파일 대상 린트, 타입 검사, 빌드, 관련 테스트로 구성하고 전체 실행 시간을 1분 이내로 유지해야 피드백 루프가 작동함.
  • 하네스 검사를 자동 실행해 에이전트가 실행 여부를 선택하지 못하게 하면, 검토자는 생성된 코드 한 줄씩이 아니라 결과물을 검토할 수 있음.

에이전트는 추측함

  • 모델은 다음에 올 가능성이 가장 높은 코드 조각을 생성함. 그럴듯한 코드는 대개 정답에 가깝지만, 바로 그 ‘가까움’이 문제임.
  • 그럴듯한 코드를 작성한 사람은 대개 의심을 품고 다시 확인하지만, 에이전트에게는 그런 의심이 없음. 주어진 신호에만 의존하며, 신호가 “파일이 작성됨”뿐이라면 에이전트는 작업이 끝났다고 판단함.
  • 따라서 각 저장소에서 에이전트가 한 줄을 작성한 뒤 그 줄이 틀렸음을 알려주는 것이 무엇인지가 핵심 질문임. 답이 “내일 검토자가 확인함”이라면 피드백 루프는 하루이며, 누군가 알아차리기 전에 에이전트가 추측을 열 개 더 쌓을 수 있음.
  • 하네스는 추측을 검증된 주장으로 바꾸어 에이전트가 다음 단계로 넘어가기 전에 확인해 주는 검사 모음임.

각 계층이 잡아내는 오류

  • 하네스의 각 검사는 서로 다른 종류의 실수를 잡으며, 실행 비용이 낮은 검사부터 수행함.
  • 린터는 사용하지 않는 변수, 이름 가리기, 본문이 없는 if, 80줄짜리 메서드 같은 부주의한 실수를 잡음. 각각이 반드시 버그인 것은 아니지만 추측의 부산물이며, 에이전트는 즉시 피드백을 받으면 이런 실수를 반복하지 않음. 이 저장소에서는 RuboCop을, 다른 곳에서는 eslint 또는 clippy를 사용할 수 있으며 실행 시간은 몇 초임.
  • 타입 검사기는 실행 전에 말이 되지 않는 코드를 차단함. 존재하지 않는 메서드 호출, 구조체를 기대하는 함수에 문자열 전달, 호출자가 검사하지 않은 경로로 nil 반환 등이 해당함.
  • 타입이 있는 코드베이스에서 작업하는 에이전트는 각 오류에 대해 정확한 줄 번호가 포함된 수정을 받음. 타입이 없는 코드베이스에서는 런타임에, 그것도 테스트가 지나가는 경로에서만 문제를 발견함.
  • 동적 언어는 의도적으로 이 검사를 미루며, 코드를 작성하는 사람이 타입 정보를 머릿속에 담고 있던 시기에는 합리적인 절충이었음. 에이전트는 추측 사이에 아무것도 유지하지 않으며, 대표적인 실수는 존재하지 않는 메서드를 호출하는 것임.
  • 언어를 바꿀 필요는 없지만 해당 계층을 보완해야 함. 코드베이스가 감당할 수 있는 곳에는 점진적 타입을 도입하거나, 에이전트가 건드린 모든 호출 지점을 실행하는 테스트를 마련해야 함.
  • 컴파일러는 설계도가 빌드되는지 증명함. tsc, go build, cargo build의 빌드 성공은 동작을 보장하지 않지만, 에이전트가 설명한 것이 실제로 기계가 될 수 있고 참조한 모든 조각이 존재하며 서로 맞는다는 점은 증명함. 빌드가 성공하지 않은 에이전트는 아직 테스트를 실행할 단계가 아님.
  • 포매터는 코드베이스 전체가 한 팀에서 작성한 것처럼 보이게 함. 이를 외관상의 문제로 치부하기 쉽지만, 실제로 중요하며 사람보다 에이전트가 작업할 때 더 중요함.
  • 에이전트가 제약 없이 작성하면 인자 재배치, 들쭉날쭉한 들여쓰기, 파일마다 달라지는 따옴표 스타일 등 차이점 노이즈가 생김. 포매터는 이를 없애 검토 대상 차이를 변경 사항만으로 제한함.
  • 또한 사람이 판단력을 들여 검토할 필요가 없는 리뷰 의견 범주 전체를 제거함.
  • 제대로 된 테스트 환경은 테스트 주행에 의미를 부여함. 테스트는 에이전트가 변경 사항이 의도한 대로 동작했는지 배우는 곳이지만, 테스트 트랙도 현실적이어야 함.
  • 네트워크를 호출하고 열두 번 중 한 번 실패하는 테스트 스위트는 신호 대신 노이즈를 제공함. 그러면 에이전트는 문제를 고치는 대신 실패 상태를 전제로 추론하기 시작함.
  • 테스트 환경은 결정적이고, 로컬에서 실행되며, 빨라야 함. 그렇지 않으면 신뢰할 수 없는 시뮬레이터에서 시험 주행을 하는 것과 같음.

순서와 속도가 피드백 루프를 결정함

  • 변경된 파일을 대상으로 비용이 가장 낮은 계층부터 실행하고, 전체 검사를 1분 이내에 마쳐야 함. 순서는 린트, 타입 검사, 빌드, 관련 테스트임.
  • 각 계층이 다음 계층으로 넘어가는 코드를 걸러내므로, 비용이 큰 검사는 저렴한 검사를 통과한 코드에만 실행됨.
  • 올해 초에 검사 속도 목표를 다뤘으며, 목표는 달라지지 않았음. 답을 받는 데 10분 걸리는 하네스는 에이전트가 기다리는 대기열이며, 그동안 에이전트는 추측을 더 쌓게 됨.
  • 결정권을 에이전트에게서 가져와야 함. “끝나면 검사 실행”이라는 지시를 받은 에이전트는 때때로 일찍 끝났다고 판단함.
  • 편집할 때마다 실행되는 훅, 결과를 보고하기 전에 실행하라고 지시하는 단일 개발 검사 명령, 또는 우회할 수 없는 사전 커밋 훅에 하네스를 연결해야 함.
  • 에이전트는 하네스 실행 여부를 선택할 수 없어야 하며, 결과에 어떻게 대응할지만 선택할 수 있어야 함.

피드백 루프가 자율성의 한계를 정함

  • 에이전트가 빌드에서 얼마나 많은 부분을 맡을 수 있는지는 하네스가 결정함.
  • 타입과 린트가 없고 테스트 스위트 실행에 20분 걸리는 저장소에서는 에이전트에게 감독이 필요한 작은 작업만 맡길 수 있으며, 다른 검증 수단이 없으므로 생성된 코드를 한 줄씩 읽어야 함.
  • 린트, 타입 검사, 빌드, 테스트가 모두 1분 이내에 끝나는 저장소에서는 에이전트에게 기능 브랜치 전체를 맡길 수 있음. 출력 수준의 실수는 차이를 보기 전에 기계가 이미 잡아내므로, 생성된 코드가 아니라 결과를 검토할 수 있음.
  • 하네스에 추가하는 검사마다 에이전트가 스스로 찾아 고칠 수 있는 오류 종류가 늘어남. 누락한 검사는 사람이 계속, 사람의 속도로 직접 잡아야 할 오류 종류가 됨.
  • 에이전트 테스트 피라미드에서 설명한 트립와이어 테스트는 같은 생각을 한 단계 더 확장함. 코드베이스의 규칙을 검사로 표현하면 에이전트가 이를 벗어나는 순간 밀리초 안에 피드백을 받음.

먼저 구축하기

  • 에이전트에게 실제 작업을 맡기고, 문제가 생길 때마다 검사를 추가하는 방식은 솔깃하지만 순서가 거꾸로이며 비용도 큼. 하네스가 무료로 잡았을 실수가 매번 사람에게 도달하기 때문임.
  • 좋은 SKILL.md는 더 나은 추측을 이끌어내는 방법이며, 하네스는 결과물을 검증하는 방법임. 두 가지 모두 필요하지만, 에이전트가 한 줄도 작성하기 전에 구축할 수 있는 것은 하네스임.
  • 에이전트에게 실제 작업을 맡기기 전에 하네스를 구축해야 함. 피드백 루프가 촘촘할수록 빌드에서 에이전트가 안전하게 맡을 수 있는 범위가 커짐.