본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 01. 11:48

에이전트 시리즈 (9): 멀티 에이전트 아키텍처 디자인 패턴 — Supervisor vs Pipeline

요약

단일 에이전트의 한계를 극복하기 위한 멀티 에이전트 아키텍처의 핵심인 '관심사의 분리'를 다룹니다. Supervisor 패턴과 Pipeline 패턴의 차이를 설명하며, 특히 Supervisor 패턴에서 라우팅 신뢰성을 높이기 위한 하이브리드 설계 방식을 제안합니다.

핵심 포인트

  • 멀티 에이전트의 핵심 가치는 관심사의 분리(Separation of Concerns)임
  • 단일 에이전트는 프롬프트 비대화 및 출력 품질 저하 위험이 있음
  • Supervisor 패턴은 LLM의 분류와 Python의 결정론적 라우팅을 결합해야 함
  • LangGraph를 활용하여 제어된 루프 형태의 그래프 토폴로지 구현 가능

단일 에이전트로는 부족할 때

이전 8개의 기사에서는 단일 에이전트 (single-agent) 시스템을 구축했습니다. 즉, 하나의 LLM, 하나의 도구 세트, 하나의 대화 기록을 사용하는 방식입니다. 이러한 아키텍처는 대부분의 문제를 잘 해결합니다.

하지만 어떤 작업들은 본질적으로 다수의 전문가를 필요로 합니다:

  • 기술 기사를 작성하려면 사실을 수집하는 연구자(researcher), 초안을 작성하는 작가(writer), 그리고 다듬는 편집자(editor)가 필요합니다. 즉, 세 가지 역할과 세 가지 사고방식이 필요합니다.
  • 고객 지원 티켓을 처리하려면 의도 분류 (intent classification), 지식 베이스 조회 (knowledge base lookup), 그리고 답변 생성 (reply generation)이 필요합니다. 이는 독립적으로 테스트 가능한 세 단계의 과정입니다.
  • 코드 리뷰에는 정적 분석 (static analysis), 보안 스캐닝 (security scanning), 그리고 가독성 검토 (readability review)가 필요합니다. 이는 서로 간섭하지 않는 세 가지 차원의 작업입니다.

단일 에이전트도 이를 처리할 수 있지만, 시스템 프롬프트 (System Prompt)가 통제 불능 수준으로 길어지고 출력 품질이 불안정해지는 것을 발견하게 될 것입니다. 이는 하나의 역할에게 모든 역할을 수행하도록 강요하기 때문입니다.

멀티 에이전트 (multi-agent)의 핵심 가치는 **관심사의 분리 (separation of concerns)**입니다. 각 에이전트는 한 가지 일을 잘 수행합니다.

두 가지 일반적인 아키텍처 패턴

멀티 에이전트 시스템에는 여러 토폴로지 (topologies)가 있습니다. 실제로는 다음 두 가지가 지배적입니다:

Supervisor 패턴 (동적 라우팅 (dynamic routing)):
  분류 (classify) → supervisor → researcher
                       ↘ writer
...

이 패턴들은 경쟁 관계가 아니라, 서로 다른 시나리오에 적합한 것입니다.

데모 1: Supervisor 패턴

디자인 접근 방식

Supervisor 패턴의 핵심 과제는 **라우팅 신뢰성 (routing reliability)**입니다. 만약 LLM이 매 단계마다 "다음에 누구를 호출할지"를 결정하게 되면, 다음과 같은 경향을 보입니다:

  • 동일한 작업자 (worker)를 여러 번 호출함
  • 어떤 작업자가 이미 호출되었는지 잊어버림
  • 언제 종료해야 하는지 인식하지 못함

더 나은 디자인은 2단계 하이브리드 (two-phase hybrid) 방식입니다:

1단계: LLM이 작업을 한 번 분류함 (simple_fact vs full_article)
2단계: Python이 분류 결과와 호출 목록을 기반으로 결정론적 (deterministically)으로 라우팅함

LLM은 "이 작업이 어떤 종류인지 이해하는 것"을 처리하고, Python은 "올바른 순서를 실행하는 것"을 처리합니다. 각자가 잘하는 일을 수행하는 것입니다.

LangGraph 구현

class SupervisorState(TypedDict):
    messages: Annotated[list, add_messages]
    task: str
...

그래프 토폴로지 (Graph Topology)

g = StateGraph(SupervisorState)
g.set_entry_point("classify")
g.add_edge("classify", "supervisor")
...

classify → supervisor → [workers] → supervisor → ... → FINISH는 제어된 루프 (controlled loop)를 형성합니다. classify 노드는 정확히 한 번 실행되며, supervisor 노드는 경량 상태 머신 (lightweight state machine) 역할을 합니다.

측정 결과 (Measured Results)

작업: "Python 리스트 컴프리헨션(list comprehensions)에 관한 짧은 기사를 작성하세요"

[classify] task_type = full_article
[supervisor] → researcher
[researcher] working...
...

classify를 위한 LLM 호출 한 번 이후, 순수 Python 라우팅 (routing)이 이루어집니다. 실행 체인 (execution chain)은 researcher → writer → reviewer → FINISH로 깔끔하고 예측 가능합니다.

데모 2: 파이프라인 패턴 (Pipeline Pattern)

파이프라인은 Supervisor보다 코드가 훨씬 적게 필요합니다. 작성해야 할 라우팅 로직이 없기 때문입니다:

class PipelineState(TypedDict):
    topic: str
    outline: str
...

측정 결과 (Measured Results)

[outline_agent] 957 chars
[draft_agent] 1846 chars
[polish_agent] 2168 chars
...

각 단계의 출력이 다음 단계의 입력이 됩니다. 콘텐츠는 점진적으로 성장합니다: 개요(outline) 957자 → 초안(draft) 1846자 → 다듬어진 글(polished) 2168자.

LLM 라우팅 결정이 전혀 없습니다 — 경로는 그래프가 작성될 때 이미 결정되었습니다.

데모 3: 동일한 그래프, 다른 경로

Supervisor 패턴의 가장 강력한 장점은 다음과 같습니다: 서로 다른 작업 유형이 동일한 그래프를 통해 서로 다른 실행 경로를 취하며, 코드 변경이 필요하지 않습니다.

단순한 사실 질문에 대해 동일한 Supervisor 그래프를 실행하면 다음과 같습니다:

Task: "Python은 몇 년도에 만들어졌나요?"

[classify] task_type = simple_fact
...

나란히 비교하기:

작업 (Task)                         호출된 워커 (Workers Called)    단계 (Steps)
──────────────────────────────────────────────────────────────────────────
기사 작성 (full_article)            researcher→writer→reviewer    3
...

파이프라인은 이를 수행할 수 없습니다. 파이프라인의 경로는 하드코딩(hardcoded)되어 있기 때문입니다.

패턴 선택 매트릭스 (Pattern Selection Matrix)

차원 (Dimension)           파이프라인 (Pipeline)              감독자 (Supervisor)
─────────────────────────────────────────────────────────────────────────────
실행 경로 (Execution path) 고정됨, 하드와이어드 (hardwired)    동적임, 분류 기반 (classification-driven)
...

경험 법칙 (Rule of thumb):

  • 필요한 단계를 정확히 알고 있다면 → 파이프라인 (Pipeline)
  • 작업에 따라 단계를 조정해야 한다면 → 감독자 (Supervisor)

멀티 에이전트 디자인 체크리스트 (Multi-Agent Design Checklist)

패턴 선택 (Pattern Selection)

  • 고정되고 알려진 단계 → 파이프라인 (Pipeline); 동적인 결정이 필요함 → 감독자 (Supervisor)
  • 워커 (worker)가 3명 이하이고 책임이 명확하다면, 두 패턴 모두 작동함
  • 워커가 5명을 초과한다면, 계층적 감독자 (hierarchical Supervisor, 감독자의 감독자)를 고려할 것

상태 설계 (State Design)

  • 각 워커는 필요한 필드만 읽고, 생성한 필드만 작성함
  • 감독자의 상태 (state)에는 중복 호출을 방지하기 위해 called: list[str]가 반드시 포함되어야 함
  • 파이프라인의 상태는 디버깅을 용이하게 하기 위해 단계별 이름이 붙은 필드(outline, draft, polished)를 사용함

라우팅 신뢰성 (Routing Reliability)

  • 순수 LLM 라우팅 (pure LLM routing)을 피할 것 (LLM은 호출 이력을 안정적으로 추적할 수 없음)
  • 권장 사항: 일회성 분류에는 LLM을 사용하고, 결정론적 라우팅 (deterministic routing)에는 Python을 사용함
  • 예기치 않은 루프를 방지하기 위해 recursion_limit을 설정할 것 (20~30 정도가 적당함)

워커 설계 (Worker Design)

  • 각 워커는 명확한 입출력 계약 (input/output contract)을 가지고 정확히 한 가지 일만 수행함
  • 워커는 상태 (State)를 통해 통신하며, 서로를 직접 호출해서는 안 됨
  • 명확한 워커 시스템 프롬프트 (Worker System Prompts)를 작성할 것 — 워커가 문맥을 추측하게 만들지 마세요

관찰 가능성 (Observability)

  • 각 노드 실행을 로그로 남길 것 (워커 이름, 입출력 요약)
  • 사후에 라우팅 결정을 추적할 수 있도록 called 리스트를 기록할 것
  • 비정상적인 분기(예: 조기 종료(FINISH)) 발생 시 경고 로그를 추가할 것

요약 (Summary)

다섯 가지 핵심 요점:

  1. 멀티 에이전트(Multi-agent)는 관심사의 분리(Separation of concerns)에 관한 것입니다: 복잡성을 위한 것이 아니라, 단일 에이전트(Agent)의 시스템 프롬프트(System Prompt)가 너무 많은 역할을 수행해야 할 때 한계에 부딪히기 때문입니다.
  2. 파이프라인(Pipeline)은 단순성과 예측 가능성 측면에서 승리합니다: 실행 경로가 코드 내에 존재하며, 추적(Trace)이 선형적이고, 테스트 및 디버깅 비용이 최소화됩니다.
  3. 슈퍼바이저(Supervisor)는 적응성 측면에서 승리합니다: 동일한 그래프(Graph)가 한 단계의 사실적 질문부터 세 단계의 전체 기사 작성까지 처리할 수 있으며, 코드 변경이 필요하지 않습니다.
  4. LLM 분류(Classification) + Python 실행(Execution)은 최상의 조합입니다: LLM은 자신이 잘하는 일(작업 유형 이해)을 수행하고, Python은 자신이 잘하는 일(신뢰할 수 있는 시퀀싱)을 수행합니다.
  5. called 리스트는 슈퍼바이저 상태(Supervisor State)의 핵심 필드입니다: 라우팅 결정론(Routing determinism)의 기초이며, 이것이 없으면 슈퍼바이저는 중복 호출 및 무한 루프에 빠지기 쉽습니다.

참고 문헌 (References)

홈페이지에서 더 유용한 지식과 흥미로운 제품들을 찾아보세요.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0