자율 주행 코드베이스를 향하여

장시간 실행되는 자율 코딩에 관한 연구에 보내주신 큰 반응에 힘을 얻고 있습니다.
이 작업은 현재 모델의 한계를 극한까지 시험해 보기 위한 내부 연구에서 시작되었습니다. 연구의 일환으로, 우리는 수천 개의 에이전트를 오케스트레이션하고 그 행동을 관찰하기 위한 새로운 에이전트 하네스를 만들었습니다. 지난달에는 이 시스템이 1주일 동안 연속으로 안정적으로 실행될 만큼 성숙해졌고, 연구 프로젝트(웹 브라우저)에 대한 커밋의 절대다수를 수행하게 되었습니다. 이 브라우저는 외부 사용을 목적으로 한 것이 아니었고, 코드에 미완성된 부분이 있을 것이라 예상했습니다.
그럼에도 불구하고 몇 가지 특이점이 있더라도 수천 개의 에이전트가 함께 작업해, 인간의 개입 없이도 거의 전부 실행 가능한 결과물을 만들어 냈다는 사실은 공유할 가치가 있는 의미 있는 이정표라고 느껴졌습니다. 그 이후로도 우리는 연구를 이어 왔고, 이 하네스가 어떻게 만들어졌는지 더 깊이 다뤄 보고자 했습니다.
또한 일부 사용자분들께는 이 연구의 일부를 직접 사용해 볼 수 있도록 제공하고 있습니다.
배경
이 연구 프로젝트는 처음에는 제가 개인적으로 진행하던 사이드 프로젝트였습니다.
브라우저는 흥미로운 벤치마크처럼 느껴졌습니다. 프런티어 모델들의 한계를 드러내기에는 충분히 복잡했고, 서로 연동되어야 하는 다양한 하위 시스템도 많았습니다.
처음 계획은 JavaScript 지원 없이 웹 페이지 렌더링만 지원하는 것이었습니다. 저는 먼저 Opus 4.5에 프롬프트를 보내 브라우저 엔진을 만드는 자세한 계획을 작성해 달라고 요청했습니다. 그리고 그 계획을 어디까지 밀어붙일 수 있는지 보기 위해 여러 번 "계속해"라고 재촉하며 이어서 쓰도록 했습니다.
이 시도는 금방 실패했습니다. 모델은 자신이 무엇을 하고 있는지 금세 놓쳐 버렸고, 실제로는 목표에 한참 못 미쳤는데도 자주 작업이 완료되었다고 선언해 버렸으며, 복잡한 구현 세부 사항에서는 곧잘 막혀 버렸습니다. 하지만 동시에 매우 깊은 지식과 지능의 징후도 보였습니다. 작은 단위의 코드라면 꽤 잘 작성할 수 있었습니다.
핵심 문제는 브라우저라는 과제가 너무 방대해서 더 작은 하위 작업들로 쪼개야 했다는 점이었습니다. 이어서 저는 에이전트에게, 여러 에이전트가 병렬로 맡을 수 있는 주요 작업들의 의존성 그래프를 설계하도록 했습니다. 작업마다 에이전트를 수동으로 띄우고, 멈추면 다시 재촉했습니다. 이렇게 해서 처리량은 늘었지만, 결과는 크게 나아지지 않았습니다. 에이전트들끼리 서로 소통할 수 없었고, 프로젝트 전체에 대한 피드백을 제공할 수도 없었습니다. 시스템이 더 동적으로 변할 필요가 있었습니다.
한편 GPT-5.1(그리고 이후의 GPT-5.2)은 지시를 정확하게 따르는 능력에서 더 나은 결과를 보여주기 시작했습니다. 장시간 실행되는 에이전트에 잘 맞을 것 같았기에, 우리는 이러한 실험을 바탕으로 OpenAI 모델을 사용하도록 하네스를 업데이트했습니다.
이 시점에서 하네스는 JavaScript 없이 동작하는 단순한 버전의 웹 브라우저를 만들 수 있게 되었지만, 하나의 에이전트만으로 완전한 브라우저 엔진을 구축하는 것은 시간 면에서 사실상 불가능했습니다.
이것이 다음 연구 단계의 시작이었습니다. 계산 자원을 10배 더 투입하면 의미 있는 처리량도 10배 높일 수 있을까요?
단일 에이전트에서 멀티 에이전트로
우리는 간단한 Rust 기반 하네스로 새 저장소를 시작했습니다.
분산 시스템의 복잡성을 다루는 대신, 충분한 리소스를 가진 하나의 대형 Linux VM(Virtual Machine)에서 하네스를 실행했습니다. 하네스를 제어하기 위해 우리는 VM에 SSH로 접속한 뒤, 단순한 터미널 인터페이스를 사용했습니다.
우리는 초반부터 시스템에 대한 적절한 관측 가능성(observability)을 확보하는 데 더 많은 시간을 투자했습니다. 모든 에이전트 메시지, 시스템 동작, 명령 출력에 타임스탬프를 붙여 로깅해, 세션을 분석하고 다시 재생할 수 있도록 했습니다. 이는 우리가 수동으로 검토하는 데에만 도움이 된 것이 아니라, 대량의 데이터를 Cursor로 다시 보내 빠르게 패턴을 찾고 필요한 정보를 골라내는 데에도 유용했습니다.
자체 조정(Self-coordination)
우리가 처음 떠올린 멀티 에이전트 아이디어는 가장 단순한 방식이었다. 동등한 역할을 가진 에이전트들이 공용 상태 파일을 사용해 서로 무엇을 하고 있는지 확인하고, 자신이 무엇을 할지 결정한 뒤 그 파일을 업데이트하게 하는 것이다.


우리는 무엇을 해야 하는지 최대한 규정하지 않고, 대신 에이전트가 스스로 조정하는 방법을 알아내도록 맡기려 했다. 이 접근은 금방 실패했다.
조정 파일은 곧바로 더 많은 문제를 만들어 냈다. 에이전트들이 락(잠금)을 너무 오래 쥐고 있거나, 해제하는 것을 잊거나, 허용되지 않을 때 락을 걸거나 해제하려고 시도했고, 전반적으로 조정 파일에 락을 걸고 있는 것의 의미를 이해하지 못했다. 락킹은 잘못 구현하기는 매우 쉬운 반면, 올바르게 구현할 수 있는 여지는 극히 좁았고, 프롬프트를 더 늘린다고 해서도 해결되지 않았다.
락킹은 경합도 지나치게 늘렸다. 에이전트가 20개가 있어도, 대부분의 시간을 락을 기다리는 데 쓰느라 처리량은 1~3개 수준으로 떨어졌다. 우리는 에이전트에게 다른 에이전트의 작업을 명시적으로 기다리는 도구를 제공해 봤지만, 거의 사용되지 않았다. 락을 사용하지 않는 낙관적 동시성 제어 방식도 시도했는데, 오버헤드는 줄었지만 혼란을 없애지는 못했다.
에이전트 사이에 구조가 없다 보니 어느 한 에이전트도 크고 복잡한 작업을 떠맡지 않았다. 그들은 경합과 충돌을 피하려고, 프로젝트 전체에 대한 책임을 지기보다는 더 작고 안전한 변경을 선택하는 쪽을 택했다.
구조와 역할 추가하기
다음으로, 역할을 분리해 각 에이전트에 소유권과 책임을 부여했습니다.


먼저 Planner가 사용자의 지시사항을 향해 진척을 내기 위한 구체적인 접근 방식과 산출물을 설계합니다. 그런 다음 이 계획이 Executor에게 넘어가는데, Executor는 계획이 완전히 달성되도록 책임지는 단일 리드 에이전트가 됩니다. Executor는 Workers를 위한 작업을 쪼개 생성할 수 있고, 이를 통해 선형적인 확장성과 처리량을 확보할 수 있습니다.
지속적인 진행과 책임성을 위해, 독립적인 Judge가 Executor의 작업이 끝난 후 실행되어 작업이 완료되었는지와 추가 반복을 수행해야 하는지를 판단합니다. 이는 많은 조율 문제를 해결했습니다. 실행을 소유하고 감독하는 단일 역할을 두면, 전체 시스템은 여전히 결과를 내는 가운데 Workers는 자신의 작업에만 좁게 집중할 수 있습니다.
관찰과 힐클라이밍
이 설계에 도달하기까지는 시스템을 면밀히 관찰해야 했습니다.
큰 문제가 있으면 여러 에이전트와 도구 호출 전반에서 반복적으로 발생하는 경향이 있었습니다. 예를 들어, 많은 에이전트가 동시에 git restore를 실행하면서 리소스 경합이 지나치게 심해지는 경우를 발견했습니다. 우리는 Cursor를 사용해 로그를 분석하고 프롬프트와 대조해 보며, 실제 동작이 왜 기대와 맞지 않는지 파악하려 했습니다.
결국 이 시스템은 가장 느린 워커(worker)에 의해 병목이 생긴다는 것을 알아냈습니다. 구조가 너무 경직되어 있었습니다.
모든 계획을 처음에 한 번에 세우는 방식은, 새로운 이슈가 발견될 때 시스템이 동적으로 다시 조정되기 어렵게 만들었습니다. 일부 에이전트는 비생산적인 방향으로 진행하다가, 다음 루프 반복이 오기 전까지는 스스로 방향을 수정하지 못하는 상황에 빠지곤 했습니다.
연속 실행기
다음 버전에서는 독립적인 플래너가 제거되었습니다.
이제 실행기는 작업을 생성하는 것뿐 아니라 목표를 어떻게 달성할지에 대한 계획도 세울 수 있게 되었습니다. 하나뿐인 에이전트였기 때문에, 어디엔가 계획을 적어 둘 필요도 없었고, 하나의 고정된 계획에 얽매일 필요도 없었으며, 모든 워커 에이전트가 끝날 때까지 엄격히 기다릴 필요도 없었습니다.
최신 상태 보장하기
모든 역할의 에이전트가 오랜 시간에 걸쳐 점점 엇나가지 않도록 하기 위해, 우리는 다음과 같은 최신성 메커니즘을 도입했습니다:
scratchpad.md는 내용을 계속 덧붙이기보다는 자주 다시 작성해야 합니다.- 개별 에이전트는 컨텍스트 한계에 도달했을 때 자동으로 요약해야 합니다.
- 시스템 프롬프트에 자기 성찰과 정렬(alignment) 관련 리마인드 문구를 추가했습니다.
- 에이전트가 언제든지 방향을 전환(pivot)하고 기존 가정을 의심·도전하도록 유도했습니다.
이제 시스템은 매우 동적이고 유연해졌습니다. 코드베이스를 능동적으로 탐색하고, 결정을 재검토하며, 워커를 관리하고, 작업을 서로 교차시키며(interleave) 처리하고, 최신 정보를 지속적으로 반영할 수 있게 되었습니다. 또한 에이전트가 지시를 끝까지 따르는 데 상당히 능숙하다는 것을 확인했기 때문에, 시스템을 단순하게 유지하기 위해 judge 컴포넌트는 제거했습니다.


비정상적인 동작
이런 개선에도 불구하고, continuous executor는 비정상적인 동작을 보이기 시작했습니다. 무작위로 sleep 상태에 들어가고, 에이전트 실행을 멈추고, 스스로 일을 해버리고, 계획 수립을 거부하며 몇 개의 아주 좁게 집중된 작업만 생성하고, worker 변경 사항을 제대로 병합하지 못하고, 너무 이른 시점에 완료되었다고 주장했습니다.
알고 보니 이 executor는 동시에 너무 많은 역할과 목표를 부여받고 있었습니다. 예를 들어: 계획 수립, 탐색, 리서치, 작업 생성, worker 상태 확인, 코드 리뷰, 편집 수행, 출력 병합, 루프 종료 여부 판단 등이었습니다. 지금 돌이켜보면, 과부하가 걸린 것이 당연했습니다.
최종 시스템 설계
최종 설계는 우리가 지금까지 배운 모든 내용을 반영합니다:
- 루트 planner가 사용자의 지시 전체 범위를 책임집니다. 현재 상태를 파악하고 목표를 향해 나아갈 수 있도록 구체적이고 명확하게 정의된 작업(태스크)을 만들어내는 역할을 합니다. 스스로 코딩은 하지 않습니다. 자신이 만든 작업이 실제로 수행되고 있는지, 누가 수행하는지는 알지 못합니다.
- 어떤 planner가 자신의 범위를 더 잘게 나눌 수 있다고 판단하면, 위임된 좁은 범위를 전적으로 책임지는 subplanner들을 생성합니다. 각 subplanner는 그 조각에 대해서만 유사한 방식으로 전적인 책임을 집니다. 이는 재귀적으로 이뤄집니다.
- worker들은 작업을 가져와 완료까지 추진할 전적인 책임을 집니다. 이들은 더 큰 시스템에 대해 알지 못합니다. 다른 planner나 worker와 소통하지 않습니다. 각자 자신의 repo 사본에서 작업하고, 완료되면 시스템이 해당 작업을 요청한 planner에게 제출하는 단일 handoff를 작성합니다.
흥미롭게도, 이는 오늘날 일부 소프트웨어 팀이 실제로 운영되는 방식과도 일치합니다.


subplanner들은 worker들을 빠르게 여러 개로 확장해 처리량을 높이는 동시에, 전체 시스템이 하나의 에이전트에 의해 완전히 소유되고 책임질 수 있도록 보장합니다. 이는 단일 planner가 쉽게 압도되고 시야가 좁아질 수 있는 대규모 프로젝트와 작업에서도 큰 도움이 되었습니다.
handoff에는 무엇을 했는지만이 아니라, 중요한 메모, 우려 사항, 원래 계획에서 벗어난 부분, 발견한 내용, 생각, 피드백까지 포함됩니다. planner는 이를 후속 메시지로 전달받습니다. 이를 통해 시스템은 끊임없이 움직입니다. planner가 "완료"된 상태라 해도, 계속해서 업데이트를 받고, 최신 repo를 가져오며, 이후 계획을 세우고 후속 결정을 내릴 수 있습니다.
모든 에이전트에는 이 메커니즘이 있어, 전역 동기화나 교차 통신의 오버헤드 없이도, 점점 더 넓은 시야를 가진 소유자들에게 정보를 상향 전파하면서, 시스템이 놀라울 정도로 동적이고 스스로 수렴하도록 유지할 수 있습니다.
Integrator 제거하기
처음에는 전역적으로 컨텍스트를 파악하는 중앙 품질 관리를 위해, 그리고 너무 많은 worker 들이 동시에 push, rebase, conflict 해결, merge 를 시도하면서 생기는 경합을 줄이기 위해 integrator 를 도입했습니다.
하지만 이 구성 요소는 금방 명백한 병목으로 드러났습니다. 수백 개의 worker 가 있었지만, 모든 작업이 반드시 통과해야 하는 하나의 관문(즉, “red tape”)만 존재했던 것입니다. 우리는 프롬프트를 바꿔 보기도 했지만, 결국 이것이 불필요한 요소이며 시스템을 단순화하기 위해 제거할 수 있다고 판단했습니다.
처리량과 트레이드오프
이 시스템은 일주일 동안 총 1,000만 건의 도구 호출을 처리했고, 시간당 약 1,000개의 커밋까지 처리량이 증가했습니다. 시스템이 한 번 가동된 이후로는 사람의 개입이 전혀 필요하지 않았습니다.
이 처리량을 달성하기 위해 여러 가지 의도적인 트레이드오프(절충)가 있었습니다.
커밋 정확성
모든 커밋 전에 100%의 정확성을 요구했을 때, 처리량이 크게 직렬화되고 전반적인 속도가 느려졌습니다. API 변경이나 오타 같은 아주 작은 오류 하나만 있어도 전체 시스템이 사실상 멈춰 서 버렸습니다. 작업자 에이전트들은 자신에게 주어진 범위를 벗어나 관련 없는 것들을 고치기 시작했고, 많은 에이전트들이 한꺼번에 몰려들어 서로를 방해하며 같은 이슈를 고치려 했습니다.
이런 동작은 유용하지도, 꼭 필요하지도 않았습니다. 어느 정도의 여유를 허용하면 에이전트들은 다른 이슈들이 곧 다른 에이전트들에 의해 수정될 것이라고 신뢰할 수 있습니다. 이는 시스템이 전체 코드베이스에 대해 실질적인 소유권과 위임 구조를 갖고 있기 때문에 가능한 일입니다. 오류는 발생하지만 빠르게 수정됩니다. 오류율은 작고 일정하게 유지되며, 완전히 깨끗한 상태는 드물 수 있지만 안정적이고 관리 가능한 수준으로 머무르고, 폭발적으로 증가하거나 악화하지 않습니다.
이는 이상적인 효율적 시스템은 일정 수준의 오류율을 감수하되, 릴리스 전에 에이전트가 정기적으로 스냅샷을 찍고 빠르게 정리(fixup) 작업을 수행하는 최종 "green" 브랜치가 필요함을 시사합니다.
동기화 오버헤드
가끔 여러 에이전트가 동일한 파일을 수정하거나 같은 코드를 리팩터링하는 경우가 있습니다. 이런 상황을 완전히 없애려 하거나 과도하게 복잡한 해결책을 설계하는 대신, 우리는 어느 정도의 일시적인 혼선을 감수하고, 시스템이 짧은 시간 안에 자연스럽게 수렴하고 안정되도록 둡니다.
이 과정에서 토큰이 조금 더 소모되고 국지적인 충돌이 생기지만, 전체 시스템은 더 단순해집니다. 모델 간 동작을 맞추고 과부하를 주지 않기 쉬워지고, 관리와 모니터링이 쉬워지며, 마찰이 줄고, 전반적인 생산성이 향상됩니다. 또한 지나치게 복잡한 접근 방식을 피할 수 있습니다.
인프라에서 얻은 교훈
각 멀티 에이전트 실행은 분산 시스템으로 인한 조기 복잡성을 피하기 위해, 충분한 시스템 리소스를 갖춘 개별 대형 머신에서 수행했습니다. 대부분의 실행은 수백 개의 에이전트 수준에서 정점을 찍었고, 이는 보통 이 머신들을 포화 상태로 만들긴 했지만 과도하게 자원을 할당하지는 않았기 때문에, 이 방식이 잘 맞았습니다. 이런 아키텍처 덕분에 시스템 메트릭을 관찰하기가 더 쉬워졌고, 필요할 때 상태를 공유하고 복사하는 것도 수월했습니다.
에이전트의 RAM 사용량을 제한한 뒤에는 디스크가 병목 지점이 되었습니다. 특히 모놀리식 프로젝트에서는 수백 개의 에이전트가 동시에 컴파일을 수행하면서, 빌드 아티팩트에 대해 초당 수 GB 수준의 읽기와 쓰기가 발생했습니다. 이는 하네스(harness)의 전체 처리량에 큰 영향을 미쳤고, 흥미로운 교훈을 남겼습니다. 프로젝트 구조, 아키텍처 결정, 개발자 경험이 토큰 및 커밋 처리량에 영향을 줄 수 있는데, 이는 코드베이스를 다루는 작업(예: 컴파일)이, 이상적으로는 그래야 할 생각하고 코딩하는 시간보다 더 많은 시간을 차지하기 때문입니다.
일반적인 개발 환경에도 제약과 비효율이 존재했습니다. 단일 사용자 워크스페이스에서는 타당하거나 크게 문제 되지 않는 것들이, 한 머신에서 수백 개의 에이전트가 동일한 작업을 수행할 때는 두드러지게 드러날 수 있습니다. 이를 해결하는 단순한 방법은 각 에이전트에 개별 머신을 주는 것입니다. 하지만 이러한 기본 요소와 도구 일부를 재고하고 재설계하는 것만으로도, 큰 효율성 향상을 얻을 수 있는 흥미로운 쉬운 기회들이 많이 있습니다.
예를 들어, Git과 Cargo 같은 많은 도구는 주로 단순한 동시성 제어 메커니즘으로 공유 잠금(shared lock)을 사용합니다. 데이터베이스 같은 동시 시스템에서 잘 확립된 메커니즘을 도입하면, 이런 도구들이 멀티 에이전트 시스템에서도 똑같이 잘 동작하도록 만들 수 있을까요? 모든 에이전트는 레포의 사본을 하나씩 가지고 있지만, 대부분의 파일과 아티팩트는 동일합니다. 더 정교한 프로덕션 스토리지 시스템에서 사용되는 copy-on-write와 중복 제거 같은 단순한 기능을 추가하면, 별도의 인프라를 구축하지 않고도 전형적인 "단일 사용자" 시스템에서 비슷한 손쉬운 이점을 얻을 수 있지 않을까요?
에이전트에게 의도를 명시하기
이 멀티 에이전트 시스템에 주어진 지시는 매우 중요했습니다.
처음에는 지시를 최우선 목표로 두지 않고, 안정적이고 효과적인 하니스(harness)를 만드는 데 집중했습니다. 하지만 지시의 중요성은 곧 분명해졌습니다. 우리는 사실상 일반적인 코딩 에이전트와 상호작용하고 있었지만, 시간과 컴퓨트 자원이 몇 자릿수 더 많은 상태였습니다. 이로 인해 비최적이거나 불명확한 지시까지 포함해 모든 것이 크게 증폭되었습니다.
초기 지시에 더 많은 시간을 들이는 것이 타당합니다. 결국 에이전트는 여전히 에이전트입니다. 여러분의 지시를 엄격히 따르도록 학습되어 있고, 그 경로를 따라가며, 지시가 나쁘더라도 바꾸거나 무시하지 않습니다.
우리는 연구 프로젝트에서 성과를 내고 싶었기 때문에, 프로젝트와 하니스가 발전함에 따라 초기 지시를 수정했습니다. 우리는 새로운 멀티 에이전트 시스템을 운용하는 법을 배우는 동시에 브라우저를 만드는 법도 배우고 있었고, 하니스 자체의 문제가 아닌데도 부실하거나 과소 정의된 스펙이 출력 품질에 그대로 반영되는 것을 볼 수 있었습니다. 하니스는 단지 우리의 지시를 정확히 따르고 있었을 뿐입니다.
브라우저 프로젝트에서 나온 몇 가지 예시는 다음과 같습니다.
- 초기에는 스펙 구현과 버그 수정에 초점을 맞춘 지시를 내렸습니다. "spec implementation"과 같은 지시는 모호해서, 에이전트들이 실제로는 거의 쓰이지 않는 난해한 기능에 깊이 파고들고, 우선순위를 지능적으로 조정하지 못하곤 했습니다.
- 우리는 암묵적으로, 사용자에게 친숙한 범위 안에서의 성능 기대치가 존재한다고 가정했습니다. 하지만 성능을 다른 목표들과 균형 잡도록 만들기 위해서는 명시적인 지시와 강제 타임아웃이 필요했습니다.
- 시스템의 복잡한 부분에서는, 에이전트가 메모리 누수를 일으키거나 데드락을 유발하는 코드를 작성할 수 있습니다. 인간이라면 이를 눈치챘겠지만, 에이전트에게는 항상 자명하지 않았습니다. 시스템이 우아하게 복구하고 보다 방어적으로 동작할 수 있도록, 명시적인 프로세스 기반 자원 관리 도구가 필요했습니다.
JavaScript가 없는 단순 브라우저의 초기 버전은, 전체 브라우저로 진화하기에 부적합한 아키텍처로 수렴했습니다. 이는 초기 스펙의 실패였습니다.
마찬가지로, 에이전트들에게 이 프로젝트가 브라우저를 처음부터 만드는 것이라는 점을 분명히 알려줬음에도, 그들은 스스로 구현할 수 있었거나, 올바른 구현이 진행되는 동안 임시 비계로 사용할 수 있었을 의존성을 일부 끌어다 썼습니다. 이는 지시에서의 누락이었습니다. 이후 실행에서는 의존성에 대한 철학과 사용해서는 안 되는 라이브러리를 명확하게 서술했고, 이를 통해 문제가 해결되었습니다.
그 이후 실행에서는 서로 독립적으로 동작하는 많은 크레이트들로 대대적인 리팩터링을 진행하며 모놀리식 구조에서 벗어났습니다. 리포는 심각하게 깨진 상태였지만, 멀티 에이전트 시스템은 며칠 만에 동작하는 코드로 수렴했습니다. 이는 시스템이 완전히 깨진 상태에서도 더 악화되거나 정체되지 않고, 협업적이고 지능적으로 작업을 진행할 수 있는 강력한 능력을 지녔다는 것을 보여줍니다. 이 실행에서는 컴파일을 기다리는 시간도 훨씬 줄었고, 이전보다 여러 배 높은 처리량으로 동작했습니다.
아키텍처와 지시는 중요합니다. 에이전트는 뛰어난 엔지니어링 능력을 지녔지만, 좋든 나쁘든 끝까지 지시를 따릅니다. 지나치게 좁은 지표와 완전히 비구조적인 자유 사이의 균형을 찾는 일은 까다로웠고, 무엇이 자명한지, 무엇은 명시적으로 언급해야 하는지를 파악하는 것도 마찬가지였습니다.
이 모든 것은 의도를 끌어내고, 명확히 규정하고, 이해하는 일이 얼마나 중요한지를 보여주며, 이러한 중요성은 규모가 커질수록 더 커집니다. 조향 가능성(steerability)과 가시성(observability)은 앞으로도 계속 탐구할 만한 흥미로운 연구 분야가 될 것입니다.
프롬프트 최적화
프롬프트 설계는 모델 진화 과정에서 매우 중요한 부분이었습니다.
모델이 이미 잘할 수 있는 일은 굳이 지시하지 않고, 모델이 잘 모르는 일(예: 멀티 Agent 협업)이나 특정 도메인에 특화된 일(예: 테스트 실행 방법, 배포 파이프라인)만 지시하는 편이 낫다는 것을 발견했습니다. 모델을, 엔지니어링 역량은 뛰어나지만 여러분의 특정 코드베이스와 프로세스는 모르는 아주 유능한 새 팀원으로 대하시면 됩니다.
지침을 나열하는 것보다 제약을 주는 것이 더 효과적입니다. "TODO 금지, 부분 구현 금지"는 "구현을 끝까지 마무리하세요"보다 더 잘 작동합니다. 모델은 일반적으로 기본 설정만으로도 꽤 괜찮은 결과를 냅니다. 제약은 그 행동의 경계를 정의해 주는 것입니다.
더 고수준이거나 복잡한 작업에 대해서는 체크리스트식 사고를 피하세요. 여러분의 의도에 대해서는 자세히 설명하되, 해야 할 개별 항목을 너무 구체적으로 나열하면 모델은 더 넓은 범위보다 그 항목들을 달성하는 데에만 집중하는 경향이 있습니다. 또한 목록에 없는 것들은 암묵적으로 우선순위가 떨어지게 됩니다. 일반적으로는 모델이 스스로의 판단과 주도성을 활용하도록 두는 것이 더 좋습니다.
작업 범위를 정할 때는 수량에 대해 구체적인 수치와 범위를 제시하는 것이 유용하다는 것도 알게 되었습니다. "많은 작업을 생성해라"라는 식의 지시는 실제로는 소량만 생성하는 결과를 낳는 경향이 있습니다. 보수적인 기본값으로, 안전하게 행동하면서, 형식적으로만 지시를 따르는 쪽으로 작동하는 것입니다. 반면 "작업을 20~100개 생성해라"라고 하면, 의도가 더 큰 범위에 있고, 야심 차게 행동해야 한다는 메시지를 전달하게 되고, 그에 따라 훨씬 더 넓은 스코프의 전혀 다른 행동 패턴이 나타나는 것을 관찰했습니다.
시스템 설계에서 얻은 인사이트
연구를 통해 다음과 같은 원칙을 정립했습니다:
- 시스템은 안티프래질해야 한다. 동시에 실행되는 에이전트 수가 늘어날수록 실패 확률도 함께 증가합니다. 따라서 개별 에이전트가 실패하더라도 시스템 전체는 이를 견뎌 내고, 다른 에이전트가 복구를 시도하거나 대체 접근 방식을 시도할 수 있어야 합니다.
- 가정보다 실증에 기반한다. 인간 조직이나 기존 시스템 설계를 참고해 “이렇게 동작해야 한다”는 가정부터 두기보다는, 실제 데이터와 관찰 결과에 기반해 시스템을 조정해 나가고자 했습니다.
- 처리량(throughput)을 명시적으로 설계한다. 이는 시스템을 크게 느리게 만들더라도 100% 완벽하게 동작하는 코드를 고집하기보다는, 최종 정합성 검증 단계가 한 번 더 필요하더라도 작지만 안정적인 오류율을 허용하는 등 코딩의 다른 측면과 트레이드오프를 감수하겠다는 의미입니다.
이러한 시스템은 제대로 설계되면 놀랄 만큼 단순해지는 경우가 많지만, 우리가 여러 가지 다른 접근 방식을 충분히 탐색해 보기 전까지는 어떤 단순한 접근이 실제로 통할지 명확하지 않았습니다. 현재 시스템 설계는 오버헤드가 거의 없이 동작하면서, 토큰 처리량이 실용적인 방식으로 선형적으로 확장되도록 해 줍니다. 이 하네스(harness)에 대해서는 더 큰 추가 반복 작업이 필요하지 않았습니다.
결론
취향, 판단, 방향성은 인간에게서 나왔지만, AI는 이 연구를 빠르게 반복·탐색하는 데 있어 강력한 생산성 증폭기 역할을 했습니다.
이는 일종의 "선순환" AI 루프와도 비슷합니다. AI로 AI를 개발하고, 모델과 에이전트, 하네스가 개선될수록 그것이 다시 스스로를 강화해 점점 더 빠르게 가속하는 구조입니다. 우리는 우리를 형성하는 도구를 만들고 있습니다.
이번 연구에는 오늘날 일부 소프트웨어 팀이 일하는 방식과, 어떤 시적인 의미에서 닮은 점도 있습니다. 이 모델들은 명시적으로 이런 방식으로 학습된 것이 아니기 때문에, 이것이 발현된 행동이며 어쩌면 소프트웨어 프로젝트를 구성하는 올바른 방식일 수 있음을 시사합니다.
우리는 매우 장시간 동작하는 에이전트에 대한 연구를 계속 진행할 예정이며, 그 결과는 향후 제품에 반영될 것입니다.