LLMOps 15

Supervisor 패턴: 평가와 제어의 분리

Critic이 평가도 하고 분기도 하면 더 간단해 보인다.

근거 · 교안 p41-p43

Supervisor 패턴: 평가와 제어의 분리 대표 이미지

1장: Critic은 ‘평가만’ 한다?

솔라는 모니터에 떠 있는 아키텍처 다이어그램을 보며 미간을 찌푸렸다. 네모난 상자들이 화살표로 연결된, 익숙한 Supervisor 패턴의 설계도였다. Planner가 계획을 세우고, Executor가 실행하고, 그 결과를 Critic이 평가한다. 그리고 이 모든 것을 Supervisor가 지휘한다. 머리로는 이해가 됐다. 하지만 화살표 하나가 계속 눈에 거슬렸다.

Critic이 내놓은 평가 결과가 곧장 다음 단계로 이어지지 않고, 다시 Supervisor에게 돌아가는 흐름이었다.

“언니, 이거 좀 이상하지 않아?”

거실에서 책을 읽던 루나가 고개를 들었다. 솔라는 손가락으로 모니터의 한 부분을 가리켰다.

“여기, 크리틱(Critic)이 결과를 평가했으면 바로 다음에 뭘 할지 결정하면 되잖아. ‘이건 결과가 안 좋으니 다시 계획을 세워’라든지. 왜 굳이 그 평가 결과를 슈퍼바이저(Supervisor)한테 다시 보고하고, 슈퍼바이저가 결정을 내리는 거지? 한 단계를 더 거치는 거니까 오히려 비효율적인 것 같은데.”

솔라의 말에는 확신이 섞여 있었다. 결과를 가장 잘 아는 건 방금 평가를 마친 당사자다. 그가 바로 다음 행동을 지시하는 것이 가장 빠르고 직관적인 방식처럼 보였다.

루나는 잠시 솔라가 가리키는 다이어그램을 들여다보았다. 그리고는 말없이 일어나 부엌으로 향하더니, 찬장에서 예쁜 그릇 두 개를 꺼내 식탁 위에 놓았다. 하나는 솔라 앞에, 다른 하나는 자신의 앞에.

“솔라, 네가 오늘 저녁으로 아주 특별한 파스타를 만든다고 상상해 보자.”

“파스타?”

갑작스러운 제안에 솔라가 되물었다.

“응. 아주 복잡하고 섬세한 레시피야. 너는 최고의 요리사이자, 동시에 미각이 아주 예민한 시식가라고 해보자. 지금 막 소스를 만들어서 맛을 봤어. 그런데, 너무 짰어.”

루나는 말을 이었다. “그럼 어떻게 할 거야?”

“너무 짜면? 음… 설탕을 조금 넣거나, 아니면 크림을 더 부어서 희석하겠지.”

솔라는 간단하게 답했다. 당연한 절차였다.

“그렇지. 너는 ‘짜다’라는 평가를 내리자마자 ‘설탕을 넣는다’는 다음 행동을 직접 결정했어. 평가와 제어가 한 사람에게서 바로 이어진 거야.”

루나는 솔라 앞의 그릇을 가리켰다. “이게 네가 생각하는 효율적인 방식이야. Critic이 평가하고, 바로 다음 행동을 결정하는 것.”

“맞아. 간단하잖아.”

“좋아. 그럼 이번엔 역할을 나눠보자.” 루나가 자기 앞의 그릇을 가리켰다. “나는 전체 요리 과정을 총괄하는 헤드 셰프(Supervisor)야. 너는 오직 맛이 어떤지만 정확하게 평가해서 보고하는 전문 시식가(Critic)고. 다시 소스를 맛봤는데 짜. 이번엔 나한테 뭐라고 말할 거야?”

솔라는 잠시 생각에 잠겼다. 역할이 바뀌었다. 나는 ‘시식가’일 뿐이다. “음… ‘셰프님, 지금 소스가 기준보다 조금 짭니다.’ 라고 보고하겠지?”

“그리고?”

“그리고…?” 솔라의 말끝이 흐려졌다. “그 다음은 내가 정하는 게 아니잖아. 셰프인 언니가 정하는 거지.”

“바로 그거야.” 루나가 고개를 끄덕였다. “나는 시식가인 네게 ‘짜다’는 보고를 받았어. 하지만 헤드 셰프인 나는 네가 모르는 다른 정보들을 가지고 있어.”

“다른 정보?”

“예를 들어, ‘이 레시피는 원래 짠맛이 특징이고, 나중에 곁들일 담백한 빵이 그 맛을 완벽하게 중화시켜준다’는 사실을 나는 알고 있을 수 있어. 또는 ‘이미 조리 시간이 5분밖에 남지 않아서 소스를 고칠 시간이 없다’는 제약 조건을 알고 있을 수도 있지. 혹은 ‘이 소스는 세 번이나 다시 만든 것이니, 더 이상은 안 된다’는 규칙을 가지고 있을 수도 있고.”

루나의 설명을 듣는 동안 솔라의 눈이 조금씩 커졌다.

“만약 시식가인 네가 ‘짜다’는 이유만으로 멋대로 설탕을 넣어버렸다면 어떻게 됐을까? 레시피의 큰 그림이 망가지거나, 시간 안에 요리를 끝내지 못했을 수도 있어. 시식가의 임무는 ‘짜다’, ‘싱겁다’, ‘달다’ 같은 상태를 왜곡 없이, 정확하게 평가해서 보고하는 것까지야. 그 평가를 바탕으로 전체 상황과 규칙에 맞춰 다음 행동을 결정하는 건 헤드 셰프의 몫이지.”

순간, 모니터 속의 다이어그램이 다르게 보이기 시작했다. 화살표의 흐름이 마치 부엌의 풍경과 겹쳐졌다.

“아…”

솔라가 나직이 탄성을 내뱉었다.

“그럼 크리틱은 ‘결과가 좋다/나쁘다’라는 순수한 평가만 하고, 슈퍼바이저는 그 평가뿐만 아니라 ‘최대 반복 횟수’나 ‘전체 작업 시간’ 같은 다른 규칙들을 함께 보고 다음 단계를 결정하는 거구나. 크리틱이 자기 판단만으로 ‘다시 해!’라고 결정해버리면, 전체 시스템의 규칙이 깨질 수도 있겠네.”

솔라는 자신의 처음 생각이 얼마나 단편적이었는지 깨달았다. Critic이 평가와 제어를 함께 하는 것은 단순한 게 아니었다. 자신의 좁은 시야에 갇혀 더 큰 그림을 보지 못하는 위험한 결정일 수 있었다. 평가 담당자는 오직 평가에만 집중해야 가장 순수하고 정확한 결과가 나온다. 제어 담당자는 그 결과를 포함한 여러 정보를 종합해야 가장 올바른 결정을 내릴 수 있다.

“역할을 명확하게 나누는 거였어. 서로의 영역을 침범하지 않아야 전체 시스템이 안정적으로 돌아가는 거구나.”

이제 다이어그램의 모든 화살표가 제자리를 찾은 듯 편안하게 느껴졌다. 하지만 솔라의 머릿속에는 새로운 궁금증이 고개를 들었다.

“알겠어. 역할 분리는 이해했어. 그런데 언니, 그러면 슈퍼바이저는 크리틱한테 ‘통과’ 또는 ‘실패’ 같은 단순한 신호만 받는다는 거잖아. 그렇게 단순한 정보만으로 어떻게 ‘계획을 수정해서 다시 실행해’라거나, ‘이제 그만하고 종료해’ 같은 복잡한 흐름을 만들어내는 거지? 그 제어 과정이 머릿속에 잘 그려지지 않아.”

2장: Supervisor가 반복을 통제하는 이유

솔라의 질문이 남긴 여운을 깨고, 루나는 책상 위 빈 종이를 가져와 반으로 접었다. 그리고 왼쪽과 오른쪽에 각각 다른 그림을 그리기 시작했다. 복잡하지 않은, 세 개의 동그라미와 화살표 몇 개로 이루어진 간단한 도식이었다. 솔라는 말없이 언니의 손끝을 지켜봤다.

왼쪽 그림은 세 개의 동그라미 실행, 평가, 계획이 꼬리를 물고 서로에게 직접 연결된 형태였다. 실행의 결과는 평가로, 평가의 결정은 다시 계획으로, 계획은 또다시 실행으로 이어지는, 마치 닫힌 삼각형 같은 모습이었다. 반면 오른쪽 그림은 달랐다. 중앙에 큰 별, Supervisor를 그리고, 그 주위로 실행, 평가, 계획 동그라미를 배치했다. 모든 화살표는 각 동그라미에서 출발해 중앙의 별로 향하거나, 별에서 동그라미로 뻗어 나갈 뿐, 동그라미끼리 직접 연결된 선은 하나도 없었다.

“자, 솔라. 네가 궁금해했던 ‘제어 과정’을 두 가지 방식으로 그려봤어. 왼쪽은 네 생각처럼 ‘평가’가 직접 다음 단계를 결정하는 구조. 오른쪽은 우리가 얘기하던 Supervisor 패턴 구조야.”

솔라는 두 그림을 번갈아 보았다. 역할 분리의 원칙을 막 깨달았음에도, 여전히 왼쪽 그림에 마음이 갔다.

“솔직히 말하면… 아직도 왼쪽이 더 빠르고 직관적으로 보여. 평가자가 문제를 발견하면 바로 계획자한테 ‘다시 해!’라고 말하는 거잖아. 상황에 즉각 반응하니까 더 유연할 것 같아.”

루나는 고개를 끄덕이며 솔라의 말을 인정했다. 그리고는 펜을 들어 왼쪽 그림의 동그라미에 각각 조립팀원, 품질검수원, 설계자라고 써넣었다.

“이케아에서 산 책장을 조립하는 팀이라고 상상해 보자. 조립팀원이 설명서대로 나사를 박았어. 품질검수원이 그걸 보고 ‘어, 구멍 위치가 틀렸네. 설계자, 설계도 다시 그려줘!’라고 소리쳐. 그럼 설계자는 부랴부랴 설계도를 수정해서 다시 넘겨주겠지.”

“응, 완벽한 팀워크 아니야?” 솔라가 맞장구쳤다.

“과연 그럴까?” 루나가 물었다. “만약 세 번째 시도에서도 품질검수원이 ‘또 틀렸어!’라고 외친다면? 그때 진짜 문제는 뭘까?”

“음… 설계자가 계속 실수를…” 솔라는 대답하다가 잠시 멈칫했다. 다른 가능성이 떠올랐다.

“그럴 수도 있지. 하지만 만약, 설계도에는 아무 문제가 없었고, 조립팀원이 무심코 다른 규격의 드릴 비트를 사용한 거라면? 아니면, 품질검수원이 설계도를 잘못 이해하고 있었던 거라면?”

루나의 지적에 솔라는 할 말을 잃었다. 왼쪽 그림의 화살표를 다시 보니, 이제는 다르게 보였다. 품질검수원은 오직 설계자에게만 말을 건다. 조립팀원은 누구와도 소통하지 않고 그저 주어진 일만 반복한다. 설계자는 품질검수원의 불평에 설계도만 들여다본다.

“아무도 진짜 원인을 찾으려 하지 않네. 품질검수원은 설계자 탓만 하고, 설계자는 자기 설계도만 고치고 있고… 조립팀원의 드릴 비트가 문제일 거라는 생각은 아무도 못 하겠구나. 각자 자기 앞의 책임만 떠넘기는 것처럼 보여.”

“맞아. 서로가 서로에게 직접 묶여 있기 때문이야. 품질검수원의 역할은 ‘설계자에게 재작업을 요청하는 것’이 되어버렸어. ‘결과가 어떤 상태인지 보고하는 것’이 아니라. 이런 구조를 ‘단단한 결합(Tight Coupling)‘이라고 해. 한 부품을 바꾸려면 그와 연결된 다른 부품까지 전부 손봐야 하는 경직된 구조지.”

루나는 펜을 들어 오른쪽 그림, Supervisor 패턴을 가리켰다.

“그럼 이 구조에서는 어떨까? 조립팀원이 나사를 박고, 품질검수원은 그 결과를 총괄 매니저(Supervisor)에게 보고해. ‘현재 조립된 구멍은 5mm인데, 설계도에는 6mm라고 되어 있습니다.’ 이 보고를 받은 매니저는 이제 여러 가지를 확인할 수 있어.”

루나는 중앙의 별에서 뻗어 나가는 화살표들을 하나씩 짚었다.

“매니저는 설계자에게 물어볼 수 있지. ‘설계도에 6mm가 맞는지 재확인해주세요.’ 그리고 동시에 조립팀원에게도 물을 수 있어. ‘사용한 드릴 비트 규격이 뭔가요?’”

그제야 솔라의 얼굴이 환해졌다.

“아! 슈퍼바이저는 모든 정보를 중앙에서 받으니까, 문제의 원인이 어디에 있는지 추적할 수 있구나! 조립팀원의 실수인지, 설계도의 문제인지, 아니면 검수원의 착각인지. 누구 하나를 탓하는 게 아니라, 전체 흐름을 보고 진짜 고장 난 부분을 찾아내는 거네.”

“바로 그거야. 각 작업자(Worker)는 오직 슈퍼바이저에게 보고하고, 슈퍼바이저의 지시를 받을 뿐이야. 품질검수원은 설계자가 누군지, 조립팀원이 몇 명인지 알 필요가 없어. 그저 자기 역할, 즉 ‘평가’에만 충실하면 돼. 이렇게 역할 간 직접적인 의존이 없는 구조를 ‘느슨한 결합(Loose Coupling)‘이라고 불러. 훨씬 유연하고, 확장하기 쉽고, 지금처럼 문제의 위치를 추적하기에도 유리하지.”

솔라는 두 그림을 뚫어져라 보았다. 처음에는 비효율적으로 보였던 오른쪽 그림의 ‘중앙 통제’ 방식이, 사실은 전체 시스템을 더 건강하고 유연하게 만드는 핵심 원리라는 것을 깨달았다. 평가와 제어의 분리는 단순히 역할을 나누는 것 이상이었다. 각 구성 요소를 독립적으로 만들어 전체 시스템의 안정성과 유지보수성을 높이는, 고도의 설계 전략이었던 것이다.

“이젠 알겠어. 평가자가 제어까지 해버리면 서로 단단하게 묶여서, 문제가 생겼을 때 어디가 진짜 원인인지 알 수 없게 되는구나. 슈퍼바이저가 통제권을 가져야만 각 부품들이 독립적으로 움직이고, 문제의 근원도 명확하게 밝힐 수 있는 거였어.”

이제 Supervisor의 존재 이유가 명확해졌다. 하지만 하나의 의문이 풀리자, 더 구체적인 궁금증이 생겨났다.

“좋아, 구조적인 이점은 완전히 이해했어. 슈퍼바이저가 교통경찰처럼 모든 흐름을 통제하는 거지. 그런데 그 교통경찰도 신호등을 바꾸는 규칙이 있잖아. 언제까지 재시도를 허용하고, 언제 멈춰야 할까? 계속 실패만 보고되는데, 슈퍼바이저가 영원히 ‘다시 해봐’라고 말할 수는 없잖아. 또, 작업 하나가 너무 오래 걸리면 어떻게 해? 사람을 불러야 하는 순간은 어떻게 판단하는 거지? 그 ‘통제 정책’이라는 게 궁금해졌어.”

3장: 중앙에서 제어되는 반복과 예외

솔라의 질문이 남긴 정적을 깬 것은 창밖에서 들려온 날카로운 경적 소리였다. 두 자매의 시선이 잠시 창밖으로 향했다. 늦은 오후, 아파트 단지 앞 작은 사거리는 꼬리에 꼬리를 문 차들로 혼잡했다. 신호등이 없는 교차로였다. 서로 먼저 가려고 눈치를 보며 조금씩 앞으로 나서는 차들 때문에 흐름은 더 엉망이 되어가고 있었다.

루나는 말없이 태블릿을 들어 근처의 다른 교차로를 비추는 실시간 교통 CCTV 화면을 띄웠다. 그곳은 여러 차선이 합류하는 복잡한 곳이었지만, 차들은 신호등의 지시에 따라 일사불란하게 움직이고 있었다. 막힘없는 흐름이었다. 루나는 두 풍경—창밖의 혼돈과 화면 속의 질서—을 나란히 보여주었다. 어떤 설명도 덧붙이지 않았다.

“통제 정책이라는 게 저 신호등 같은 거구나.”

솔라가 먼저 입을 열었다. 이전 장에서 루나가 그렸던 두 개의 다이어그램이 떠올랐다. 창밖의 교차로는 각 운전자(Worker)가 스스로 판단해서 움직이는 ‘단단한 결합’ 구조와 닮아 있었다. 반면, 화면 속 신호등이 있는 교차로는 명백한 ‘슈퍼바이저’의 모습이었다.

“맞아. 하지만 만약 저 신호등이 고장 나서 빨간불만 계속 켜져 있다면 어떨까? 아니면 한쪽 방향에만 10분 내내 녹색불을 준다면?”

“난리 나겠지. 사람들은 경적을 울려대고, 결국 누군가 경찰에 신고할 거야. 교통경찰이 와서 수신호로 정리해야 할 거고.”

“바로 그거야. 좋은 신호등, 즉 좋은 슈퍼바이저는 그냥 켜져 있기만 하는 게 아니야. 아주 구체적인 ‘운영 규칙’을 가지고 있어.”

루나는 태블릿 화면을 끄고, 다시 책상 위 종이를 가져왔다. 이번에는 간단한 의사 코드(pseudo-code) 몇 줄을 적기 시작했다.

< Critic이 직접 분기하는 경우 >

결과 = 실행()
while (Critic.평가(결과) != '성공') {
  결과 = 실행()
}

“이건 우리가 처음에 비효율적이라고 했던, Critic이 직접 제어하는 구조야. 아주 단순하지. 하지만 여기에는 네가 궁금했던 ‘통제 정책’이 들어갈 자리가 없어.” 루나가 말했다. “만약 작업이 3번 실패하면 그만두고 싶다면? ‘실행()’ 함수가 한없이 길어지면 멈추게 하고 싶다면? 이 코드 어디에 그 규칙을 넣어야 할까?”

솔라는 코드를 뚫어져라 쳐다봤다. 막막했다. while 루프 안 어딘가에 넣어야 할 것 같은데, 지저분해 보였다. 평가자의 역할에 제어 로직이 덕지덕지 붙는 모양새였다.

루나는 그 아래에 새로운 코드를 적었다.

< Supervisor가 제어하는 경우 >

supervisor {
  max_retries = 3
  timeout = 60초
  
  for i in 1..max_retries {
    결과 = 실행(timeout)
    
    if (Critic.평가(결과) == '성공') {
      종료
    }
  }

  // 3번 모두 실패한 경우
  사람에게_알림(HITL)
}

“차이가 보여?”

“아…”

솔라의 입에서 짧은 탄성이 터져 나왔다. 두 번째 코드는 훨씬 명확했다. 교통경찰의 규칙들이 한눈에 보였다.

“첫 번째 코드에서는 ‘무한 반복’이나 ‘무한 대기’를 막을 방법이 없어. 신호등 없이 서로 눈치만 보는 교차로처럼, 시스템은 언제든 교착 상태에 빠질 수 있어. 하지만 두 번째 구조에서는 슈퍼바이저가 명확한 규칙을 가지고 전체 상황을 지휘해.”

루나는 코드의 각 부분을 짚었다.

max_retries = 3은 ‘최대 3번만 재시도한다’는 규칙이야. Critic이 계속 ‘실패’를 보고하더라도, 슈퍼바이저는 3번이 되면 루프를 중단시키지. 영원히 ‘다시 해봐’라고 말하지 않는 거야.”

timeout = 60초는 작업 하나가 너무 오래 걸리는 걸 방지해. 실행 함수가 60초 안에 응답이 없으면, 슈퍼바이저가 강제로 중단시키고 다음 단계를 결정해. 한 차가 교차로를 막고 멈춰서는 걸 막는 거지.”

“그리고 마지막 사람에게_알림(HITL)은?”

“Human-in-the-Loop. 정해진 규칙(3번 재시도) 안에서 문제가 해결되지 않았을 때, 슈퍼바이저는 더 이상 자동화를 고집하지 않고 사람을 호출해. 교통경찰이 출동하는 것처럼 말이야. 이건 시스템이 스스로 해결할 수 없는 예외 상황을 다루는 아주 중요한 안전장치야.”

이제 모든 조각이 맞춰졌다. 평가와 제어의 분리는 단순히 역할을 나누는 깔끔함을 넘어, 반복, 시간, 그리고 예측 불가능한 예외 상황까지 모두 한곳에서 통제하기 위한 필수적인 설계 원칙이었다. Critic이 제어까지 하려는 시도는 마치 운전자 개개인이 직접 신호등 역할을 하겠다는 것과 같았다. 당장은 유연해 보여도, 전체 시스템은 금방 한계에 부딪히고 마비될 수밖에 없었다. 중앙 통제는 복잡한 게 아니라, 오히려 복잡함을 다루기 위한 가장 단순하고 강력한 도구였다.

“이제 알겠어. 슈퍼바이저가 왜 ‘교통경찰’인지. 단순히 길을 터주는 게 아니라, 최대 통과 차량 수, 신호 대기 시간, 사고 시 대응 매뉴얼까지 모두 가지고 있는 거였구나.”

솔라는 다시 자신의 모니터로 시선을 돌렸다. 처음에는 비효율적으로 보였던 그 아키텍처 다이어그램이 이제는 견고하고 신뢰성 높은 시스템의 청사진으로 보였다. 그녀는 망설임 없이 키보드에 손을 올렸다. Supervisor 상자 옆에 작은 메모 상자를 하나 추가하고, 그 안에 방금 깨달은 ‘통제 정책’들을 써 내려가기 시작했다.

# Supervisor - Control Policy
- max_iterations: 5
- task_timeout: 120s
- on_failure_limit: escalate_to_operator (HITL)

자신의 손으로 직접 제어 규칙을 명시하는 순간, 흐릿했던 제어 과정이 비로소 손에 잡히는 듯 선명해졌다. 평가와 제어의 분리는 더 이상 추상적인 원칙이 아니었다. 안정적인 시스템을 만들기 위한 구체적인 설계 도구였다.