- Published on
LLM Agent Framework 비교: AutoGen vs CrewAI vs LangGraph 실전 선택 가이드
- Authors
- Name
- 들어가며
- 에이전트 프레임워크가 필요한 이유
- 아키텍처 비교
- 멀티 에이전트 패턴 비교표
- 각 프레임워크 실전 코드 예제
- 도구 통합 비교
- 메모리와 상태 관리
- 프로덕션 배포 고려사항
- 실패 사례와 복구 전략
- 선택 가이드: 의사결정 트리
- 운영 시 주의사항
- 마치며
- 참고자료

들어가며
LLM 기반 에이전트 시스템은 단순한 챗봇을 넘어서, 도구를 사용하고 계획을 세우며 멀티스텝 작업을 자율적으로 수행하는 AI 애플리케이션의 핵심 아키텍처로 자리잡았다. 2024~2025년 동안 에이전트 프레임워크 생태계는 폭발적으로 성장했고, 2026년 현재 프로덕션 수준의 안정성을 갖춘 세 가지 프레임워크가 시장의 주류로 확립되었다.
AutoGen (Microsoft)은 대화 기반 멀티 에이전트 상호작용에 특화되어, 에이전트 간 자연어 대화를 통해 복잡한 작업을 해결하는 패러다임을 제시했다. CrewAI는 역할 기반 에이전트 팀을 직관적인 API로 구성하여 빠른 프로토타이핑과 비즈니스 워크플로우 자동화에 강점을 보인다. LangGraph (LangChain)는 유향 그래프(directed graph)로 에이전트 워크플로우를 모델링하여, 복잡한 상태 관리와 조건부 분기가 필요한 프로덕션 시스템에 최적화되어 있다.
각 프레임워크는 서로 다른 설계 철학과 강점을 가지고 있으며, 프로젝트의 요구사항에 따라 최적의 선택이 달라진다. 이 글에서는 세 프레임워크의 아키텍처를 심층 비교하고, 실전 코드 예제를 통해 각각의 사용법을 익히며, 프로덕션 배포에서 마주치는 실패 사례와 복구 전략까지 포괄적으로 다룬다.
에이전트 프레임워크가 필요한 이유
단일 LLM 호출의 한계
단순한 LLM API 호출은 다음과 같은 한계를 가진다.
- 단발성 응답: 한 번의 호출로 복잡한 멀티스텝 작업을 완료할 수 없다. 조사, 분석, 실행, 검증의 연쇄 작업이 필요한 경우 여러 번의 호출을 조율해야 한다.
- 도구 사용 제약: LLM이 외부 API, 데이터베이스, 파일 시스템 등과 상호작용하려면 Function Calling 결과를 다시 LLM에 피드백하는 루프가 필요하다.
- 상태 관리 부재: 대화 히스토리, 작업 진행 상황, 중간 결과물을 체계적으로 관리하는 메커니즘이 없다.
- 오류 복구 부재: API 호출 실패, 환각(hallucination), 무한 루프 등에 대한 시스템 수준의 대응이 불가능하다.
에이전트 프레임워크의 역할
에이전트 프레임워크는 이러한 문제를 다음과 같이 해결한다.
| 문제 | 프레임워크 해결 방식 |
|---|---|
| 멀티스텝 실행 | 계획(planning) -> 실행(execution) -> 관찰(observation) 루프 자동 관리 |
| 도구 통합 | 도구 레지스트리, 입출력 스키마 자동 생성, 결과 파싱 |
| 상태 관리 | 대화 메모리, 작업 상태, 중간 결과물 영속화 |
| 오류 복구 | 재시도 정책, 타임아웃, 폴백 전략, Human-in-the-loop |
| 관찰성 | 실행 트레이싱, 토큰 사용량 추적, 디버깅 도구 |
아키텍처 비교
AutoGen: 대화 중심 멀티 에이전트
AutoGen의 핵심 설계 철학은 "에이전트 간 대화를 통한 문제 해결"이다. 모든 상호작용이 메시지 기반이며, 에이전트들은 대화 패턴(conversation pattern)에 따라 발언 순서와 내용이 결정된다.
핵심 구성 요소:
- AssistantAgent: LLM 기반 에이전트로, 시스템 프롬프트와 도구를 가진다
- UserProxyAgent: 사용자를 대리하는 에이전트로, 코드 실행이나 사용자 입력 전달을 담당한다 (v0.4에서는 별도의 사용자 프록시 없이 AssistantAgent와 팀으로 구성)
- GroupChat / Teams: 여러 에이전트가 참여하는 그룹 대화 또는 팀 구조
- ConversationPattern: RoundRobin, Selector, Swarm 등 대화 흐름 패턴
아키텍처 특징:
- 모든 에이전트 상호작용이 메시지 교환으로 이루어진다
- 에이전트 간 관계가 대화 패턴으로 정의된다
- 코드 실행 환경(Docker, 로컬)을 내장으로 제공한다
- v0.4(AgentChat)에서 비동기(async) 우선 설계를 채택했다
CrewAI: 역할 기반 에이전트 팀
CrewAI는 "각 에이전트에게 명확한 역할(role)과 목표(goal)를 부여하고, 태스크(task)를 할당하여 팀으로 운영한다"는 직관적인 비유를 사용한다. 실제 조직의 팀 운영과 유사한 모델이다.
핵심 구성 요소:
- Agent: 역할(role), 목표(goal), 배경(backstory), 도구(tools)를 가진 에이전트
- Task: 에이전트에게 할당되는 구체적 작업 단위
- Crew: 에이전트들과 태스크를 조합한 실행 단위
- Process: Sequential(순차), Hierarchical(계층), Parallel(병렬) 실행 방식
아키텍처 특징:
- LangChain에 의존하지 않는 독립적 구현(자체 엔진)
- YAML 기반 에이전트/태스크 설정 파일 분리 지원
- 태스크 간 결과 위임(delegation) 메커니즘 내장
- 빠른 프로토타이핑에 최적화된 간결한 API
LangGraph: 그래프 기반 상태 머신
LangGraph는 에이전트 워크플로우를 유향 그래프(directed graph) 로 모델링한다. 각 노드는 실행 함수이고, 엣지는 상태에 따른 전이(transition)를 정의한다. 이는 본질적으로 상태 머신(state machine)의 구조와 동일하다.
핵심 구성 요소:
- StateGraph: 상태를 관리하는 유향 그래프
- State (TypedDict): 그래프 전체에서 공유되는 타입 안전한 상태
- Node: 상태를 입력받아 업데이트를 반환하는 함수
- Edge: 노드 간 전이를 정의 (조건부 엣지 포함)
- Checkpointer: 상태를 영속화하여 재개 가능한 실행 지원
아키텍처 특징:
- 명시적인 제어 흐름으로 예측 가능한 실행
- 타입 안전한 상태 관리 (TypedDict 또는 Pydantic)
- 내장 Human-in-the-loop 지원
- LangSmith와의 통합을 통한 뛰어난 관찰성
- v1.0 도달 (2025 말)로 API 안정화
멀티 에이전트 패턴 비교표
| 비교 항목 | AutoGen | CrewAI | LangGraph |
|---|---|---|---|
| 설계 철학 | 대화 중심 협업 | 역할 기반 팀 | 그래프 기반 상태 머신 |
| 에이전트 정의 | AssistantAgent + 시스템 프롬프트 | Agent(role, goal, backstory) | Node 함수 + State |
| 오케스트레이션 | ConversationPattern (RoundRobin, Selector, Swarm) | Process (Sequential, Hierarchical, Parallel) | 조건부 엣지 + 상태 전이 |
| 상태 관리 | 대화 히스토리 기반 | 태스크 간 컨텍스트 전달 | TypedDict/Pydantic 상태 객체 |
| 도구 통합 | Function 기반 도구 등록 | @tool 데코레이터 / 내장 도구 | ToolNode + tools_condition |
| Human-in-the-loop | UserProxy 패턴 | 태스크 수준 승인 | Interrupt + Checkpointer |
| 코드 실행 | Docker/로컬 내장 | 별도 도구로 구현 | 별도 도구로 구현 |
| 메모리 | 대화 기반 단기/장기 | 단기 + 선택적 장기 | Checkpointer 기반 영속화 |
| 관찰성 | AutoGen Studio UI | 로깅 기반 | LangSmith 통합 (추적/재생) |
| 학습 곡선 | 중간 | 낮음 | 높음 |
| 프로덕션 성숙도 | 중간 (MS Agent Framework 전환 중) | 중상 | 높음 (v1.0) |
| 라이선스 | MIT | MIT | MIT |
| Python 버전 | 3.10+ | 3.10~3.13 | 3.9+ |
각 프레임워크 실전 코드 예제
AutoGen: 리서치 에이전트 팀 구성
AutoGen v0.4 AgentChat API를 사용한 멀티 에이전트 리서치 시스템 예제이다.
import asyncio
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat, SelectorGroupChat
from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
from autogen_ext.models.openai import OpenAIChatCompletionClient
# 1. LLM 클라이언트 설정
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
api_key="your-api-key",
)
# 2. 도구 정의
async def search_web(query: str) -> str:
"""웹에서 정보를 검색합니다."""
# 실제로는 Tavily, SerpAPI 등 사용
return f"'{query}'에 대한 검색 결과: [검색 결과 요약]"
async def analyze_data(data: str) -> str:
"""데이터를 분석하고 인사이트를 추출합니다."""
return f"분석 결과: {data[:100]}에 대한 심층 분석 완료"
# 3. 에이전트 정의
researcher = AssistantAgent(
name="Researcher",
model_client=model_client,
system_message="""당신은 전문 리서처입니다.
주어진 주제에 대해 웹 검색을 수행하고 관련 정보를 수집합니다.
수집한 정보를 구조화하여 Analyst에게 전달합니다.""",
tools=[search_web],
)
analyst = AssistantAgent(
name="Analyst",
model_client=model_client,
system_message="""당신은 데이터 분석가입니다.
Researcher가 수집한 정보를 분석하고 핵심 인사이트를 추출합니다.
분석 결과를 Writer에게 전달합니다.""",
tools=[analyze_data],
)
writer = AssistantAgent(
name="Writer",
model_client=model_client,
system_message="""당신은 보고서 작성자입니다.
Analyst의 분석 결과를 바탕으로 최종 보고서를 작성합니다.
보고서 작성이 완료되면 'TERMINATE'를 출력합니다.""",
)
# 4. 팀 구성 및 실행
termination = MaxMessageTermination(max_messages=15) | TextMentionTermination("TERMINATE")
# SelectorGroupChat: LLM이 다음 발언자를 선택
team = SelectorGroupChat(
participants=[researcher, analyst, writer],
model_client=model_client,
termination_condition=termination,
selector_prompt="""다음 발언자를 선택하세요.
정보 수집이 필요하면 Researcher, 분석이 필요하면 Analyst,
최종 보고서 작성이 필요하면 Writer를 선택합니다.""",
)
async def main():
result = await team.run(
task="2026년 AI 에이전트 프레임워크 시장 동향을 조사하고 보고서를 작성하세요."
)
for message in result.messages:
print(f"[{message.source}]: {message.content[:200]}")
print(f"\n총 메시지 수: {len(result.messages)}")
asyncio.run(main())
CrewAI: 콘텐츠 제작 파이프라인
CrewAI를 사용한 블로그 포스트 자동 생성 파이프라인 예제이다.
from crewai import Agent, Task, Crew, Process
from crewai.tools import tool
from langchain_openai import ChatOpenAI
# 1. LLM 설정
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
# 2. 커스텀 도구 정의
@tool("Search Tool")
def search_tool(query: str) -> str:
"""주어진 쿼리로 웹 검색을 수행합니다."""
# 실제로는 Tavily, SerpAPI 등 사용
return f"'{query}' 검색 결과: [관련 정보 요약]"
@tool("SEO Analyzer")
def seo_analyzer(content: str) -> str:
"""콘텐츠의 SEO 점수를 분석합니다."""
word_count = len(content.split())
return f"단어 수: {word_count}, SEO 점수: {'양호' if word_count > 500 else '부족'}"
# 3. 에이전트 정의
research_agent = Agent(
role="기술 리서처",
goal="최신 기술 트렌드를 조사하고 정확한 정보를 수집한다",
backstory="""10년 경력의 기술 저널리스트로, AI와 소프트웨어 엔지니어링
분야의 최신 동향을 항상 파악하고 있습니다.""",
tools=[search_tool],
llm=llm,
verbose=True,
allow_delegation=True, # 다른 에이전트에게 작업 위임 가능
)
writer_agent = Agent(
role="기술 블로그 작성자",
goal="독자가 쉽게 이해할 수 있는 깊이 있는 기술 블로그를 작성한다",
backstory="""기술 블로그 전문 작가로, 복잡한 기술 개념을
명확하고 실용적인 글로 풀어내는 것에 능숙합니다.""",
llm=llm,
verbose=True,
)
editor_agent = Agent(
role="편집장",
goal="콘텐츠의 품질, 정확성, SEO 최적화를 검수한다",
backstory="""기술 미디어 편집장으로, 콘텐츠의 기술적 정확성과
가독성, SEO 최적화를 동시에 추구합니다.""",
tools=[seo_analyzer],
llm=llm,
verbose=True,
)
# 4. 태스크 정의
research_task = Task(
description="""'{topic}'에 대한 종합 리서치를 수행합니다.
- 최신 동향 3가지 이상 수집
- 핵심 기술 개념 정리
- 실제 사용 사례 2건 이상 포함""",
expected_output="구조화된 리서치 보고서 (마크다운 형식)",
agent=research_agent,
)
writing_task = Task(
description="""리서치 결과를 바탕으로 기술 블로그 포스트를 작성합니다.
- 2000자 이상의 본문
- 코드 예제 2개 이상 포함
- 실무자 관점의 인사이트 포함""",
expected_output="완성된 블로그 포스트 (마크다운 형식)",
agent=writer_agent,
context=[research_task], # research_task 결과를 컨텍스트로 받음
)
editing_task = Task(
description="""작성된 블로그 포스트를 검수합니다.
- 기술적 정확성 확인
- 가독성 개선
- SEO 분석 수행
- 최종 수정본 출력""",
expected_output="검수 완료된 최종 블로그 포스트",
agent=editor_agent,
context=[writing_task],
)
# 5. Crew 구성 및 실행
crew = Crew(
agents=[research_agent, writer_agent, editor_agent],
tasks=[research_task, writing_task, editing_task],
process=Process.sequential, # 순차 실행
verbose=True,
memory=True, # 단기 메모리 활성화
max_rpm=10, # API 호출 속도 제한
)
# 실행
result = crew.kickoff(inputs={"topic": "LLM 에이전트 프레임워크 비교"})
print(f"최종 결과:\n{result}")
print(f"토큰 사용량: {crew.usage_metrics}")
LangGraph: 조건부 분기가 있는 고객 지원 에이전트
LangGraph를 사용한 상태 기반 고객 지원 에이전트 예제이다. 조건부 분기, Human-in-the-loop, 오류 복구를 포함한다.
from typing import Annotated, Literal, TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition
from langgraph.checkpoint.memory import MemorySaver
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage, SystemMessage
# 1. 상태 정의
class SupportState(TypedDict):
messages: Annotated[list, add_messages]
category: str # 문의 카테고리 (billing, technical, general)
sentiment: str # 고객 감정 (positive, neutral, negative)
escalated: bool # 상위 담당자 에스컬레이션 여부
resolution: str # 해결 상태
# 2. 도구 정의
@tool
def lookup_order(order_id: str) -> str:
"""주문 정보를 조회합니다."""
orders = {
"ORD-001": {"status": "배송중", "eta": "2026-03-11"},
"ORD-002": {"status": "배송완료", "delivered": "2026-03-07"},
}
if order_id in orders:
return f"주문 {order_id}: {orders[order_id]}"
return f"주문 {order_id}를 찾을 수 없습니다."
@tool
def create_ticket(summary: str, priority: str) -> str:
"""고객 지원 티켓을 생성합니다."""
return f"티켓 생성 완료 - 요약: {summary}, 우선순위: {priority}, 티켓 ID: TKT-{hash(summary) % 10000}"
@tool
def refund_order(order_id: str, reason: str) -> str:
"""주문 환불을 처리합니다. (승인 필요)"""
return f"환불 요청 접수: {order_id}, 사유: {reason}. 관리자 승인 대기중."
tools = [lookup_order, create_ticket, refund_order]
llm = ChatOpenAI(model="gpt-4o", temperature=0).bind_tools(tools)
# 3. 노드 함수 정의
def classify_intent(state: SupportState) -> dict:
"""고객 문의를 분류합니다."""
classifier_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
last_message = state["messages"][-1].content
response = classifier_llm.invoke([
SystemMessage(content="""고객 문의를 분류하세요.
카테고리: billing, technical, general
감정: positive, neutral, negative
JSON 형식으로 응답: {"category": "...", "sentiment": "..."}"""),
HumanMessage(content=last_message)
])
import json
try:
result = json.loads(response.content)
except json.JSONDecodeError:
result = {"category": "general", "sentiment": "neutral"}
return {
"category": result.get("category", "general"),
"sentiment": result.get("sentiment", "neutral"),
}
def agent_respond(state: SupportState) -> dict:
"""에이전트가 응답합니다."""
system_prompt = f"""당신은 고객 지원 에이전트입니다.
현재 문의 카테고리: {state.get('category', 'unknown')}
고객 감정: {state.get('sentiment', 'unknown')}
에스컬레이션 여부: {state.get('escalated', False)}
고객에게 친절하고 정확하게 응답하세요.
필요한 경우 도구를 사용하여 정보를 조회하거나 티켓을 생성하세요."""
messages = [SystemMessage(content=system_prompt)] + state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
def check_escalation(state: SupportState) -> dict:
"""에스컬레이션 필요 여부를 판단합니다."""
if state.get("sentiment") == "negative" and state.get("category") == "billing":
return {"escalated": True}
return {"escalated": False}
def human_escalation(state: SupportState) -> dict:
"""상위 담당자에게 에스컬레이션합니다."""
return {
"messages": [SystemMessage(content="[시스템] 이 문의는 상위 담당자에게 전달되었습니다. 잠시 기다려주세요.")],
"resolution": "escalated_to_human",
}
# 4. 라우팅 함수
def route_after_classification(state: SupportState) -> Literal["check_escalation", "agent_respond"]:
"""분류 결과에 따라 라우팅합니다."""
if state.get("sentiment") == "negative":
return "check_escalation"
return "agent_respond"
def route_after_escalation_check(state: SupportState) -> Literal["human_escalation", "agent_respond"]:
"""에스컬레이션 판단 결과에 따라 라우팅합니다."""
if state.get("escalated"):
return "human_escalation"
return "agent_respond"
# 5. 그래프 구성
workflow = StateGraph(SupportState)
# 노드 추가
workflow.add_node("classify_intent", classify_intent)
workflow.add_node("check_escalation", check_escalation)
workflow.add_node("agent_respond", agent_respond)
workflow.add_node("tools", ToolNode(tools))
workflow.add_node("human_escalation", human_escalation)
# 엣지 정의
workflow.add_edge(START, "classify_intent")
workflow.add_conditional_edges("classify_intent", route_after_classification)
workflow.add_conditional_edges("check_escalation", route_after_escalation_check)
workflow.add_conditional_edges("agent_respond", tools_condition, {"tools": "tools", END: END})
workflow.add_edge("tools", "agent_respond")
workflow.add_edge("human_escalation", END)
# 체크포인터 (상태 영속화)
checkpointer = MemorySaver()
app = workflow.compile(checkpointer=checkpointer)
# 6. 실행
config = {"configurable": {"thread_id": "customer-123"}}
result = app.invoke(
{"messages": [HumanMessage(content="주문 ORD-001 배송이 너무 늦어요. 환불해주세요.")]},
config=config,
)
for msg in result["messages"]:
print(f"[{msg.__class__.__name__}]: {msg.content[:200]}")
print(f"카테고리: {result['category']}, 감정: {result['sentiment']}")
도구 통합 비교
각 프레임워크에서 외부 도구를 통합하는 방식을 비교한다.
도구 정의 패턴 비교
| 항목 | AutoGen | CrewAI | LangGraph |
|---|---|---|---|
| 정의 방식 | Python 함수 + type hint | @tool 데코레이터 또는 BaseTool 상속 | @tool 데코레이터 (LangChain) |
| 스키마 자동 생성 | 함수 시그니처 기반 | docstring + type hint | docstring + type hint |
| 비동기 지원 | async 함수 네이티브 | 제한적 | async 지원 |
| 에러 처리 | try/except 직접 구현 | ToolException 클래스 | ToolException + 폴백 |
| 내장 도구 | 코드 실행, 웹 검색 | SerperDev, 파일 읽기/쓰기, 코드 인터프리터 | 웹 검색, Retriever, Python REPL |
주요 차이점
AutoGen은 Python 함수를 직접 도구로 등록하는 가장 간결한 방식을 사용한다. 함수의 type hint와 docstring으로부터 자동으로 JSON 스키마가 생성되며, async 함수를 네이티브로 지원한다. 코드 실행 환경을 기본 제공하여 에이전트가 생성한 코드를 Docker 컨테이너 또는 로컬 환경에서 즉시 실행할 수 있다는 점이 독특한 강점이다.
CrewAI는 @tool 데코레이터로 도구를 정의하거나, BaseTool 클래스를 상속하여 더 복잡한 도구를 구현할 수 있다. 내장 도구 라이브러리가 풍부하며, 에이전트가 다른 에이전트에게 작업을 위임하는 것 자체도 일종의 도구로 동작한다.
LangGraph는 LangChain의 도구 생태계를 그대로 활용한다. ToolNode와 tools_condition을 사용하여 도구 호출과 결과 처리를 그래프의 노드로 명시적으로 모델링한다. 이를 통해 도구 호출 전후에 커스텀 로직(로깅, 검증, 변환)을 삽입하기 용이하다.
메모리와 상태 관리
단기 메모리 (대화 컨텍스트)
| 프레임워크 | 단기 메모리 방식 | 윈도우 관리 | 토큰 제한 처리 |
|---|---|---|---|
| AutoGen | 메시지 리스트 자동 관리 | 수동 또는 summarize 패턴 | 대화 요약 에이전트 활용 |
| CrewAI | 태스크 간 컨텍스트 자동 전달 | 자동 (설정 가능) | 자동 요약 |
| LangGraph | State의 messages 필드 | add_messages reducer | Trimmer/Summarizer 노드 |
장기 메모리 (세션 간 영속화)
AutoGen은 대화 히스토리를 파일이나 데이터베이스에 저장하는 커스텀 구현이 필요하다. AutoGen Studio UI를 통해 세션 관리 기능을 제공하지만, 프로그래밍 수준의 장기 메모리 API는 제한적이다.
CrewAI는 memory=True 설정으로 단기 메모리를 활성화하고, 선택적으로 ChromaDB 등의 벡터 스토어를 사용한 장기 메모리를 설정할 수 있다. 에이전트가 과거 작업에서 학습한 패턴을 자동으로 활용하는 메커니즘이 내장되어 있다.
LangGraph는 Checkpointer 인터페이스를 통해 가장 체계적인 상태 영속화를 제공한다. MemorySaver(인메모리), SqliteSaver, PostgresSaver 등 다양한 백엔드를 지원하며, thread_id 기반으로 대화를 재개하고, 특정 시점으로 되돌리는 것이 가능하다. 이는 프로덕션 환경에서 매우 중요한 기능이다.
프로덕션 배포 고려사항
배포 아키텍처 비교
| 항목 | AutoGen | CrewAI | LangGraph |
|---|---|---|---|
| 서빙 방식 | FastAPI 직접 구현 | FastAPI/Flask 직접 구현 | LangServe / LangGraph Cloud |
| 스케일링 | 수동 구현 | 수동 구현 | LangGraph Platform (관리형) |
| 상태 영속화 | 커스텀 구현 | 내장 (제한적) | Checkpointer (PostgreSQL 등) |
| 동시성 | asyncio 기반 | 스레드/프로세스 기반 | asyncio + Checkpointer 기반 |
| 모니터링 | 커스텀 로깅 | 커스텀 로깅 | LangSmith (트레이싱, 평가) |
| 비용 추적 | 콜백 기반 | usage_metrics 내장 | LangSmith 토큰 추적 |
프로덕션 체크리스트
- 레이트 리밋 관리: 각 에이전트의 LLM 호출 빈도를 제어한다. CrewAI의
max_rpm, LangGraph의 커스텀 미들웨어, AutoGen의 콜백을 활용한다. - 타임아웃 설정: 에이전트가 무한 루프에 빠지는 것을 방지한다. 최대 반복 횟수와 전체 실행 시간 제한을 반드시 설정한다.
- 비용 상한선: 토큰 사용량을 실시간 모니터링하고, 예산 초과 시 실행을 중단하는 안전장치를 구현한다.
- 오류 격리: 하나의 에이전트 실패가 전체 시스템을 중단시키지 않도록 서킷 브레이커 패턴을 적용한다.
- 감사 로깅: 에이전트의 모든 의사결정과 도구 호출을 추적 가능한 형태로 기록한다.
실패 사례와 복구 전략
사례 1: 에이전트 무한 루프
증상: 에이전트가 같은 도구를 반복 호출하거나, 두 에이전트가 끝없이 대화를 주고받는다.
원인: 종료 조건이 모호하거나, 에이전트의 프롬프트가 명확한 완료 기준을 제시하지 않는 경우 발생한다.
프레임워크별 복구:
- AutoGen:
MaxMessageTermination(max_messages=20)으로 최대 메시지 수 제한.TextMentionTermination("TERMINATE")과 조합하여 이중 안전장치 구성. - CrewAI:
max_iter파라미터로 에이전트의 최대 반복 횟수 제한.Crew(max_rpm=10)으로 API 호출 속도도 제한. - LangGraph:
recursion_limit파라미터로 그래프 순회 최대 횟수 제한. 상태에step_count를 추가하고 조건부 엣지에서 확인.
사례 2: 도구 호출 실패와 환각
증상: 에이전트가 존재하지 않는 도구를 호출하거나, 도구에 잘못된 인자를 전달한다.
원인: LLM이 도구의 스키마를 정확히 이해하지 못하거나, 컨텍스트 윈도우가 부족하여 도구 정의를 "잊는" 경우 발생한다.
복구 전략:
- 도구의 docstring과 파라미터 설명을 최대한 명확하고 상세하게 작성한다.
- 도구 호출 결과에 대한 검증 레이어를 추가한다. 예상 형식과 다른 결과가 반환되면 재시도하거나 폴백 로직을 실행한다.
- LangGraph에서는 도구 노드 다음에 검증 노드를 추가하여 결과의 유효성을 확인할 수 있다.
사례 3: 컨텍스트 윈도우 초과
증상: 긴 대화나 대량의 도구 결과로 인해 토큰 제한에 도달하여 에이전트가 핵심 정보를 잃는다.
원인: 멀티 에이전트 시스템에서는 메시지가 기하급수적으로 누적되어 컨텍스트 윈도우를 빠르게 소진한다.
복구 전략:
- 대화 히스토리를 주기적으로 요약하여 압축한다.
- 도구 결과를 요약하거나 핵심 정보만 추출하여 컨텍스트에 포함한다.
- LangGraph에서는
trim_messages유틸리티를 사용하여 최근 N개 메시지만 유지한다.
사례 4: 에이전트 간 태스크 위임 실패
증상: CrewAI에서 에이전트가 다른 에이전트에게 작업을 위임했지만, 위임받은 에이전트가 맥락을 이해하지 못하고 엉뚱한 결과를 반환한다.
원인: 위임 시 충분한 컨텍스트가 전달되지 않거나, 위임 대상 에이전트의 역할과 맞지 않는 작업이 전달된 경우이다.
복구 전략:
- 에이전트의
allow_delegation=False로 불필요한 위임을 방지한다. - 태스크 설명에 구체적인 입출력 형식을 명시한다.
context파라미터를 통해 선행 태스크의 결과를 명시적으로 전달한다.
사례 5: API 장애 시 전체 시스템 중단
증상: LLM API 장애 또는 도구 API 장애로 인해 전체 에이전트 시스템이 중단된다.
원인: 단일 실패 지점에 대한 폴백 메커니즘이 없는 경우이다.
복구 전략:
# LangGraph에서의 오류 복구 패턴 예시
from tenacity import retry, stop_after_attempt, wait_exponential
from langchain_core.messages import AIMessage
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, max=10))
def resilient_agent_node(state: SupportState) -> dict:
"""재시도 로직이 포함된 에이전트 노드."""
try:
response = llm.invoke(state["messages"])
return {"messages": [response]}
except Exception as e:
# 폴백: 미리 정의된 응답 반환
fallback_response = AIMessage(
content="죄송합니다. 일시적인 시스템 오류가 발생했습니다. "
"잠시 후 다시 시도해주세요. 긴급한 경우 고객센터 "
"1588-0000으로 연락해주세요."
)
return {
"messages": [fallback_response],
"resolution": f"error_fallback: {str(e)}",
}
선택 가이드: 의사결정 트리
프로젝트의 요구사항에 따른 프레임워크 선택 기준을 정리한다.
CrewAI를 선택해야 하는 경우
- 빠른 프로토타이핑이 가장 중요한 경우. 수 시간 내에 멀티 에이전트 시스템을 구축하고 시연해야 할 때.
- 역할 기반 워크플로우가 자연스러운 경우. "리서처 -> 분석가 -> 작성자" 같은 선형적 파이프라인.
- 비기술 이해관계자에게 에이전트 정의를 보여줘야 하는 경우. role/goal/backstory 형식이 가장 직관적이다.
- 프로덕션 수준의 상태 관리나 복잡한 조건부 로직이 크게 필요하지 않은 경우.
AutoGen을 선택해야 하는 경우
- 에이전트 간 자유로운 대화가 핵심인 경우. 토론, 합의, 브레인스토밍 같은 대화 패턴.
- 코드 생성 및 실행이 주요 기능인 경우. 내장 코드 실행 환경이 큰 장점.
- Microsoft 생태계와의 통합이 필요한 경우. Azure OpenAI, Microsoft Agent Framework과의 호환성.
- .NET 환경에서도 에이전트를 실행해야 하는 경우. AutoGen은 .NET 버전도 제공한다.
LangGraph를 선택해야 하는 경우
- 복잡한 조건부 로직과 분기가 필요한 경우. 상태에 따라 실행 경로가 동적으로 변경되는 워크플로우.
- 프로덕션 수준의 안정성이 필수인 경우. 상태 영속화, 재개 가능한 실행, 감사 로그.
- Human-in-the-loop가 핵심 요구사항인 경우. 특정 단계에서 사람의 승인을 받은 뒤 진행.
- **관찰성(Observability)**이 중요한 경우. LangSmith를 통한 상세한 트레이싱과 디버깅.
- 장기 실행 워크플로우를 지원해야 하는 경우. 수 시간에서 수 일에 걸친 비동기 작업 관리.
의사결정 요약 표
| 우선 고려사항 | 1순위 추천 | 2순위 추천 | 비고 |
|---|---|---|---|
| 프로토타이핑 속도 | CrewAI | AutoGen | LangGraph는 초기 셋업 비용이 높다 |
| 프로덕션 안정성 | LangGraph | CrewAI | AutoGen은 MS Agent Framework 전환 과도기 |
| 대화형 에이전트 | AutoGen | LangGraph | CrewAI는 대화보다 태스크 중심 |
| 복잡한 워크플로우 | LangGraph | AutoGen | 조건부 분기, 병렬 실행, 상태 머신 |
| 코드 실행 | AutoGen | LangGraph | AutoGen의 코드 실행 환경이 가장 성숙 |
| 관찰성/디버깅 | LangGraph | AutoGen | LangSmith 통합이 압도적 장점 |
| 학습 곡선 | CrewAI | AutoGen | LangGraph는 그래프 개념 이해 필요 |
| 커뮤니티/생태계 | LangGraph | CrewAI | LangChain 생태계의 규모가 가장 큼 |
운영 시 주의사항
비용 관리
멀티 에이전트 시스템은 단일 LLM 호출 대비 토큰 소비가 5~20배 증가할 수 있다. 에이전트 수가 늘어나고 대화가 길어질수록 비용이 기하급수적으로 증가한다. 반드시 에이전트별, 태스크별 토큰 사용량을 추적하고 예산 상한선을 설정해야 한다. 가능하면 분류기 같은 간단한 작업에는 GPT-4o-mini 같은 소형 모델을, 핵심 추론에만 대형 모델을 사용하는 모델 라우팅 전략을 적용한다.
보안 고려사항
에이전트가 도구를 통해 외부 시스템에 접근할 때 다음을 주의한다.
- 최소 권한 원칙: 에이전트에게 필요한 최소한의 API 권한만 부여한다. 읽기 전용 작업에 쓰기 권한을 주지 않는다.
- 입력 검증: 에이전트가 도구에 전달하는 인자를 검증한다. SQL Injection이나 Command Injection 공격 벡터가 될 수 있다.
- 출력 필터링: 에이전트의 응답에 민감 정보(PII, 자격증명)가 포함되지 않도록 필터링 레이어를 추가한다.
- 샌드박싱: 코드 실행 도구는 반드시 격리된 환경(Docker 컨테이너)에서 실행한다.
테스트 전략
에이전트 시스템의 테스트는 전통적인 소프트웨어 테스트보다 복잡하다. LLM의 비결정적 출력 때문에 동일한 입력에 대해 다른 결과가 나올 수 있다.
- 단위 테스트: 각 도구 함수를 독립적으로 테스트한다. 에이전트 없이 도구의 입출력을 검증한다.
- 통합 테스트: 모의(mock) LLM 응답을 사용하여 에이전트의 도구 호출 패턴을 검증한다.
- 평가(Eval): LLM-as-Judge 패턴을 사용하여 에이전트의 최종 출력 품질을 자동 평가한다. LangSmith의 평가 기능이나 별도의 평가 파이프라인을 구축한다.
- 부하 테스트: 동시 사용자 수와 요청 빈도에 따른 시스템 성능을 측정한다. 특히 LLM API의 레이트 리밋에 주의한다.
버전 관리와 마이그레이션
세 프레임워크 모두 빠르게 발전하고 있어 API 변경이 빈번하다. 특히 AutoGen은 v0.2에서 v0.4(AgentChat)로의 대규모 아키텍처 변경이 있었고, 향후 Microsoft Agent Framework로의 전환이 예고되어 있다. 프레임워크 버전을 고정하고(pinning), 주요 업데이트 시 별도의 브랜치에서 마이그레이션 테스트를 수행하는 것이 안전하다.
마치며
LLM 에이전트 프레임워크의 선택은 은탄환이 없다. CrewAI는 "빠르게 만들어서 보여주는" 시나리오에서 빛을 발하고, AutoGen은 에이전트 간 풍부한 대화 상호작용이 핵심인 경우에 적합하며, LangGraph는 프로덕션 수준의 안정성과 제어가 필요한 복잡한 워크플로우에 최적이다.
실무에서 권장하는 접근은 다음과 같다. 먼저 CrewAI로 빠르게 프로토타입을 구축하여 에이전트 시스템의 가치를 검증한다. 검증이 완료되면 프로덕션 요구사항(상태 관리, 재개 가능성, 관찰성, Human-in-the-loop)을 기준으로 LangGraph로 재구현하는 것을 고려한다. 대화 중심의 에이전트가 핵심이라면 AutoGen을 선택하되, Microsoft Agent Framework로의 전환 계획을 함께 수립한다.
어떤 프레임워크를 선택하든, 에이전트 시스템의 성공은 프레임워크 자체보다는 프롬프트 엔지니어링의 품질, 도구 설계의 견고함, 그리고 실패 모드에 대한 철저한 대비에 달려 있다는 점을 기억해야 한다.
참고자료
- AutoGen GitHub Repository - Microsoft - AutoGen 공식 리포지토리 및 문서
- CrewAI 공식 문서 - CrewAI 퀵스타트 가이드 및 API 레퍼런스
- LangGraph GitHub Repository - LangChain - LangGraph 공식 리포지토리
- CrewAI vs LangGraph vs AutoGen: Choosing the Right Multi-Agent AI Framework (DataCamp) - DataCamp의 3종 프레임워크 비교 튜토리얼
- AutoGen vs LangGraph vs CrewAI: Which Agent Framework Actually Holds Up in 2026? (DEV Community) - 2026년 시점의 실전 비교
- LangGraph Tutorial: Complete Guide to Building AI Workflows (Codecademy) - LangGraph 종합 가이드
- Open Source AI Agent Frameworks Compared (OpenAgents) - 오픈소스 에이전트 프레임워크 종합 비교 (2026)
- AutoGen v0.4 AgentChat 공식 문서 - AutoGen AgentChat 튜토리얼