콘텐츠로 이동
Data Prep
상세

LLM 오케스트레이션 프레임워크 가이드

LLM 애플리케이션 개발을 위한 오케스트레이션 프레임워크 비교 및 선택 가이드. 각 프레임워크의 철학, 아키텍처, 장단점을 분석하고 상황별 최적 선택을 제시한다.


개요

LLM 오케스트레이션이란 LLM 호출, 데이터 검색, 도구 사용, 메모리 관리 등을 체계적으로 조합하여 복잡한 AI 애플리케이션을 구축하는 것을 의미한다. 단순한 API 호출을 넘어 프롬프트 관리, 에러 처리, 평가, 관측까지 통합적으로 다룬다.

왜 프레임워크가 필요한가

직접 구현 시 문제 프레임워크가 해결하는 것
프롬프트 관리 파편화 템플릿 시스템, 버전 관리
LLM 호출 에러 처리 반복 재시도, fallback, rate limit 관리
RAG 파이프라인 매번 재구현 검색-생성 통합 추상화
에이전트 루프 구현 복잡 Agent 추상화, 도구 연동
디버깅 어려움 트레이싱, 로깅 내장
평가 체계 부재 평가 프레임워크 통합

프레임워크 비교

종합 비교표

프레임워크 철학 언어 Stars 주요 패턴 학습 곡선
LangChain 범용 빌딩 블록 Python, JS 100k+ Chain, Agent, Graph 높음
LlamaIndex 데이터 프레임워크 Python, TS 37k+ Index, Query Engine 중간
DSPy 프로그래밍 > 프롬프팅 Python 20k+ Module, Optimizer 높음
Semantic Kernel 엔터프라이즈 AI C#, Python, Java 22k+ Plugin, Planner 중간
Haystack 파이프라인 중심 Python 18k+ Pipeline, Component 중간
CrewAI 멀티 에이전트 Python 25k+ Agent, Task, Crew 낮음
AutoGen 대화형 에이전트 Python 35k+ Agent, GroupChat 중간

상세 비교

항목 LangChain LlamaIndex DSPy Semantic Kernel
핵심 강점 생태계 크기, 통합 RAG 최적화, 인덱싱 자동 프롬프트 최적화 엔터프라이즈, 다언어
약점 추상화 과다, 복잡 RAG 외 기능 제한적 학습 곡선, 문서 부족 생태계 작음
RAG 지원 좋음 최고 좋음 (모듈형) 좋음
Agent 지원 LangGraph (최고) 기본 기본 Planner
메모리 다양한 옵션 ChatMemory 없음 ChatHistory
스트리밍 지원 지원 제한적 지원
Observability LangSmith 통합 기본 콜백 없음 기본 로깅
프로덕션 사례 많음 많음 증가 중 Microsoft 생태계

아키텍처 패턴

1. Chain 패턴

순차적으로 단계를 연결하는 가장 기본적인 패턴.

Input → [Prompt Template] → [LLM] → [Output Parser] → Output

적합한 경우: 단순 변환, 분류, 요약 등 단일 단계 작업

2. Agent 패턴

LLM이 도구를 선택하고 반복적으로 실행하는 자율적 패턴.

┌─────────────────────────────────────────┐
│              Agent Loop                 │
│                                         │
│  Input ──▶ [LLM: 판단] ──▶ 완료?       │
│              │                 │        │
│              │ No              │ Yes    │
│              ▼                 ▼        │
│         [Tool 선택]        Output      │
│              │                          │
│              ▼                          │
│         [Tool 실행]                     │
│              │                          │
│              ▼                          │
│         [결과 관찰] ───▶ [LLM: 판단]   │
│                                         │
└─────────────────────────────────────────┘

적합한 경우: 도구 사용, 복잡한 질의, 동적 워크플로

3. Graph 패턴 (LangGraph)

노드와 엣지로 복잡한 워크플로를 정의하는 패턴.

┌─────────────────────────────────────────────┐
│              Graph Workflow                  │
│                                             │
│  [분류기] ──┬──▶ [SQL 생성] ──▶ [실행]     │
│             │                      │        │
│             ├──▶ [RAG 검색] ──▶ [생성]     │
│             │                      │        │
│             └──▶ [예측 API] ──────┐│        │
│                                   ││        │
│                       ┌───────────┘│        │
│                       ▼            ▼        │
│                  [결과 통합] ──▶ [응답]     │
│                                             │
└─────────────────────────────────────────────┘

적합한 경우: 조건 분기, 병렬 처리, Human-in-the-loop, 복잡한 에이전트

4. Pipeline 패턴 (Haystack)

재사용 가능한 컴포넌트를 파이프라인으로 조립하는 패턴.

[Converter] → [Splitter] → [Embedder] → [Writer]
[Query] → [Embedder] → [Retriever] → [Ranker] → [Generator]

적합한 경우: 모듈화된 ETL + 검색 + 생성 파이프라인


상황별 선택 가이드

어떤 앱을 만드는가?
├── RAG 앱 (문서 Q&A)
│   ├── 단순 RAG → LlamaIndex
│   ├── 복잡한 인덱싱 전략 → LlamaIndex
│   └── Agent + RAG 결합 → LangGraph + LlamaIndex
├── Agent 시스템
│   ├── 단일 에이전트 (도구 사용) → LangGraph
│   ├── 멀티 에이전트 (역할 분담) → CrewAI / AutoGen
│   └── 복잡한 워크플로 (조건, 루프) → LangGraph
├── 프롬프트 최적화
│   └── 자동 프롬프트 튜닝 → DSPy
├── 엔터프라이즈 통합
│   ├── Microsoft 생태계 (.NET, Azure) → Semantic Kernel
│   └── Java 백엔드 → Semantic Kernel / Spring AI
└── 빠른 프로토타입
    ├── 간단한 체인 → LangChain
    └── 에이전트 프로토타입 → CrewAI

선택 매트릭스

요구사항 1순위 2순위 비고
RAG 앱 LlamaIndex LangChain LlamaIndex가 인덱싱 전략 풍부
도구 사용 에이전트 LangGraph CrewAI LangGraph가 제어 정밀도 높음
멀티 에이전트 CrewAI AutoGen CrewAI가 단순, AutoGen이 유연
프롬프트 최적화 DSPy - 유일한 전용 프레임워크
엔터프라이즈 Semantic Kernel Haystack 다언어, 보안, 거버넌스
최소 오버헤드 직접 구현 DSPy 프레임워크 없이 가능한지 먼저 검토

코드 비교: 동일한 RAG 파이프라인

요구사항

문서를 검색하고 답변을 생성하는 기본 RAG 파이프라인.

LangChain 구현

from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser

# 1. 인덱싱
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(documents)
vectorstore = Chroma.from_documents(chunks, OpenAIEmbeddings())
retriever = vectorstore.as_retriever(search_kwargs={"k": 5})

# 2. 체인 구성
prompt = ChatPromptTemplate.from_template("""
다음 문서를 참고하여 질문에 답하세요.

문서:
{context}

질문: {question}
""")

chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | ChatOpenAI(model="gpt-4o", temperature=0)
    | StrOutputParser()
)

# 3. 실행
answer = chain.invoke("빈집 정비 절차는?")

LlamaIndex 구현

from llama_index.core import (
    VectorStoreIndex, 
    SimpleDirectoryReader,
    Settings,
)
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding

# 1. 설정
Settings.llm = OpenAI(model="gpt-4o", temperature=0)
Settings.embed_model = OpenAIEmbedding()

# 2. 인덱싱 + 쿼리 엔진 (한 줄)
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=5)

# 3. 실행
response = query_engine.query("빈집 정비 절차는?")
print(response)

DSPy 구현

import dspy

# 1. 설정
lm = dspy.LM("openai/gpt-4o", temperature=0)
dspy.configure(lm=lm)

# 2. Retriever 정의
retriever = dspy.ColBERTv2(url="http://colbert-server:8893/api/search")

# 3. RAG 모듈 정의
class RAG(dspy.Module):
    def __init__(self, num_passages=5):
        self.retrieve = dspy.Retrieve(k=num_passages)
        self.generate = dspy.ChainOfThought("context, question -> answer")

    def forward(self, question):
        context = self.retrieve(question).passages
        return self.generate(context=context, question=question)

# 4. 컴파일 (자동 프롬프트 최적화)
from dspy.teleprompt import BootstrapFewShot

trainset = [
    dspy.Example(
        question="빈집 정비 절차는?",
        answer="빈집 정비는 실태조사, 정비계획 수립..."
    ).with_inputs("question"),
]

optimizer = BootstrapFewShot(metric=dspy.evaluate.answer_exact_match)
compiled_rag = optimizer.compile(RAG(), trainset=trainset)

# 5. 실행
result = compiled_rag("빈집 정비 절차는?")

구현 비교

항목 LangChain LlamaIndex DSPy
코드량 중간 (~20줄) 적음 (~10줄) 많음 (~30줄)
커스터마이징 높음 중간 높음 (자동 최적화)
프롬프트 관리 수동 자동 (기본) 자동 (최적화)
디버깅 용이성 좋음 (LangSmith) 보통 보통
프로덕션 전환 직접적 직접적 컴파일 단계 필요

성능 비교

오버헤드 측정 (동일 RAG 파이프라인)

항목 직접 구현 LangChain LlamaIndex DSPy
콜드 스타트 100ms 350ms 280ms 200ms
요청당 오버헤드 ~0ms ~15ms ~10ms ~5ms
메모리 (idle) 50MB 180MB 150MB 80MB
의존성 수 3-5 50+ 30+ 10+

참고: LLM API 호출 자체가 500ms-3s이므로 프레임워크 오버헤드는 대부분 무시할 수준.

복잡도 비교

시나리오 직접 구현 LangChain LlamaIndex
단순 RAG 쉬움 쉬움 매우 쉬움
멀티모달 RAG 어려움 중간 쉬움
에이전트 + 도구 매우 어려움 쉬움 (LangGraph) 어려움
프롬프트 최적화 수동 (노가다) 수동 수동
프롬프트 자동 최적화 불가 불가 불가

DSPy의 경우 프롬프트 자동 최적화가 가능하여, 반복적인 프롬프트 엔지니어링이 필요한 경우 큰 이점이 있다.


프로덕션 고려사항

에러 핸들링

# LangChain: fallback chain
from langchain_core.runnables import RunnableWithFallbacks

primary = ChatOpenAI(model="gpt-4o")
fallback = ChatOpenAI(model="gpt-4o-mini")

chain_with_fallback = primary.with_fallbacks([fallback])
# 직접 구현: 재시도 + fallback
import tenacity

MODELS = ["gpt-4o", "gpt-4o-mini", "claude-3.5-sonnet"]

@tenacity.retry(
    stop=tenacity.stop_after_attempt(3),
    wait=tenacity.wait_exponential(min=1, max=10),
    retry=tenacity.retry_if_exception_type(
        (openai.RateLimitError, openai.APITimeoutError)
    ),
)
async def call_with_retry(messages, model_index=0):
    try:
        return await client.chat.completions.create(
            model=MODELS[model_index],
            messages=messages,
        )
    except openai.RateLimitError:
        if model_index + 1 < len(MODELS):
            return await call_with_retry(messages, model_index + 1)
        raise

재시도 전략

에러 타입 전략 최대 재시도
Rate Limit (429) Exponential backoff 5회
Timeout 즉시 재시도 3회
Server Error (500) Exponential backoff 3회
Invalid Response 프롬프트 변경 후 재시도 2회
Context Length 입력 축소 후 재시도 1회

Fallback 전략

Primary Model (gpt-4o)
    ├── Rate Limit → Secondary Model (claude-3.5-sonnet)
    ├── Timeout → Same model, 재시도
    ├── Context Too Long → 입력 축소 후 재시도
    └── 전체 실패 → Cached response 또는 에러 메시지

프로덕션 체크리스트

항목 설명 우선순위
재시도 로직 Exponential backoff + jitter 필수
Fallback 모델 주 모델 장애 시 대체 모델 필수
Rate limit 관리 토큰 버킷, 큐잉 필수
타임아웃 설정 요청별 적절한 timeout 필수
입력 검증 토큰 수 사전 체크, 유해 입력 필터 필수
출력 검증 JSON 파싱, 포맷 체크 권장
캐싱 Semantic cache, exact cache 권장
비동기 처리 async/await, 배치 API 권장
모니터링 메트릭 수집, 알림 필수
비용 제한 일일/월간 비용 cap 권장

프레임워크 없이 가능한 경우

모든 프로젝트에 프레임워크가 필요한 것은 아니다.

프레임워크 불필요

  • 단일 LLM 호출 (분류, 요약 등)
  • 고정된 2-3단계 파이프라인
  • 팀에 LLM 경험이 풍부
  • 최소 의존성 요구

프레임워크 권장

  • RAG + Agent + 도구 결합
  • 멀티 에이전트 시스템
  • 빠른 프로토타이핑 후 반복
  • 팀에 LLM 경험이 부족
  • 평가/모니터링 통합 필요

참고 자료

자료 링크
LangChain Documentation https://python.langchain.com/docs/
LlamaIndex Documentation https://docs.llamaindex.ai/
DSPy Documentation https://dspy.ai/
Semantic Kernel https://learn.microsoft.com/semantic-kernel/
LangGraph Guide https://langchain-ai.github.io/langgraph/

최종 업데이트: 2026-03-25