다중 에이전트 시스템

다중 에이전트 시스템 (Multi-Agent Systems)

서로 협력하고 작업을 위임하는 특화된 에이전트 팀을 구축해요.

출처: 문서

본문

왜 다중 에이전트인가? (Why Multi-Agent?)

복잡한 작업은 특화(specialization)에서 이점을 얻어요. 모든 걸 하려는 하나의 모놀리식 에이전트 대신, 초점화된 에이전트 팀을 만들 수 있어요:

  • 전체 목표를 이해하고 위임하는 조정자(coordinator)
  • 파일시스템과 셸 접근으로 코드를 작성하는 개발자(developer)
  • 코드 품질을 검사하는 리뷰어(reviewer)
  • 정보를 위해 웹을 검색하는 연구자(researcher)

각 에이전트는 특정 역할에 맞게 최적화된 자신만의 모델, 도구, 지침을 가져요.

두 가지 패턴: 위임 vs. 핸드오프 (Two Patterns: Delegation vs. Handoffs)

Docker Agent는 두 가지 다중 에이전트 패턴을 지원해요:

Delegation (sub_agents) Handoffs (handoffs)
토폴로지 계층적 (부모 → 자식 → 부모) 동료 간 그래프 (A → B → C → A)
세션 자식이 하위 세션에서 실행 대화가 같은 세션에 유지
컨텍스트 자식이 깨끗한 작업 설명을 받음 다음 에이전트가 전체 대화 기록을 봄
제어 흐름 부모가 자식이 끝날 때까지 블록 후 계속 활성 에이전트 전환 — 이전 에이전트는 루프에서 벗어남
도구 transfer_task handoff
최적 용도 전문가에게 작업 위임 파이프라인 워크플로, 대화 라우팅

같은 구성에서 두 패턴을 결합할 수 있어요 — 에이전트가 sub_agents 와 handoffs 를 모두 가질 수 있답니다.

Tip 언제 어떤 것을 써야 할까 sub_agents — 조정자가 전문가에게 작업을 보내고 그 결과를 종합해야 할 때 사용. handoffs — 에이전트들이 같은 대화를 번갈아 처리해야 할 때(파이프라인, 라우팅) 사용. background_agents — 여러 독립 작업을 동시에 실행할 수 있을 때 사용.

sub_agents로 위임하기 (Delegation with sub_agents)

에이전트는 내장된 transfer_task 도구로 작업을 위임해요. 이 도구는 sub_agents 가 있는 모든 에이전트에 자동으로 제공돼요. 부모 에이전트가 자식 에이전트에 작업을 보내고, 결과를 기다린 뒤 계속해요.

  • 사용자가 루트 에이전트에 메시지를 보냄
  • 루트 에이전트가 요청을 분석하고 어떤 하위 에이전트가 처리할지 결정
  • 루트 에이전트가 대상 에이전트, 작업 설명, 기대 출력과 함께 transfer_task 호출
  • 하위 에이전트가 자신의 도구를 사용해 자신의 에이전트 루프에서 작업 처리
  • 결과가 루트 에이전트로 흘러가고, 루트 에이전트가 사용자에게 응답
# The transfer_task tool call looks like:
transfer_task(
  agent="developer",
  task="Create a REST API endpoint for user authentication",
  expected_output="Working Go code with tests"
)

Note 자동 승인 (Auto-Approved) 다른 도구와 달리 transfer_task 는 항상 자동 승인돼요 — 사용자 확인이 필요 없어요. 이로써 에이전트 간 원활한 위임이 가능해져요.

핸드오프 라우팅 (Handoffs Routing)

핸드오프는 에이전트가 전체 대화를 다른 에이전트에게 넘기는 동료 간 라우팅 패턴이에요. 위임과 달리 하위 세션이 없어요 — 대화가 단일 세션에 유지되고 활성 에이전트만 전환돼요.

이 패턴은 다음에 이상적이에요:

  • 파이프라인 워크플로 — 데이터가 특화된 에이전트 체인을 통해 흐름
  • 대화형 라우팅 — 조정자가 사용자를 올바른 전문가에게 라우팅하고, 전문가는 끝나면 되돌려보낼 수 있음
  • 그래프 토폴로지 — 에이전트가 순환을 형성해(A → B → C → A) 반복 워크플로를 가능하게 함

동작 원리 (How It Works)

  • 사용자가 시작 에이전트에 메시지를 보냄
  • 에이전트 A가 메시지를 처리한 뒤 에이전트 B로 라우팅하기 위해 handoff 호출
  • 에이전트 B가 활성 에이전트가 되어 전체 대화 기록을 봄
  • 에이전트 B는 응답하거나, 자신의 도구를 사용하거나, 다른 에이전트에게 핸드오프할 수 있음
  • 에이전트가 핸드오프 없이 직접 응답할 때까지 계속됨
# The handoff tool call looks like:
handoff(
  agent="summarizer"
)

Note 범위 지정된 핸드오프 대상 (Scoped Handoff Targets) 각 에이전트는 자신의 handoffs 배열에 나열된 에이전트에게만 핸드오프할 수 있어요. handoff 도구는 자동으로 주입되므로 수동으로 추가할 필요가 없어요.

예제 (Example)

조정자가 연구자에게 라우팅하고, 연구자는 요약자에게 핸드오프하고, 요약자는 조정자에게 돌아와요:

Root ──→ Researcher ──→ Summarizer ──→ Root
agents:
  root:
    model: anthropic/claude-sonnet-4-5
    description: Coordinator that routes queries
    instruction: |
      Route research queries to the researcher.
    handoffs:
      - researcher

  researcher:
    model: openai/gpt-5
    description: Web researcher
    instruction: |
      Search the web, then hand off to the summarizer.
    toolsets:
      - type: mcp
        ref: docker:duckduckgo
    handoffs:
      - summarizer

  summarizer:
    model: openai/gpt-5
    description: Summarizes findings
    instruction: |
      Summarize the research results, then hand off
      back to root.
    handoffs:
      - root

Tip 전체 파이프라인 예제 분기와 여러 처리 단계가 있는 더 복잡한 핸드오프 그래프는 examples/handoff.yaml 참고.

강제 핸드오프 (Forced Handoffs)

handoffs 를 쓰면 모델이 handoff 도구 호출 여부를 스스로 결정해요 — 즉 잊어버릴 수 있고, 엄격한 순서에 의존하는 파이프라인이 깨질 수 있어요. force_handoff 는 그 불확실성을 없애줘요: 에이전트가 최종 응답을 만들 때마다 런타임 자체가 LLM의 도구 호출을 건너뛰고 대화를 이름 있는 에이전트로 라우팅해요. 전체 대화 컨텍스트는 그대로 이어져요.

agents:
  root:
    model: anthropic/claude-sonnet-4-5
    description: Extracts key facts from the input
    instruction: |
      Extract the key facts from the user's input as a bullet list.
    force_handoff: summarizer

  summarizer:
    model: anthropic/claude-sonnet-4-5
    description: Produces the final summary
    instruction: |
      Summarize the extracted facts for the user.

구성 로드 시점에 강제되는 규칙:

  • 대상은 구성에 정의된 에이전트(또는 외부 참조)여야 함
  • 에이전트는 자기 자신에게 force_handoff 할 수 없음
  • force_handoff 엣지의 체인은 순환을 형성하면 안 됨 (A → B → A는 거부됨)

실행 가능한 예제는 examples/force_handoff.yaml 참고.

백그라운드 에이전트로 병렬 위임 (Parallel Delegation with Background Agents)

transfer_task 는 순차적이에요 — 조정자가 하위 에이전트가 끝날 때까지 기다렸다가 계속해요. 여러 에이전트에게 동시에 작업을 분산해야 할 때는 대신 background_agents toolset을 사용해요.

조정자의 toolsets에 추가해요:

agents:
  root:
    model: anthropic/claude-sonnet-4-5
    description: Research coordinator
    sub_agents: [researcher, analyst, writer]
    toolsets:
      - type: think
      - type: background_agents

그러면 조정자는 다음을 할 수 있어요:

  • run_background_agent 로 한 번에 여러 작업을 발송 — 각각 즉시 작업 ID를 반환
  • list_background_agents 또는 view_background_agent 로 진행 상황 모니터링
  • 작업이 끝나면 결과 수집
  • 더 이상 필요 없는 작업은 stop_background_agent 로 취소
# Start two tasks in parallel
run_background_agent(agent="researcher", task="Find recent papers on LLM agents")
run_background_agent(agent="analyst", task="Analyze our current architecture")

# Check on all tasks
list_background_agents()

# Read results when ready
view_background_agent(task_id="agent_task_abc123")

레지스트리의 외부 하위 에이전트 (External Sub-Agents from Registries)

하위 에이전트는 로컬에 정의할 필요가 없어요 — sub_agents 목록에서 어떤 OCI 호환 레지스트리의 에이전트든 직접 참조할 수 있어요. 이렇게 하면 구성을 복제하지 않고 공유 에이전트를 사용해 팀을 구성할 수 있어요.

agents:
  root:
    model: openai/gpt-5
    description: Coordinator that delegates to local and external sub-agents
    instruction: |
      Delegate tasks to the most appropriate sub-agent.
    sub_agents:
      - local_helper
      - myorg/agent:tag # pulled from registry automatically

  local_helper:
    model: openai/gpt-5
    description: A local helper agent for simple tasks
    instruction: You are a helpful assistant.

외부 하위 에이전트는 마지막 경로 세그먼트(태그 제외) 뒤에 자동으로 이름이 붙어요 — 예를 들어 myorg/agent:tag 는 agent 가 돼요. name:reference 구문으로 명시적인 이름을 줄 수도 있어요:

sub_agents:
  - my_agent:myorg/agent:tag # available as "my_agent"
  - reviewer:docker.io/myorg/review-agent:latest

외부 하위 에이전트를 다이제스트로 고정 (Pin external sub-agents to a digest)

외부 참조는 기본적으로 태그를 사용해요: myorg/agent 는 myorg/agent:latest 의 줄임말이에요. 태그 참조는 매 docker agent run 마다 레지스트리에서 다시 확인돼요: 고정되지 않은 각 외부 하위 에이전트는 시작 시 다이제스트 조회를 유발하며, 그 하위 에이전트가 세션에서 호출되지 않더라도 그래요. 정상 연결에서는 보통 참조당 1~2초를 더해요(네트워크와 레지스트리에 따라 다름), 그리고 레지스트리나 자격 증명 헬퍼가 잘못 동작하면 멈출 수 있는 경로 중 하나예요.

참조를 불변 다이제스트(@sha256:…)로 고정하면 런타임이 네트워크 왕복 없이 로컬 캐시에서 바로 제공하므로 시작이 빠르고 팀이 완전히 재현 가능해요:

sub_agents:
  - reviewer:docker.io/myorg/review-agent@sha256:44117e73263afa5c861bdf3730dae7925918ffdd146827eee5bcff20bc55e8fa

레지스트리에서 다이제스트를 복사하거나(Docker Hub는 태그 옆에 표시), docker buildx imagetools inspect <reference> 같은 표준 OCI 도구로 읽어요. Docker Agent는 태그 대신 다이제스트를 여전히 사용하는 외부 OCI 참조에 대해 시작 경고를 기록해요.

handoffs 와 force_handoff 의 외부 참조도 같은 실행당 비용을 가지므로, 그것들도 다이제스트로 고정하세요.

Tip 외부 하위 에이전트는 어떤 OCI 호환 레지스트리에서든 동작해요. 레지스트리 참조에 대한 자세한 내용은 Agent Distribution 참고. 로컬과 외부 하위 에이전트를 섞는 완전한 예제는 examples/sub-agents-from-registry.yaml 참고.

Harness 기반 하위 에이전트 (Harness-Backed Sub-Agents)

하위 에이전트는 모델 API 대신 외부 코딩 CLI — Claude Code, Codex, opencode, 또는 pi — 로 지원될 수 있어요. model: 필드 대신 harness: 블록을 추가하면 harness 하위 에이전트를 만들 수 있어요:

agents:
  root:
    model: anthropic/claude-sonnet-4-5
    description: Orchestrator that plans and delegates
    instruction: |
      Break down coding tasks and delegate to the coding agents.
    sub_agents:
      - claude-coder
      - codex-coder

  claude-coder:
    description: Claude Code specialist
    harness:
      type: claude-code
      effort: high

  codex-coder:
    description: Codex specialist
    harness:
      type: codex

오케스트레이터는 다른 하위 에이전트처럼 transfer_task 를 사용해 harness 하위 에이전트에 작업을 보내요. Docker Agent가 오케스트레이션과 훅을 처리하고, 외부 CLI가 코딩 루프를 구동해요.

Tip 더 알아보기 전체 필드 참조, 병렬 디스패치 패턴, harness 에이전트 내부에서 동작하지 않는 것들은 Coding Harnesses 참고.

예제: 개발 팀 (Example: Development Team)

agents:
  root:
    model: anthropic/claude-sonnet-4-5
    description: Technical lead coordinating development
    instruction: |
      You are a technical lead managing a development team.
      Analyze requests and delegate to the right specialist.
      Ensure quality by reviewing results before responding.
    sub_agents: [developer, reviewer, tester]
    toolsets:
      - type: think

  developer:
    model: anthropic/claude-sonnet-4-5
    description: Expert software developer
    instruction: |
      You are an expert developer. Write clean, efficient code
      and follow best practices.
    toolsets:
      - type: filesystem
      - type: shell
      - type: think

  reviewer:
    model: openai/gpt-5
    description: Code review specialist
    instruction: |
      You review code for quality, security, and maintainability.
      Provide actionable feedback.
    toolsets:
      - type: filesystem

  tester:
    model: openai/gpt-5
    description: Quality assurance engineer
    instruction: |
      You write tests and ensure software quality. Run tests
      and report results.
    toolsets:
      - type: shell
      - type: todo

예제: 연구 팀 (Example: Research Team)

agents:
  root:
    model: anthropic/claude-sonnet-4-5
    description: Research coordinator
    instruction: |
      Coordinate research tasks. Delegate web searches to
      the researcher and writing to the writer.
    sub_agents: [researcher, writer]
    toolsets:
      - type: think

  researcher:
    model: openai/gpt-5
    description: Web researcher
    instruction: Search the web and gather information.
    toolsets:
      - type: mcp
        ref: docker:duckduckgo
      - type: memory
        path: ./research.db

  writer:
    model: anthropic/claude-sonnet-4-5
    description: Content writer
    instruction: Write clear, well-structured content.
    toolsets:
      - type: filesystem

다중 모델 팀 (Multi-Model Teams)

다중 에이전트 시스템의 핵심 장점은 역할마다 다른 모델을 사용해 각 작업에 최적의 모델을 고르는 거예요:

models:
  fast:
    provider: openai
    model: gpt-5-mini
    temperature: 0.2 # precise

  creative:
    provider: openai
    model: gpt-5
    temperature: 0.8 # creative

  local:
    provider: dmr
    model: ai/qwen3 # runs locally, no API cost

agents:
  analyst:
    model: fast # cheap and fast for analysis
  writer:
    model: creative # creative for content
  helper:
    model: local # free for simple tasks

공유 도구 (Shared Tools)

todo 같은 도구는 협력적 작업 추적을 위해 에이전트 간에 공유할 수 있어요:

toolsets:
  - type: todo
    shared: true # all agents see the same todo list

모범 사례 (Best Practices)

  • 에이전트를 초점화하세요 — 각 에이전트는 명확하고 좁은 역할을 가져야 해요
  • 명확한 설명을 작성하세요 — 조정자가 설명을 사용해 누구에게 위임할지 결정해요
  • 최소한의 도구를 주세요 — 각 에이전트에게 특정 역할에 필요한 도구만 주세요
  • 필요할 때 think 도구를 사용하세요 — 네이티브 추론이 없는 모델의 경우, 조정자에게 think 도구를 줘서 위임에 대해 추론하게 하세요. 내장 thinking이 있는 모델(예: thinking_budget 사용)은 필요 없어요.
  • 올바른 모델을 사용하세요 — 복잡한 추론에는 유능한 모델을, 간단한 작업에는 저렴한 모델을
  • 올바른 패턴을 고르세요 — 계층적 작업 위임에는 sub_agents, 파이프라인 워크플로와 대화 라우팅에는 handoffs 를

Note Docker Agent 너머로 다른 에이전트 프레임워크와의 상호운용성을 위해, Docker Agent는 A2A 프로토콜을 지원하고 MCP Mode를 통해 에이전트를 노출할 수 있어요.

더 알아보기 (Learn more)