멀티 에이전트 오케스트레이션: 복잡한 작업을 AI 팀으로 해결

AI 기술

멀티 에이전트AI 오케스트레이션Claude API에이전트 패턴AI 자동화

이 글은 누구를 위한 것인가

  • 복잡한 다단계 작업을 AI로 자동화하려는 팀
  • 오케스트레이터-서브에이전트 패턴을 구현하려는 개발자
  • 병렬 AI 작업으로 속도를 높이려는 팀

들어가며

단일 LLM 호출로 처리하기 너무 복잡한 작업이 있다. "경쟁사 10개를 조사하고, 각각의 가격을 비교하고, 보고서를 작성해라" — 오케스트레이터 에이전트가 작업을 분해하고, 서브에이전트들이 병렬로 실행한다.

이 글은 bluefoxdev.kr의 멀티 에이전트 오케스트레이션 가이드 를 참고하여 작성했습니다.


1. 멀티 에이전트 패턴

[오케스트레이터-서브에이전트 패턴]

오케스트레이터:
  - 작업 분해 (task decomposition)
  - 서브에이전트 할당
  - 결과 집계
  - 에러 처리

서브에이전트:
  - 특화된 단일 작업
  - 도구(tool) 사용 가능
  - 독립적 실행

[실행 모드]
  순차 실행: A → B → C (의존성 있을 때)
  병렬 실행: A || B || C (독립 작업)
  DAG 실행: 의존성 그래프 기반

[에이전트 통신]
  공유 컨텍스트: 중앙 상태 저장소
  메시지 패싱: 큐 기반 비동기
  직접 호출: 오케스트레이터가 결과 전달

[에러 처리]
  재시도: 지수 백오프 (최대 3회)
  폴백: 간소화된 대안 작업
  체크포인트: 중간 결과 저장

2. 멀티 에이전트 구현

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

interface Task {
  id: string;
  type: string;
  input: any;
  dependencies?: string[];
}

interface TaskResult {
  taskId: string;
  success: boolean;
  output?: any;
  error?: string;
}

// 서브에이전트: 특화 작업 실행
async function runSubAgent(task: Task): Promise<TaskResult> {
  const agentPrompts: Record<string, string> = {
    research: '웹 검색 결과를 분석하고 핵심 정보를 추출하는 리서치 전문가입니다.',
    summarize: '복잡한 내용을 명확하게 요약하는 전문가입니다.',
    analyze: '데이터를 분석하고 인사이트를 도출하는 분석가입니다.',
    write: '명확하고 설득력 있는 콘텐츠를 작성하는 전문가입니다.',
  };

  try {
    const response = await client.messages.create({
      model: 'claude-sonnet-4-6',
      max_tokens: 2048,
      system: agentPrompts[task.type] ?? '전문 AI 어시스턴트입니다.',
      messages: [{
        role: 'user',
        content: `작업 ID: ${task.id}\n\n${JSON.stringify(task.input, null, 2)}`,
      }],
    });

    return {
      taskId: task.id,
      success: true,
      output: response.content[0].type === 'text' ? response.content[0].text : '',
    };
  } catch (error) {
    return { taskId: task.id, success: false, error: String(error) };
  }
}

// 오케스트레이터: 작업 분해 + 실행 관리
async function orchestrate(goal: string): Promise<string> {
  // 1단계: 작업 분해
  const planResponse = await client.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 1024,
    system: '작업을 분해하고 실행 계획을 JSON으로 반환하는 오케스트레이터입니다.',
    messages: [{
      role: 'user',
      content: `다음 목표를 달성하기 위한 작업 계획을 JSON으로 작성하세요.

목표: ${goal}

형식:
{"tasks": [{"id": "t1", "type": "research|summarize|analyze|write", "input": {...}, "dependencies": []}]}`,
    }],
  });

  const planText = planResponse.content[0].type === 'text' ? planResponse.content[0].text : '{"tasks":[]}';
  const match = planText.match(/\{[\s\S]*\}/);
  const plan: { tasks: Task[] } = match ? JSON.parse(match[0]) : { tasks: [] };

  // 2단계: 의존성 기반 병렬 실행
  const results = new Map<string, TaskResult>();

  const executeTasks = async (tasks: Task[]) => {
    const ready = tasks.filter(t =>
      !t.dependencies?.length || t.dependencies.every(dep => results.get(dep)?.success)
    );
    if (!ready.length) return;

    // 준비된 작업 병렬 실행
    const batchResults = await Promise.all(ready.map(runSubAgent));
    batchResults.forEach(r => results.set(r.taskId, r));

    // 남은 작업 재귀 실행
    const remaining = tasks.filter(t => !results.has(t.id));
    if (remaining.length) await executeTasks(remaining);
  };

  await executeTasks(plan.tasks);

  // 3단계: 결과 집계
  const aggregateResponse = await client.messages.create({
    model: 'claude-sonnet-4-6',
    max_tokens: 2048,
    system: '에이전트 작업 결과를 통합해 최종 답변을 작성합니다.',
    messages: [{
      role: 'user',
      content: `목표: ${goal}\n\n작업 결과:\n${Array.from(results.values()).map(r => `[${r.taskId}]: ${r.output ?? r.error}`).join('\n\n')}\n\n위 결과를 바탕으로 최종 보고서를 작성하세요.`,
    }],
  });

  return aggregateResponse.content[0].type === 'text' ? aggregateResponse.content[0].text : '';
}

마무리

멀티 에이전트의 핵심은 작업 분해와 병렬 실행이다. 오케스트레이터가 의존성 그래프를 분석해 독립 작업을 Promise.all로 병렬 처리하면 전체 실행 시간을 크게 줄인다. 각 서브에이전트는 특화된 역할을 가지며, 실패 시 체크포인트에서 재시작할 수 있도록 중간 결과를 저장한다.