Multi-Agent 10
Supervisor 패턴으로 순서, 조건, 반복을 중앙 통제하기
Router처럼 한 번 분기하면 복잡한 작업도 처리된다고 생각하기 쉽다.
근거 · 교안 p27-p29
1장: Supervisor, 그 단순한 Router가 아닌
솔라는 막 필기를 마친 노트를 덮으며 중얼거렸다. 노트 맨 위에는 ‘Supervisor 패턴’이라고 적혀 있었다. 그 아래로는 깔끔하게 정리된 정의가 이어졌다.
Supervisor: 누가 다음 일을 할지 판단하는 Agent현재 상태를 확인하고다음에 호출할 Agent를 결정
“결국 교통정리네.”
솔라는 확신에 찬 목소리로 말했다.
“사용자가 처음 딱 들어왔을 때, 이쪽으로 가세요, 저쪽으로 가세요, 하고 한 번만 방향을 알려주는 역할. 복잡해 보여도 결국엔 Router랑 비슷한 거 아니야?”
옆에서 조용히 책을 읽던 루나가 고개를 들었다. 솔라의 노트를 물끄러미 보던 루나는 아무 말 없이 주방으로 가더니, 냉장고에 붙어 있던 작은 화이트보드와 마커를 들고 왔다.
“피자 주문하는 챗봇을 만든다고 생각해 보자.”
루나는 화이트보드 중앙에 ‘Supervisor’라고 적고 동그라미를 쳤다.
“솔라 네 말대로라면, 손님이 ‘피자 주문할게요’라고 말하는 순간, Supervisor가 ‘아, 주문이군. 주문 담당 Worker에게!’ 하고 일을 넘기면 끝인 거지?”
“응. 그게 제일 깔끔하잖아. Supervisor는 첫 관문에서 길을 안내하고, 나머지는 전문가인 Worker들이 알아서 처리하는 거지. 주문 담당, 결제 담당, 배송 담당… 이렇게.”
솔라는 자신 있게 대답했다. 마치 잘 짜인 분업 시스템을 설명하는 것 같았다.
루나는 고개를 끄덕이며 화이트보드에 ‘주문 Worker’라고 적고 Supervisor로부터 화살표를 그었다.
“좋아. ‘주문 Worker’가 메뉴를 보여주고, 손님이 페퍼로니 피자를 골랐어. 그다음은?”
“음… 주소를 물어봐야지. 배달해야 하니까.”
“그건 누가 물어보지? ‘주문 Worker’가?”
“아니, 그건 ‘주소 확인 Worker’가 하는 게 맞을 것 같아. 역할은 명확하게 나눠야 하니까.”
“그럼 ‘주문 Worker’가 일을 끝낸 뒤에, 누가 ‘주소 확인 Worker’를 호출해?”
루나의 질문에 솔라는 잠시 말문이 막혔다. 당연히 다음 단계로 넘어가야 하는데, 그 ‘다음 단계로 넘기는’ 주체가 불분명했다.
“어… 그건… ‘주문 Worker’가 직접 호출하면 되나?”
“그럴 수도 있지. 그럼 ‘주소 확인 Worker’가 주소를 입력받고 나면? 그다음엔 ‘결제 Worker’를 호출해야 할 텐데. 그럼 또 ‘주소 확인 Worker’가 ‘결제 Worker’를 직접 호출하고…?”
루나는 화살표를 이어 그렸다. ‘주문 Worker → 주소 확인 Worker → 결제 Worker → …’ 점점 길어지는 화살표의 사슬을 본 솔라의 표정이 미묘하게 굳어졌다.
“잠깐만. 뭔가 이상해. 만약 중간에 손님이 토핑을 추가하고 싶다고 하면 어떡해? 그럼 다시 ‘주문 Worker’로 돌아가야 하잖아. 근데 ‘주소 확인 Worker’가 ‘주문 Worker’의 존재를 어떻게 알고 돌아가지?”
“바로 그 점이야.”
루나는 ‘주문 Worker’와 ‘주소 확인 Worker’를 잇던 화살표를 지웠다. 대신, ‘주문 Worker’가 일을 마친 후 다시 ‘Supervisor’로 돌아가는 화살표를 그렸다.
“Worker들은 자기 일만 해. 페퍼로니 피자를 골랐다는 ‘결과’를 Supervisor에게 보고하고 끝나는 거야. 그럼 Supervisor는 그 보고를 보고 생각하겠지.”
루나는 Supervisor 동그라미를 톡톡 두드렸다.
“‘아, 손님이 피자를 골랐구나. 그럼 현재 상태는 피자 선택 완료 상태군. 이제 뭘 해야 할까? 아, 주소를 물어봐야지. 그럼 다음엔 주소 확인 Worker를 부르자.’”
솔라의 눈이 동그래졌다. Router처럼 한 번 길을 안내하고 사라지는 존재가 아니었다. 모든 일의 갈피마다 나타나서 상황을 파악하고 다음 할 일을 결정하는 지휘자였다.
“아…! Supervisor는 한 번 분기하고 끝나는 게 아니었어. Worker가 일을 끝낼 때마다 다시 개입해서, 지금 상황이 어떤지 보고 다음 Worker를 정해주는 거구나.”
솔라는 화이트보드를 뚫어져라 쳐다보며 자신의 처음 생각을 수정했다. 머릿속에서 얽혀 있던 개념이 제자리를 찾는 느낌이었다. 그녀는 루나가 그렸던 그림 위에 자신의 생각을 덧붙여 중얼거렸다.
“그러니까 Supervisor는 현재 상태를 확인하고, 다음에 호출할 Worker를 결정해. 그리고 호출된 Worker는 자기 일을 끝내고, 그 실행 결과를 다시 상태(State)에 기록해서 Supervisor에게 알려주는 거고. 계속 이렇게 상호작용하는 거구나.”
루나는 조용히 고개를 끄덕였다. Supervisor는 단순히 입구에 서 있는 안내원이 아니었다. 전체 작업 흐름의 중심에 서서, 매 순간의 변화를 감지하고 전체 오케스트라를 지휘하는 역할이었다.
“알겠어. 한 번에 끝나는 게 아니라, 계속 상태를 보고 판단하는 거구나.”
솔라는 이제 Supervisor가 단순한 Router가 아니라는 사실을 명확히 깨달았다. 하지만 새로운 궁금증이 고개를 들었다.
“그런데 언니, 그 ‘상태’라는 게 정확히 뭐야? ‘피자 선택 완료’ 같은 텍스트 꼬리표 같은 건가? Supervisor는 그걸 어떻게 확인하고, 또 ‘다음’은 뭘 기준으로 결정하는 거지?”
2장: 상태, 순서, 조건, 반복을 엮는 실
솔라의 이전 질문의 여운이 가시기도 전에, 루나는 조용히 화이트보드를 들고 왔다. 이전 장에서 피자 주문의 흐름을 그려 보였던 바로 그 보드였다. 루나는 피자, 주소, 결제 Worker를 그린 복잡한 그림을 말없이 지웠다. 텅 빈 보드 위로 마커가 움직이며 새로운 흐름을 그려나갔다.
[초안 작성] → [내부 검토] → [최종 발행]
단순하고 명료한 세 개의 상자가 화살표로 이어져 있었다. 이전보다 훨씬 간결해 보이는 그림이었다.
솔라는 고개를 갸웃했다. “이번엔 문서 생성 파이프라인 같은 거네. Supervisor가 ‘초안 작성 Worker’를 부르고, 일이 끝나면 ‘내부 검토 Worker’를, 또 끝나면 ‘최종 발행 Worker’를 순서대로 부르는 거겠지? 그럼 ‘상태’는 ‘초안 작성 완료’, ‘내부 검토 완료’ 같은 꼬리표 역할을 하는 거고.”
솔라는 자신이 완전히 이해했다는 듯 자신 있게 말했다. Supervisor가 각 단계의 완료 신호를 받아 다음 Worker를 호출하는, 잘 닦인 일방통행 도로를 떠올렸다. 상태는 그저 다음 이정표를 가리키는 표지판일 뿐이라고 생각했다.
루나는 솔라의 말을 부정하지 않았다. 대신, 아무 말 없이 [내부 검토] 상자를 손가락으로 톡톡 두드렸다.
“만약 검토 결과, 초안을 도저히 발행할 수 없는 수준이라 반려(reject)해야 한다면?”
“반려?”
순간 솔라의 머릿속이 멈칫했다. 일방통행 도로에 갑자기 나타난 차단기 같았다.
“음… ‘내부 검토 Worker’가 반려 결과를… Supervisor에게 보고하겠지. 지난번에 그랬으니까. Worker끼리는 서로 대화하지 않으니까.”
솔라는 지난 장에서 배운 원칙을 떠올리며 더듬더듬 말했다.
“그래, 맞아. ‘내부 검토 Worker’는 ‘검토 결과: 반려’라는 사실을 상태에 기록하고 자신의 임무를 마쳐. 그럼 그 보고를 받은 Supervisor는 이제 어떻게 해야 할까?”
루나의 질문은 단순했지만, 솔라가 세운 가설의 허점을 정확히 찔렀다. 상태가 그저 다음 단계로 넘어가는 표지판이라면, ‘반려’라는 상태 다음엔 갈 곳이 없었다. ‘최종 발행’으로 갈 수는 없지 않은가.
“어… Supervisor는… 다음 Worker인 ‘최종 발행 Worker’를 호출하면 안 돼. 그건 확실해. 그럼… 워크플로우가 그냥 거기서 끝나나?”
“그럴 수도 있지. 하지만 보통은 초안을 수정해서 다시 검토받아야 하지 않을까?”
“아! 수정!”
솔라의 눈이 번쩍 뜨였다. 그녀는 화이트보드 앞으로 다가가 루나의 손에 들린 마커를 집어 들었다.
“Supervisor가 ‘상태: 반려됨’을 확인하고… ‘최종 발행 Worker’가 아니라, 다시 ‘초안 작성 Worker’를 호출하는 거야! 처음으로 돌아가는 거지!”
솔라는 [내부 검토] 상자에서 Supervisor로 향하는 점선을 그린 뒤, 다시 Supervisor에게서 [초안 작성] 상자로 향하는 굵은 화살표를 그렸다. 순서대로만 흐를 것 같던 워크플로우에 되돌아가는 길이 생긴 순간이었다.
“바로 그거야.”
루나가 처음으로 작게 미소를 보였다.
솔라는 자신이 그린 되돌아가는 화살표를 보며 흥분된 목소리로 말을 이었다. “그러니까 Supervisor는 상태를 보고 단순히 앞으로만 가는 게 아니었어! ‘검토 통과’라는 상태를 보면 다음 단계인 ‘발행’으로 가고, ‘반려’라는 상태를 보면 이전 단계인 ‘초안 작성’으로 돌아갈 수도 있는 거구나. 이게 바로… 조건에 따른 분기(conditional branch)네!”
그녀는 기존의 [내부 검토]에서 [최종 발행]으로 가는 길 위에 IF 상태 == '승인' 이라고 적었다. 그리고 방금 새로 그린, 되돌아가는 화살표 위에는 IF 상태 == '반려' 라고 적었다.
“그리고 이건 반복이기도 해! 반려되고, 수정하고, 다시 검토받는 과정이 계속될 수 있잖아. Supervisor가 ‘승인’ 상태를 받을 때까지 이 순환(loop)은 계속되는 거야. 와….”
솔라는 감탄하며 한 발짝 물러서서 자신이 완성한 다이어그램을 바라봤다. 이제 Supervisor의 역할이 완전히 다르게 보였다. Supervisor는 단순히 작업 순서를 넘기는 존재가 아니었다. 워크플로우의 중심에서 ‘상태’라는 실을 붙들고, 순서와 조건과 반복을 자솔라재로 엮어내는 지휘자였다.
솔라는 고개를 끄덕이며 중얼거렸다. “Supervisor는 현재 상태를 확인하고 다음 Worker를 결정해. 그리고 Worker는 실행 결과를 State에 기록하지. 이제 이 문장이 진짜 다르게 들려. Worker가 기록하는 그 ‘결과’가 전체 흐름을 뒤바꿀 수 있는 강력한 의사결정의 근거가 되는 거였어.”
상태 기반 의사결정이 어떻게 복잡한 제어를 가능하게 하는지, 그 원리를 깨달은 순간이었다. 하지만 이 강력한 통제 방식은 어쩐지 무겁게 느껴지기도 했다.
“언니, 그런데 말이야. 이 방식, 확실히 강력하긴 한데… 좀 복잡한 것 같기도 해. 모든 걸 중앙에서 통제해야 하니까. 그냥 코드로 if문이랑 for문을 쓰는 거랑 뭐가 다른 거지? 언제 이렇게까지 ‘중앙 통제’가 필요한 걸까?”
3장: 중앙 통제, 언제 그 힘이 필요한가
솔라는 자신의 노트를 내려다보며 미간을 찌푸렸다. 이전 장에서 루나와 함께 그렸던 ‘문서 생성 파이프라인’ 다이어그램 옆, 빈 페이지에 자신만의 생각을 휘갈겨 놓은 흔적이 가득했다. 복잡한 화살표와 조건문들이 얼기설기 얽혀 있었다. 그녀는 혼잣말로 중얼거렸다.
“강력한 건 알겠어. 그런데 이렇게 모든 걸 Supervisor에게 보고하고 지시받는 방식은 너무 번거로운 거 아니야? 작업 순서가 정해져 있고, 중간에 분기가 필요하면 그냥 코드로 if-else 문을 쓰고, 반복이 필요하면 for나 while 루프를 쓰면 되잖아. 그게 훨씬 빠르고 직관적이지 않나?”
솔라의 생각은 명확했다. Supervisor 패턴은 굳이 필요 없는 관리 계층을 하나 더 두는, 과한 설계처럼 느껴졌다. 그녀가 그린 복잡한 화살표들은 Worker들이 서로를 직접 호출하며 if 조건에 따라 다른 Worker를 부르는 구조였다. 보기에는 지저분했지만, 그녀는 이게 더 현실적이라고 생각했다.
그때, 루나가 솔라의 노트에 시선을 주었다. 솔라의 고민을 읽기라도 한 듯, 루나는 화이트보드 대신 새로운 비유를 꺼냈다.
“솔라, 우리 둘이서 똑똑한 ‘여행 가이드 Agent’를 만든다고 상상해볼까?”
“여행 가이드 Agent?”
“응. 사용자가 ‘파리 4박 5일 여행’이라고 말하면, 알아서 항공권, 숙소, 그리고 주요 관광지 입장권까지 예약해주는 Agent야. 이 Agent는 세 명의 전문가 Worker로 이루어져 있어. ‘항공권 예약 Worker’, ‘숙소 예약 Worker’, ‘액티비티 예약 Worker’.”
루나의 말에 솔라는 곧바로 머릿속으로 워크플로우를 그렸다. 이건 문서 파이프라인보다 훨씬 명쾌했다.
“간단하네. 일단 ‘항공권 예약 Worker’가 파리행 비행기 표를 예매하고, 성공하면 그 다음엔 ‘숙소 예약 Worker’를 호출해서 비행기 날짜에 맞춰 숙소를 예약해. 숙소까지 잡으면 마지막으로 ‘액티비티 예약 Worker’가 루브르 박물관 입장권 같은 걸 예매하고 끝! 순서가 명확하잖아.”
솔라는 자신의 ‘직접 구현’ 방식에 딱 맞는 예시라고 생각하며 자신 있게 말했다. 각 Worker가 자기 일을 끝내고 다음 Worker를 호출하면 그만이었다. Supervisor는 필요 없어 보였다.
루나는 조용히 고개를 끄덕이며, 솔라의 생각에 결정적인 질문 하나를 던졌다.
“만약 ‘숙소 예약 Worker’가 일을 시작했는데, 항공권 날짜에 파리의 모든 호텔이 만실이라면?”
“음… 예약에 실패했으니까….”
솔라의 말끝이 흐려졌다. 그녀의 머릿속 시나리오가 삐걱거리기 시작했다. 그녀가 설계한 대로라면, ‘숙소 예약 Worker’는 그저 실패하고 멈출 뿐이다. 혹은 기껏해야 다음 차례인 ‘액티비티 예약 Worker’에게 ‘나 실패했어’라고 알릴 텐데, 그건 아무 의미가 없었다.
“그럼… ‘숙소 예약 Worker’가 ‘항공권 예약 Worker’에게 다시 연락해서, ‘이 날짜는 안 되니 다른 날짜로 항공권을 다시 찾아봐’라고 요청해야겠지?”
“과연 그게 좋은 방법일까?”
루나가 되물었다.
“그렇게 되면 ‘숙소 예약 Worker’는 ‘항공권 예약 Worker’의 존재와 그 기능(‘다른 날짜로 다시 찾아봐’)까지 알아야 해. 만약 항공권 예약 정책이 바뀌어서 날짜 변경이 아니라 아예 다른 항공사를 찾아야 한다면? ‘숙소 예약 Worker’의 코드까지 수정해야 하지. 두 Worker가 너무 끈끈하게 엮여버리는 거야.”
솔라는 그제야 자신의 설계가 가진 함정을 깨달았다. 한 Worker가 다른 Worker의 내부 사정까지 알아야 한다면, 그건 더 이상 깔끔한 분업이 아니었다. 하나의 Worker를 수정하면 다른 Worker까지 연쇄적으로 고쳐야 하는 악몽 같은 상황이 펼쳐질 수 있었다.
“아….”
탄식이 터져 나왔다. 문제의 핵심은 복잡한 if문이나 for문 자체에 있는 게 아니었다. 독립적으로 존재해야 할 Worker들이 서로의 발목을 잡게 되는 ‘의존성’ 문제였다.
“하지만 Supervisor가 있다면 이야기가 달라져.”
루나는 솔라의 노트 한편에 간결한 그림을 그리기 시작했다. 중앙에 ‘여행 총괄 Supervisor’를 그리고, 그 주위로 세 명의 Worker를 배치했다. 모든 화살표는 Supervisor를 향하거나 Supervisor에게서 뻗어 나왔다.
“‘숙소 예약 Worker’는 그냥 ‘상태: 숙소 예약 실패(만실)’라고 Supervisor에게 보고하고 자기 임무를 끝내. 그럼 이 보고를 받은 Supervisor가 전체 상황을 보고 판단하는 거야. ‘아, 숙소가 없구나. 그럼 항공권을 취소하고 사용자에게 날짜 변경을 제안해야겠다’라거나, ‘아니면, 항공권은 그대로 두고 더 비싼 호텔을 찾아보라고 숙소 예약 Worker를 다시 호출해볼까?’ 하고 말이야.”
Worker들은 오직 Supervisor와만 대화했다. 서로의 존재조차 알 필요가 없었다. 모든 의사결정과 흐름 제어, 예외 처리는 오직 ‘여행 총괄 Supervisor’의 책임이었다.
솔라는 자신이 그렸던 복잡한 화살표들과 루나의 간결한 중앙 집중형 그림을 번갈아 보았다. 이제야 모든 것이 명확해졌다.
“알겠다! 언제 중앙 통제가 필요한지! 작업 순서가 명확히 정해져 있지 않거나, 지금처럼 예상치 못한 문제로 순서가 뒤바뀔 수 있을 때. 그리고 중간 결과를 검증하거나 승인해야 할 때, 그냥 다음 단계로 넘어가는 게 아니라 ‘숙소 예약 성공’이라는 상태를 확인해야 할 때. 마지막으로, 이 모든 하나의 흐름을 중앙에서 관리해야 할 때! Worker들이 제멋대로 얽히게 두는 게 아니라!”
솔라는 코드로 직접 구현하는 것이 ‘직관적’이라고 생각했던 자신의 생각이 얼마나 단편적이었는지 깨달았다. 그 방식은 모든 것이 순조롭게 진행될 때만 유효했다. 변화와 예외가 발생하자, 그녀의 설계는 속수무책으로 엉망이 되었다.
그녀는 망설임 없이 아까 그렸던 복잡한 화살표들을 지워버렸다. 그리고 루나의 그림을 본떠, 자신의 ‘여행 가이드 Agent’ 워크플로우 설계 초안을 새로 작성하기 시작했다.
- Supervisor: 여행 총괄 Agent
- Workers: 항공권 탐색, 숙소 예약, 액티비티 추천
- Workflow Logic (in Supervisor):
항공권 탐색 Worker호출.State: 항공권_예약_완료→숙소 예약 Worker호출.State: 숙소_예약_실패→항공권 탐색 Worker다시 호출 (날짜 변경 옵션 전달).State: 숙소_예약_완료→액티비티 예약 Worker호출.- …
노트에 새로운 설계도를 그리며 솔라는 다시 한번 그 문장을 떠올렸다.
“Supervisor는 현재 상태를 확인하고 다음 Worker를 결정하며, Worker는 실행 결과를 State에 기록한다. 이제야 이 문장이 그냥 설명이 아니라, 복잡한 문제 속에서 길을 잃지 않게 해주는 설계도처럼 보여.”
더 이상 Supervisor 패턴은 번거로운 추가 계층이 아니었다. 복잡하고 예측 불가능한 워크플로우의 혼돈을 질서로 바꾸는 강력한 중앙 통제 센터, 그 자체였다.