단일 LLM 에이전트의 한계를 넘는 Google ADK 멀티에이전트 패턴 8가지
AI 에이전트 하나로 모든 걸 해결하려다가 막막했던 경험, 다들 한 번쯤 있을 겁니다. 저도 처음에 단일 LLM 에이전트에 프롬프트를 잔뜩 욱여넣었다가 컨텍스트 한계에 부딪히고, 응답 품질이 들쑥날쑥해지는 걸 경험했거든요. 결국 "이건 구조의 문제구나" 싶어서 멀티에이전트 아키텍처를 본격적으로 파고들게 됐습니다.
Google은 Agent Development Kit(ADK)를 통해 복잡한 AI 워크플로를 설계하기 위한 8가지 멀티에이전트 패턴을 체계화했습니다. 단순히 "에이전트 여러 개 쓰기"가 아니라, 역할을 명확히 나누고 조합하는 AI 마이크로서비스 아키텍처의 청사진입니다. 각 패턴마다 ADK Python 코드 예시를 함께 제시하니, 어떤 상황에 어떤 패턴을 골라야 하는지 판단 기준을 세우고 직접 실험해볼 수 있는 코드까지 가져갈 수 있습니다.
코드는 전부 Python 기반입니다. ADK가 Python(과 JS)으로 제공되기 때문인데, Java나 Go 배경이라면 패턴의 개념과 트레이드오프를 아키텍처 관점으로 읽어도 충분히 활용할 수 있습니다. LLM을 이미 사용해본 경험이 있는 개발자라면 바로 따라올 수 있는 깊이로 작성했습니다.
핵심 개념
세 가지 기초 실행 모델
8가지 패턴에 들어가기 전에, ADK의 세 가지 기초 실행 모델을 먼저 짚고 가면 나머지가 훨씬 자연스럽게 들어옵니다.
| 실행 모델 | ADK 클래스 | 특징 |
|---|---|---|
| 순차 실행 | SequentialAgent |
A → B → C, 앞 결과를 다음이 이어받음 |
| 반복 실행 | LoopAgent |
종료 조건까지 하위 에이전트를 반복 |
| 병렬 실행 | ParallelAgent |
여러 에이전트를 동시에 실행 |
이 세 가지가 레고 블록이라면, 8가지 패턴은 그 블록으로 만들 수 있는 구조물입니다.
from google.adk.agents import SequentialAgent, ParallelAgent, LoopAgent, LlmAgent
data_collector = LlmAgent(name="DataCollector", model="gemini-2.0-flash")
analyzer = LlmAgent(name="Analyzer", model="gemini-2.0-flash")
reporter = LlmAgent(name="Reporter", model="gemini-2.0-flash")
pipeline = SequentialAgent(
name="DataPipeline",
sub_agents=[data_collector, analyzer, reporter]
)8가지 패턴 전체 지도
| # | 패턴명 | 핵심 구조 | 언제 쓰나 |
|---|---|---|---|
| 1 | Sequential Pipeline | A → B → C 순차 실행 | 결정적 데이터 변환 흐름 |
| 2 | Coordinator / Dispatcher | 중앙 LLM이 전문 에이전트로 라우팅 | 다양한 입력 유형 처리 |
| 3 | Parallel Fan-Out / Gather | 동시 실행 후 Synthesizer가 집계 | 독립적인 병렬 작업 |
| 4 | Hierarchical Decomposition | 상위가 목표를 하위 작업으로 분해 | 초대형 복잡 작업 |
| 5 | Generator-Critic | 생성 → 검토 → 반복 | 품질 보증이 핵심인 결과물 |
| 6 | Human-in-the-Loop | 고위험 작업 시 인간 승인 대기 | 비가역 작업, 컴플라이언스 |
| 7 | Loop Agent | 종료 조건 충족까지 반복 | 상태 기반 반복 작업 |
| 8 | Custom / Agentic Workflow | LLM이 런타임에 동적 라우팅 결정 | 예측 불가능한 복잡 흐름 |
AI 마이크로서비스 아키텍처: 각 에이전트가 단일 책임을 갖고, 독립적으로 테스트·교체 가능한 구조. 기존 MSA 철학을 AI 에이전트 레이어에 적용한 것입니다.
패턴 간의 관계를 의식하면서 읽으면 선택 감각이 훨씬 빨리 생깁니다. 특히 5번과 7번처럼 서로 닮은 패턴의 차이를 짚어가며 살펴봅니다.
패턴 1: Sequential Pipeline
가장 직관적인 패턴입니다. 앞 에이전트의 출력이 다음 에이전트의 입력이 되는 파이프라인 구조인데, 솔직히 이게 제일 먼저 손이 가는 패턴이기도 합니다. 데이터 수집 → 분석 → 리포트 생성처럼 순서가 명확할 때 빛을 발합니다.
from google.adk.agents import SequentialAgent, LlmAgent
research_agent = LlmAgent(
name="Researcher",
model="gemini-2.0-flash",
instruction="주어진 주제에 대한 핵심 데이터를 수집하세요."
)
summary_agent = LlmAgent(
name="Summarizer",
model="gemini-2.0-flash",
instruction="이전 에이전트의 조사 결과를 3문단으로 요약하세요."
)
fact_check_agent = LlmAgent(
name="FactChecker",
model="gemini-2.0-flash",
instruction="요약된 내용에서 오류나 불확실한 주장을 검토하세요."
)
pipeline = SequentialAgent(
name="ResearchPipeline",
sub_agents=[research_agent, summary_agent, fact_check_agent]
)한 가지 함정을 짚자면, 앞 에이전트가 실패하면 뒤 에이전트 전체가 의미 없는 결과를 내뱉습니다. 파이프라인의 각 단계에 검증 로직 없이 그냥 흘려보내면, 조용히 잘못된 결과가 누적되는 경우가 생기니 주의가 필요합니다.
패턴 2: Coordinator / Dispatcher
중앙 LLM이 사용자 의도를 분석해서 전문 에이전트에게 라우팅하는 패턴입니다. 고객 지원 시스템에서 "결제 문의냐, 기술 지원이냐"를 판단하는 상황을 떠올리면 됩니다.
from google.adk.agents import LlmAgent
billing_agent = LlmAgent(
name="BillingSpecialist",
model="gemini-2.0-flash",
instruction="결제, 환불, 구독 관련 문의를 처리합니다."
)
tech_agent = LlmAgent(
name="TechSupport",
model="gemini-2.0-flash",
instruction="기술적 오류, 설치, 연동 관련 문의를 처리합니다."
)
coordinator = LlmAgent(
name="Coordinator",
model="gemini-2.0-flash",
instruction="""사용자 문의를 분석하고 적절한 전문가에게 라우팅하세요.
- 결제/환불/구독 → BillingSpecialist
- 기술 오류/설치/연동 → TechSupport""",
sub_agents=[billing_agent, tech_agent]
)Coordinator의 라우팅 메커니즘:
sub_agents를 전달받은LlmAgent에는 ADK가 각 하위 에이전트로 전환할 수 있는 "에이전트 이전(transfer)" 도구를 자동으로 등록합니다. LLM은 사용자 입력을 분석한 뒤 이 도구들 중 하나를 tool call 형태로 호출하는 방식으로 라우팅 결정을 내립니다. 분기 조건이 코드가 아닌 LLM의 판단에 있기 때문에, 사전에 정의하지 않은 입력 유형에도 유연하게 대응할 수 있습니다.
저도 처음엔 "LLM이 알아서 라우팅한다고?" 싶었는데, 실제로 써보면 의외로 정확합니다. 다만 인스트럭션에서 각 전문 에이전트의 역할 경계를 명확하게 기술하지 않으면 엉뚱한 곳으로 보내는 경우가 생기더라고요.
패턴 3: Parallel Fan-Out / Gather
독립적인 작업을 동시에 처리하고 결과를 하나로 합치는 패턴입니다. 여러 데이터 소스를 동시에 조회하거나, 여러 언어로 동시 번역할 때 지연 시간을 획기적으로 줄일 수 있습니다.
from google.adk.agents import SequentialAgent, ParallelAgent, LlmAgent
doc_search = LlmAgent(
name="DocSearch",
model="gemini-2.0-flash",
instruction="공식 문서에서 관련 내용을 검색합니다."
)
history_search = LlmAgent(
name="HistorySearch",
model="gemini-2.0-flash",
instruction="사용자 이력에서 관련 케이스를 검색합니다."
)
parallel_search = ParallelAgent(
name="ParallelSearch",
sub_agents=[doc_search, history_search]
)
synthesizer = LlmAgent(
name="Synthesizer",
model="gemini-2.0-flash",
instruction="병렬 검색 결과를 통합해 최적의 답변을 생성합니다."
)
# ParallelAgent 결과를 Synthesizer로 흘려보내는 순차 래퍼가 필수
search_and_synthesize = SequentialAgent(
name="SearchAndSynthesize",
sub_agents=[parallel_search, synthesizer]
)주의할 점이 있습니다. ParallelAgent만 정의하고 synthesizer를 뒤에 연결하지 않으면 병렬 검색 결과가 집계되지 않습니다. 반드시 SequentialAgent로 감싸서 결과가 다음 에이전트로 흘러가게 해야 합니다. 이게 빠진 채로 실행하면 synthesizer가 아무 입력 없이 혼자 동작하는 상황이 됩니다.
패턴 4: Hierarchical Decomposition
단일 컨텍스트 윈도에 담을 수 없는 복잡한 작업을 상위 에이전트가 하위 작업으로 분해해서 위임하는 패턴입니다. 소프트웨어 프로젝트 전체를 설계하거나, 수십 페이지짜리 법률 문서를 분석할 때처럼 대규모 목표를 처리할 때 유용합니다.
from google.adk.agents import LlmAgent
frontend_agent = LlmAgent(
name="FrontendArchitect",
model="gemini-2.0-flash",
instruction="프론트엔드 아키텍처와 UI 컴포넌트 설계를 담당합니다."
)
backend_agent = LlmAgent(
name="BackendArchitect",
model="gemini-2.0-flash",
instruction="백엔드 API 설계와 데이터베이스 스키마를 담당합니다."
)
infra_agent = LlmAgent(
name="InfraEngineer",
model="gemini-2.0-flash",
instruction="배포 인프라와 CI/CD 파이프라인 설계를 담당합니다."
)
project_manager = LlmAgent(
name="ProjectManager",
model="gemini-2.0-flash",
instruction="""프로젝트 요구사항을 분석하고 아키텍처 설계 작업을 분배합니다.
각 전문가에게 적절한 하위 작업을 위임하고 결과를 통합하세요.
- UI/UX 관련 → FrontendArchitect
- API/DB 관련 → BackendArchitect
- 인프라/배포 관련 → InfraEngineer""",
sub_agents=[frontend_agent, backend_agent, infra_agent]
)이 패턴은 처음 도입할 때 상위 에이전트의 분해 기준을 명확히 잡지 않으면 "관리자 에이전트가 모든 결정을 직접 내리는" 패턴으로 변질되기 쉽습니다. 하위 에이전트들이 실질적인 역할 없이 메아리만 반복하게 되는 상황이 생기더라고요. 각 하위 에이전트의 권한 범위와 출력 형식을 인스트럭션에서 명시적으로 정의하는 게 핵심입니다.
패턴 5: Generator-Critic (Iterative Refinement)
품질이 핵심인 결과물에 씁니다. Generator가 초안을 만들고, Critic이 검토하고, 기준을 통과할 때까지 반복하는 구조인데, 실무에서 코드 리뷰나 문서 품질 보증에 정말 잘 맞습니다.
from google.adk.agents import LoopAgent, LlmAgent
generator = LlmAgent(
name="Generator",
model="gemini-2.0-flash",
instruction="기술 문서 초안을 작성합니다. 이전 피드백이 있다면 반영하세요."
)
critic = LlmAgent(
name="Critic",
model="gemini-2.0-flash",
instruction="""문서를 검토하고 피드백을 제공하세요.
모든 기준을 충족하면 응답에 'APPROVED'를 포함하고 escalate 플래그를 설정하세요.
기준: 정확성, 명확성, 완전성"""
)
refinement_loop = LoopAgent(
name="RefinementLoop",
sub_agents=[generator, critic],
max_iterations=5
)루프 종료 메커니즘: ADK의
LoopAgent는 하위 에이전트가 세션 상태에escalate: true를 설정하면 루프를 종료합니다. 실제 구현에서는 Critic 에이전트가 'APPROVED'를 출력할 때 이 플래그를 함께 설정하도록 인스트럭션을 구성하거나,after_agent_callback으로 출력을 파싱해서 플래그를 직접 세팅하는 방식을 사용합니다.max_iterations는 Critic이 절대 승인하지 않는 극단적인 상황을 대비한 안전망입니다.
패턴 6: Human-in-the-Loop
비가역적이거나 고위험 작업에서 자동화를 잠시 멈추고 인간의 승인을 받는 패턴입니다. 금융 거래 실행, 대용량 데이터 삭제, 외부 시스템으로의 메시지 발송 같은 상황에서 안전망 역할을 합니다.
from google.adk.agents import LlmAgent
from google.adk.tools import FunctionTool
def request_human_approval(trade_details: dict) -> dict:
"""
개념 설명용 단순화 버전.
실제 구현에서는 아래 흐름처럼 외부 채널을 활용합니다:
[실무 패턴 pseudocode]
approval_id = generate_uuid()
slack_client.chat_postMessage(
channel="#trade-approvals",
blocks=[
{"type": "section", "text": f"거래 승인 요청: {trade_details}"},
{"type": "actions", "elements": [
{"type": "button", "text": "승인", "value": f"approve:{approval_id}"},
{"type": "button", "text": "거부", "value": f"reject:{approval_id}"}
]}
]
)
result = approval_store.wait_for_response(approval_id, timeout_sec=3600)
return {"approved": result.approved, "approver": result.user}
"""
print(f"[승인 요청] 거래 내용: {trade_details}")
approval = input("승인하시겠습니까? (yes/no): ")
return {"approved": approval.lower() == "yes", "details": trade_details}
approval_tool = FunctionTool(func=request_human_approval)
approval_agent = LlmAgent(
name="ApprovalGateway",
model="gemini-2.0-flash",
instruction="거래 추천안을 사용자에게 제시하고 승인을 요청합니다. 미승인 시 처리를 중단합니다.",
tools=[approval_tool]
)이 패턴은 처음 도입할 때 승인 기준을 명확히 잡지 않으면 결국 아무도 확인하지 않는 버튼이 됩니다. "금액이 얼마 이상일 때", "어떤 계정에 영향을 미칠 때"처럼 트리거 임계값을 처음부터 명문화하는 게 중요합니다.
패턴 7: Loop Agent
종료 조건을 충족할 때까지 하위 에이전트 집합을 반복 실행하는 패턴입니다. 패턴 5(Generator-Critic)이 LoopAgent의 특수한 응용 케이스라면, Loop Agent 패턴 자체는 더 범용적인 구조입니다.
from google.adk.agents import LoopAgent, LlmAgent
# 웹 크롤링 시나리오: 수집 완료까지 반복
data_fetcher = LlmAgent(
name="DataFetcher",
model="gemini-2.0-flash",
instruction="""다음 페이지의 데이터를 수집하세요.
더 이상 수집할 데이터가 없으면 세션 상태에 escalate 플래그를 설정하세요."""
)
data_processor = LlmAgent(
name="DataProcessor",
model="gemini-2.0-flash",
instruction="수집된 데이터를 정제하고 저장 형식으로 변환합니다."
)
crawl_loop = LoopAgent(
name="CrawlLoop",
sub_agents=[data_fetcher, data_processor],
max_iterations=100
)Generator-Critic과의 차이를 명확히 짚으면, Generator-Critic은 "품질 기준 통과"라는 단일 목표를 향해 반복하는 좁은 패턴입니다. Loop Agent는 "상태 기반 종료 조건"이 있는 모든 반복 작업에 쓸 수 있는 더 넓은 개념으로, 크롤링, 대용량 배치 처리, 재시도 로직이 Loop Agent의 영역입니다. 두 패턴이 중복처럼 보일 수 있는데, 실제로는 적용 범위의 차이입니다.
패턴 8: Custom / Agentic Workflow
LLM이 런타임에 직접 라우팅과 도구 조합을 결정하는 가장 유연한 패턴입니다. 앞의 7가지가 사전에 정의된 구조라면, 이 패턴은 LLM이 스스로 워크플로를 구성합니다.
from google.adk.agents import LlmAgent
from google.adk.tools import FunctionTool
def search_documentation(query: str) -> str:
return f"'{query}'에 대한 문서 검색 결과..."
def run_code_analysis(code: str) -> str:
return f"코드 분석 결과..."
def create_ticket(title: str, description: str) -> str:
return f"티켓 생성 완료: {title}"
# LLM이 런타임에 도구 조합과 순서를 스스로 결정
agentic_agent = LlmAgent(
name="AgenticWorkflow",
model="gemini-2.0-flash",
instruction="""사용자 요청을 분석하고 필요한 도구를 조합해서 처리하세요.
문제를 진단하고, 해결책을 찾고, 필요하면 티켓을 생성하는 등
상황에 따라 판단해서 진행하면 됩니다.""",
tools=[
FunctionTool(func=search_documentation),
FunctionTool(func=run_code_analysis),
FunctionTool(func=create_ticket)
]
)솔직히 이 패턴은 처음에 "LLM이 알아서 다 결정하겠지"라고 생각했다가 디버깅 지옥을 경험했습니다. 어떤 도구를 왜 어떤 순서로 호출했는지 추적하기가 다른 패턴보다 훨씬 어렵거든요. 완전히 예측 불가능한 복잡한 흐름에만 제한적으로 적용하고, 가능하면 앞의 구조화된 패턴들을 먼저 검토해보는 것을 권장합니다.
실전 적용
예시 1: 고객 지원 시스템 — 패턴 복합 조합
현실에서는 패턴을 단독으로 쓰는 경우보다 조합하는 경우가 훨씬 많습니다. 실제 고객 지원 시스템을 구성한다면 이런 식으로 네 가지 패턴을 엮을 수 있습니다.
from google.adk.agents import SequentialAgent, ParallelAgent, LoopAgent, LlmAgent
# 병렬 검색 (Pattern 3: Parallel Fan-Out)
doc_searcher = LlmAgent(name="DocSearcher", model="gemini-2.0-flash",
instruction="공식 문서에서 해결책을 검색합니다.")
history_searcher = LlmAgent(name="HistorySearcher", model="gemini-2.0-flash",
instruction="유사 사례 이력을 검색합니다.")
parallel_research = ParallelAgent(
name="ParallelResearch",
sub_agents=[doc_searcher, history_searcher]
)
# 품질 보증 루프 (Pattern 5: Generator-Critic)
answer_generator = LlmAgent(name="AnswerGenerator", model="gemini-2.0-flash",
instruction="검색 결과를 바탕으로 고객 답변을 생성합니다.")
tone_critic = LlmAgent(name="ToneCritic", model="gemini-2.0-flash",
instruction="""답변의 톤과 정확성을 검토합니다.
기준 통과 시 'APPROVED', 미통과 시 구체적 피드백을 제공합니다.""")
quality_loop = LoopAgent(
name="QualityLoop",
sub_agents=[answer_generator, tone_critic],
max_iterations=3
)
# 기술 지원 전체 파이프라인 (Pattern 1: Sequential)
tech_support_pipeline = SequentialAgent(
name="TechSupportPipeline",
sub_agents=[parallel_research, quality_loop]
)
# 최상위 라우터 (Pattern 2: Coordinator)
billing_specialist = LlmAgent(name="BillingSpecialist", model="gemini-2.0-flash",
instruction="결제 및 구독 관련 문의를 전담합니다.")
support_coordinator = LlmAgent(
name="SupportCoordinator",
model="gemini-2.0-flash",
instruction="""고객 문의 유형을 분석해 적절한 팀으로 라우팅합니다.
- 결제/환불/청구 → BillingSpecialist
- 기술 문제/오류/사용법 → TechSupportPipeline""",
sub_agents=[billing_specialist, tech_support_pipeline]
)| 구성 요소 | 사용 패턴 | 역할 |
|---|---|---|
SupportCoordinator |
Coordinator | 의도 분석 후 라우팅 |
ParallelResearch |
Parallel Fan-Out | 문서·이력 동시 검색 |
QualityLoop |
Generator-Critic | 답변 품질 보증 |
TechSupportPipeline |
Sequential | 기술 지원 흐름 조율 |
예시 2: 금융 거래 처리 — Human-in-the-Loop 포함
실제 매매 실행처럼 비가역적인 작업에는 Human-in-the-Loop가 필수입니다. ADK에서는 콜백 함수로 인간 승인 단계를 파이프라인 중간에 삽입할 수 있습니다.
from google.adk.agents import SequentialAgent, LlmAgent
from google.adk.tools import FunctionTool
def request_human_approval(trade_details: dict) -> dict:
# 실제 구현: Slack 웹훅이나 이메일로 승인 요청 후 비동기 폴링
print(f"[승인 요청] 거래 내용: {trade_details}")
approval = input("승인하시겠습니까? (yes/no): ")
return {"approved": approval.lower() == "yes", "details": trade_details}
approval_tool = FunctionTool(func=request_human_approval)
data_agent = LlmAgent(name="MarketDataAgent", model="gemini-2.0-flash",
instruction="실시간 시장 데이터와 과거 데이터를 수집합니다.")
analysis_agent = LlmAgent(name="AnalysisAgent", model="gemini-2.0-flash",
instruction="포트폴리오와 시장 데이터를 분석합니다.")
recommendation_agent = LlmAgent(name="RecommendationAgent", model="gemini-2.0-flash",
instruction="분석 결과를 바탕으로 거래 추천안을 생성합니다.")
approval_agent = LlmAgent(
name="ApprovalGateway",
model="gemini-2.0-flash",
instruction="거래 추천안을 사용자에게 제시하고 승인을 요청합니다.",
tools=[approval_tool]
)
execution_agent = LlmAgent(name="TradeExecutor", model="gemini-2.0-flash",
instruction="승인된 거래만 실행합니다. 미승인 시 처리를 중단합니다.")
trading_pipeline = SequentialAgent(
name="TradingPipeline",
sub_agents=[data_agent, analysis_agent, recommendation_agent,
approval_agent, execution_agent]
)장단점 분석
장점
| 항목 | 내용 |
|---|---|
| 전문화 및 정확도 | 역할이 분리된 에이전트는 각 도메인에 집중해 단일 에이전트보다 높은 정확도를 냅니다 |
| 모듈성 | 에이전트 단위로 독립 테스트가 가능하고, 장애 격리도 용이합니다 |
| 병렬 확장성 | Parallel 패턴으로 독립 작업의 지연 시간을 극적으로 단축할 수 있습니다 |
| 적응력 | Coordinator 패턴은 예측하지 못한 입력 유형에도 유연하게 대응합니다 |
| 초대형 작업 처리 | Hierarchical 패턴으로 단일 컨텍스트 윈도를 초과하는 작업을 분해해 처리합니다 |
단점 및 주의사항
| 항목 | 내용 | 대응 방안 |
|---|---|---|
| LLM 호출 비용 증가 | 에이전트가 많을수록 API 호출 수와 비용이 늘어납니다 | 경량 모델(Haiku, Flash)을 서브 에이전트에 배치 |
| 원인 귀속 어려움 | 어느 에이전트에서 오류가 발생했는지 추적하기 어렵습니다 | OpenTelemetry, Langfuse 등으로 분산 추적 구성 |
| 순차 의존성 함정 | 순차 의존성이 강한 작업에 멀티에이전트를 도입하면 오히려 성능이 저하됩니다 | 병렬화 가능성 먼저 검토 후 패턴 선택 |
| 오케스트레이션 복잡도 | 에이전트 수가 늘수록 상태 관리와 에러 핸들링이 복잡해집니다 | 에이전트 수를 최소로 유지, 필요 시 점진적 확장 |
| Human-in-the-Loop 병목 | 인간 승인이 필요한 단계는 자동화의 속도 이점을 부분적으로 희생합니다 | 승인이 필요한 임계값을 명확히 설정해 불필요한 개입 최소화 |
A2A 프로토콜: Agent2Agent(A2A)는 서로 다른 벤더의 에이전트끼리 통신할 수 있게 하는 Google의 오픈 표준입니다. MCP(Model Context Protocol)가 에이전트-도구 통신을 담당한다면, A2A는 에이전트-에이전트 통신 레이어입니다. Google, Microsoft, AWS, Salesforce를 포함한 150개 이상의 조직이 지원합니다.
실무에서 가장 흔한 실수
-
모든 작업에 Coordinator를 붙이는 것 — 단순한 순차 흐름에 Coordinator를 추가하면 LLM 호출이 늘어나고 오히려 느려집니다. 분기가 필요한 상황인지 먼저 확인해보시면 좋습니다.
-
순차 의존성이 강한 작업에 Parallel을 도입하는 것 — Google 연구에서 180개 에이전트 설정을 평가한 결과, 순차 의존성이 강한 작업에서 멀티에이전트 협력은 성능을 오히려 저하시켰습니다. "이 두 작업이 정말 독립적인가?"를 먼저 확인해보는 것을 권장합니다.
-
Generator-Critic 루프에 종료 조건을 설정하지 않는 것 — Critic이 절대 APPROVED를 내놓지 않으면 무한 루프가 됩니다.
max_iterations를 반드시 설정하고, 최대 반복 도달 시 graceful fallback 로직도 함께 고려해보시면 좋습니다.
마치며
멀티에이전트 패턴은 복잡성을 없애는 도구가 아니라, 복잡성을 구조화하는 도구입니다. 패턴을 많이 쌓을수록 좋은 게 아니라, 작업의 성질에 맞는 패턴 하나를 잘 선택하는 게 핵심입니다. 가장 단순한 것부터 시작해서 실제로 병목이 생기거나 품질이 부족할 때 패턴을 추가하는 점진적인 접근이 현실에서 가장 잘 작동합니다.
지금 바로 시작해볼 수 있는 것들:
pip install google-adk로 ADK를 설치한 뒤, ADK 공식 멀티에이전트 문서에서SequentialAgent예제를 직접 실행해볼 수 있습니다. 세 가지 기초 실행 모델을 손으로 돌려보면 감이 확 달라집니다.- 현재 진행 중인 프로젝트에서 "LLM 에이전트 하나로 처리하기 버거운 흐름"을 하나 찾아, 위 8가지 패턴 표와 대조해 어떤 패턴이 맞는지 골라볼 수 있습니다.
- OpenTelemetry 또는 Langfuse를 에이전트 체인에 붙여서 각 에이전트의 호출 수, 지연 시간, 비용을 측정해볼 수 있습니다. 설계 결정이 실제로 어떤 트레이드오프를 만들어내는지 눈으로 확인하는 게 다음 패턴 선택에 가장 좋은 근거가 됩니다.
참고 자료
- Developer's guide to multi-agent patterns in ADK | Google Developers Blog
- Google's Eight Essential Multi-Agent Design Patterns | InfoQ
- Multi-agent systems | ADK 공식 문서
- Choose a design pattern for your agentic AI system | Google Cloud Architecture Center
- Announcing the Agent2Agent Protocol (A2A) | Google Developers Blog
- Google Publishes Scaling Principles for Agentic Architectures | InfoQ
- Towards a science of scaling agent systems | Google Research Blog
- Google ADK vs LangGraph | ZenML Blog
- Four Design Patterns for Event-Driven, Multi-Agent Systems | Confluent