코드를 직접 실행하는 AI 코드 리뷰어, TREX
Greptile이 단순히 코드를 읽는 것을 넘어 직접 코드를 실행하여 런타임 버그를 잡아내는 AI 코드 리뷰어 'TREX'를 개발했습니다. 이 도구는 메인 리뷰 에이전트가 문제를 식별하면 하위 에이전트를 병렬로 파견하여 실제 환경에서 코드를 테스트하고 그 결과를 시각적으로 제공합니다. 개발자들은 코드 리뷰 과정에서 기존 AI 도구로는 발견하기 어려웠던 논리적 오류나 UI 회귀 등의 복잡한 버그를 효과적으로 찾아낼 수 있게 되었습니다.
안녕하세요, Greptile의 소프트웨어 엔지니어인 Shlok입니다. 저희는 최근에 풀 리퀘스트(Pull Request)를 검토하는 것은 물론, 실제로 코드를 실행하여 무엇이 잘못되었는지 보여주는 코드 리뷰어를 개발했습니다.
1976년, Michael Fagan은 IBM에서 공식적인 코드 인스펙션(Code Inspection)을 도입하는 논문을 발표했습니다. 당시 개발자들은 코드 프린트물을 뽑아서 한 방에 같이 앉아 한 줄씩 코드를 읽어 내려갔습니다. 오늘날에도 우리는 여전히 화면상에서 diff(변경 사항)를 읽습니다. AI 도구들이 이 과정을 더 빠르게 만들긴 했지만, 대부분은 여전히 코드를 '읽는' 수준에 불과합니다.
이러한 접근 방식은 코드 자체에 명백하게 드러나는 버그를 찾는 데는 효과적입니다. 하지만 문제는 코드상에는 전혀 나타나지 않고 프로그램이 실행될 때만 존재하는 부류의 버그들이 있다는 것입니다. 특정 상태(State)의 순서가 필요한 논리적 오류, 페이지 로딩 후에 나타나는 UI 회귀(Regression), 실제 요청이 필요한 레이스 컨디션(Race condition) 등을 떠올려 보십시오. 변경 사항을 완벽하게 읽더라도 이런 유형의 버그는 완전히 놓칠 수밖에 없습니다. 정적 코드 리뷰에는 한계가 있습니다. 코드가 '무엇이라고 말하는지'는 추론할 수 있지만, 실제로 '무엇을 하는지'는 알려줄 수 없습니다.
TREX("Test, Run, Execute"의 약자)는 Greptile이 이러한 한계에 대응하여 내놓은 해결책입니다. 즉, 코드 리뷰에 직접 통합된 실행 계층(Execution layer)입니다.
문맥(Context) 낭비 없는 에이전트 오케스트레이션 TREX는 처음에 테스트를 생성하고 실행하는 독립적인 에이전트로 시작했으며, Greptile과는 완전히 별개의 제품이었습니다. 저희는 이 과정에서 버그가 자연스럽게 발견될 것이라 기대했습니다. 하지만 그렇지 않았습니다. 테스트를 생성하는 것은 버그를 찾는 것과 같은 활동이 아니었습니다. 분리된 TREX 에이전트가 테스트를 작성해 보았지만, 그 테스트들은 사용자가 실제로 하려는 것과는 전혀 관련이 없었습니다. 이는 불필요한 노이즈를 만들어냈고 엣지 케이스(Edge case)들도 놓쳤습니다. 지금 와서 보면 당연한 사실이지만, 저희가 이 교훈을 깨닫는 데는 예상보다 훨씬 오랜 시간이 걸렸습니다.
처음에는 각 에이전트에 고유한 문맥 창(Context window)을 제공할 수 있다는 가정하에 이 에이전트들을 분리하여 구축했습니다. 하지만 이는 두 에이전트가 지식을 공유하지 않은 채 별도로 실행된다는 것을 의미했습니다. 그 결과, 종종 동일한 코드베이스 부분을 두 번이나 탐색하며 겹치는 작업을 했고 서로 무엇을 찾았는지 알지 못해 결국 컴퓨팅 리소스만 낭비했습니다.
가장 명확한 해결책은 이들을 하나의 에이전트로 합치는 것처럼 보였습니다. 저희는 그렇게 시도해 보았고, 또 다른 문제에 부딪혔습니다. 전체 리뷰를 처리하는 단일 에이전트가 과부하에 걸린 것입니다. 서비스를 구동하고, 스크린샷을 찍고, 테스트를 실행하는 등 모든 과정을 거치자, 하나의 에이전트가 깔끔하게 처리하기에는 감당해야 할 문맥(Context)이 너무 많았습니다.
해결책은 TREX를 완전히 별개의 제품으로 존재하게 하는 대신, 기존 Greptile 리뷰어와 동일한 문맥을 공유하도록 만드는 것이었습니다. 이는 에이전트 내부에서 다른 에이전트들을 관리한 저희의 첫 사례였습니다. 두 개의 독립적인 에이전트와 달리, 이는 TREX가 제로 베이스에서 시작하지 않음을 의미합니다. TREX는 Greptile 리뷰어 에이전트가 이미 발견한 내용을 상속받고, 자체적인 문맥 창을 가지며, 조사해 달라고 요청받은 특정 문제에만 범위가 제한됩니다.
Greptile 리뷰어 에이전트가 오케스트레이터(Orchestrator, 지휘자) 역할을 합니다. 이 메인 에이전트가 diff를 읽고, 조사할 가치가 있는 문제를 식별한 뒤, 문제마다 전용 TREX 에이전트를 실행하여 모두 병렬로 처리합니다. 이렇게 파생된 TREX 에이전트들은 오케스트레이터 에이전트의 지식을 바탕으로, 필요한 컴퓨팅 권한과 자유를 가지고 작동합니다.
좋은 예시로 인증 게이트(Auth gate) 뒤에 숨겨진 UI 기능을 들 수 있습니다. 이를 로컬에서 테스트하려면 환경을 설정하고 인증을 처리한 다음 기능 플래그(Feature flag)를 올바른 상태로 만들어야 합니다. 하위 에이전트(Subagent)는 이 모든 과정을 스스로 파악하고 처리한 뒤, 렌더링된 기능의 스크린샷을 캡처하여 돌아옵니다.
작업 과정을 보여주는 멀티모달 아티팩트(Multi-modal artifacts) 설계 초기 버전의 TREX는 테스트한 내용과 그 결과를 글머리 기호(Bullet point) 형태로 나열하여 출력했습니다. 합리적인 출발점이었지만, 충분한 정보를 제공하지는 못했습니다. 에이전트나 인간 리뷰어가 "결제 흐름(Checkout flow)을 테스트했고 실패를 발견함"이라는 글머리 기호를 읽는다 해도 큰 도움이 되지 않을 것입니다. 프로세스의 어느 부분에서 문제가 발생했는지 알 수 없기 때문입니다. 테스트가 실패했다면, 그것이 설정 문제였습니까? 검증(Assertion) 문제였습니까? 아니면 환경 문제였습니까? 저희는 초기 버전에서 (이 한계를 깨닫고 시각화를 도입했습니다.)