🤖

본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.

⚠️

본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.

이미지 로딩 중...

Multi-Agent Patterns 멀티 에이전트 아키텍처 완벽 가이드 - 슬라이드 1/8
A

AI Generated

2025. 12. 27. · 2 Views

Multi-Agent Patterns 멀티 에이전트 아키텍처 완벽 가이드

여러 AI 에이전트가 협력하여 복잡한 작업을 수행하는 멀티 에이전트 시스템의 핵심 패턴을 다룹니다. 컨텍스트 격리부터 Supervisor, Swarm, Hierarchical 패턴까지 실무에서 바로 적용할 수 있는 아키텍처 설계 원칙을 배웁니다.


목차

  1. 컨텍스트_격리_원칙
  2. 토큰_경제학
  3. Supervisor_Orchestrator_패턴
  4. Peer_to_Peer_Swarm_패턴
  5. Hierarchical_패턴
  6. 병렬화와_전문화
  7. X_to_Book_시스템_분석

1. 컨텍스트 격리 원칙

어느 날 김개발 씨가 회사에서 AI 에이전트 시스템을 개발하다가 이상한 현상을 발견했습니다. 하나의 에이전트에게 코드 작성, 테스트, 문서화를 모두 맡겼는데 작업이 길어질수록 앞서 작성한 코드를 까먹고 엉뚱한 코드를 생성하는 것이었습니다.

"왜 방금 만든 함수 이름도 기억 못 하지?"

컨텍스트 격리란 멀티 에이전트 시스템의 핵심 목적이 단순한 역할 분할이 아니라 각 에이전트가 자신의 작업에 필요한 정보만 유지하도록 하는 것입니다. 마치 대형 프로젝트에서 각 팀이 자신의 담당 영역에만 집중하는 것과 같습니다.

이를 통해 토큰 한계를 극복하고 각 에이전트가 전문성을 발휘할 수 있게 됩니다.

다음 코드를 살펴봅시다.

# 잘못된 접근: 하나의 에이전트에 모든 컨텍스트
class MonolithicAgent:
    def __init__(self):
        self.context = []  # 모든 정보가 무한정 쌓임

    def process(self, task):
        self.context.append(task)  # 컨텍스트 폭발!
        return self.llm.generate(self.context)

# 올바른 접근: 컨텍스트 격리된 에이전트들
class IsolatedAgent:
    def __init__(self, role: str, max_context: int = 4000):
        self.role = role
        self.context = []
        self.max_context = max_context  # 각자 관리 가능한 크기

    def process(self, task):
        # 역할에 필요한 정보만 유지
        relevant_context = self.filter_by_role(task)
        return self.llm.generate(relevant_context)

김개발 씨는 입사 1년 차 AI 엔지니어입니다. 최근 회사에서 야심 차게 AI 에이전트 프로젝트를 시작했습니다.

처음에는 "하나의 똑똑한 에이전트면 다 되겠지"라고 생각했습니다. 그런데 이상한 일이 벌어졌습니다.

에이전트에게 100개의 파일을 분석하고 리팩토링하라고 시켰더니, 50번째 파일쯤에서 앞서 수정한 내용을 완전히 잊어버리는 것이었습니다. 심지어 자신이 방금 만든 유틸리티 함수를 다시 만들기도 했습니다.

선배 개발자 박시니어 씨가 지나가다 김개발 씨의 화면을 봤습니다. "아, 그건 컨텍스트 윈도우 문제야.

LLM은 한 번에 처리할 수 있는 토큰 수가 제한되어 있거든." 그렇습니다. 아무리 뛰어난 LLM이라도 한 번에 기억할 수 있는 정보의 양에는 한계가 있습니다.

GPT-4가 128K 토큰을 지원한다고 해도, 실제 복잡한 작업에서는 금방 한계에 도달합니다. 쉽게 비유하자면, 컨텍스트 격리는 마치 대형 건설 현장의 업무 분담과 같습니다.

현장 소장이 모든 세부 사항을 기억하려고 하면 머리가 터질 것입니다. 대신 전기 팀은 전기 도면만, 배관 팀은 배관 도면만 집중적으로 관리합니다.

각 팀이 자신의 영역에만 집중하기 때문에 효율적으로 일할 수 있는 것입니다. 멀티 에이전트 시스템에서도 마찬가지입니다.

코드 작성 에이전트는 코드 관련 컨텍스트만, 테스트 에이전트는 테스트 관련 컨텍스트만, 문서화 에이전트는 문서 관련 컨텍스트만 유지합니다. 각자가 관리해야 할 정보의 양이 줄어들기 때문에 더 정확하고 일관된 결과를 낼 수 있습니다.

많은 개발자들이 처음에 하는 실수가 있습니다. "역할을 나누면 멀티 에이전트"라고 생각하는 것입니다.

하지만 진짜 핵심은 컨텍스트의 격리입니다. 역할을 나눠도 모든 에이전트가 같은 거대한 컨텍스트를 공유한다면 멀티 에이전트의 이점을 전혀 살릴 수 없습니다.

위 코드를 살펴보면, MonolithicAgent는 모든 작업 정보를 하나의 context 리스트에 계속 쌓습니다. 작업이 길어질수록 컨텍스트가 폭발적으로 증가합니다.

반면 IsolatedAgent는 자신의 역할에 필요한 정보만 필터링하여 유지합니다. max_context로 상한선도 설정했습니다.

실제 현업에서는 어떻게 적용할까요? 예를 들어 코드 리뷰 시스템을 만든다고 가정해봅시다.

보안 검토 에이전트는 보안 관련 패턴과 취약점 정보만 컨텍스트로 유지합니다. 성능 검토 에이전트는 성능 병목 패턴만 기억합니다.

각 에이전트가 전문 분야에 집중할 수 있어 더 정확한 리뷰가 가능해집니다. 박시니어 씨의 조언을 듣고 김개발 씨는 시스템을 재설계했습니다.

하나의 만능 에이전트 대신 세 개의 전문 에이전트로 나눴습니다. 각 에이전트는 자신의 작업에 필요한 정보만 받아서 처리했습니다.

결과는 놀라웠습니다. 100개 파일 리팩토링이 일관성 있게 완료된 것입니다.

실전 팁

💡 - 에이전트를 나눌 때는 "어떤 정보가 필요한가"를 기준으로 설계하세요

  • 에이전트 간 공유되는 정보는 최소화하고, 필요한 결과만 전달하세요
  • 각 에이전트의 컨텍스트 크기를 모니터링하여 한계를 넘지 않도록 관리하세요

2. 토큰 경제학

김개발 씨가 멀티 에이전트 시스템을 도입한 지 일주일이 지났습니다. 성능은 확실히 좋아졌는데, 월말에 청구서를 보고 깜짝 놀랐습니다.

API 비용이 예상의 15배나 나온 것입니다. "에이전트를 여러 개 쓰면 비용도 여러 배가 되는 건가요?"

토큰 경제학은 멀티 에이전트 시스템에서 반드시 고려해야 할 비용 구조입니다. 단일 에이전트 대비 평균 10-15배의 토큰을 소비하는데, 이는 에이전트 간 통신 오버헤드와 각 에이전트의 시스템 프롬프트 중복 때문입니다.

이를 이해하고 최적화해야 비용 효율적인 시스템을 구축할 수 있습니다.

다음 코드를 살펴봅시다.

# 토큰 소비 분석 예시
class TokenEconomicsAnalyzer:
    def calculate_costs(self, task_complexity: str):
        # 단일 에이전트 비용
        single_agent = {
            "system_prompt": 500,      # 한 번만 로드
            "task_context": 2000,      # 작업 컨텍스트
            "generation": 1000,        # 응답 생성
            "total": 3500
        }

        # 멀티 에이전트 비용 (3개 에이전트 기준)
        multi_agent = {
            "system_prompts": 500 * 3,          # 각 에이전트별 시스템 프롬프트
            "orchestrator_overhead": 1000,       # 오케스트레이터 통신
            "inter_agent_messages": 2000 * 3,   # 에이전트 간 메시지
            "individual_generations": 1000 * 3, # 각 에이전트 응답
            "total": 10500  # 약 3배, 복잡한 경우 15배까지
        }

        return {"ratio": multi_agent["total"] / single_agent["total"]}

월말 청구서를 본 김개발 씨는 당혹스러웠습니다. 분명 더 효율적인 시스템을 만들었다고 생각했는데, 비용이 이렇게 많이 나올 줄은 몰랐습니다.

박시니어 씨가 화이트보드에 그림을 그리며 설명을 시작했습니다. "멀티 에이전트 시스템의 토큰 경제학을 이해해야 해.

공짜 점심은 없거든." 먼저 가장 기본적인 비용 증가 요인을 살펴봅시다. 각 에이전트는 자신만의 시스템 프롬프트를 가집니다.

단일 에이전트라면 시스템 프롬프트를 한 번만 로드하면 됩니다. 하지만 5개의 에이전트를 사용한다면?

시스템 프롬프트도 5번 로드됩니다. 더 큰 문제는 에이전트 간 통신입니다.

마치 회의가 많아지면 실제 업무 시간이 줄어드는 것처럼, 에이전트들이 서로 정보를 주고받는 데도 토큰이 소비됩니다. 오케스트레이터가 작업을 분배하고, 각 에이전트가 결과를 보고하고, 다시 종합하는 모든 과정에서 토큰이 사용됩니다.

실제 수치를 보면 더 명확해집니다. 단순한 작업의 경우 단일 에이전트로 3,500 토큰이면 충분합니다.

하지만 같은 작업을 3개의 에이전트로 처리하면 최소 10,500 토큰이 필요합니다. 약 3배입니다.

작업이 복잡해져서 에이전트 간 소통이 여러 번 오가면 이 비율은 10배, 심지어 15배까지 증가합니다. 그렇다면 멀티 에이전트는 항상 비효율적인 것일까요?

그렇지 않습니다. 핵심은 언제 멀티 에이전트를 써야 하는지 판단하는 것입니다.

단순하고 명확한 작업에는 단일 에이전트가 효율적입니다. "이 함수의 버그를 고쳐줘"같은 작업에 굳이 여러 에이전트를 동원할 필요가 없습니다.

하지만 복잡하고 긴 작업, 예를 들어 "전체 코드베이스를 분석하고 리팩토링해줘"같은 경우는 다릅니다. 단일 에이전트로 이런 복잡한 작업을 처리하면 컨텍스트 한계에 부딪힙니다.

중간에 중요한 정보를 잊어버리거나, 일관성 없는 결과를 내놓습니다. 결국 여러 번 다시 시도해야 하고, 총 비용은 오히려 더 늘어납니다.

위 코드에서 TokenEconomicsAnalyzer 클래스는 이런 비용 구조를 분석합니다. 실제 프로덕션 환경에서는 이런 분석 도구를 만들어서 비용을 모니터링하는 것이 좋습니다.

비용을 최적화하는 방법도 있습니다. 첫째, 적절한 모델 선택입니다.

모든 에이전트가 GPT-4를 쓸 필요는 없습니다. 단순한 작업을 하는 에이전트는 GPT-3.5나 더 저렴한 모델을 사용해도 됩니다.

둘째, 캐싱입니다. 반복되는 시스템 프롬프트나 공통 컨텍스트를 캐싱하면 토큰을 절약할 수 있습니다.

김개발 씨는 고개를 끄덕였습니다. "그러니까 무조건 멀티 에이전트가 좋은 게 아니라, 상황에 맞게 선택해야 하는 거군요." 박시니어 씨가 웃으며 말했습니다.

"그래, 트레이드오프를 이해하는 게 핵심이야. 비용 대비 품질, 속도 대비 정확성.

엔지니어링은 결국 이런 균형을 찾는 거거든."

실전 팁

💡 - 작업 복잡도에 따라 단일/멀티 에이전트를 선택하세요

  • 중요하지 않은 에이전트에는 저렴한 모델을 사용하세요
  • 시스템 프롬프트 길이를 최적화하고, 가능하면 캐싱을 활용하세요

3. Supervisor Orchestrator 패턴

김개발 씨가 드디어 멀티 에이전트 시스템을 본격적으로 설계하기 시작했습니다. 가장 먼저 떠오른 방식은 "총괄 매니저를 두자"는 것이었습니다.

한 명의 똑똑한 에이전트가 전체를 지휘하고, 나머지 에이전트들은 지시에 따라 움직이는 구조입니다. 과연 이 방식이 최선일까요?

Supervisor/Orchestrator 패턴은 중앙의 오케스트레이터가 전체 작업 흐름을 제어하고 하위 에이전트들에게 작업을 위임하는 구조입니다. 마치 오케스트라의 지휘자처럼 전체 조화를 관리합니다.

구현이 직관적이고 제어가 용이하지만, 오케스트레이터가 병목이 될 수 있다는 단점이 있습니다.

다음 코드를 살펴봅시다.

from typing import List, Dict
from dataclasses import dataclass

@dataclass
class Task:
    id: str
    description: str
    assigned_to: str = None

class OrchestratorAgent:
    def __init__(self, workers: Dict[str, 'WorkerAgent']):
        self.workers = workers  # 하위 에이전트들
        self.task_queue = []

    def orchestrate(self, complex_task: str) -> str:
        # 1. 작업 분해
        subtasks = self.decompose_task(complex_task)

        # 2. 적절한 에이전트에게 할당
        for task in subtasks:
            worker = self.select_worker(task)
            task.assigned_to = worker.name
            result = worker.execute(task)

            # 3. 결과 검토 및 다음 단계 결정
            if not self.validate_result(result):
                result = self.handle_failure(task, result)

        # 4. 최종 결과 종합
        return self.synthesize_results()

class WorkerAgent:
    def __init__(self, name: str, specialty: str):
        self.name = name
        self.specialty = specialty

    def execute(self, task: Task) -> str:
        # 전문 분야에 맞는 작업 수행
        return f"Completed: {task.description}"

김개발 씨는 오케스트라 공연을 보러 간 적이 있습니다. 수십 명의 연주자가 각자 다른 악기를 연주하는데, 지휘자 한 명의 손짓에 맞춰 아름다운 화음을 만들어냅니다.

"이거다!" 김개발 씨는 무릎을 쳤습니다. Supervisor/Orchestrator 패턴은 바로 이 오케스트라 모델입니다.

중앙에 지휘자 역할을 하는 오케스트레이터 에이전트가 있고, 여러 전문 에이전트들이 그 지휘에 따라 움직입니다. 이 패턴의 작동 방식을 단계별로 살펴봅시다.

먼저 사용자가 복잡한 작업을 요청합니다. "이 프로젝트의 보안 취약점을 찾고, 수정하고, 테스트해줘." 오케스트레이터는 이 요청을 받아서 하위 작업으로 분해합니다.

보안 분석, 코드 수정, 테스트 실행이라는 세 가지 작업으로 나눕니다. 그다음 각 작업에 적합한 에이전트를 선택합니다.

보안 분석은 보안 전문 에이전트에게, 코드 수정은 개발 에이전트에게, 테스트는 QA 에이전트에게 할당합니다. 각 에이전트가 작업을 완료하면 결과를 오케스트레이터에게 보고합니다.

오케스트레이터는 각 결과를 검토합니다. 문제가 있으면 다시 작업을 지시하고, 다음 단계로 넘어가도 되면 진행합니다.

모든 작업이 완료되면 결과를 종합하여 사용자에게 전달합니다. 이 패턴의 가장 큰 장점은 명확한 제어입니다.

누가 무엇을 담당하는지, 현재 어떤 단계인지가 명확합니다. 디버깅도 쉽습니다.

문제가 생기면 어느 에이전트에서 발생했는지 바로 알 수 있습니다. 또한 유연한 작업 분배가 가능합니다.

오케스트레이터가 상황에 따라 어떤 에이전트에게 작업을 줄지 동적으로 결정할 수 있습니다. 보안 이슈가 심각하면 보안 에이전트에게 더 많은 작업을 할당하는 식입니다.

하지만 단점도 있습니다. 가장 큰 문제는 병목 현상입니다.

모든 통신이 오케스트레이터를 거쳐야 합니다. 오케스트레이터가 느리거나 오류가 나면 전체 시스템이 멈춥니다.

마치 지휘자가 쓰러지면 오케스트라 전체가 연주를 멈추는 것과 같습니다. 또한 오케스트레이터 자체도 복잡한 에이전트입니다.

작업을 분해하고, 적절한 에이전트를 선택하고, 결과를 검증하고, 실패를 처리해야 합니다. 오케스트레이터의 품질이 전체 시스템의 품질을 결정합니다.

위 코드에서 OrchestratorAgent 클래스를 보면 이런 흐름이 명확하게 드러납니다. orchestrate 메서드가 작업 분해, 할당, 검증, 종합의 전체 과정을 담당합니다.

WorkerAgent는 단순히 할당받은 작업을 수행할 뿐입니다. 실무에서 이 패턴은 워크플로우가 명확한 경우에 적합합니다.

예를 들어 CI/CD 파이프라인처럼 빌드, 테스트, 배포의 순서가 정해진 작업에 잘 맞습니다. 반면 창의적이고 탐색적인 작업에는 덜 적합할 수 있습니다.

실전 팁

💡 - 오케스트레이터의 로직을 단순하게 유지하여 병목을 최소화하세요

  • 에이전트 간 직접 통신이 필요 없는 독립적인 작업에 이 패턴을 사용하세요
  • 오케스트레이터 실패에 대비한 폴백 메커니즘을 구현하세요

4. Peer to Peer Swarm 패턴

김개발 씨가 Supervisor 패턴으로 시스템을 구축했는데, 예상치 못한 문제가 생겼습니다. 에이전트들이 서로 직접 협력해야 하는 상황이 자주 발생한 것입니다.

코드 에이전트가 테스트 에이전트에게 "이 부분 테스트해봤어?"라고 물어봐야 하는데, 매번 오케스트레이터를 거치니 너무 느렸습니다. 더 나은 방법이 없을까요?

Peer-to-Peer/Swarm 패턴은 에이전트들이 중앙 제어 없이 서로 직접 통신하며 협력하는 구조입니다. 마치 벌떼가 여왕벌의 세세한 지시 없이도 집을 짓는 것처럼, 각 에이전트가 자율적으로 행동하면서 전체 목표를 달성합니다.

유연하고 확장성이 좋지만, 수렴 조건을 잘 설계해야 합니다.

다음 코드를 살펴봅시다.

from typing import Set, Optional
import asyncio

class SwarmAgent:
    def __init__(self, agent_id: str, specialty: str):
        self.id = agent_id
        self.specialty = specialty
        self.peers: Set['SwarmAgent'] = set()
        self.shared_state = {}

    def connect_peer(self, peer: 'SwarmAgent'):
        self.peers.add(peer)
        peer.peers.add(self)  # 양방향 연결

    async def collaborate(self, task: str) -> str:
        # 자신이 처리할 수 있는 부분 처리
        my_result = await self.process(task)

        # 도움이 필요하면 동료에게 요청
        if self.needs_help(my_result):
            peer = self.find_capable_peer(task)
            if peer:
                peer_result = await peer.assist(task, my_result)
                my_result = self.merge_results(my_result, peer_result)

        # 수렴 조건 체크
        if self.is_converged(my_result):
            return my_result

        # 아직 완료 안 됐으면 다시 협력
        return await self.collaborate(task)

    def is_converged(self, result) -> bool:
        # 수렴 조건: 모든 테스트 통과, 목표 달성 등
        return result.get('complete', False)

자연에서 가장 효율적인 협업 사례 중 하나는 벌떼입니다. 수천 마리의 벌이 복잡한 벌집을 짓는데, 여왕벌이 "너는 저기 가서 육각형 만들어"라고 일일이 지시하지 않습니다.

각 벌이 자율적으로 행동하면서도 전체적으로는 완벽한 구조물이 만들어집니다. Peer-to-Peer 패턴 또는 Swarm 패턴은 이런 자연의 지혜를 멀티 에이전트 시스템에 적용한 것입니다.

중앙 제어자 없이 에이전트들이 서로 직접 소통하며 문제를 해결합니다. 김개발 씨의 문제 상황을 다시 생각해봅시다.

코드 에이전트가 함수를 수정했습니다. 테스트 에이전트에게 검증을 받고 싶습니다.

Supervisor 패턴에서는 이렇게 됩니다. 코드 에이전트가 오케스트레이터에게 보고하고, 오케스트레이터가 테스트 에이전트에게 전달하고, 테스트 에이전트가 결과를 오케스트레이터에게 보내고, 다시 코드 에이전트에게 전달됩니다.

네 단계나 거쳐야 합니다. Swarm 패턴에서는 간단합니다.

코드 에이전트가 테스트 에이전트에게 직접 물어봅니다. "이거 테스트해봐." 테스트 에이전트가 바로 답합니다.

"통과야" 또는 "여기 문제 있어." 두 단계로 끝납니다. 이 패턴의 핵심 개념은 자율성입니다.

각 에이전트가 스스로 판단합니다. 내가 이 작업을 할 수 있나?

도움이 필요한가? 누구에게 물어봐야 하나?

중앙에서 시키는 대로만 하는 게 아니라 능동적으로 행동합니다. 위 코드의 collaborate 메서드를 보면 이 흐름이 보입니다.

먼저 자신이 처리할 수 있는 부분을 처리합니다. 그 다음 needs_help로 도움이 필요한지 판단합니다.

필요하면 find_capable_peer로 적절한 동료를 찾아 직접 요청합니다. 하지만 여기서 매우 중요한 부분이 있습니다.

바로 수렴 조건입니다. is_converged 메서드를 주목하세요.

이게 없으면 에이전트들이 영원히 서로 작업을 주고받으며 무한 루프에 빠질 수 있습니다. 수렴 조건은 "언제 작업이 완료된 것으로 볼 것인가"를 정의합니다.

모든 테스트가 통과했을 때, 특정 품질 지표를 달성했을 때, 또는 최대 반복 횟수에 도달했을 때 등 명확한 기준이 필요합니다. 실무에서 Swarm 패턴은 탐색적 작업에 적합합니다.

예를 들어 버그를 찾는 작업을 생각해봅시다. 어디서 버그가 발생하는지 미리 알 수 없습니다.

여러 에이전트가 다양한 부분을 탐색하다가, 의심스러운 부분을 발견하면 다른 에이전트와 협력하여 검증합니다. 이런 유동적인 협업은 Swarm 패턴이 제격입니다.

반면 주의할 점도 있습니다. 첫째, 디버깅이 어렵습니다.

누가 누구에게 무엇을 요청했는지 추적하기 힘듭니다. 둘째, 예측 불가능성이 있습니다.

같은 입력에도 에이전트들의 협업 순서에 따라 다른 결과가 나올 수 있습니다. 김개발 씨는 두 패턴을 비교해보았습니다.

Supervisor는 예측 가능하고 제어하기 쉽지만 유연성이 떨어집니다. Swarm은 유연하고 효율적이지만 제어하기 어렵습니다.

정답은 없고, 상황에 맞게 선택하거나 두 패턴을 조합해서 사용해야 합니다.

실전 팁

💡 - 수렴 조건을 반드시 명확하게 정의하세요 (최대 반복 횟수, 품질 기준 등)

  • 에이전트 간 통신 로그를 남겨서 디버깅에 활용하세요
  • 무한 루프 방지를 위한 타임아웃을 설정하세요

5. Hierarchical 패턴

프로젝트가 점점 커지면서 김개발 씨는 새로운 고민에 빠졌습니다. 에이전트가 10개, 20개로 늘어나니 Supervisor 패턴으로는 오케스트레이터가 감당이 안 됩니다.

Swarm 패턴으로는 혼란스럽고요. 대기업은 CEO 혼자 모든 직원을 관리하지 않잖아요.

중간 관리자들이 있죠. 에이전트 시스템에도 이런 계층 구조를 적용할 수 없을까요?

Hierarchical 패턴은 전략, 계획, 실행의 계층 구조로 에이전트를 조직하는 방식입니다. 최상위 전략 에이전트가 큰 그림을 그리고, 중간 계획 에이전트가 세부 계획을 수립하며, 하위 실행 에이전트가 실제 작업을 수행합니다.

대규모 시스템에서 복잡성을 관리하기 좋지만, 계층 간 정보 손실에 주의해야 합니다.

다음 코드를 살펴봅시다.

from abc import ABC, abstractmethod
from typing import List

class Agent(ABC):
    @abstractmethod
    def process(self, input_data): pass

class StrategicAgent(Agent):
    """최상위: 전략 수립, 목표 정의"""
    def __init__(self, tactical_agents: List['TacticalAgent']):
        self.tactical_agents = tactical_agents

    def process(self, goal: str) -> dict:
        # 고수준 전략 수립
        strategy = self.define_strategy(goal)

        # 전략을 전술로 분해하여 위임
        results = {}
        for objective in strategy['objectives']:
            agent = self.select_tactical_agent(objective)
            results[objective] = agent.process(objective)

        return self.synthesize_strategy_results(results)

class TacticalAgent(Agent):
    """중간층: 계획 수립, 리소스 할당"""
    def __init__(self, execution_agents: List['ExecutionAgent']):
        self.execution_agents = execution_agents

    def process(self, objective: str) -> dict:
        # 세부 계획 수립
        plan = self.create_plan(objective)

        # 계획을 작업으로 분해하여 위임
        results = []
        for task in plan['tasks']:
            agent = self.assign_execution_agent(task)
            results.append(agent.process(task))

        return self.aggregate_results(results)

class ExecutionAgent(Agent):
    """최하위: 실제 작업 수행"""
    def process(self, task: str) -> str:
        # 구체적인 작업 수행 (코드 작성, API 호출 등)
        return self.execute_task(task)

대기업의 조직 구조를 생각해봅시다. CEO가 모든 직원에게 일일이 지시하지 않습니다.

CEO는 임원들에게 전략적 목표를 제시합니다. "올해 매출 20% 성장시키자." 임원들은 이를 팀장들에게 구체적인 계획으로 전달합니다.

"신규 고객 1000명 확보해야 해." 팀장들은 팀원들에게 실제 업무를 할당합니다. "이 마케팅 캠페인 진행해." Hierarchical 패턴은 이런 조직 구조를 멀티 에이전트 시스템에 적용한 것입니다.

에이전트들을 전략, 전술(계획), 실행의 세 계층으로 나눕니다. 전략 계층의 에이전트는 가장 추상적인 수준에서 일합니다.

"이 프로젝트의 보안을 강화해"라는 목표를 받으면, 이를 "인증 시스템 개선", "입력 검증 강화", "암호화 적용"같은 전략적 목표로 분해합니다. 코드 한 줄 작성하지 않습니다.

큰 그림만 그립니다. 전술 계층의 에이전트는 전략을 실행 가능한 계획으로 바꿉니다.

"인증 시스템 개선"이라는 목표를 받으면, "JWT 토큰 구현", "리프레시 토큰 로직 추가", "세션 만료 처리"같은 구체적인 작업 목록을 만듭니다. 아직 코드를 작성하지는 않지만, 무엇을 해야 하는지 명확하게 정의합니다.

실행 계층의 에이전트가 실제 작업을 수행합니다. "JWT 토큰 구현"이라는 작업을 받으면 실제로 코드를 작성하고, 테스트하고, 결과를 보고합니다.

이 패턴의 가장 큰 장점은 확장성입니다. 에이전트가 100개가 되어도 전략 에이전트는 5개의 전술 에이전트만 관리하면 됩니다.

각 전술 에이전트는 20개의 실행 에이전트를 관리합니다. 어떤 에이전트도 20개 이상을 직접 관리하지 않으니 복잡성이 분산됩니다.

또한 관심사의 분리가 명확합니다. 전략 에이전트는 "왜"에 집중합니다.

전술 에이전트는 "무엇을"에 집중합니다. 실행 에이전트는 "어떻게"에 집중합니다.

각 계층이 자기 역할에만 집중하니 전문성이 높아집니다. 위 코드를 보면 각 클래스가 같은 Agent 인터페이스를 구현하지만, 하는 일이 다릅니다.

StrategicAgent는 전략을 정의하고 결과를 종합합니다. TacticalAgent는 계획을 수립하고 결과를 집계합니다.

ExecutionAgent는 실제 작업을 수행합니다. 하지만 주의할 점도 있습니다.

가장 큰 위험은 정보 손실입니다. 전화 게임을 생각해보세요.

처음 사람이 "사과"라고 했는데 열 번째 사람에게는 "자두"로 전달될 수 있습니다. 계층을 거칠 때마다 정보가 왜곡되거나 손실될 수 있습니다.

이를 방지하려면 각 계층 간 인터페이스를 명확하게 정의해야 합니다. 전략에서 전술로 넘어갈 때 어떤 정보가 필요한지, 실행 결과를 전술로 보고할 때 어떤 형식이어야 하는지 미리 규격화해야 합니다.

김개발 씨는 Hierarchical 패턴을 적용하여 시스템을 재설계했습니다. 기존에 하나의 오케스트레이터가 15개 에이전트를 관리하던 것을, 1개 전략 에이전트, 3개 전술 에이전트, 15개 실행 에이전트의 계층 구조로 바꿨습니다.

시스템이 훨씬 관리하기 쉬워졌습니다.

실전 팁

💡 - 계층 간 인터페이스를 명확하게 정의하여 정보 손실을 방지하세요

  • 중간 계층의 에이전트도 충분히 똑똑해야 합니다, 단순 전달자가 되면 안 됩니다
  • 실행 계층의 결과가 전략 계층까지 제대로 올라가는지 모니터링하세요

6. 병렬화와 전문화

김개발 씨의 멀티 에이전트 시스템이 안정화되었습니다. 그런데 새로운 욕심이 생겼습니다.

"더 빠르게 할 수는 없을까?" 현재 시스템은 작업을 순차적으로 처리합니다. 보안 분석이 끝나야 코드 수정을 시작하고, 코드 수정이 끝나야 테스트를 시작합니다.

동시에 처리할 수 있는 작업은 동시에 하면 훨씬 빠르지 않을까요?

병렬화는 독립적인 작업을 여러 에이전트가 동시에 처리하는 기법이고, 전문화는 각 에이전트가 특정 도메인에 최적화된 설정을 갖는 것입니다. 병렬화로 처리 시간을 단축하고, 전문화로 각 도메인에서 최고의 품질을 얻을 수 있습니다.

이 두 가지를 조합하면 속도와 품질을 모두 잡을 수 있습니다.

다음 코드를 살펴봅시다.

import asyncio
from typing import Dict, Any

class SpecializedAgent:
    def __init__(self, domain: str, model: str, system_prompt: str):
        self.domain = domain
        self.model = model  # 도메인별 최적 모델
        self.system_prompt = system_prompt  # 전문화된 프롬프트

    async def analyze(self, code: str) -> Dict[str, Any]:
        # 전문 분야에 특화된 분석 수행
        return await self.llm_call(self.system_prompt, code)

class ParallelOrchestrator:
    def __init__(self):
        # 각 도메인에 전문화된 에이전트들
        self.agents = {
            "security": SpecializedAgent(
                "security", "gpt-4", "You are a security expert..."
            ),
            "performance": SpecializedAgent(
                "performance", "gpt-4", "You are a performance engineer..."
            ),
            "style": SpecializedAgent(
                "style", "gpt-3.5-turbo", "You check code style..."  # 저렴한 모델
            ),
        }

    async def analyze_code(self, code: str) -> Dict[str, Any]:
        # 모든 분석을 동시에 실행 (병렬화)
        tasks = [
            agent.analyze(code)
            for agent in self.agents.values()
        ]

        # 모든 결과를 기다림
        results = await asyncio.gather(*tasks)

        return {
            domain: result
            for domain, result in zip(self.agents.keys(), results)
        }

레스토랑 주방을 상상해봅시다. 훌륭한 레스토랑에서는 셰프 한 명이 모든 요리를 순서대로 만들지 않습니다.

수셰프, 소스 담당, 디저트 담당이 각자의 요리를 동시에 준비합니다. 그리고 각 담당자는 자기 분야의 전문가입니다.

이렇게 해야 손님에게 빠르고 맛있는 식사를 제공할 수 있습니다. 멀티 에이전트 시스템의 병렬화전문화도 같은 원리입니다.

먼저 병렬화를 살펴봅시다. 코드 리뷰 시스템을 예로 들어보겠습니다.

보안 검토, 성능 검토, 스타일 검토가 필요합니다. 이 세 가지 작업은 서로 독립적입니다.

보안 검토 결과가 성능 검토에 영향을 주지 않습니다. 그렇다면 왜 순차적으로 처리해야 할까요?

위 코드의 analyze_code 메서드를 보세요. asyncio.gather를 사용하여 모든 분석을 동시에 실행합니다.

세 가지 분석이 각각 10초씩 걸린다면, 순차 실행시 30초, 병렬 실행시 10초입니다. 3배 빨라집니다.

다음으로 전문화입니다. 모든 에이전트가 같은 설정을 사용할 필요가 없습니다.

보안 분석은 복잡하고 중요합니다. 가장 똑똑한 GPT-4를 사용합니다.

시스템 프롬프트도 보안 전문가의 관점에서 작성합니다. 반면 스타일 검토는 상대적으로 단순합니다.

들여쓰기가 맞는지, 네이밍 컨벤션을 따르는지 확인하는 정도입니다. 저렴한 GPT-3.5-turbo로 충분합니다.

비용도 아끼고 속도도 빨라집니다. SpecializedAgent 클래스를 보면 각 에이전트가 domain, model, system_prompt를 별도로 가집니다.

보안 에이전트는 보안 전문가처럼 생각하도록 프롬프트가 작성되어 있습니다. 성능 에이전트는 성능 엔지니어처럼 생각합니다.

같은 코드를 봐도 각자 다른 관점에서 분석합니다. 실무에서 병렬화를 적용할 때 주의할 점이 있습니다.

모든 작업을 병렬화할 수는 없습니다. 의존성이 있는 작업은 순차적으로 실행해야 합니다.

예를 들어 코드를 수정한 후에 테스트를 실행해야 합니다. 수정 전에 테스트해봐야 의미가 없습니다.

따라서 작업을 분석할 때 의존성 그래프를 그려보는 것이 좋습니다. A 작업이 끝나야 B를 시작할 수 있는지, 아니면 독립적으로 실행 가능한지 파악합니다.

독립적인 작업끼리는 병렬로, 의존적인 작업은 순차로 실행합니다. 전문화에서는 과도한 세분화를 주의해야 합니다.

에이전트를 너무 세분화하면 에이전트 간 통신 오버헤드가 커집니다. 반대로 너무 일반적이면 전문성이 떨어집니다.

적절한 균형점을 찾아야 합니다. 김개발 씨는 자신의 시스템에 병렬화와 전문화를 적용했습니다.

코드 리뷰 시간이 절반으로 줄었고, 각 분야의 검토 품질도 높아졌습니다. 특히 보안 에이전트가 이전에는 놓쳤던 취약점을 발견하기 시작했습니다.

전문화된 프롬프트 덕분이었습니다.

실전 팁

💡 - 작업의 의존성을 분석하여 병렬화 가능한 부분을 식별하세요

  • 중요도와 복잡도에 따라 에이전트별로 다른 모델을 사용하세요
  • 각 에이전트의 시스템 프롬프트를 도메인 전문가 관점에서 작성하세요

7. X to Book 시스템 분석

김개발 씨가 이론을 충분히 배웠으니 이제 실습할 차례입니다. 박시니어 씨가 흥미로운 프로젝트를 보여줬습니다.

"X-to-Book이라는 시스템인데, 트위터 스레드나 블로그 글을 책 한 권 분량으로 확장하는 멀티 에이전트 시스템이야." 실제 프로덕션에서 운영 중인 이 시스템을 분석하며 배운 내용을 정리해봅시다.

X-to-Book은 짧은 콘텐츠를 책 분량으로 확장하는 멀티 에이전트 시스템의 실제 사례입니다. 이 시스템은 Hierarchical 패턴과 병렬화를 조합하여 사용합니다.

기획 에이전트가 책의 구조를 설계하고, 장별 작성 에이전트가 병렬로 내용을 생성하며, 편집 에이전트가 일관성을 검토합니다. 이를 통해 멀티 에이전트 패턴의 실제 적용 방법을 이해할 수 있습니다.

다음 코드를 살펴봅시다.

from typing import List, Dict
import asyncio

class XToBookSystem:
    """X-to-Book 멀티 에이전트 시스템"""

    def __init__(self):
        self.planner = PlannerAgent()      # 전략 계층
        self.writers = [WriterAgent(i) for i in range(5)]  # 실행 계층
        self.editor = EditorAgent()         # 품질 검증

    async def generate_book(self, source_content: str) -> Dict:
        # 1. 기획 단계 (전략)
        book_plan = await self.planner.create_outline(source_content)
        # 결과: {"title": "...", "chapters": [...], "target_pages": 200}

        # 2. 집필 단계 (병렬 실행)
        chapter_tasks = []
        for i, chapter in enumerate(book_plan['chapters']):
            writer = self.writers[i % len(self.writers)]
            chapter_tasks.append(
                writer.write_chapter(chapter, book_plan['context'])
            )

        chapters = await asyncio.gather(*chapter_tasks)  # 동시 집필

        # 3. 편집 단계 (품질 검증)
        edited_book = await self.editor.review_and_edit({
            'plan': book_plan,
            'chapters': chapters
        })

        return edited_book

class PlannerAgent:
    """책 구조 기획 - 목차, 분량, 톤앤매너 설계"""
    async def create_outline(self, content: str) -> Dict:
        # 원본 분석 후 책 구조 설계
        return {"title": "...", "chapters": [...], "context": {...}}

class WriterAgent:
    """장별 집필 - 각 챕터 내용 작성"""
    def __init__(self, writer_id: int):
        self.id = writer_id

    async def write_chapter(self, chapter_info: Dict, context: Dict) -> str:
        # 컨텍스트 기반 챕터 집필
        return f"Chapter content..."

박시니어 씨가 화면을 공유하며 X-to-Book 시스템의 아키텍처를 보여줬습니다. "이 시스템은 트위터의 긴 스레드나 블로그 시리즈를 입력받아서 200페이지 분량의 책을 자동으로 생성해." 김개발 씨의 눈이 반짝였습니다.

"와, 그런 게 가능해요? 어떻게 구현된 거예요?" 시스템은 크게 세 단계로 동작합니다.

기획, 집필, 편집입니다. 이것은 우리가 배운 Hierarchical 패턴의 전략-계획-실행 구조와 일맥상통합니다.

첫 번째 단계인 기획을 봅시다. PlannerAgent가 원본 콘텐츠를 분석합니다.

트위터 스레드 100개를 입력하면, 이것을 어떤 책으로 만들 수 있을지 구상합니다. 목차를 설계하고, 각 장의 분량을 정하고, 책 전체의 톤앤매너를 결정합니다.

코드 한 줄 작성하지 않습니다, 아니 이 경우는 본문 한 줄 작성하지 않습니다. 오직 전략만 수립합니다.

두 번째 단계는 집필입니다. 여기서 병렬화가 빛을 발합니다.

책이 10개 장으로 구성된다고 해봅시다. 5명의 WriterAgent가 2개 장씩 맡아서 동시에 집필합니다.

한 명이 순차적으로 10개 장을 쓰면 10시간 걸릴 작업이, 5명이 병렬로 하면 2시간에 끝납니다. 위 코드의 generate_book 메서드에서 asyncio.gather를 사용하는 부분을 주목하세요.

chapter_tasks 리스트에 모든 집필 작업을 담고, gather로 동시에 실행합니다. 이것이 바로 실전에서의 병렬화 적용입니다.

세 번째 단계는 편집입니다. 여러 WriterAgent가 각자 집필했기 때문에 스타일이 조금씩 다를 수 있습니다.

1장에서 "독자 여러분"이라고 했는데 5장에서 "당신"이라고 하면 일관성이 깨집니다. EditorAgent가 전체를 검토하며 이런 불일치를 잡아냅니다.

여기서 컨텍스트 격리의 중요성이 드러납니다. 각 WriterAgent는 자기가 맡은 장과 전체 책의 컨텍스트만 알면 됩니다.

다른 장의 상세 내용을 알 필요가 없습니다. 이렇게 해야 토큰을 효율적으로 사용할 수 있습니다.

하지만 완전히 격리하면 일관성 문제가 생깁니다. 그래서 book_plan['context']라는 공유 컨텍스트를 만들어서 모든 WriterAgent에게 전달합니다.

책의 제목, 대상 독자, 톤앤매너, 핵심 용어 정의 등이 여기에 포함됩니다. 필요한 만큼만 공유하는 것입니다.

이 시스템의 토큰 경제학도 고려되어 있습니다. PlannerAgent는 복잡한 의사결정을 해야 하므로 가장 강력한 모델을 사용합니다.

WriterAgent들은 창작 작업이므로 중간 수준의 모델로도 충분합니다. EditorAgent는 비교와 수정 작업이므로 역시 강력한 모델이 필요합니다.

김개발 씨가 질문했습니다. "만약 한 장의 집필이 실패하면 어떻게 되나요?" 좋은 질문입니다.

이 시스템은 장애 격리도 고려했습니다. 3번 장 집필이 실패해도 다른 장에는 영향이 없습니다.

3번 장만 재시도하면 됩니다. 모놀리식 접근이었다면 전체를 처음부터 다시 해야 했을 것입니다.

박시니어 씨가 정리했습니다. "이 시스템은 우리가 배운 모든 패턴을 조합해서 사용해.

Hierarchical 구조로 복잡성을 관리하고, 병렬화로 속도를 높이고, 컨텍스트 격리로 토큰을 절약하고, 전문화로 품질을 올렸지."

실전 팁

💡 - 실제 시스템에서는 여러 패턴을 조합하여 사용하세요

  • 공유 컨텍스트는 최소한으로 유지하되, 일관성에 필요한 정보는 반드시 포함하세요
  • 각 단계의 실패를 독립적으로 처리할 수 있도록 설계하세요

이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!

#AI#MultiAgent#LLM#Architecture#Orchestration#AI Engineering

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.

함께 보면 좋은 카드 뉴스

클라우드 보안 실전 완벽 가이드

AWS, Docker, Kubernetes 환경에서 보안을 자동화하고 취약점을 사전에 탐지하는 방법을 알아봅니다. 초급 개발자도 바로 적용할 수 있는 실전 보안 점검 스크립트와 CI/CD 파이프라인 보안 구축 방법을 다룹니다.

Memory Systems 에이전트 메모리 아키텍처 완벽 가이드

AI 에이전트가 정보를 기억하고 활용하는 메모리 시스템의 핵심 아키텍처를 다룹니다. 벡터 스토어의 한계부터 Knowledge Graph, Temporal Knowledge Graph까지 단계별로 이해할 수 있습니다.

Phase 5 취약점 발굴과 분석 완벽 가이드

보안 전문가가 되기 위한 취약점 발굴의 핵심 기법을 다룹니다. 코드 리뷰부터 퍼징, 바이너리 분석까지 실무에서 바로 활용할 수 있는 기술을 초급자 눈높이에 맞춰 설명합니다.

침해사고 대응 실무 완벽 가이드

보안 침해사고가 발생했을 때 초기 대응부터 디지털 포렌식, 침해 지표 추출까지 실무에서 바로 활용할 수 있는 파이썬 기반 대응 기법을 다룹니다. 초급 개발자도 이해할 수 있도록 실제 시나리오와 함께 설명합니다.

Context Compression 컨텍스트 압축 전략 완벽 가이드

LLM 애플리케이션에서 컨텍스트 윈도우를 효율적으로 관리하는 압축 전략을 다룹니다. Anchored Summarization부터 프로브 기반 평가까지, 토큰 비용을 최적화하면서 정보 품질을 유지하는 핵심 기법들을 실무 관점에서 설명합니다.