개발자를 위한 ADK 멀티 에이전트 패턴 가이드

발행: (2025년 12월 17일 오전 10:40 GMT+9)
16 min read

Source: Google Developers Blog

DEC. 16, 2025

소프트웨어 개발 분야는 이미 이 교훈을 배웠습니다: 모놀리식 애플리케이션은 확장되지 않습니다. 대규모 전자상거래 플랫폼이든 복잡한 AI 애플리케이션이든, 하나의 모든 것을 담당하는 엔터티에 의존하면 병목 현상이 발생하고 디버깅 비용이 증가하며 특화된 성능이 제한됩니다.

같은 원리가 AI 에이전트에도 적용됩니다. 너무 많은 책임을 맡은 단일 에이전트는 “모든 것을 조금씩 하는 사람, 아무것도 잘 못하는 사람”이 됩니다. 지시가 복잡해질수록 특정 규칙을 따르는 정도가 떨어지고 오류율이 누적되어 점점 더 많은 “환각”이 발생합니다. 에이전트가 실패했을 때 전체 프롬프트를 다 뜯어내어 버그를 찾아야 할 필요는 없습니다.

신뢰성은 탈중앙화와 전문화에서 나옵니다. 다중 에이전트 시스템(MAS)은 마이크로서비스 아키텍처에 해당하는 AI 버전을 구축할 수 있게 해줍니다. 개별 에이전트에 Parser, Critic, Dispatcher와 같은 특정 역할을 할당함으로써, 본질적으로 더 모듈화되고 테스트 가능하며 신뢰할 수 있는 시스템을 만들 수 있습니다.

이 가이드에서는 Google Agent Development Kit(ADK)을 사용해 순차 파이프라인부터 Human‑in‑the‑Loop 패턴까지 여덟 가지 핵심 설계 패턴을 설명하고, 프로덕션 수준의 에이전트 팀을 구축하는 데 필요한 구체적인 패턴과 의사코드를 제공합니다.

1. 순차 파이프라인 패턴 (일명 조립 라인)

이 패턴을 고전적인 조립 라인에 비유해 보세요. Agent A가 작업을 마치면 바로 Agent B에게 배턴을 넘깁니다. 선형이며 결정론적이고, 데이터가 어디서 왔는지 항상 알 수 있기 때문에 디버깅이 매우 쉽습니다.

사용 사례: 원시 문서 처리 — 파서 → 추출기 → 요약기.

Sequential pattern

ADK에서는 SequentialAgent 프리미티브가 오케스트레이션을 대신 수행합니다. 핵심은 상태 관리인데, output_key를 사용해 공유된 session.state에 기록하면 체인에 있는 다음 에이전트가 정확히 어디서 작업을 이어받아야 할지 알 수 있습니다.

# ADK Pseudocode

# Step 1: Parse the PDF
parser = LlmAgent(
    name="ParserAgent",
    instruction="Parse raw PDF and extract text.",
    tools=[PDFParser],
    output_key="raw_text"
)

# Step 2: Extract structured data
extractor = LlmAgent(
    name="ExtractorAgent",
    instruction="Extract structured data from {raw_text}.",
    tools=[RegexExtractor],
    output_key="structured_data"
)

# Step 3: Summarize
summarizer = LlmAgent(
    name="SummarizerAgent",
    instruction="Generate summary from {structured_data}.",
    tools=[SummaryEngine]
)

# Orchestrate the Assembly Line
pipeline = SequentialAgent(
    name="PDFProcessingPipeline",
    sub_agents=[parser, extractor, summarizer]
)

Source:

2. Coordinator / Dispatcher Pattern (aka the concierge)

때때로 체인이 필요하지 않을 때가 있습니다; 대신 의사결정자가 필요합니다. 이 패턴에서는 중앙의 지능형 에이전트가 디스패처 역할을 합니다. 사용자의 의도를 분석하고 요청을 작업에 가장 적합한 전문 에이전트에게 라우팅합니다.

사용 사례: 사용자를 “청구(Billing)” 전문가 또는 “기술 지원(Tech Support)” 전문가에게 연결해야 하는 복잡한 고객 서비스 봇.

Coordinator‑dispatcher pattern

이 패턴은 LLM 기반 위임에 의존합니다. 상위 CoordinatorAgent를 정의하고 전문 sub_agents 목록을 제공합니다. ADK의 AutoFlow 메커니즘은 자식에 대해 제공한 설명을 기반으로 실행을 전환합니다.

# ADK Pseudocode

billing_specialist = LlmAgent(
    name="BillingSpecialist",
    description="Handles billing inquiries and invoices.",
    tools=[BillingSystemDB]
)

tech_support = LlmAgent(
    name="TechSupportSpecialist",
    description="Troubleshoots technical issues.",
    tools=[DiagnosticTool]
)

# The Coordinator (Dispatcher)
coordinator = LlmAgent(
    name="CoordinatorAgent",
    instruction=(
        "Analyze user intent. Route billing issues to BillingSpecialist "
        "and bugs to TechSupportSpecialist."
    ),
    sub_agents=[billing_specialist, tech_support]
)

3. 병렬 Fan‑Out / Gather 패턴 (일명 문어)

속도가 중요합니다. 작업들이 서로 의존하지 않는다면 왜 하나씩 실행하나요? 이 패턴에서는 여러 에이전트가 동시에 작업을 수행해 지연 시간을 줄이거나 다양한 관점을 얻습니다. 그런 다음 최종 “합성기” 에이전트가 그 출력들을 모아 집계합니다.

사용 사례: 자동 코드 리뷰 — “보안 감사자”, “스타일 강제자”, “성능 분석가”를 병렬로 실행한 뒤 피드백을 결합합니다.

Parallel fan‑out

ADK의 ParallelAgent는 하위 에이전트를 동시에 실행합니다. 이 에이전트들은 별도의 실행 스레드에서 동작하지만 세션 상태를 공유합니다. 경쟁 조건을 방지하려면 각 에이전트가 데이터를 고유한 키에 기록하도록 해야 합니다.

# ADK Pseudocode

security_auditor = LlmAgent(
    name="SecurityAuditor",
    instruction="Identify security vulnerabilities in the code.",
    tools=[StaticAnalysisTool],
    output_key="security_report"
)

style_enforcer = LlmAgent(
    name="StyleEnforcer",
    instruction="Check code style compliance.",
    tools=[Linter],
    output_key="style_report"
)

performance_analyst = LlmAgent(
    name="PerformanceAnalyst",
    instruction="Analyze performance bottlenecks.",
    tools=[Profiler],
    output_key="performance_report"
)

# Synthesizer that gathers the reports
synthesizer = LlmAgent(
    name="ReviewSynthesizer",
    instruction=(
        "Combine {security_report}, {style_report}, and {performance_report} "
        "into a single cohesive review comment."
    ),
    tools=[Summarizer],
    output_key="final_review"
)

# Parallel orchestration
parallel_review = ParallelAgent(
    name="ParallelCodeReview",
    sub_agents=[security_auditor, style_enforcer, performance_analyst],
    gather_agent=synthesizer
)

정리된 마크다운 내용

# Define parallel workers
security_scanner = LlmAgent(
    name="SecurityAuditor",
    instruction="Check for vulnerabilities like injection attacks.",
    output_key="security_report"
)

style_checker = LlmAgent(
    name="StyleEnforcer",
    instruction="Check for PEP8 compliance and formatting issues.",
    output_key="style_report"
)

complexity_analyzer = LlmAgent(
    name="PerformanceAnalyst",
    instruction="Analyze time complexity and resource usage.",
    output_key="performance_report"
)

# Fan‑out (The Swarm)
parallel_reviews = ParallelAgent(
    name="CodeReviewSwarm",
    sub_agents=[security_scanner, style_checker, complexity_analyzer]
)

# Gather / Synthesize
pr_summarizer = LlmAgent(
    name="PRSummarizer",
    instruction=(
        "Create a consolidated Pull Request review using "
        "{security_report}, {style_report}, and {performance_report}."
    )
)

# Wrap in a sequence
workflow = SequentialAgent(sub_agents=[parallel_reviews, pr_summarizer])

4. 계층적 분해 (일명 러시아 인형)

때때로 작업이 단일 에이전트의 컨텍스트 윈도우에 비해 너무 클 수 있습니다. 고수준 에이전트는 복잡한 목표를 하위 작업으로 나누고 이를 위임할 수 있습니다. 라우팅 패턴과 달리, 부모 에이전트는 작업의 일부만 위임하고 결과를 기다린 뒤 자신의 추론을 계속할 수 있습니다.

아래 다이어그램에서 ReportWriter는 직접 연구를 수행하지 않습니다. 대신 ResearchAssistant에게 위임하고, ResearchAssistant는 다시 WebSearchSummarizer 도구를 관리합니다.

계층적 작업 분해 에이전트

하위 에이전트를 도구처럼 취급할 수 있습니다. 에이전트를 AgentTool로 감싸면, 부모 에이전트가 이를 명시적으로 호출할 수 있어 하위 에이전트 전체 워크플로를 하나의 함수 호출로 다룰 수 있습니다.

# ADK Pseudocode

# Level 3: Tool Agents
web_searcher = LlmAgent(
    name="WebSearchAgent",
    description="Searches web for facts."
)

summarizer = LlmAgent(
    name="SummarizerAgent",
    description="Condenses text."
)

# Level 2: Coordinator Agent
research_assistant = LlmAgent(
    name="ResearchAssistant",
    description="Finds and summarizes info.",
    # Coordinator manages the tool agents
    sub_agents=[web_searcher, summarizer]
)

# Level 1: Top‑Level Agent
report_writer = LlmAgent(
    name="ReportWriter",
    instruction=(
        "Write a comprehensive report on AI trends. "
        "Use the ResearchAssistant to gather info."
    ),
    # Wrap the sub‑agent hierarchy as a tool for the parent
    tools=[AgentTool(research_assistant)]
)

Source:

5. Generator와 Critic (편집자 책상)

고품질의 신뢰할 수 있는 결과물을 만들려면 종종 두 번째 눈이 필요합니다. 이 패턴에서는 콘텐츠 생성과 검증을 분리합니다. 한 에이전트는 Generator 역할을 수행해 초안을 만들고, 다른 에이전트는 Critic 역할을 수행해 특정 하드코딩된 기준이나 논리 검사를 통해 검토합니다.

아키텍처에는 조건부 루프가 포함됩니다: 검토가 통과하면 루프가 종료되고 콘텐츠가 최종 확정됩니다; 검토에 실패하면 피드백이 Generator에게 전달되어 규격에 맞는 초안을 다시 생성합니다. 이는 구문 검사가 필요한 코드 생성이나 규정 준수 검토가 필요한 콘텐츠 제작에 특히 유용합니다.

생성기 비평가 에이전트 패턴

ADK 구현에서는 두 개의 프리미티브를 사용합니다—초안‑검토 상호작용을 관리하는 SequentialAgent와 품질 게이트 및 종료 조건을 강제하는 상위 LoopAgent입니다.

# ADK Pseudocode

# The Generator
generator = LlmAgent(
    name="Generator",
    instruction=(
        "Generate a SQL query. If you receive {feedback}, "
        "fix the errors and generate again."
    ),
    output_key="draft"
)

# The Critic
critic = LlmAgent(
    name="Critic",
    instruction=(
        "Check if {draft} is valid SQL. If correct, output 'PASS'. "
        "If not, output error details."
    ),
    output_key="feedback"
)

# The Loop
loop = LoopAgent(
    name="ValidationLoop",
    sub_agents=[generator, critic],
    condition_key="feedback",
    exit_condition="PASS"
)

Source:

6. 반복적 정제 (조각가라고도 함)

훌륭한 작업은 보통 한 번의 초안으로 완성되지 않습니다. 인간 작가가 수정하고 다듬고 편집하듯이, 에이전트도 답을 정확히 맞추기 위해 여러 차례 시도가 필요할 때가 있습니다. 이 패턴에서는 에이전트가 생성, 비판, 정제의 사이클을 돌면서 출력물이 특정 품질 기준에 도달할 때까지 반복합니다.

Generator‑and‑Critic 패턴이 이진 통과/실패에 초점을 맞추는 반면, 이 패턴은 정성적인 향상을 강조합니다. Generator가 초안 초안을 만들고, Critique Agent가 최적화 메모를 제공하며, Refinement Agent가 그 메모를 기반으로 출력을 다듬습니다.

iterative refinement agent pattern (1)

이 패턴은 LoopAgent를 사용해 구현됩니다. 핵심 요소는 종료 메커니즘입니다. max_iterations로 강제 제한을 설정할 수 있지만, ADK는 에이전트가 조기에 완료를 신호하도록도 허용합니다. 품질 기준이 최대 반복 횟수에 도달하기 전에 충족되면, 에이전트는 EventActions 내에서 escalate=True를 설정해 종료를 트리거할 수 있습니다.

# ADK Pseudocode

# Generator
generator = LlmAgent(
    name="Generator",
    instruction="Generate an initial rough draft.",
    output_key="current_draft"
)

# Critique Agent
critic = LlmAgent(
    name="Critic",
    instruction=(
        "Review {current_draft} and provide improvement suggestions. "
        "If the draft meets quality standards, set {feedback} to 'PASS'."
    ),
    output_key="feedback"
)

# Refinement Agent
refiner = LlmAgent(
    name="Refiner",
    instruction=(
        "Incorporate the suggestions from {feedback} into the draft "
        "and output the refined version."
    ),
    output_key="refined_draft"
)

# Loop Agent
refinement_loop = LoopAgent(
    name="IterativeRefinementLoop",
    sub_agents=[generator, critic, refiner],
    condition_key="feedback",
    exit_condition="PASS",
    max_iterations=5   # optional hard limit
)

LLM Agent 예시

critic = LlmAgent(
    name="Critic",
    instruction="Review {current_draft}. List ways to optimize it for performance.",
    output_key="critique_notes"
)

# Refiner Agent 
refiner = LlmAgent(
    name="Refiner",
    instruction="Read {current_draft} and {critique_notes}. Rewrite the draft to be more efficient.",
    output_key="current_draft"   # Overwrites the draft with a better version
)

# The Loop (Critique → Refine)
loop = LoopAgent(
    name="RefinementLoop",
    max_iterations=3,
    sub_agents=[critic, refiner]
)

# Complete Workflow
workflow = SequentialAgent(sub_agents=[generator, loop])

7. Human‑in‑the‑Loop (the human safety net)

AI 에이전트는 강력하지만, 중요한 의사결정에서는 인간이 직접 관여해야 할 때가 있습니다. 이 모델에서는 에이전트가 기본 작업을 수행하지만, 되돌릴 수 없거나 큰 영향을 미치는 고위험 행동(예: 금융 거래 실행, 프로덕션에 코드 배포, 민감한 데이터 처리 등)은 반드시 인간이 승인해야 합니다. 이를 통해 안전성과 책임성을 확보합니다.

다이어그램은 Transaction Agent가 일상적인 작업을 처리하는 모습을 보여줍니다. 고위험 검사가 필요할 경우, ApprovalTool Agent를 호출하여 실행을 일시 중지하고 Human Reviewer가 “예” 또는 “아니오”라고 응답하기를 기다립니다.

human‑in‑the‑loop pattern

ADK를 사용하면 맞춤형 도구를 통해 이를 구현할 수 있습니다. 에이전트는 approval_tool을 호출하여 실행을 일시 중지하거나 외부 시스템을 트리거해 인간 개입을 요청할 수 있습니다.

# ADK Pseudocode
transaction_agent = LlmAgent(
    name="TransactionAgent",
    instruction="Handle routine processing. If high stakes, call ApprovalTool.",
    tools=[ApprovalTool]
)

approval_agent = LlmAgent(
    name="ApprovalToolAgent",
    instruction="Pause execution and request human input."
)

workflow = SequentialAgent(sub_agents=[transaction_agent, approval_agent])

8. 복합 패턴 (믹스‑앤‑매치)

실제 기업용 애플리케이션은 하나의 상자에 딱 들어맞는 경우가 거의 없습니다. 이러한 패턴들을 조합하여 프로덕션 수준의 시스템을 구축하게 될 것입니다.

예를 들어, 견고한 Customer Support 시스템은 요청을 라우팅하기 위해 Coordinator를 사용할 수 있습니다. 사용자가 기술적인 문제를 겪고 있다면, 해당 분기는 Parallel 방식으로 문서와 사용자 기록을 동시에 검색하도록 트리거할 수 있습니다. 최종 답변은 Generator → Critic 루프를 거쳐 어조 일관성을 확인한 뒤 사용자에게 전달될 수 있습니다.

composite agent pattern

시작하기 전에 몇 가지 프로‑팁

  • State management is vital: In ADK, session.state is your whiteboard. Use descriptive output_key values so downstream agents know exactly what they are reading.
  • Clear descriptions: When using routing, the description field of your sub‑agents serves as API documentation for the LLM. Be precise.
Back to Blog

관련 글

더 보기 »