LLM 단독 호출을 넘어서
멀티 에이전트 워크플로우 실전 가이드
단순 프롬프트 엔지니어링의 한계를 어떻게 극복할까요? 오케스트레이터-에이전트 패턴으로 복잡한 작업을 자동화하는 방법을 코드와 함께 단계별로 살펴봅니다.
왜 지금 멀티 에이전트인가
GPT-4, Claude, Gemini 같은 강력한 모델이 등장하면서 처음에는 "프롬프트만 잘 짜면 된다"는 기대가 높았습니다. 실제로 단순한 Q&A나 요약, 번역 같은 작업은 단일 LLM 호출로도 충분합니다. 하지만 현실의 업무는 다릅니다.
수십 개의 파일을 분석해 보고서를 쓰거나, 외부 API를 조합해 데이터를 수집하고, 그 결과를 검증하고 다시 처리하는 파이프라인—이런 작업에서는 단일 LLM 호출이 컨텍스트 한계와 신뢰성 한계에 금방 부딪힙니다.
멀티 에이전트 워크플로우는 "모델을 더 스마트하게 만드는 것"이 아닙니다. 복잡한 작업을 검증 가능한 작은 단계로 분해하여 각 단계에서 모델이 집중할 수 있도록 설계하는 시스템 엔지니어링입니다.
핵심 패턴 세 가지
실무에서 반복적으로 등장하는 패턴이 있습니다. 모든 복잡한 에이전트 시스템은 결국 이 세 가지를 조합한 것입니다.
계획을 수립하는 LLM이 전문화된 에이전트에게 작업을 위임합니다. 가장 일반적이고 확장성이 높은 패턴입니다.
각 단계의 출력이 다음 단계의 입력이 됩니다. 예측 가능한 순서가 있는 ETL, 문서 처리에 적합합니다.
에이전트 A가 생성하고, 에이전트 B가 평가하고, 결과가 기준을 통과할 때까지 반복합니다. 고품질 출력이 필요할 때 사용합니다.
아키텍처 설계하기
오케스트레이터-에이전트 패턴의 실제 구조를 살펴봅시다. 아래 다이어그램은 "경쟁사 분석 리포트 자동 생성" 시스템의 예입니다.
오케스트레이터는 목표를 받아 계획을 세우고 서브 에이전트를 호출합니다. 각 에이전트는 자신에게 필요한 도구(Tool)만 갖습니다. 이 분리 덕분에 각 에이전트를 독립적으로 테스트하고 교체할 수 있습니다.
파이썬으로 직접 구현하기
기본 에이전트 루프
복잡한 프레임워크 없이도 핵심 로직은 단순합니다. 에이전트의 본질은 생각 → 행동 → 관찰의 반복입니다.
import anthropic from typing import Callable client = anthropic.Anthropic() def run_agent( task: str, tools: list[dict], tool_handlers: dict[str, Callable], max_steps: int = 10 ) -> str: messages = [{"role": "user", "content": task}] for _ in range(max_steps): response = client.messages.create( model="claude-sonnet-4-20250514", max_tokens=4096, tools=tools, messages=messages ) # 도구 호출이 없으면 최종 답변 반환 if response.stop_reason == "end_turn": return response.content[0].text # 도구 호출 처리 tool_results = [] for block in response.content: if block.type == "tool_use": handler = tool_handlers[block.name] result = handler(**block.input) tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": str(result) }) # 대화 히스토리 업데이트 messages += [ {"role": "assistant", "content": response.content}, {"role": "user", "content": tool_results} ] raise RuntimeError("Max steps exceeded")
오케스트레이터 구현
오케스트레이터는 서브 에이전트 자체를 도구로 등록합니다. 이렇게 하면 LLM이 어떤 에이전트를 언제 호출할지 스스로 결정합니다.
def create_orchestrator(): # 서브 에이전트를 도구로 래핑 agent_tools = [ { "name": "research_agent", "description": "웹에서 정보를 수집하고 요약합니다", "input_schema": { "type": "object", "properties": { "query": {"type": "string"}, "depth": {"type": "integer", "default": 3} } } }, { "name": "analysis_agent", "description": "데이터를 분석하고 인사이트를 추출합니다", "input_schema": { "type": "object", "properties": { "data": {"type": "string"}, "focus": {"type": "string"} } } } ] handlers = { "research_agent": lambda **kw: run_agent( kw["query"], research_tools, research_handlers ), "analysis_agent": lambda **kw: run_agent( kw["data"], analysis_tools, analysis_handlers ) } return lambda task: run_agent(task, agent_tools, handlers)
실전에서 마주치는 함정들
멀티 에이전트 시스템은 실패 지점이 여러 개입니다. 단일 에이전트 오류는 쉽게 디버깅할 수 있지만, 에이전트 A의 잘못된 출력이 에이전트 B와 C까지 오염시킬 수 있습니다. 검증 레이어를 반드시 추가하세요.
| 흔한 실수 | 증상 | 해결책 |
|---|---|---|
| 에이전트 컨텍스트 비대화 | 응답 품질 저하, 지연 증가 | 단계마다 컨텍스트 요약 후 전달 |
| 무한 루프 | max_tokens 초과, 비용 폭증 | max_steps + 타임아웃 필수 설정 |
| 도구 오류 미처리 | 에이전트가 멈추거나 환각 발생 | try/except + 폴백 메시지 반환 |
| 병렬 호출 미활용 | 불필요한 순차 실행, 느린 파이프라인 | asyncio.gather로 독립 에이전트 병렬화 |
단일 LLM vs 멀티 에이전트: 언제 무엇을?
모든 작업에 멀티 에이전트가 정답은 아닙니다. 아래 기준으로 판단해 보세요.
| 기준 | 단일 LLM 호출 | 멀티 에이전트 |
|---|---|---|
| 작업 복잡도 | 단순, 명확한 단일 질의 | 다단계, 조건 분기 포함 |
| 외부 도구 의존 | 없거나 1~2개 | 3개 이상, 동적 선택 필요 |
| 응답 시간 | 빠름 (초 이하) | 느림 (수십 초 ~ 분) |
| 비용 | 저렴 | LLM 호출 횟수 비례 증가 |
| 디버깅 용이성 | 입출력 추적 간단 | 중간 상태 로깅 필수 |
다음 단계
멀티 에이전트 시스템의 진입 장벽은 낮아졌습니다. LangGraph, LlamaIndex Workflows, 혹은 직접 구현한 루프— 어떤 방식이든 작은 단위부터 검증하는 것이 핵심입니다.
다음 글에서는 에이전트 간 공유 메모리와 장기 실행 작업에서의 체크포인트 패턴을 다룰 예정입니다.
'자동화 생산성' 카테고리의 다른 글
| AI로 유튜브 채널 혼자 다 만들기 (0) | 2026.05.30 |
|---|---|
| RAG vs. 긴 컨텍스트 윈도우, 뭘 써야 할까? (0) | 2026.05.27 |
| 무료 AI 도구 조합으로 콘텐츠 제작비 0원 만들기 | Gemma + FLUX + Kokoro TTS (1) | 2026.05.23 |
| Claude Code로 GitHub 이슈 담당자 자동 배정 완전 자동화 2026 (0) | 2026.05.10 |
| Claude Code로 코드리뷰 완전 자동화하기 2026 (0) | 2026.05.10 |