The Orchestration Tax is You
TMT요즘엔 에이전트를 더 많이 띄우는 건 정말 쉬워졌습니다. 하지만 더 많은 에이전트를 돌린다고 해서, 곧바로 “당신”의 용량이 늘어나는 건 아닙니다. 이들이 쓴 코드를 실제 코드베이스에 녹여 넣고, 제대로 방향을 잡아주기 위한 모든 판단은 여전히 단 하나의 직렬 프로세서, 바로 당신을 통해서만 흘러가야 합니다. 오케스트레이션 세금이란, 이 사실을 잊은 대가라고 볼 수 있습니다. 그리고 이 문제를 진짜로 해결하는 유일한 방법은, 동시성 시스템을 설계하듯이 당신 자신의 주의력(Attention)을 아키텍처 차원에서 설계하기 시작하는 것입니다.
이번 주 Google I/O에서 Richard Seroter, Aja Hammerly, Ciera Jaspan과 함께 지금의 소프트웨어 엔지니어링이 어떤 모습이고 앞으로 어떻게 변해갈지를 주제로 한 패널에 참여했습니다. 대화가 거의 끝날 즈음 Richard가 “개발자들이 이 자리에서 꼭 하나만 가져가서 다르게 했으면 하는 게 있다면 무엇인가요?”라고 물었습니다. 저는 몇 달 동안 계속 맴돌던 생각을 꺼냈습니다. 바쁘게 느끼는 것과 생산적인 것은 전혀 다르다는 점입니다. 에이전트를 20개 돌리면 엄청 바쁘게 느껴질 수 있습니다. 하지만 그게 곧 에이전트 20개 분량의 결과물이 실제로 배송(shipped)됐다는 뜻은 아닙니다.
그 토론 중에 Richard가 이 문제에 이름을 붙여줬습니다. “당신이 오케스트레이션 세금에 대해 얘기했잖아요.”라고 하더군요. “혼자서 에이전트 스무 개를 머릿속으로 관리하는 건 불가능하죠.” 그의 말이 완전히 맞습니다. 이 생각을 제대로 풀어보고 싶습니다. 이건 의지나 규율(discipline)의 문제가 아니라 아키텍처의 문제이기 때문입니다.
제가 패널에서 거의 즉흥적으로 했지만 계속 떠오르는 문장이 하나 있습니다. “여러 에이전트를 돌린다고 해서, 당신이 여러 명이 되는 건 아니다”라는 말입니다.
사람들이 계산에 넣지 않는 비대칭성
에이전트 기반 워크플로에는 잘 드러나지 않는 비대칭성이 하나 있습니다. 에이전트를 시작하는 일은 엄청나게 싸게 먹힙니다. 키보드 한 번 치거나, 문장 하나만 입력하면 됩니다. 하지만 그 에이전트의 결과를 마무리 짓는 일은 결코 싸지 않습니다. 누군가는 결과물이 제대로 됐는지 확인하고, 다른 에이전트가 건드린 것들과 충돌 없이 잘 맞물리는지 정리해 줘야 합니다. 그 “누군가”가 바로 당신입니다. 그리고 당신은 딱 한 사람뿐입니다.
지난달에 썼던 Your parallel Agent limit에서는, 주로 여러 개의 병렬 스레드 중 어느 것이 조용히 실패하고 있는지 모르는 데서 오는 묘한 불안감에 대해 이야기했습니다. 이 글은 그 비용의 구조가 실제로 어떤 모양인지에 대한 이야기입니다. 에이전트 개발을 하나의 동시성 시스템으로 보기 시작하면, 인간 역시 그 안에 포함된 하나의 컴포넌트라는 사실을 깨닫게 됩니다. 그것도 느리고, 직렬적인 컴포넌트 말이죠.
당신은 단일 스레드 리소스다
동시성 코드를 짜본 사람이라면 이미 올바른 직관을 갖고 있습니다. 다만 그 직관을 시스템의 잘못된 부분에 적용해 왔을 뿐입니다.
파이썬에는 GIL(Global Interpreter Lock)이 있습니다. 스레드를 몇 개를 만들든, 실제로 파이썬 바이트코드를 실행하는 건 한 번에 하나의 스레드뿐입니다. 모두가 그 락을 얻어야만 실행할 수 있기 때문이죠. 당신은 당신의 AI 에이전트들을 위한 GIL과 같습니다. 에이전트는 동시에 얼마든지 돌릴 수 있습니다. 하지만 아키텍처에 대한 진짜 이해가 필요한 작업이나, 머지 충돌을 해결하는 일 같은 건 결국 락을 잡아야만 처리됩니다. 락은 하나뿐이고, 그 락을 쥔 사람은 당신입니다.
Amdahl의 법칙은 이 점을 매우 정확하게 수식화합니다. 병렬화해서 얻을 수 있는 속도 향상은, 끝까지 직렬로 남는 작업의 비율에 의해 상한이 정해집니다. 파이프라인에서 상당 부분이 병렬화할 수 없다면, 코어를 아무리 많이 던져 넣어도 일정 한계치 이상으로는 빨라지지 않습니다. 에이전트 개발에서 이 직렬 구간은 “판단(judgement)”입니다. 에이전트를 8개 띄운다고 해서 당신의 판단 시간이 빨라지는 건 아닙니다. 그냥 당신에게로 들어오는 작업 큐만 훨씬 깊어질 뿐입니다.
성능 엔지니어링에서 오래전부터 알려진 사실이 하나 있습니다. 병목이 아닌 부분을 최적화해 봐야 전체 처리량은 늘지 않습니다. 오히려 병목 앞에 쌓여 있는 미완료 작업의 더미만 키워 놓을 뿐입니다. 에이전트를 늘리는 건, 애초에 시스템의 제약이 아니었던 부분을 최적화하는 셈입니다. 진짜 제약은 리뷰 단계이고, 시스템의 처리량은 이 단계의 처리량과 정확히 같습니다. 오케스트레이션 세금은, 에이전트가 만들어내는 생산량과 당신이 실제로 머지할 수 있는 양 사이에 구조적으로 생기는 간극입니다. 직렬 리소스를 하나 두고, 그 리소스가 동시에 돌아가는 시스템 전체를 책임지도록 설계했을 때 생기는 현상입니다.
갈아 넣는다고 구조적 한계가 사라지진 않는다
패널에서 저는, 지금 쓰는 도구들 덕분에 제 자신이 한 번도 이렇게까지 생산적이라고 느껴본 적은 없지만, 동시에 이렇게까지 피곤했던 적도 없다고 말했습니다. 두 가지 모두 사실이고, 둘의 원인은 같습니다.
이 피로감에는 아주 뚜렷한 이유가 있습니다. 여유 없이 100%로 돌아가는 직렬 프로세서가 느끼는 감각이 정확히 이런 느낌입니다. 한동안 보지 않았던 에이전트를 다시 확인할 때마다 우리는 컨텍스트 스위칭 비용을 지불합니다. 머릿속을 비우고, 완전히 다른 문맥을 처음부터 다시 로딩하는 것이죠. CPU는 이걸 마이크로초 단위로 해치우고, 아키텍트들은 그 비용을 줄이려고 여전히 애를 씁니다. 당신은 이걸 몇 분에 걸쳐 하고, 게다가 매번 문맥을 완벽하게 다시 불러오지도 못합니다. 에이전트 다섯 개를 돌린다는 건, 단순히 일을 다섯 번 한 게 아니라, “완전한 콜드 리로드를 다섯 번” 한 데 더해서, 백그라운드에서는 지금 어떤 에이전트를 먼저 봐야 할지 계속 신경 쓰고 있는 상태를 의미합니다.
구조적인 한계를 해결하는 방법은 “더 열심히 하는 것”이 아닙니다. 세금은 어떻게든 지불되기 마련입니다. 억지로 갈아 넣어서 버티려고 하면, 그 한계는 결국 얕은 코드 리뷰나, 인지적 항복(cognitive surrender) 같은 형태로 드러납니다. 에이전트가 쓴 코드를 그대로 받아들이고, 내 의견을 제대로 만들기 위해 드는 주의력을 이미 감당할 수 없어진 상태가 되는 것입니다. 이 세금은 의식적으로, 주도적으로 치를 것인지, 아니면 당신이 자기 시스템을 이해하는 능력을 조용히 갉아먹도록 내버려 둘 것인지 둘 중 하나입니다.
당신의 주의력을 설계하라
그러니 자신의 주의력을, 그 자체로 희소한 직렬 리소스로 취급해야 합니다. 분산 시스템을 설계할 때 병목에 대해 깊이 고민하지 않은 채로 설계하는 일은 없을 겁니다. 당신의 두뇌에도 똑같은 존중을 부여해야 합니다.
제가 실제로 해 보았고 꽤 잘 버텨 준 것들은 다음과 같습니다.
리뷰 속도에 맞춰 에이전트 수를 조절하라, UI에 맞추지 말고. 좋은 동시성 시스템은 큐가 무한정 길어지지 않도록 백프레셔(backpressure)를 겁니다. 생산자(producer)는 소비자(consumer)의 속도에 맞춰 속도를 늦춥니다. 당신이 띄우는 에이전트 수는 생산자고, 당신의 리뷰 속도는 소비자입니다. 적절한 병렬 에이전트 수는, 당신이 실제로 제대로 코드 리뷰를 끝마칠 수 있는 정도입니다. 대부분의 사람에게 이 수치는 낮은 한 자릿수일 겁니다. AI 도구는 UI 차원에서 얼마든지 20개를 띄우게 해 주겠지만, 그건 그냥 UI 기능일 뿐입니다.
작업을 분류하라. Richard가 “이 상황을 어떻게 헤쳐 나가느냐”고 물었을 때 제가 말한 방식입니다. 저는 작업을 두 묶음으로 나눕니다. 하나는 “클라우드에서 돌아가는 백그라운드 에이전트에게 맡겨도 되는, 독립적인 작업”입니다. 이쪽은 비동기로 돌려도 되고, 대부분 마지막 게이트에서 한 번만 봐 주면 됩니다. 다른 하나는 “판단 자체가 곧 작업의 본질인, 복잡한 일들”입니다. 예를 들면 이상한 버그나 아키텍처 설계 같은 것들입니다. 가장 큰 실수는 이 두 번째 묶음을 병렬화하려 드는 것입니다. 복잡한 일을 여러 개 동시에 붙든다고 해서 산출물이 늘어나지 않습니다. 그냥 락을 계속 두들기면서 전체 퀄리티만 떨어뜨릴 뿐입니다.
리뷰를 배치로 처리하라. 컨텍스트 스위칭은 할 때마다 큰 비용을 청구해 갑니다. 에이전트 네 개를 한 번에 모아서 한 세션 안에서 리뷰하는 편이, 하나 보고 다른 일을 했다가 또 다시 돌아와 콜드 스타트하는 것보다 훨씬 쌉니다. 에이전트에게는 느슨한 끈을 줘야 합니다. 일이 어느 정도 쌓이게 두고, 그걸 한꺼번에 처리하는 편이 낫습니다.
락은 오직 “판단”에만 써라. 기계가 스스로 검증할 수 있는 일에 뇌를 낭비하지 마세요. 에이전트에게 통과하는 테스트를 직접 작성하게 하거나, 스크린샷을 생성하게 만드세요. 지루한 80%는 에이전트가 스스로 증명하게 하고, 정말로 사람의 판단이 필요한 나머지 20%에만 당신의 희소한 주의력을 쓰는 식입니다.
당신의 직렬 시간을 보호하라. 병목 구간에는 당신의 가장 좋은 시간대가 필요하지, 에이전트를 확인하러 왔다 갔다 하는 자투리 시간이 필요한 게 아닙니다. 때로는 가장 레버리지가 높은 선택지가, 오케스트레이션을 아예 멈춰 버리고, 에이전트가 잔뜩 떠 있는 노트북을 덮어 두고, 하나의 문제에만 온전히 락을 건 채로 깊이 생각하는 것일 때도 있습니다. 오케스트레이션은 진짜 일이 아닙니다. 그건 진짜 일 주위를 둘러싼 오버헤드일 뿐입니다.
Aja는 지금 가장 긴급한 스킬은 “아키텍처”라고 지적했습니다. 어떤 것은 하나의 에이전트 안에 담아야 하고, 어떤 것은 그 범위를 넘어서는지 구분할 줄 아는 능력이 중요해졌다는 것이죠. 여기에 저는 한 가지를 더 보태고 싶습니다. 당신 자신도 그 시스템 안의 하나의 컴포넌트라는 점입니다. 당신의 주의력에는 잘 알려진, 낮은 직렬 처리량이 있습니다. 시스템이 이 숫자를 존중할 수도 있고, 반대로 이 숫자를 무시하고 돌아가기 위해, 몰래 당신의 기준을 낮추는 방식으로 우회할 수도 있습니다.
바쁨과 생산성의 차이
이게 중요한 이유는, 이 방식의 실패가 당신에게는 잘 보이지 않기 때문입니다. 에이전트 스무 개가 돌아가고 있으면 엄청난 생산성이 나오는 것처럼 느껴집니다. 대시보드는 꽉 차 있고, 여기저기서 뭔가 계속 움직입니다. 하지만 그런 느낌과 실제로 main에 좋은 코드를 머지하는 일은 완전히 분리되어 있습니다. 당신은 최대치로 바쁠 수 있지만, 실제로는 거의 아무것도 만들어내지 못할 수 있습니다. 내부에서 느끼기에는 두 상태가 거의 똑같이 느껴집니다.
Ciera는 Margaret-Anne Storey의 부채(debt)에 관한 연구를 언급했습니다. 우리는 기술 부채(technical debt)와 인지 부채(cognitive debt)에 대해 이야기했습니다. 오케스트레이션 세금을 제때 치르지 않으면, 이 둘을 동시에 쌓아 올리는 결과를 낳습니다. 제대로 읽지도 않은 코드를 머지하게 되고, 코드베이스에 대한 당신의 멘탈 모델은 완전히 낡아 버립니다. 이런 것들은 오늘의 대시보드에는 전혀 드러나지 않습니다. 프로덕션이 깨지고, 시스템을 들여다봤을 때 ‘이게 어떻게 돌아가는지 전혀 모르겠다’는 깨달음으로 돌아올 뿐입니다.
그래서 진짜 핵심은 이것입니다. 에이전트를 띄우는 것 자체는 스킬이 아닙니다. 누구나 20개쯤 돌릴 수 있습니다. 진짜 실력은, 복제나 병렬화가 도저히 불가능한 단 하나의 직렬 리소스 주위를 어떻게 설계하느냐에 있습니다. 그 리소스가 바로 당신의 주의력입니다. 프로덕션에서 의존하는 다른 것들을 아키텍처링하듯, 이 리소스 역시 그렇게 아키텍처 차원에서 설계해야 합니다.