분산 Agent 시스템
단일 에이전트의 한계
섹션 제목: “단일 에이전트의 한계”단일 에이전트 시스템은 단순하고 디버깅이 쉽습니다. 하지만 규모가 커지면 한계에 부딪힙니다.
단일 에이전트 한계:- 컨텍스트 윈도우 한도 (대규모 코드베이스 처리 불가)- 직렬 실행 (병렬 작업 불가)- 단일 실패 지점 (에이전트 실패 시 전체 중단)- 특화 불가 (모든 작업을 동일한 에이전트가 처리)분산 에이전트 시스템은 이 문제를 마이크로서비스와 유사한 방식으로 해결합니다.
마이크로서비스 스타일 에이전트
섹션 제목: “마이크로서비스 스타일 에이전트”각 에이전트가 명확한 책임 영역을 갖고 독립적으로 동작합니다.
에이전트 서비스 분해 예시
섹션 제목: “에이전트 서비스 분해 예시”코드 생성 팀├── spec-agent: 요구사항 분석, 스펙 작성├── architect-agent: 설계 결정, 아키텍처 다이어그램├── coder-agent: 실제 코드 구현├── test-agent: 테스트 코드 작성└── reviewer-agent: 코드 리뷰, 피드백에이전트 서비스 인터페이스
섹션 제목: “에이전트 서비스 인터페이스”interface AgentServiceConfig { name: string version: string capabilities: string[] model: string maxConcurrency: number timeoutMs: number}
interface AgentTask { id: string type: string payload: Record<string, unknown> priority: 'low' | 'normal' | 'high' deadline?: Date}
interface AgentResult { taskId: string success: boolean output: unknown tokensUsed: number durationMs: number error?: string}
abstract class BaseAgentService { constructor(protected config: AgentServiceConfig) {}
abstract canHandle(task: AgentTask): boolean abstract execute(task: AgentTask): Promise<AgentResult>
async handleWithTimeout(task: AgentTask): Promise<AgentResult> { const timer = new Promise<AgentResult>((_, reject) => setTimeout( () => reject(new Error(`Task timeout after ${this.config.timeoutMs}ms`)), this.config.timeoutMs ) ) return Promise.race([this.execute(task), timer]) }}이벤트 드리븐 아키텍처
섹션 제목: “이벤트 드리븐 아키텍처”에이전트들이 직접 통신하는 대신 이벤트 버스를 통해 느슨하게 결합됩니다.
이벤트 버스 패턴
섹션 제목: “이벤트 버스 패턴”개발자 작업 요청 ↓ EventBus.publish('task.created', taskData) ↓ ┌───────────────────────────────────┐ │ Event Bus │ └───────────────────────────────────┘ ↓ ↓ ↓spec-agent coder-agent reviewer-agent(구독 중) (구독 중) (구독 중)
각 에이전트가 관련 이벤트만 처리이벤트 버스 구현
섹션 제목: “이벤트 버스 구현”type EventHandler<T = unknown> = (event: AgentEvent<T>) => Promise<void>
interface AgentEvent<T = unknown> { id: string type: string payload: T timestamp: Date source: string correlationId?: string}
class AgentEventBus { private handlers: Map<string, EventHandler[]> = new Map() private eventLog: AgentEvent[] = []
subscribe<T>(eventType: string, handler: EventHandler<T>): () => void { const existing = this.handlers.get(eventType) ?? [] this.handlers.set(eventType, [...existing, handler as EventHandler])
// 구독 취소 함수 반환 return () => { const current = this.handlers.get(eventType) ?? [] this.handlers.set( eventType, current.filter(h => h !== handler) ) } }
async publish<T>(eventType: string, payload: T, source: string): Promise<void> { const event: AgentEvent<T> = { id: crypto.randomUUID(), type: eventType, payload, timestamp: new Date(), source }
this.eventLog.push(event as AgentEvent)
const handlers = this.handlers.get(eventType) ?? [] await Promise.all(handlers.map(h => h(event as AgentEvent))) }
getEventLog(filter?: { type?: string; since?: Date }): AgentEvent[] { return this.eventLog.filter(e => { if (filter?.type && e.type !== filter.type) return false if (filter?.since && e.timestamp < filter.since) return false return true }) }}오케스트레이터 패턴
섹션 제목: “오케스트레이터 패턴”복잡한 작업을 여러 에이전트에게 분배하고 결과를 집계하는 오케스트레이터가 필요합니다.
interface SubTask { agentType: string task: AgentTask dependsOn?: string[] // 다른 서브태스크 ID}
class AgentOrchestrator { constructor( private agents: Map<string, BaseAgentService>, private eventBus: AgentEventBus ) {}
async executeWorkflow(subtasks: SubTask[]): Promise<Map<string, AgentResult>> { const results = new Map<string, AgentResult>() const pending = new Map(subtasks.map(st => [st.task.id, st]))
while (pending.size > 0) { // 의존성이 모두 완료된 태스크 찾기 const ready = [...pending.values()].filter(st => (st.dependsOn ?? []).every(dep => results.has(dep)) )
if (ready.length === 0) { throw new Error('순환 의존성 감지: 실행 불가능한 워크플로우') }
// 준비된 태스크 병렬 실행 await Promise.all( ready.map(async st => { const agent = this.agents.get(st.agentType) if (!agent) throw new Error(`에이전트 미발견: ${st.agentType}`)
const result = await agent.handleWithTimeout(st.task) results.set(st.task.id, result) pending.delete(st.task.id)
await this.eventBus.publish('subtask.completed', result, 'orchestrator') }) ) }
return results }}분산 vs 중앙화 결정 가이드
섹션 제목: “분산 vs 중앙화 결정 가이드”| 기준 | 중앙화 선택 | 분산화 선택 |
|---|---|---|
| 팀 규모 | 1~5명 | 10명 이상 |
| 작업 복잡도 | 단순, 순차적 | 복잡, 병렬 가능 |
| 컨텍스트 크기 | 단일 컨텍스트로 충분 | 다수의 대용량 컨텍스트 필요 |
| 실패 허용도 | 단순 재시도로 충분 | 부분 실패 허용 필요 |
| 운영 복잡도 | 낮게 유지 선호 | 복잡도 수용 가능 |
스케일링 패턴
섹션 제목: “스케일링 패턴”수평 확장
섹션 제목: “수평 확장”동일한 에이전트를 여러 인스턴스로 실행합니다.
class AgentPool { private instances: BaseAgentService[] = [] private queue: AgentTask[] = [] private activeCount = 0
constructor( private factory: () => BaseAgentService, private maxInstances: number ) { // 초기 인스턴스 생성 for (let i = 0; i < Math.min(2, maxInstances); i++) { this.instances.push(factory()) } }
async submit(task: AgentTask): Promise<AgentResult> { if (this.activeCount < this.maxInstances) { return this.executeImmediate(task) } return this.enqueue(task) }
private async executeImmediate(task: AgentTask): Promise<AgentResult> { this.activeCount++ const agent = this.instances[this.activeCount - 1] ?? this.factory()
try { return await agent.handleWithTimeout(task) } finally { this.activeCount-- this.processQueue() } }
private enqueue(task: AgentTask): Promise<AgentResult> { return new Promise((resolve, reject) => { this.queue.push({ ...task, _resolve: resolve, _reject: reject } as AgentTask & { _resolve: (r: AgentResult) => void _reject: (e: Error) => void }) }) }
private processQueue(): void { const next = this.queue.shift() as (AgentTask & { _resolve?: (r: AgentResult) => void }) | undefined if (next && next._resolve) { this.executeImmediate(next).then(next._resolve) } }}분산 에이전트 시스템은 단일 에이전트의 컨텍스트 한계와 직렬 실행 제약을 극복합니다. 이벤트 드리븐 아키텍처로 에이전트 간 느슨한 결합을 유지하고, 오케스트레이터 패턴으로 복잡한 워크플로우를 관리하세요. 하지만 분산 시스템의 복잡성은 실제로 필요한 팀에게만 정당화됩니다. 단순함을 기본값으로 유지하세요.