콘텐츠로 이동

분산 Agent 시스템

단일 에이전트 시스템은 단순하고 디버깅이 쉽습니다. 하지만 규모가 커지면 한계에 부딪힙니다.

단일 에이전트 한계:
- 컨텍스트 윈도우 한도 (대규모 코드베이스 처리 불가)
- 직렬 실행 (병렬 작업 불가)
- 단일 실패 지점 (에이전트 실패 시 전체 중단)
- 특화 불가 (모든 작업을 동일한 에이전트가 처리)

분산 에이전트 시스템은 이 문제를 마이크로서비스와 유사한 방식으로 해결합니다.

마이크로서비스 스타일 에이전트

섹션 제목: “마이크로서비스 스타일 에이전트”

각 에이전트가 명확한 책임 영역을 갖고 독립적으로 동작합니다.

코드 생성 팀
├── spec-agent: 요구사항 분석, 스펙 작성
├── architect-agent: 설계 결정, 아키텍처 다이어그램
├── coder-agent: 실제 코드 구현
├── test-agent: 테스트 코드 작성
└── reviewer-agent: 코드 리뷰, 피드백
agents/base-agent-service.ts
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
(구독 중) (구독 중) (구독 중)
각 에이전트가 관련 이벤트만 처리
harness/event-bus.ts
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
})
}
}

복잡한 작업을 여러 에이전트에게 분배하고 결과를 집계하는 오케스트레이터가 필요합니다.

harness/orchestrator.ts
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
}
}

| 기준 | 중앙화 선택 | 분산화 선택 | |---|---|---| | 팀 규모 | 1~5명 | 10명 이상 | | 작업 복잡도 | 단순, 순차적 | 복잡, 병렬 가능 | | 컨텍스트 크기 | 단일 컨텍스트로 충분 | 다수의 대용량 컨텍스트 필요 | | 실패 허용도 | 단순 재시도로 충분 | 부분 실패 허용 필요 | | 운영 복잡도 | 낮게 유지 선호 | 복잡도 수용 가능 |

동일한 에이전트를 여러 인스턴스로 실행합니다.

harness/agent-pool.ts
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)
}
}
}

분산 에이전트 시스템은 단일 에이전트의 컨텍스트 한계와 직렬 실행 제약을 극복합니다. 이벤트 드리븐 아키텍처로 에이전트 간 느슨한 결합을 유지하고, 오케스트레이터 패턴으로 복잡한 워크플로우를 관리하세요. 하지만 분산 시스템의 복잡성은 실제로 필요한 팀에게만 정당화됩니다. 단순함을 기본값으로 유지하세요.