Phase 4: 고급/프로덕션 | 예상 학습 시간: 30분
🎯 학습 목표
- 오케스트레이터-워커(orchestrator-workers) 패턴의 구조와 각 서브에이전트가 독립적인 컨텍스트를 갖는 이유를 설명할 수 있다
- Claude Agent SDK의 서브에이전트를 활용해 병렬 작업을 위임하는 최소 구현을 작성할 수 있다
- 멀티 에이전트 시스템에서 토큰 비용이 급증하는 이유를 이해하고, 언제 단일 에이전트 대신 멀티 에이전트를 선택해야 하는지 판단할 수 있다
1. 왜 에이전트를 여러 개로 나누는가
Day 19에서 다룬 ReAct나 Plan-and-Execute는 하나의 에이전트가 컨텍스트 하나를 계속 들고 다니며 작업합니다. 하지만 작업 범위가 넓어질수록 컨텍스트 창이 관련 없는 정보로 오염되고, 서로 다른 하위 작업을 순차적으로만 처리해야 하는 병목이 생깁니다.
Anthropic이 실제로 리서치 기능을 만들며 공개한 사례가 이 문제를 잘 보여줍니다. 리드 에이전트(오케스트레이터)가 사용자 질문을 분석해 조사 전략을 세우고, 여러 서브에이전트를 동시에 생성해 각기 다른 하위 주제를 병렬로 탐색시킵니다. 각 서브에이전트는 독립적으로 웹 검색을 수행하고 결과를 평가한 뒤, 자신의 조사 결과만 리드 에이전트에 반환합니다. 리드 에이전트는 이 결과들을 종합해 최종 답을 만들거나, 부족하다고 판단되면 추가 서브에이전트를 더 생성합니다. Opus 4를 오케스트레이터로, Sonnet 4를 서브에이전트로 쓴 구성은 단일 Opus 4 모델 대비 90.2% 성능 향상을 보였다고 보고되었습니다.
핵심은 서브에이전트가 서로의 존재를 모른다는 점입니다. 각 서브에이전트는 자기 완결적인 작업 설명(task description), 기대하는 출력 형식, 그리고 깨끗한 컨텍스트 창을 받아 시작하며, 작업 도중 다른 서브에이전트와 조율하지 않습니다. 이 격리 덕분에 병렬 실행이 가능하고, 한 서브에이전트의 컨텍스트가 다른 서브에이전트의 판단을 오염시키지 않습니다.
| 구분 | 단일 에이전트 | 멀티 에이전트 (오케스트레이터-워커) |
|---|---|---|
| 컨텍스트 | 하나를 계속 누적 | 서브에이전트마다 격리된 컨텍스트 |
| 실행 방식 | 순차적 | 병렬 가능 |
| 적합한 작업 | 단일 스레드로 충분한 작업 | 독립적으로 분해 가능한 광범위 조사·분석 |
| 토큰 비용 | 상대적으로 낮음 | 훨씬 높음 (아래 3번 참고) |
| 디버깅 난이도 | 낮음 | 높음 (비결정적 병렬 실행) |
2. Claude Agent SDK로 서브에이전트 구성하기
Claude Agent SDK는 오케스트레이터(부모 에이전트)가 서브에이전트를 정의하고 호출하는 구조를 지원합니다. 서브에이전트는 별도의 대화 세션에서 실행되며, 중간 도구 호출과 결과는 서브에이전트 내부에 머물고 최종 메시지만 오케스트레이터로 반환됩니다. 이는 오케스트레이터의 컨텍스트를 서브에이전트의 세부 작업 과정으로부터 보호하기 위한 설계입니다.
import anthropic
client = anthropic.Anthropic()
def run_subagent(topic: str) -> str:
"""독립된 컨텍스트에서 하위 주제 하나를 조사하는 서브에이전트"""
response = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system=(
"당신은 리서치 서브에이전트입니다. 주어진 하위 주제만 조사하고, "
"다른 서브에이전트의 존재를 가정하지 마세요. 조사 결과를 "
"3문장 이내로 요약해 반환하세요."
),
messages=[{"role": "user", "content": f"조사 주제: {topic}"}],
)
return response.content[0].text
def orchestrator(user_query: str) -> str:
# 1. 오케스트레이터가 질문을 하위 주제로 분해
plan_response = client.messages.create(
model="claude-opus-4-6",
max_tokens=500,
messages=[{
"role": "user",
"content": f"다음 질문을 병렬 조사 가능한 3개 하위 주제로 나눠 "
f"JSON 배열로만 답하라: {user_query}",
}],
)
subtopics = ["경쟁사 가격 정책", "최근 기능 출시", "고객 리뷰 트렌드"] # 파싱 결과 예시
# 2. 서브에이전트를 병렬로 실행 (실제로는 asyncio나 스레드풀 사용)
findings = [run_subagent(t) for t in subtopics]
# 3. 오케스트레이터가 결과를 종합
synth = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
messages=[{
"role": "user",
"content": f"다음 조사 결과들을 종합해 답변을 작성하라:\n"
+ "\n".join(findings),
}],
)
return synth.content[0].text
실무에서는 run_subagent 호출을 asyncio.gather나 스레드풀로 묶어 실제 병렬 실행을 구현합니다. 예제에서는 흐름을 명확히 보여주기 위해 순차 호출로 단순화했습니다.
3. 비용과 트레이드오프 — 언제 멀티 에이전트를 쓸까
멀티 에이전트 시스템의 가장 큰 단점은 토큰 소비입니다. Anthropic의 자체 측정에 따르면 멀티 에이전트 시스템은 일반 챗 상호작용 대비 약 4배, 단일 에이전트 대비 약 15배의 토큰을 사용합니다. 서브에이전트마다 시스템 프롬프트, 도구 정의, 대화 히스토리를 처음부터 다시 구성해야 하기 때문입니다.
따라서 멀티 에이전트는 다음 조건을 만족할 때만 도입할 가치가 있습니다.
- 작업을 독립적으로 병렬 실행 가능한 하위 작업으로 쪼갤 수 있어야 합니다 (예: 여러 경쟁사를 각각 조사, 코드베이스의 여러 모듈을 각각 리뷰).
- 작업의 가치가 토큰 비용 증가를 정당화할 만큼 커야 합니다. 단순 Q&A나 순차적 의존성이 강한 작업(예: 이전 단계 결과가 다음 단계 입력이 되는 작업)에는 적합하지 않습니다.
- 결과를 종합하는 최종 단계가 명확해야 합니다. 서브에이전트 출력이 서로 모순되거나 중복될 때 오케스트레이터가 이를 정리할 수 있는 로직이 필요합니다.
반대로 다음과 같은 경우는 단일 에이전트(Day 19의 ReAct)나 워크플로우로 충분합니다: 단계 수가 적고 예측 가능한 작업, 순차적 의존성이 강한 작업, 실시간 응답성이 중요한 작업(멀티 에이전트는 지연 시간도 늘어납니다). “필요할 때만 복잡도를 추가하라"는 원칙은 멀티 에이전트 설계에서 더욱 엄격하게 적용해야 합니다.
📝 핵심 요약
- 멀티 에이전트는 오케스트레이터(리드 에이전트)가 작업을 분해해 서브에이전트에 위임하고, 서브에이전트가 병렬로 실행한 결과를 종합하는 오케스트레이터-워커 패턴을 따른다
- 각 서브에이전트는 독립된 컨텍스트 창과 자기 완결적 작업 설명을 받으며, 서로의 존재를 모른 채 실행된다 — 이 격리가 병렬성과 컨텍스트 오염 방지의 핵심이다
- Claude Agent SDK의 서브에이전트는 별도 세션에서 실행되고 최종 메시지만 부모 에이전트로 반환되어 오케스트레이터의 컨텍스트를 보호한다
- 멀티 에이전트는 단일 에이전트 대비 약 15배의 토큰을 소비하므로, 독립적으로 병렬화 가능하고 가치가 큰 작업에만 선택적으로 도입해야 한다
- 순차적 의존성이 강하거나 실시간 응답이 중요한 작업에는 멀티 에이전트보다 Day 19의 단일 에이전트 패턴이 더 적합하다
🔗 참고 자료
- How we built our multi-agent research system
- Subagents in the SDK
- Multi-agent sessions
- Building Effective AI Agents
⬅️ 이전: Day 19 — 에이전트 설계 패턴 | 다음: Day 21 — RAG 패턴 ➡️