콘텐츠로 이동

Multi-Agent 오케스트레이션

언제 멀티 에이전트가 필요한가?

섹션 제목: “언제 멀티 에이전트가 필요한가?”

단일 에이전트는 컨텍스트 윈도우 한계, 도구 다양성 한계, 단일 실행 스레드 한계를 가집니다. 다음 상황에서 멀티 에이전트가 필요합니다:

  • 태스크가 단일 컨텍스트 윈도우를 초과하는 경우
  • 전문화된 에이전트가 각 도메인을 더 잘 처리하는 경우
  • 독립적 태스크를 병렬로 실행해야 하는 경우
  • 검증을 별도 에이전트가 독립적으로 수행해야 하는 경우

패턴 1: Supervisor (중앙 집중 조율)

섹션 제목: “패턴 1: Supervisor (중앙 집중 조율)”

Supervisor 패턴에서는 중앙 에이전트(Supervisor)가 하위 에이전트들에게 태스크를 배분하고 결과를 통합합니다.

┌─────────────┐
│ Supervisor │ ← 사용자 요청
└──────┬──────┘
┌───────────┼───────────┐
↓ ↓ ↓
┌───────┐ ┌───────┐ ┌───────┐
│Agent A│ │Agent B│ │Agent C│
│(코드) │ │(테스트)│ │(문서) │
└───────┘ └───────┘ └───────┘
└───────────┴───────────┘
결과 통합 및 반환
class SupervisorAgent:
def run(self, goal: str) -> str:
# 태스크 분해
subtasks = self.llm.decompose(goal)
results = {}
for subtask in subtasks:
# 적합한 하위 에이전트 선택
agent = self.route_to_agent(subtask)
results[subtask.id] = agent.run(subtask.description)
# 결과 통합
return self.llm.synthesize(goal, results)
def route_to_agent(self, subtask: Subtask) -> Agent:
routing = {
"code": self.code_agent,
"test": self.test_agent,
"docs": self.docs_agent,
}
return routing[subtask.type]

적합한 상황:

  • 태스크 유형이 명확히 구분되는 경우
  • 중앙 조율이 필요한 복잡한 워크플로우
  • 결과를 통합해야 하는 경우

주의사항: Supervisor가 단일 장애점(SPOF)이 됩니다. Supervisor 자체의 품질이 전체 시스템을 결정합니다.

패턴 2: Peer-to-Peer (직접 핸드오프)

섹션 제목: “패턴 2: Peer-to-Peer (직접 핸드오프)”

P2P 패턴에서는 에이전트들이 서로 직접 핸드오프합니다. 중앙 조율자가 없고 각 에이전트가 다음 에이전트를 결정합니다.

Agent A → (핸드오프) → Agent B → (핸드오프) → Agent C
↑ │
└─────────────── (피드백 루프) ─────────────────┘
@dataclass
class HandoffMessage:
from_agent: str
to_agent: str
content: str
metadata: dict
class PeerAgent:
def run(self, message: HandoffMessage) -> HandoffMessage | None:
result = self.process(message.content)
# 다음 에이전트 결정
next_agent = self.determine_next(result)
if next_agent is None:
return None # 파이프라인 종료
return HandoffMessage(
from_agent=self.name,
to_agent=next_agent,
content=result,
metadata={"previous": message.metadata},
)

적합한 상황:

  • 선형 파이프라인 (A → B → C)
  • 각 단계의 책임이 명확한 경우
  • 에이전트 추가/제거가 자주 필요한 경우

Hierarchical 패턴은 트리 구조로 에이전트를 조직합니다. 상위 에이전트가 하위 에이전트를 관리하고, 하위 에이전트는 다시 더 하위 에이전트를 가질 수 있습니다.

┌──────────────┐
│ Coordinator │ Level 0
└──────┬───────┘
┌─────────┴─────────┐
↓ ↓
┌──────────┐ ┌──────────┐
│ Manager A│ │ Manager B│ Level 1
└────┬─────┘ └────┬─────┘
┌────┴────┐ ┌────┴────┐
↓ ↓ ↓ ↓
Worker1 Worker2 Worker3 Worker4 Level 2

적합한 상황:

  • 대규모 복잡 태스크 (대형 코드베이스 전체 리팩토링)
  • 도메인별 전문화가 필요한 경우
  • 병렬 실행과 조율이 동시에 필요한 경우
기준SupervisorPeer-to-PeerHierarchical
복잡도중간낮음높음
확장성제한적높음매우 높음
디버깅 용이성중간높음낮음
병렬 실행가능제한적매우 용이
단일 장애점있음없음있음 (각 레벨)
적합 규모중소형소형대형

필요 이상으로 에이전트를 추가하지 마세요. 에이전트가 늘어날수록 오케스트레이션 복잡도가 기하급수적으로 증가합니다.

에이전트 간 통신은 구조화된 형태로 정의하세요.

@dataclass
class AgentMessage:
task_id: str
content: str
context: dict
expected_output_format: str # 출력 형식 명시

각 에이전트는 가능한 한 독립적으로 작동해야 합니다. 공유 상태를 최소화하고, 각 에이전트가 자체 완결적인 컨텍스트를 갖도록 설계하세요.

# 나쁜 패턴: 공유 전역 상태
shared_state = {}
# 좋은 패턴: 메시지 패싱
def agent_b(message_from_a: AgentMessage) -> AgentMessage:
# message_from_a에만 의존, 전역 상태 없음
result = process(message_from_a.content)
return AgentMessage(task_id=message_from_a.task_id, content=result)

Multi-Agent 오케스트레이션은 Supervisor(중앙 조율), P2P(직접 핸드오프), Hierarchical(계층 구조) 세 가지 핵심 패턴으로 구성됩니다. 패턴 선택은 태스크 규모, 병렬화 필요성, 디버깅 용이성을 기준으로 합니다. 최소 에이전트 원칙을 지키고 명확한 인터페이스를 설계하는 것이 안정적인 멀티 에이전트 시스템의 기반입니다.

Section 02 학습을 완료했습니다. 다음 Section 03에서는 세 축 중 첫 번째인 Context Engineering을 심화 학습합니다.