The Coming Loop

Armin Ronacher

다가오는 루프

원문은 Armin Ronacher님이 에 게재했습니다. 이 블로그 구독하기

나는 더 이상 Claude에게 프롬프트를 보내지 않는다. Claude에게 프롬프트를 보내고 무엇을 할지 파악하는 루프를 돌리고 있다. 내 일은 루프를 작성하는 것이다.

— Boris Cherny

지난 몇 달 동안 코딩 에이전트 위에 무언가를 구축하는 사람들을 점점 더 많이 지켜봤다. 그냥 코딩 에이전트를 쓰는 것과는 의미 있게 다른 무언가다. 그중 일부는 Pi 위에서 일어나고 있는데, 확실히 보기 좋다! 하지만 어디서나 패턴은 같다. 일종의 큐에 작업이 들어가고, 머신이 그것을 집어 들어 시도하다 멈추면, 어떤 하네스가 그것이 정말 끝인지 판단한다.

그렇지 않다면 하네스는 같은 세션을 이어가거나, 다른 메시지를 주입하거나, 수정된 컨텍스트로 새로운 세션을 시작하거나, 작업을 다른 머신에 넘긴다. 작업은 모델 스스로라면 보통 “다 했다”고 말했을 지점을 넘어서도 살아남는다.

나는 그 유형의 루프에 대해 인정하고 싶을 만큼보다 더 많이 생각한다.

모든 코딩 에이전트 안에는 이미 에이전트 루프가 있다. 모델이 도구를 호출하고, 결과를 반영하고, 다른 도구를 호출하고, 파일을 읽고, 파일을 수정하고, 테스트를 실행하다 결국 어떤 답을 내놓는다. 그 루프는 우리가 오랫동안 꽤 익숙해진 것이다. 다른 루프는 하네스 레벨의 루프, 즉 에이전트 루프 바깥의 루프다. 그 루프 역시 새로운 것도 아니다. 우리는 Claude Code 초창기부터 이런 것의 여러 버전을 해왔다. 하지만 그 루프는 에이전틱 엔지니어링에서 점점 더 존재감을 키우고 있고, 최근 몇 주 사이에는 트위터 담론을 지배하기 시작했다.

나는 아직 이걸 잘 못한다

현재 내 상태는, 내가 깊이 애정을 갖고 있는 코드에 대해서는 이런 작업 방식으로는 별다른 성공을 거두지 못했다는 것이다. 그리고 알고 보니 그런 코드가 꽤 많다.

그 이유 중 하나는 취향이고 다른 하나는 통제감이다. 나는 코드가 어떤 모습이어야 하는지에 대해 높은 기준을 세우려 하고, 내가 배포하는 코드를 이해하고 싶다. 압박 상황에서도, 다른 사람과의 논의에서도, 먼저 클랭커(clanker)에게 설명을 듣지 않고도 시스템이 무엇을 하는지 설명할 수 있기를 원한다. 물론 이런 코드를 이해하려는 욕구가 몇 년 후에도 여전히 내게 남아 있을지에 대한 의문은 있다. 다만 지금으로서는 이해가 중요하다는 단계를 아직 넘어서지 못했다.

이런 바람을 갖고 있다 보니, 내가 주의를 기울이지 않은 채 작성된 코드, 특히 루프에서 나온 코드를 경험할 때 뭔가 부족함을 느낀다. 현세대 모델들은 지나치게 방어적이고, 지나치게 복잡하며, 추론이 지나치게 국소적인 코드를 만들어내는 경향이 있다. 강력한 불변식(invariant)을 피한다. 잘못된 상태를 불가능하게 만들기보다는 폴백을 추가한다. 코드를 중복하고, 엉성한 추상화를 만들어내며, 불분명한 설계를 더 많은 장치로 덮어버린다. 더 나쁜 것은 지금까지 이 부분이 개선되는 것을 거의 보지 못했다는 점이다. 오히려 그 측면에서는 우리가 잘못된 방향으로 가고 있는 것처럼 느껴지기도 한다. 적어도 내 취향에는 Claude Code with ultracode 같은 요즘의 핸즈오프 하네스가 만들어내는 코드가 지난 가을 우리가 만들던 코드보다 더 나쁘다. 예컨대 Fable과 함께라면 Claude Code는 한 문제에 30분 이상 중단 없이 작업할 텐데, 이전에는 그 과정에 인간이 훨씬 더 많이 개입했기 때문이다.

게다가 모델은 국소적인 실패를 관찰하고 국소적인 방어를 추가하는 경향이 있다는 것은 잘 알려져 있다. Karpathy가 언급한 바와 같이 모델은 “예외를 죽을 만큼 두려워한다”. 중요한 불변식이 있는 시스템, 특히 영속화된 데이터 형식이나 핵심 인프라에서는 올바른 수정이 “모든 비정상 케이스를 처리한다”가 아니다. 올바른 수정은 비정상 케이스 자체를 표현 불가능하게 하거나 애초에 기록될 수 없게 만드는 것이다. 하지만 많은 수동 스티어링을 가해도 그런 유형의 코드는 LLM에서 자연스럽게 나오지 않으며, 설령 그렇게 나온다 해도 모델은 이제는 불가능해진 오류까지 처리하려 든다.

그런 동작을 루프 뒤에 두면 증폭되는 경향이 있다. 각 반복마다 작은 방어 로직이 하나씩 추가되면, 시스템은 더 견고해 보이는 동시에 점점 더 이해하기 어려워진다. 손을 뗄수록 그 현상은 더 심해진다. 이런 도구를 명확한 가이드 없이 주니어에게 쥐여주면 정말 나쁜 관행을 가르치게 되기도 한다. 왜 그렇게 하느냐고 물으면, 그들은 그럴듯하게 자신의 논리를 주장할 것이기 때문이다.

루프가 통하는 곳

동시에 루프 패턴이 통하지 않는 척하는 것은 정직하지 못하다. 이미 일부 영역에서는 놀라울 정도로 잘 작동하고 있기 때문이다.

코드 포팅이 그중 하나다. 이미 대규모 자동 포팅의 인상적인 사례들이 있으며, Bun을 Zig에서 Rust로 옮긴 작업도 보고된 바 있다. 나 자신도 MiniJinja를 Go로 포팅하는 데 이를 성공적으로 사용했다. 성능 탐색도 이 방식이 훌륭하게 작동하는 또 다른 사례다. 머신이 실험을 시도하고, 벤치마크하고, 실패를 버리고, 탐색을 계속할 수 있다. 보안 스캔도 자연스럽게 들어맞고, 거의 모든 유형의 리서치도 마찬가지다. 시스템에 복잡한 문제 공간을 탐색하고 반드시 오래 남을 코드를 커밋하지 않고도 보고하도록 시키는 것이다. 이런 성공 사례들의 공통점 중 하나는 아예 새로운 코드를 생성하는 것이 아니라 이미 존재하는 코드를 변환하거나, 의도적으로 수명이 길지 않은 코드를 만들어낸다는 점이다. 개념 증명이나 아이디어를 만들거나, 발견 사항을 드러내거나, 기계적 변환에 더 가깝다.

나는 오래 유지될 필요가 없는 결과물을 만들어내거나, 명확하게 검증 가능한 기계적 변환 형태를 만들어내는 루프가, 하네스가 목표를 기계적으로 측정하는 일반적인 능력보다 더 중요하다고 믿는다. 루프의 많은 성공적인 활용 사례는 다른 LLM을 심사자나 오케스트레이터로 사용한다. 기계적 변환 케이스는 이진 테스트 케이스로 검증할 수 있지만, 대신 LLM이 판단하게 할 수도 있다!

예컨대 Claude Code는 자신이 직접 실행할 전체 실험 워크플로를 만드는 데 점점 더 능숙해지고 있다. 물론 그 코드가 만들어내는 것은 엉망이지만, 그건 워크플로의 한 단계가 순수한 개선이나 완료로 이어졌는지를 판단하는 데 하네스가 서툴러서가 아니라 모델의 잘못에 가깝다.

하네스에는 그저 계속 진행하게 해주는 어떤 신호만 있으면 된다. 객관적이거나 이진적일 필요도 없고, 그저 다음 반복을 이끌 만큼 유용하기만 하면 된다.

하루 중 지루한 부분을 덜어내고 실험하고 측정해서 아이디어를 주는 루프는 이미 정말 좋아한다.

유기체로서의 소프트웨어

반면에 같은 루핑 방법론을 이용해 오래 남을 코드를 작성하는 것은 아직 내게는 와닿지 않는다. 내가 떠올리고 싶은 은유는 소프트웨어가 결정론적 기계에서 유기체로 이동하는 것이다.

나는 기계를 이해하도록 장려하는 환경에서 소프트웨어 엔지니어가 되었다. 이해를 더 깊게 하기 위해 벗겨낼 수 있는 레이어가 항상 있었다. 결정론적이고 관찰 가능한 동작을 보이지 않는 기계는 용인될 수는 있어도 일반적으로 그리 최적으로 여겨지지는 않았다. 소프트웨어 아키텍처 측면에서도 결정론을 줄이기보다는 더 밀어붙이는 것이 바람직하다고 봤다. 마찬가지로 코드를 이해할 수 있는 능력은 부인할 수 없는 목표였다. 실제로 항상 가능한 것은 아니었지만, 우리는 영리한 아키텍처를 통해 새로운 엔지니어조차 복잡한 코드베이스를 탐색할 수 있게 코드를 작성한다는 데 자부심을 가졌다. 잘 설계된 시스템에는 불변식이 어디에 있고, 어느 부분이 하중을 지탱하며, 어떤 변경이 안전한지를 아는 엔지니어가 항상 있었다. 이상적으로는 그 모든 것이 잘 문서화되어 있었다. 그런 이해가 부족한 곳은 일반적으로 개선해야 할 대상으로 여겨졌다.

물론 그 이상은 항상 흔들려 왔다. 많은 소프트웨어 시스템, 특히 매우 성공한 시스템들은 팀의 엔지니어들이 코드를 깨끗하게 유지할 수 있었던 시기가 있었다. 대형 소프트웨어 시스템은 한 사람의 머릿속에 들어가지 못할 만큼 크고, 동적이며, 외부 서비스에 의존하는 경우가 적지 않다. LLM이 없어도 우리는 이미 분산 시스템을 다소 의사처럼 진단한다. 증상을 관찰하고, 가설을 세우고, “추가 검사를 주문”하고, 몇 가지 처방을 시도한 뒤 다시 관찰하는 식이다.

그런데 LLM과 함께라면 우리는 그 방향으로 훨씬 더 멀리, 훨씬 더 빠르게 나아가고 있다. 우리는 코드를 작성하는 데도 그들을 쓰고 진단과 처방에도 쓴다. 이미 프로덕션 이슈가 발생하면 첫 단계로 클랭커에게 로그를 읽게 하고, 근본 원인을 제안하게 하며, 선제적으로 패치를 올리게 하는 세상에서 사는 엔지니어들이 많다. 그렇게 나온 패치는 종종 다른 머신이 리뷰하고, 때로는 사람의 감독 없이 main에 머지되기도 한다.

분명 강력하고 매력적으로 들린다는 것을 부정할 수 없다. 하지만 특히 인간의 감독이 점점 줄어드는 상황에서 그 아이디어에 굴복한다는 것은 우리가 더 이상 전체 시스템을 같은 방식으로 이해하지 못할 수도 있다는 것을 받아들이는 것을 의미한다. 우리는 그것을 치료하고, 모니터링하고, 안정화하지만, 반드시 이해하는 것은 아니다.

일부 소프트웨어에서는 그게 괜찮다는 데 의심의 여지가 없다. 모든 코드 한 줄이 인간의 저작을 받을 자격이 있는 것은 아니며, 과거에는 더 나쁜 코드가 작성됐을 수도 있다.

하지만 모든 소프트웨어가 이런 식으로 작성되기를 나는 바라는가?

완전히 발을 뺄 수는 없다

매우 불편한 점은, 완전히 기계가 주도하는 이 미래에서 발을 빼는 것이 선택지가 아닐 수도 있다는 것이다.

보안이 오늘날 가장 명확한 예다. 당신이 소프트웨어를 만드는 데 루프를 쓰지 않더라도 다른 사람들은 당신의 소프트웨어를 상대로 루프를 쓸 것이다. 공격자들은 머신을 끊임없이 돌릴 것이고, 공격자가 아니더라도 보안 연구자들이 그럴 것이며, 그 자동화된 작업 중 일부는 먼지를 일으키겠지만 실제 이슈를 찾아내기도 할 것이다. 그리고 신호와 노이즈 모두 당신이 기계로 맞서지 않으면 대처가 거의 불가능할 정도의 물량으로 당신에게 밀려올 것이다.

curl의 summer of bliss에 대한 Daniel Stenberg의 글은 메인테이너들이 이미 받고 있는 압박의 좋은 예다. 내가 아는 한 AI는 오늘날 curl의 핵심 개발에서 큰 역할을 하지 않는다. 그럼에도 불구하고 메인테이너들은 리포트에 압도당하고 있으며, 그 대부분은 이제 AI가 생성한 것들이다.

공격자와 리포터가 루프를 돌리면 방어자도 따라잡기 위해 결국 루프를 돌려야 한다. 직접 패치를 작성하기 위해서가 아니라도, 트리아지와 재현을 위해서라도. 그리고 압박은 커질 것이다.

경쟁 측면에서도 마찬가지다. 어떤 팀은 순수한 속도로 다른 팀을 압도할 것이다. 어떤 프로젝트는 아주 작은 그룹이 머신을 효과적으로 오케스트레이션하는 방법을 터득하면서 갑자기 더 빠르게 움직일 것이다. 어떤 스타트업은 예전에는 50명이 필요했던 일을 5명으로 해낼 것이다. 어떤 이들은 말 그대로 당신의 제품에 머신을 붙여 루프를 돌리며 “저거처럼 만들어”라고 시킬 수도 있다. 그리고 그들의 사용자가 만족한다면, 그게 정말 중요한가?

모든 소프트웨어가 똑같이 영향을 받지는 않을 것이다. 어떤 도메인은 허술함을 처벌하고 신뢰와 책임을 요구하겠지만, 많은 소프트웨어는 순수한 속도, 빠른 실험, 광범위한 커버리지가 엄청나게 중요한 세상에서 살아간다.

새로운 의존성을 쌓다

내게 가장 무서운 부분은 우리가 새로운 방식으로 이 새로운 기계들에 의존하게 된다는 것이다. 소프트웨어는 항상 도구에 의존해 왔다. 컴파일러를 돈 주고 사야 했던 시절을 기억한다. 이 새로운 도구들은 소프트웨어를 만드는 데 실제 비용이 들던 시절을 떠올리게 한다. 하지만 이제는 일회성 결제가 아니라 지속적인 의존이다. 지갑이 두둑해야 한다는 의존을 넘어 인지적 의존이기도 하다.

코드베이스가 루프에 의해 생성되고, 루프에 의해 리뷰되며, 루프에 의해 패치되고, 루프에 의해 유지된다면, 같은 클래스의 시스템에 더 이상 접근할 수 없게 되면 무슨 일이 일어날까? 어떤 무역 제재가 가장 강력한 모델에 대한 접근을 빼앗아가면 어떻게 될까? 비용 자체가 감당할 수 없을 만큼 커지면 어떻게 될까? 당신과 팀이 기계를 쓰지 않고는 코드를 이해할 수 있는 마지막 남은 능력마저 잃어버리면 어떻게 될까?

우리는 인간이 유지보수하기 어려울 뿐 아니라 유지보수 모델의 일부로 기계의 참여를 전제하는 코드베이스를 만들어낼 수도 있다. 이미 그런 일이 벌어지고 있다! 모든 곳에서 일어나고 있는 것은 아니며, 심지어 문제적이라고 여겨지는 방식으로 일어나고 있는 것도 아닐 수 있지만, 점점 더 많이 목격하고 있다. 사람들은 점점 더 자신이 완전히 설명할 수 없는 코드를 머지한다. 사람들은 클랭커가 제공한 컨텍스트로 메시지를 보강하거나 다시 쓰지 않고서는 이슈 리포트를 작성하거나 채팅에서 논의하는 능력을 잃어간다. 점점 더 많은 사람들이 요약하거나 맥락화하는 데 기계에 의존한다. 점점 더 나는 LLM이라는 간접 경로를 통해 나와 대화하는 사람들을 마주한다.

다시 말하지만, 어쩌면 그게 틀린 것도 아닐지 모른다. 하지만 우리가 해오던 방식에는 엄청난 변화다.

미래의 하네스

나는 일이 이 방향으로 가고 있다는 데 의심이 거의 없지만, 그곳으로 가려면 코딩 에이전트뿐 아니라 모든 곳의 툴링에 대해 무언가를 해야 한다.

단순히 더 많은 루프를 오케스트레이션하는 것만으로는 충분하지 않을 것이다. 변경 사항이나 오케스트레이션, 에이전트에 대한 더 나은 시각화도 우리의 이해를 회복시켜주지 못할 것이다. 인간을 다시 루프 안으로 끌어들이고 루프의 변경 사항을 장기적으로 이해 가능하게 만드는 영리한 방법을 찾거나, 점점 더 복잡해지는 이 시스템들을 구성하는 더 나은 방법을 찾아야 한다.

바로 이 지점에서 Pi의 역할에 대한 내 생각도 바뀌고 있다. Pi는 신중해 왔고, 나는 그 신중함이 좋다고 생각한다. 모든 상호작용이 내가 따라갈 수 없는 통제 불능의 기계 떼가 변경을 만드는 미래를 원치 않는다. 스스로를 작성하는 소프트웨어를 향한 경쟁에서 이기려는 노력 끝에 Pi가 유지보수 불가능한 엉망이 되기를 원치 않으며, Pi가 이런 유형의 엔지니어링을 조장하는 것도 원치 않는다. 동시에 Pi는 하네스이며, 하네스는 사람들이 이런 새로운 유형의 실험을 실행하는 중심에 있다.

코딩 작업을 위한 태스크 큐, 에이전트의 오케스트레이션, 서브에이전트, 지속 가능한 세션은 점점 더 중요해질 것이다. 루프를 맹목적으로 받아들이지 않고 유보적인 입장을 가진 우리조차도 그런 실험을 시작해야 할 것이다. 그 미래를 어떻게 경계 짓고 생존 가능하게 만들지 이해해야 하기 때문에, 우리는 해야만 한다.

루프를 제어하기

이 글에서 읽을 수 있듯, 나는 이 미래에 대해 매우 불안하다. 두려움 때문이 아니라 지금까지 이 기술에 대한 경험을 바탕으로 한 경계심 때문이다.

하네스 루프라는 아이디어를 받아들인다는 것은 작업이 끝났는지를 하네스가 결정한다는 뜻이다. 에이전트 루프에서는 모델이 결국 “다 했다”고 말하고 내가 리뷰한다. 그 전에도 나는 보통 중간에 방향을 잡아준다. 나는 관여하고 있고 그 과정에서 배우는 것을 즐긴다. 하네스가 운영하는 루프에서는 내 역할이 무엇인지조차 잘 모르겠다. “완료” 신호조차 모든 의미를 잃고 그저 또 다른 판단하는 기계에게 전달될 뿐이다. 내 역할은 메신저로 축소된다.

오늘날 나는 그런 식으로 구축된 시스템에서 나오는 코드를 별로 좋아하지 않으며, AI의 도움으로 만들어진 소프트웨어와 많이 상호작용하는 것도 즐기지 않는다. 루핑은 강력하지만 책임을 점점 더 제거하고, 적어도 오늘날에는 우리가 기계에 굴복하도록 매우 부추긴다.

그럼에도 불구하고 현재로서는 그 미래가 못마땅함에도 불구하고 이 루핑 미래가 우리의 미래가 될 것이라는 데 의심이 없다. 나는 이미 놀랍도록 작은 팀들이 불가능한 속도로 만들어내는 것을 보고 있으며, 코드베이스가 오직 더 많은 기계만이 진단할 수 있는 불투명하고 혼란스러운 유기체로 점점 더 변해가는 것을 본다. 그 코드베이스들은 동시에 유용하면서도 엉망이다.

그래서 나는 우리가 루프를 돌릴 것인지가 문제가 아니라는 점을 받아들이려 한다. 분명히 우리는 그럴 것이기 때문이다. 어쩌면 문제는 루프의 미래에서 어떻게 판단을 포기하지 않을지, 좋은 엔지니어링의 원칙을 어떻게 유지할지, 책임 있는 인간이 계속 감독할 수 있도록 어떻게 보장할지, 제정신을 유지하기 위해 코드를 어떻게 아키텍팅할지를 어떻게 다시 생각해야 하는가일지도 모른다.

이 글은 muse-spark-1.2-contributor 모델을 사용해 번역했습니다.

댓글