
도메인 모델로 바라본 오케스트레이터-서브에이전트 (Orchestrator-Subagent) 패턴
요약
멀티 에이전트 시스템의 성능 향상을 위해 도메인 주도 설계(DDD)의 개념을 적용하는 오케스트레이터-서브에이전트 패턴을 소개합니다. 모델의 지능보다 작업 분할 방식과 경계 설정이 핵심이며, 서브에이전트를 독립적인 애그리거트로 취급하여 컨텍스트를 격리하는 것이 중요합니다.
핵심 포인트
- 멀티 에이전트 성능 향상은 모델 성능보다 작업 분할 방식에 달려 있음
- 오케스트레이터-서브에이전트 패턴은 DDD의 애그리거트 개념과 유사함
- 서브에이전트는 독립적인 컨텍스트, 도구, 탐색 궤적을 가짐
- 오케스트레이터는 서브에이전트의 내부 추론 과정 대신 결과물만 수신함
언어 모델 (Language Model)에 약간 더 나은 프롬프트 (Prompt)를 제공하면 몇 퍼센트의 성능 향상을 얻을 수 있습니다. 하지만 Anthropic의 측정 결과에 따르면, 모델에게 위임할 수 있는 두 번째 복사본을 제공했을 때 연구 작업에서 단독으로 작동하는 동일 모델 대비 90.2%의 비약적인 상승을 기록했습니다. 여기서 당신을 괴롭힐 부분은 이것입니다. 이 개선은 모델이 더 똑똑해진 것과는 거의 관련이 없었습니다. 리드 에이전트 (Lead Agent)와 워커 (Worker)들은 모두 동일한 계열의 모델을 사용했습니다. 변화된 것은 작업이 분할되는 방식이었습니다.
이것은 머신러닝 (Machine Learning)의 결과가 아닙니다. 도메인 모델링 (Domain Modeling)의 결과입니다. 그리고 이는 에이전트 시스템을 구축하는 대부분의 팀이 거꾸로 가고 있는 지점을 시사합니다. 멀티 에이전트 오케스트레이션 (Multi-agent Orchestration)의 어려운 부분은 프롬프트나 모델 선택이 아닙니다. 그것은 경계 (Boundaries), 불변량 (Invariants), 그리고 실패 처리 (Failure Handling)이며, 즉 도메인 주도 설계 (Domain-Driven Design, DDD)가 해결하기 위해 만들어진 바로 그 문제들입니다. 이는 CQRS와 자격 증명 (Credentialing) 아티클에서 일반적인 백엔드에 사용했던 것과 동일한 관점을 에이전트 스택 (Agent Stack)에 투영한 것입니다.
패턴의 명확한 정의
오케스트레이터-서브에이전트 (Orchestrator-Subagent) 패턴 (때로는 오케스트레이터-워커 (Orchestrator-Worker)라고도 함)은 설명하기 간단합니다. 리드 에이전트가 목표를 설정하고, 접근 방식을 계획하며, 각각 좁은 권한을 가진 서브에이전트(Subagent) 몇 개(보통 3~5개)를 생성합니다. 서브에이전트들은 병렬로 실행됩니다. 작업이 완료되면 리드 에이전트는 그 결과들을 종합하여 하나의 답변으로 합성하며, 종종 마지막에 별도의 인용(Citation) 또는 검증(Verification) 단계를 거칩니다.
이 모든 것을 작동하게 만들면서 동시에 어렵게 만드는 결정적인 세부 사항은 다음과 같습니다. 각 서브에이전트는 자신만의 컨텍스트 윈도우 (Context Window), 자신만의 도구 (Tools), 그리고 자신만의 탐색 궤적 (Exploration Trajectory)을 가지고 독립적인 컨텍스트에서 실행됩니다. 오케스트레이터는 과업을 부여하고 결과물을 돌려받습니다. 오케스트레이터는 서브에이전트의 중간 추론 과정, 막다른 길, 혹은 그 결과에 도달하기 위해 수행된 40번의 도구 호출 (Tool Calls)을 결코 보지 못합니다. 오직 완성된 결과물만이 돌아옵니다.
만약 그러한 구성이 익숙하게 들린다면, 당연한 결과입니다. 여러분은 이미 한 구성 요소가 다른 구성 요소에 지시를 내리고, 내부 구현은 봉인된 채 결과만을 받는 시스템을 모델링해 왔습니다. 그것이 바로 애그리거트 경계 (Aggregate Boundary)이며, 이를 경계로 취급하는 것이 핵심입니다.
컨텍스트 윈도우 (Context Window)는 애그리거트 경계이다
DDD (도메인 주도 설계, Domain-Driven Design)에서 애그리거트 (Aggregate)는 단일 단위로 취급되는 객체들의 클러스터이며, 루트 (Root)만이 유일하게 접근 가능한 합법적인 통로입니다. 애그리거트 내부로 직접 들어가 내부 구현을 읽지 않습니다. 루트에 메시지를 보내면, 루트가 결과를 돌려줍니다. 내부적인 추론 과정은 비공개로 유지됩니다.
서브에이전트 (Subagent)의 컨텍스트 윈도우 (Context Window)는 바로 이러한 경계이며, 이는 규율이 아닌 런타임 (Runtime)에 의해 강제됩니다. 오케스트레이터 (Orchestrator)는 물리적으로 서브에이전트의 윈도우 내부를 볼 수 없습니다. 오직 공개된 결과물만을 받을 뿐입니다. 이는 해당 경계를 넘어 돌아오는 결과물이 단순히 느슨한 문자열들의 집합이 아니라, 적절한 값 객체 (Value Object)로 모델링되어야 함을 의미합니다.
// 결과는 경계를 넘나드는 유일한 요소입니다.
// 가공되지 않은 문자열이 아닌, 출처(Provenance)를 포함한 값 객체로 모델링하세요.
class SubagentResult {
...
이는 자격 증명 (Credentialing) 관련 글에서 다룬 VerifiedCredential과 동일한 방식입니다. 즉, 프라이빗 생성자 (Private Constructor)와 경계에서 불변식 (Invariant)을 강제하는 단일 팩토리 (Factory)를 사용하는 것입니다. 자격 증명 사례에서는 1차 출처 검증 없이는 자격 증명이 존재할 수 없었습니다. 여기서는 출처 없이는 결과가 존재할 수 없습니다. 이렇게 되면 오케스트레이터의 합성 (Synthesis) 단계에서는 각 결과를 방어적으로 재확인할 필요 없이, 자신이 보유한 모든 결과가 이미 그 기준을 통과했다고 가정할 수 있습니다.
오케스트레이션 (Orchestration)이 애그리거트 루트이다
그렇다면 무엇이 불변식 (Invariants)을 소유할까요? 서브에이전트가 아닙니다. 그들은 설계 단계부터 격리되어 있으며 서로의 존재를 알지 못합니다. 오케스트레이션 실행 (Orchestration run) 자체가 애그리거트 루트 (Aggregate Root)입니다. 목표를 보유하고, 어떤 작업이 미결 상태인지 추적하며, 가장 중요한 단 하나의 규칙을 강제하는 주체가 바로 이것입니다.
그 규칙은 여러분이 이전에 보았던 가드(guard)와 같습니다. 즉, 위임된 모든 작업이 해결될 때까지는 합성(synthesize)하지 말라는 것입니다. 자격 증명(credentialing) 도메인에서는 "내용에 빈틈이 있는 상태로는 위원회 검토를 위해 파일을 보낼 수 없다"는 규칙이었습니다. 여기서도 구조는 동일합니다:
class Orchestration {
private status: OrchestrationStatus = OrchestrationStatus.Planning;
private tasks: Map<string, TaskState> = new Map();
...
이 내용 중 AI에 특화된 부분은 전혀 없습니다. 이것은 유효하지 않은 상태로 진입하기를 거부하는 애그리거트(aggregate)일 뿐입니다. "작업(tasks)"이 우연히 웹을 탐색하는 언어 모델(language models)이라는 사실은, 불변식(invariant)이 신경 쓰지 않는 구현 세부 사항(implementation detail)에 불과합니다.
버그는 불변식(Invariant)의 부재에서 발생한다
다음은 Anthropic 팀이 보고한 실제 실패 사례입니다. 오케스트레이터(orchestrator)가 때때로 지나치게 열성적으로 행동하여, 단 두 명의 서브에이전트(subagent)만 있으면 되는 질문에 50명의 서브에이전트를 생성하곤 했습니다. 다른 경우에는 존재하지 않는 출처를 쫓으며 루프(loop)를 돌기도 했습니다.
이를 모델이 불안정(flaky)하기 때문이라고 해석하고 싶은 유혹이 들 것입니다. 하지만 그렇지 않습니다. 적어도 그렇게 해석하는 것은 유용하지 않습니다. 50명의 작업자를 생성하는 현상은, 위임(delegation)이 이를 보호하는 불변식(invariant) 없이 경계가 없는 루프(unbounded loop)일 때 발생하는 일입니다. 도메인에는 규칙이 있고, 팬아웃(fan-out)에는 합리적인 상한선이 있습니다. 만약 그 규칙이 모델 내부 어디에도 존재하지 않는다면, 모델은 그 규칙을 준수할 방법이 없습니다. 따라서 규칙이 위치해야 할 곳, 즉 프롬프트(prompt)에 묻혀 있는 마법의 숫자가 아니라 애그리거트(aggregate)가 참조하는 정책(policy)에 규칙을 두어야 합니다:
class FanOutPolicy {
constructor(private readonly maxSubagents: number) {}
...
이러한 재정의가 중요한 이유는 에이전트 시스템이 실제로 실패하는 지점 때문입니다. AG2 (전 AutoGen), MetaGPT, ChatDev 등을 포함한 7개의 멀티 에이전트 프레임워크(multi-agent frameworks)의 실패 사례를 연구한 결과, 저자들이 '에이전트 간 불일치 (inter-agent misalignment)'라고 부르는 조정 실패(coordination failures)가 전체 오류의 36.94%를 차지한다는 사실이 밝혀졌습니다. 여기에 저자들이 명시한 사양(specification) 및 시스템 설계(system design) 문제인 41.77%를 더하면, 실패의 4분의 3 이상이 모델의 지능 문제(model being dumb)가 아닌 설계 및 조정 문제에서 비롯된다는 것을 알 수 있습니다. 버그는 에이전트 사이의 경계(boundaries)에 존재합니다. 이는 도메인 설계(domain-design) 문제이며, 따라서 도메인 설계로 해결해야 합니다.
서브에이전트(Subagent)의 결과는 반환 값(Return Values)이 아닌 외부 이벤트(External Events)이다
이제 이 부분이 자격 인증(credentialing) 사례보다 실제로 더 어렵게 만드는 지점이자, 비유를 깨뜨려야 하는 부분입니다.
일반적인 도메인에서 상태 전이(transitions)는 결정론적(deterministic)입니다. 깨끗한 파일을 승인하면 매번 승인된 상태가 됩니다. 하지만 서브에이전트는 결정론적이지 않습니다. 동일한 모델에 동일한 권한(mandate)을 두 번 부여하더라도, 두 개의 서로 다른 궤적(trajectories), 두 개의 서로 다른 토큰 비용(token costs)이 발생할 수 있으며, 때로는 아예 돌아오지 않는 작업자(worker)가 생길 수도 있습니다. 서브에이전트 호출을 값을 반환하는 함수(function)로 모델링할 수 없습니다. 대신, 제어할 수 없는 외부에서 도착하거나, 실패하거나, 혹은 중단(hang)될 수도 있는 무언가로 모델링해야 합니다.
다시 말하지만, 이는 자격 인증 모델이 이미 가지고 있던 형태이기도 합니다. 즉, 언제든 발생하여 승인된 제공자를 정지(suspension) 상태로 만들 수 있는 제재(sanction)와 같습니다. 집합체(aggregate)가 대비해야 하는 외부 이벤트인 것입니다. 위임(delegation)도 마찬가지입니다. 모든 서브에이전트는 발생 가능한 잠재적인 SubagentFailed 또는 SubagentTimedOut이며, 오케스트레이션(orchestration)은 이러한 상황이 발생했을 때도 유효한 상태를 유지해야 합니다:
onSubagentTimedOut(taskId: string): DomainEvent[] {
const task = this.tasks.get(taskId);
if (!task || task.isResolved()) return [];
...
태스크를 실패로 표시하는 것은 synthesize()의 가드(guard)가 정직하게 작동하도록 유지하는 핵심입니다. 타임아웃이 발생한 워커(worker)는 해결된(resolved) 태스크이므로, 실행(run)은 영원히 돌아오지 않을 서브에이전트(subagent)를 기다리며 영구적으로 차단되는 대신, 비록 성능은 저하되었을지라도 이미 얻은 결과물을 바탕으로 완료될 수 있습니다. "죽은 자를 기다리지 마라"는 규칙은 네트워킹의 세부 사항이 아니라 도메인 규칙(domain rule)입니다.
비용은 도메인 규칙이다
이 도메인에는 다른 대부분의 도메인에는 없는, 무시하기에는 비용이 많이 드는 불변량(invariant)이 하나 있습니다. 멀티 에이전트(multi-agent) 실행은 비용이 많이 듭니다. Anthropic은 그 수치를 일반적인 채팅 토큰의 약 15배로 책정했으며, 그들의 벤치마크 중 하나에서 토큰 사용량만으로 성능 편차의 약 80%를 설명할 수 있다는 것을 발견했습니다. 여기서 지출(spend)은 부수 효과(side effect)가 아닙니다. 그것은 주요 레버(primary lever)에 가깝습니다.
이로 인해 토큰 예산(token budget)은 자격 증명 모델(credentialing model)에서의 36개월 시계와 마찬가지로 일급 불변량(first-class invariant)이 됩니다. 애그리게이트(aggregate)는 예산을 소유하며 이를 초과하는 과도한 위임(over-delegate)을 거부하므로, 단 하나의 통제 불능인 질문이 한 달 치 지출을 조용히 태워버리는 일을 방지할 수 있습니다.
delegate(tasks: TaskMandate[], budget: TokenBudget, policy: FanOutPolicy): TaskMandate[] {
const slots = policy.admit(this.tasks.size, tasks.length);
const affordable = budget.remaining() > 0 ? slots : 0;
...
만약 플랫폼에 FinOps 가드레일(guardrails)을 연결해 본 적이 있다면, 이것은 동일한 개념을 한 단계 위로 옮긴 것입니다. 즉, 사후에 읽는 대시보드에서가 아니라 지출이 발생하는 시점에 예산을 강제하는 것입니다.
경계가 잘못되었을 때
솔직한 한계는, 현재 이 패턴이 과하게 홍보되고 있다는 점입니다. 오케스트레이션 (Orchestration)을 강력하게 만드는 그 격리 (Isolation)가 바로 그것의 엄격한 제약 조건이기도 합니다. 서브에이전트 (Subagents)들은 서로의 추론 과정을 볼 수 없으며, 오직 오케스트레이터 (Orchestrator)만이 볼 수 있는데, 그마저도 완성된 결과물만을 볼 수 있습니다. 이는 너비 우선 (Breadth-first) 작업에는 환상적인 궁합입니다. 즉, 한 번에 다섯 가지 각도에서 주제를 조사하거나, 독립적인 가지들을 탐색하거나, 병렬로 정보를 수집하는 작업 말입니다. 하지만 각 단계가 이전 단계의 중간 상태 (Intermediate state)에 의존하는 밀접하게 결합된 (Tightly coupled) 작업에는 최악의 궁합입니다. 왜냐하면 당신이 의존하고 있는 그 경계가 바로 상태의 흐름을 막고 있는 바로 그 요소이기 때문입니다.
DDD (도메인 주도 설계, Domain-Driven Design) 용어로 말하자면, 이는 당신이 이미 진단할 줄 아는 문제입니다. 만약 두 애그리거트 (Aggregates)가 자신의 업무를 수행하기 위해 끊임없이 서로의 내부 정보에 접근해야 한다면, 그것들은 두 개의 애그리거트가 아닙니다. 경계를 잘못 설정한 것이며, 그것들은 하나여야 합니다. 여기서도 규칙은 같습니다. 만약 당신의 서브에이전트들이 서로의 미완성된 생각들을 계속 필요로 한다면, 멀티 에이전트 (Multi-agent) 방식은 당신에게 아무런 이득도 주지 못하며, 격리는 15배의 비용 위에 얹어진 순수한 오버헤드 (Overhead)일 뿐입니다. 하나의 컨텍스트 (Context)를 가진 하나의 에이전트로 다시 통합하고 다음 단계로 넘어가십시오.
이것이 조용한 결론입니다. 현재 에이전트 엔지니어링 (Agent engineering)에서 흥미로운 질문은 "어떤 모델을 쓸 것인가"나 "어떤 프롬프트 (Prompt)를 쓸 것인가"가 아닙니다. 그것은 "이 경계는 어디에 위치해야 하는가", "어떤 불변량 (Invariant)이 이것을 하나로 유지하는가", 그리고 "그중 일부가 실패하면 어떤 일이 발생하는가"입니다. 우리는 이 질문들에 대해 지난 20년 동안 이미 좋은 답들을 가지고 있었습니다. 단지 그 답들이 이 특정한 의상을 입고 있었을 뿐입니다.
원문은 andriiboyko.com에 게시되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기
