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

들어가며
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 튜토리얼
Comparing LLM Agent Frameworks: AutoGen vs CrewAI vs LangGraph — A Practical Selection Guide
- Introduction
- Why You Need an Agent Framework
- Architecture Comparison
- Multi-Agent Pattern Comparison Table
- Practical Code Examples for Each Framework
- Tool Integration Comparison
- Memory and State Management
- Production Deployment Considerations
- Failure Cases and Recovery Strategies
- Selection Guide: Decision Tree
- Operational Considerations
- Conclusion
- References
- Quiz

Introduction
LLM-based agent systems have evolved well beyond simple chatbots, establishing themselves as a core architecture for AI applications that use tools, formulate plans, and autonomously carry out multi-step tasks. The agent framework ecosystem grew explosively during 2024–2025, and as of 2026, three frameworks with production-grade stability have emerged as the market mainstream.
AutoGen (Microsoft) specializes in conversation-driven multi-agent interaction, presenting a paradigm in which agents solve complex tasks through natural language dialogue with one another. CrewAI excels at rapid prototyping and business workflow automation by composing role-based agent teams through an intuitive API. LangGraph (LangChain) models agent workflows as directed graphs and is optimized for production systems that require sophisticated state management and conditional branching.
Each framework embodies a distinct design philosophy and set of strengths, and the optimal choice varies depending on the project's requirements. In this article, we provide an in-depth architectural comparison of all three frameworks, walk through practical code examples for each, and comprehensively cover failure scenarios and recovery strategies encountered in production deployments.
Why You Need an Agent Framework
Limitations of a Single LLM Call
Simple LLM API calls have the following limitations:
- One-shot responses: A single call cannot complete complex multi-step tasks. Chains of research, analysis, execution, and verification require orchestrating multiple calls.
- Tool use constraints: For an LLM to interact with external APIs, databases, and file systems, a loop that feeds Function Calling results back to the LLM is needed.
- No state management: There is no built-in mechanism to systematically manage conversation history, task progress, or intermediate artifacts.
- No error recovery: System-level handling of API failures, hallucinations, and infinite loops is impossible.
The Role of an Agent Framework
Agent frameworks address these issues as follows:
| Problem | Framework Solution |
|---|---|
| Multi-step execution | Automatic management of the planning -> execution -> observation loop |
| Tool integration | Tool registry, automatic I/O schema generation, result parsing |
| State management | Conversation memory, task state, intermediate artifact persistence |
| Error recovery | Retry policies, timeouts, fallback strategies, Human-in-the-loop |
| Observability | Execution tracing, token usage tracking, debugging tools |
Architecture Comparison
AutoGen: Conversation-Centric Multi-Agent
AutoGen's core design philosophy is "problem-solving through inter-agent conversation." All interactions are message-based, and the speaking order and content of agents are determined by conversation patterns.
Key Components:
- AssistantAgent: An LLM-based agent equipped with a system prompt and tools
- UserProxyAgent: An agent that acts as a proxy for the user, handling code execution or forwarding user input (in v0.4, teams are composed with AssistantAgents without a separate user proxy)
- GroupChat / Teams: Group conversations or team structures involving multiple agents
- ConversationPattern: Conversation flow patterns such as RoundRobin, Selector, and Swarm
Architecture Characteristics:
- All agent interactions take place through message exchange
- Inter-agent relationships are defined by conversation patterns
- Built-in code execution environments (Docker, local) are provided
- v0.4 (AgentChat) adopts an async-first design
CrewAI: Role-Based Agent Teams
CrewAI uses the intuitive metaphor of "assigning each agent a clear role and goal, allocating tasks, and operating them as a team." It mirrors how real organizations run their teams.
Key Components:
- Agent: An agent with a role, goal, backstory, and tools
- Task: A specific unit of work assigned to an agent
- Crew: An execution unit combining agents and tasks
- Process: Execution modes — Sequential, Hierarchical, or Parallel
Architecture Characteristics:
- Independent implementation not dependent on LangChain (its own engine)
- Supports YAML-based agent/task configuration file separation
- Built-in task delegation mechanism between tasks
- Concise API optimized for rapid prototyping
LangGraph: Graph-Based State Machine
LangGraph models agent workflows as directed graphs. Each node is an execution function, and edges define transitions based on state. This is essentially the same structure as a state machine.
Key Components:
- StateGraph: A directed graph that manages state
- State (TypedDict): Type-safe state shared across the entire graph
- Node: A function that receives state as input and returns updates
- Edge: Defines transitions between nodes (including conditional edges)
- Checkpointer: Supports resumable execution by persisting state
Architecture Characteristics:
- Predictable execution through explicit control flow
- Type-safe state management (TypedDict or Pydantic)
- Built-in Human-in-the-loop support
- Excellent observability through LangSmith integration
- API stabilized with v1.0 release (late 2025)
Multi-Agent Pattern Comparison Table
| Comparison Item | AutoGen | CrewAI | LangGraph |
|---|---|---|---|
| Design Philosophy | Conversation-centric collaboration | Role-based teams | Graph-based state machine |
| Agent Definition | AssistantAgent + system prompt | Agent(role, goal, backstory) | Node function + State |
| Orchestration | ConversationPattern (RoundRobin, Selector, Swarm) | Process (Sequential, Hierarchical, Parallel) | Conditional edges + state transitions |
| State Management | Conversation history-based | Context passing between tasks | TypedDict/Pydantic state object |
| Tool Integration | Function-based tool registration | @tool decorator / built-in tools | ToolNode + tools_condition |
| Human-in-the-Loop | UserProxy pattern | Task-level approval | Interrupt + Checkpointer |
| Code Execution | Built-in Docker/local | Implemented via separate tools | Implemented via separate tools |
| Memory | Conversation-based short/long-term | Short-term + optional long-term | Checkpointer-based persistence |
| Observability | AutoGen Studio UI | Logging-based | LangSmith integration (tracing/replay) |
| Learning Curve | Medium | Low | High |
| Production Maturity | Medium (transitioning to MS Agent Framework) | Medium-High | High (v1.0) |
| License | MIT | MIT | MIT |
| Python Version | 3.10+ | 3.10–3.13 | 3.9+ |
Practical Code Examples for Each Framework
AutoGen: Building a Research Agent Team
An example of a multi-agent research system using the 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 client configuration
model_client = OpenAIChatCompletionClient(
model="gpt-4o",
api_key="your-api-key",
)
# 2. Tool definitions
async def search_web(query: str) -> str:
"""Searches the web for information."""
# In practice, use Tavily, SerpAPI, etc.
return f"Search results for '{query}': [Search result summary]"
async def analyze_data(data: str) -> str:
"""Analyzes data and extracts insights."""
return f"Analysis result: In-depth analysis of {data[:100]} completed"
# 3. Agent definitions
researcher = AssistantAgent(
name="Researcher",
model_client=model_client,
system_message="""You are a professional researcher.
Perform web searches on the given topic and collect relevant information.
Structure the collected information and pass it to the Analyst.""",
tools=[search_web],
)
analyst = AssistantAgent(
name="Analyst",
model_client=model_client,
system_message="""You are a data analyst.
Analyze the information collected by the Researcher and extract key insights.
Pass the analysis results to the Writer.""",
tools=[analyze_data],
)
writer = AssistantAgent(
name="Writer",
model_client=model_client,
system_message="""You are a report writer.
Write the final report based on the Analyst's analysis results.
Output 'TERMINATE' when the report is complete.""",
)
# 4. Team composition and execution
termination = MaxMessageTermination(max_messages=15) | TextMentionTermination("TERMINATE")
# SelectorGroupChat: LLM selects the next speaker
team = SelectorGroupChat(
participants=[researcher, analyst, writer],
model_client=model_client,
termination_condition=termination,
selector_prompt="""Select the next speaker.
Choose Researcher if information gathering is needed, Analyst if analysis is needed,
and Writer if the final report needs to be written.""",
)
async def main():
result = await team.run(
task="Research the 2026 AI agent framework market trends and write a report."
)
for message in result.messages:
print(f"[{message.source}]: {message.content[:200]}")
print(f"\nTotal messages: {len(result.messages)}")
asyncio.run(main())
CrewAI: Content Creation Pipeline
An example of an automated blog post generation pipeline using CrewAI.
from crewai import Agent, Task, Crew, Process
from crewai.tools import tool
from langchain_openai import ChatOpenAI
# 1. LLM configuration
llm = ChatOpenAI(model="gpt-4o", temperature=0.7)
# 2. Custom tool definitions
@tool("Search Tool")
def search_tool(query: str) -> str:
"""Performs a web search with the given query."""
# In practice, use Tavily, SerpAPI, etc.
return f"Search results for '{query}': [Relevant information summary]"
@tool("SEO Analyzer")
def seo_analyzer(content: str) -> str:
"""Analyzes the SEO score of the content."""
word_count = len(content.split())
return f"Word count: {word_count}, SEO score: {'Good' if word_count > 500 else 'Insufficient'}"
# 3. Agent definitions
research_agent = Agent(
role="Technical Researcher",
goal="Research the latest technology trends and collect accurate information",
backstory="""A technology journalist with 10 years of experience who stays
on top of the latest trends in AI and software engineering.""",
tools=[search_tool],
llm=llm,
verbose=True,
allow_delegation=True, # Can delegate work to other agents
)
writer_agent = Agent(
role="Technical Blog Writer",
goal="Write in-depth technical blog posts that readers can easily understand",
backstory="""A professional technical blog writer skilled at breaking down
complex technical concepts into clear, practical prose.""",
llm=llm,
verbose=True,
)
editor_agent = Agent(
role="Editor-in-Chief",
goal="Review content quality, accuracy, and SEO optimization",
backstory="""An editor-in-chief at a technology media outlet who pursues
technical accuracy, readability, and SEO optimization simultaneously.""",
tools=[seo_analyzer],
llm=llm,
verbose=True,
)
# 4. Task definitions
research_task = Task(
description="""Conduct comprehensive research on '{topic}'.
- Collect at least 3 recent trends
- Organize key technical concepts
- Include at least 2 real-world use cases""",
expected_output="A structured research report (Markdown format)",
agent=research_agent,
)
writing_task = Task(
description="""Write a technical blog post based on the research results.
- At least 2000 characters of body text
- Include at least 2 code examples
- Include insights from a practitioner's perspective""",
expected_output="A completed blog post (Markdown format)",
agent=writer_agent,
context=[research_task], # Receives research_task results as context
)
editing_task = Task(
description="""Review the written blog post.
- Verify technical accuracy
- Improve readability
- Perform SEO analysis
- Output the final revised version""",
expected_output="The final reviewed blog post",
agent=editor_agent,
context=[writing_task],
)
# 5. Crew composition and execution
crew = Crew(
agents=[research_agent, writer_agent, editor_agent],
tasks=[research_task, writing_task, editing_task],
process=Process.sequential, # Sequential execution
verbose=True,
memory=True, # Enable short-term memory
max_rpm=10, # API call rate limit
)
# Execution
result = crew.kickoff(inputs={"topic": "LLM Agent Framework Comparison"})
print(f"Final result:\n{result}")
print(f"Token usage: {crew.usage_metrics}")
LangGraph: Customer Support Agent with Conditional Branching
An example of a state-based customer support agent using LangGraph, featuring conditional branching, Human-in-the-loop, and error recovery.
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. State definition
class SupportState(TypedDict):
messages: Annotated[list, add_messages]
category: str # Inquiry category (billing, technical, general)
sentiment: str # Customer sentiment (positive, neutral, negative)
escalated: bool # Whether escalated to a senior agent
resolution: str # Resolution status
# 2. Tool definitions
@tool
def lookup_order(order_id: str) -> str:
"""Looks up order information."""
orders = {
"ORD-001": {"status": "In transit", "eta": "2026-03-11"},
"ORD-002": {"status": "Delivered", "delivered": "2026-03-07"},
}
if order_id in orders:
return f"Order {order_id}: {orders[order_id]}"
return f"Order {order_id} not found."
@tool
def create_ticket(summary: str, priority: str) -> str:
"""Creates a customer support ticket."""
return f"Ticket created - Summary: {summary}, Priority: {priority}, Ticket ID: TKT-{hash(summary) % 10000}"
@tool
def refund_order(order_id: str, reason: str) -> str:
"""Processes an order refund. (Requires approval)"""
return f"Refund request received: {order_id}, Reason: {reason}. Awaiting manager approval."
tools = [lookup_order, create_ticket, refund_order]
llm = ChatOpenAI(model="gpt-4o", temperature=0).bind_tools(tools)
# 3. Node function definitions
def classify_intent(state: SupportState) -> dict:
"""Classifies the customer inquiry."""
classifier_llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
last_message = state["messages"][-1].content
response = classifier_llm.invoke([
SystemMessage(content="""Classify the customer inquiry.
Category: billing, technical, general
Sentiment: positive, neutral, negative
Respond in JSON format: {"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:
"""The agent generates a response."""
system_prompt = f"""You are a customer support agent.
Current inquiry category: {state.get('category', 'unknown')}
Customer sentiment: {state.get('sentiment', 'unknown')}
Escalation status: {state.get('escalated', False)}
Respond to the customer in a friendly and accurate manner.
Use tools to look up information or create tickets as needed."""
messages = [SystemMessage(content=system_prompt)] + state["messages"]
response = llm.invoke(messages)
return {"messages": [response]}
def check_escalation(state: SupportState) -> dict:
"""Determines whether escalation is needed."""
if state.get("sentiment") == "negative" and state.get("category") == "billing":
return {"escalated": True}
return {"escalated": False}
def human_escalation(state: SupportState) -> dict:
"""Escalates to a senior agent."""
return {
"messages": [SystemMessage(content="[System] This inquiry has been forwarded to a senior agent. Please wait a moment.")],
"resolution": "escalated_to_human",
}
# 4. Routing functions
def route_after_classification(state: SupportState) -> Literal["check_escalation", "agent_respond"]:
"""Routes based on classification results."""
if state.get("sentiment") == "negative":
return "check_escalation"
return "agent_respond"
def route_after_escalation_check(state: SupportState) -> Literal["human_escalation", "agent_respond"]:
"""Routes based on escalation check results."""
if state.get("escalated"):
return "human_escalation"
return "agent_respond"
# 5. Graph construction
workflow = StateGraph(SupportState)
# Add nodes
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)
# Define edges
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 (state persistence)
checkpointer = MemorySaver()
app = workflow.compile(checkpointer=checkpointer)
# 6. Execution
config = {"configurable": {"thread_id": "customer-123"}}
result = app.invoke(
{"messages": [HumanMessage(content="My order ORD-001 is taking too long to ship. I want a refund.")]},
config=config,
)
for msg in result["messages"]:
print(f"[{msg.__class__.__name__}]: {msg.content[:200]}")
print(f"Category: {result['category']}, Sentiment: {result['sentiment']}")
Tool Integration Comparison
This section compares how each framework integrates external tools.
Tool Definition Pattern Comparison
| Item | AutoGen | CrewAI | LangGraph |
|---|---|---|---|
| Definition Method | Python function + type hint | @tool decorator or BaseTool inheritance | @tool decorator (LangChain) |
| Auto Schema Generation | Based on function signature | docstring + type hint | docstring + type hint |
| Async Support | Native async functions | Limited | Async supported |
| Error Handling | Manual try/except | ToolException class | ToolException + fallback |
| Built-in Tools | Code execution, web search | SerperDev, file read/write, code interpreter | Web search, Retriever, Python REPL |
Key Differences
AutoGen uses the most concise approach by registering Python functions directly as tools. JSON schemas are automatically generated from the function's type hints and docstrings, and async functions are natively supported. Its unique strength is providing a built-in code execution environment, allowing agents to immediately run generated code in Docker containers or the local environment.
CrewAI lets you define tools with the @tool decorator or implement more complex tools by subclassing BaseTool. It has a rich built-in tool library, and an agent delegating work to another agent itself functions as a kind of tool.
LangGraph leverages the LangChain tool ecosystem as-is. It uses ToolNode and tools_condition to explicitly model tool invocations and result handling as graph nodes. This makes it easy to insert custom logic (logging, validation, transformation) before and after tool calls.
Memory and State Management
Short-Term Memory (Conversation Context)
| Framework | Short-Term Memory Approach | Window Management | Token Limit Handling |
|---|---|---|---|
| AutoGen | Automatic message list management | Manual or summarize pattern | Conversation summary agent |
| CrewAI | Automatic context passing between tasks | Automatic (configurable) | Auto-summarization |
| LangGraph | State's messages field | add_messages reducer | Trimmer/Summarizer node |
Long-Term Memory (Cross-Session Persistence)
AutoGen requires custom implementation to store conversation history in files or databases. While AutoGen Studio UI provides session management features, the programmatic long-term memory API is limited.
CrewAI activates short-term memory with memory=True and can optionally configure long-term memory using vector stores such as ChromaDB. It has a built-in mechanism that automatically leverages patterns learned from past tasks.
LangGraph provides the most systematic state persistence through the Checkpointer interface. It supports various backends including MemorySaver (in-memory), SqliteSaver, and PostgresSaver, and enables resuming conversations by thread_id and rolling back to specific points in time. This is an extremely important capability in production environments.
Production Deployment Considerations
Deployment Architecture Comparison
| Item | AutoGen | CrewAI | LangGraph |
|---|---|---|---|
| Serving Method | Build with FastAPI | Build with FastAPI/Flask | LangServe / LangGraph Cloud |
| Scaling | Manual implementation | Manual implementation | LangGraph Platform (managed) |
| State Persistence | Custom implementation | Built-in (limited) | Checkpointer (PostgreSQL, etc.) |
| Concurrency | asyncio-based | Thread/process-based | asyncio + Checkpointer-based |
| Monitoring | Custom logging | Custom logging | LangSmith (tracing, evaluation) |
| Cost Tracking | Callback-based | Built-in usage_metrics | LangSmith token tracking |
Production Checklist
- Rate limit management: Control the LLM call frequency of each agent. Leverage CrewAI's
max_rpm, LangGraph's custom middleware, and AutoGen's callbacks. - Timeout configuration: Prevent agents from falling into infinite loops. Always set maximum iteration counts and total execution time limits.
- Cost ceilings: Monitor token usage in real-time and implement safeguards that halt execution when the budget is exceeded.
- Error isolation: Apply the circuit breaker pattern so that a single agent failure does not bring down the entire system.
- Audit logging: Record all agent decisions and tool calls in a traceable format.
Failure Cases and Recovery Strategies
Case 1: Agent Infinite Loop
Symptoms: An agent repeatedly calls the same tool, or two agents engage in an endless back-and-forth conversation.
Root Cause: This occurs when the termination condition is ambiguous or the agent's prompt does not provide clear completion criteria.
Framework-Specific Recovery:
- AutoGen: Limit the maximum number of messages with
MaxMessageTermination(max_messages=20). Combine withTextMentionTermination("TERMINATE")for a dual safety net. - CrewAI: Limit the agent's maximum iterations with the
max_iterparameter. Also limit API call rate withCrew(max_rpm=10). - LangGraph: Limit the maximum number of graph traversals with the
recursion_limitparameter. Add astep_countto the state and check it in conditional edges.
Case 2: Tool Call Failures and Hallucinations
Symptoms: An agent calls a tool that does not exist or passes incorrect arguments to a tool.
Root Cause: This happens when the LLM does not accurately understand the tool's schema, or when the context window is insufficient, causing the LLM to "forget" the tool definitions.
Recovery Strategies:
- Write tool docstrings and parameter descriptions as clearly and in as much detail as possible.
- Add a validation layer for tool call results. If results are returned in an unexpected format, retry or execute fallback logic.
- In LangGraph, you can add a validation node after the tool node to verify result validity.
Case 3: Context Window Overflow
Symptoms: Long conversations or large volumes of tool results cause the token limit to be reached, and the agent loses critical information.
Root Cause: In multi-agent systems, messages accumulate exponentially, rapidly exhausting the context window.
Recovery Strategies:
- Periodically summarize and compress the conversation history.
- Summarize tool results or extract only key information before including them in the context.
- In LangGraph, use the
trim_messagesutility to retain only the most recent N messages.
Case 4: Failed Task Delegation Between Agents
Symptoms: In CrewAI, an agent delegates work to another agent, but the delegated agent fails to understand the context and returns irrelevant results.
Root Cause: This occurs when insufficient context is passed during delegation, or when the delegated task does not match the target agent's role.
Recovery Strategies:
- Prevent unnecessary delegation by setting
allow_delegation=Falseon agents. - Specify concrete input/output formats in the task description.
- Explicitly pass the results of preceding tasks through the
contextparameter.
Case 5: Full System Outage Due to API Failure
Symptoms: An LLM API outage or tool API failure causes the entire agent system to go down.
Root Cause: There is no fallback mechanism for a single point of failure.
Recovery Strategies:
# Example error recovery pattern in 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:
"""An agent node with built-in retry logic."""
try:
response = llm.invoke(state["messages"])
return {"messages": [response]}
except Exception as e:
# Fallback: return a predefined response
fallback_response = AIMessage(
content="We apologize for the inconvenience. A temporary system error has occurred. "
"Please try again shortly. For urgent matters, please contact "
"our customer service at 1-800-000-0000."
)
return {
"messages": [fallback_response],
"resolution": f"error_fallback: {str(e)}",
}
Selection Guide: Decision Tree
This section provides framework selection criteria based on project requirements.
When to Choose CrewAI
- When rapid prototyping is the top priority. When you need to build and demo a multi-agent system within hours.
- When role-based workflows feel natural. Linear pipelines like "Researcher -> Analyst -> Writer."
- When you need to present agent definitions to non-technical stakeholders. The role/goal/backstory format is the most intuitive.
- When production-grade state management or complex conditional logic is not a major requirement.
When to Choose AutoGen
- When free-flowing conversation between agents is central. Conversation patterns like debates, consensus-building, and brainstorming.
- When code generation and execution is a primary feature. The built-in code execution environment is a major advantage.
- When integration with the Microsoft ecosystem is needed. Compatibility with Azure OpenAI and the Microsoft Agent Framework.
- When agents also need to run in a .NET environment. AutoGen offers a .NET version as well.
When to Choose LangGraph
- When complex conditional logic and branching are required. Workflows where execution paths change dynamically based on state.
- When production-grade reliability is essential. State persistence, resumable execution, and audit logs.
- When Human-in-the-loop is a core requirement. Proceeding only after obtaining human approval at specific steps.
- When observability is important. Detailed tracing and debugging through LangSmith.
- When long-running workflows must be supported. Managing asynchronous tasks spanning hours to days.
Decision Summary Table
| Priority Consideration | 1st Choice | 2nd Choice | Notes |
|---|---|---|---|
| Prototyping speed | CrewAI | AutoGen | LangGraph has a high initial setup cost |
| Production reliability | LangGraph | CrewAI | AutoGen is in a transitional period to MS Agent Framework |
| Conversational agents | AutoGen | LangGraph | CrewAI is task-centric rather than conversation-centric |
| Complex workflows | LangGraph | AutoGen | Conditional branching, parallel execution, state machines |
| Code execution | AutoGen | LangGraph | AutoGen's code execution environment is the most mature |
| Observability/Debugging | LangGraph | AutoGen | LangSmith integration is an overwhelming advantage |
| Learning curve | CrewAI | AutoGen | LangGraph requires understanding graph concepts |
| Community/Ecosystem | LangGraph | CrewAI | The LangChain ecosystem is the largest in scale |
Operational Considerations
Cost Management
Multi-agent systems can consume 5 to 20 times more tokens compared to single LLM calls. Costs grow exponentially as the number of agents increases and conversations become longer. You must track token usage per agent and per task, and set budget ceilings. Where possible, apply a model routing strategy that uses smaller models like GPT-4o-mini for simple tasks such as classifiers, and reserves larger models for core reasoning only.
Security Considerations
Exercise caution when agents access external systems through tools:
- Principle of least privilege: Grant agents only the minimum API permissions they need. Do not give write permissions for read-only operations.
- Input validation: Validate the arguments agents pass to tools. These can become attack vectors for SQL injection or command injection.
- Output filtering: Add a filtering layer to ensure agent responses do not contain sensitive information (PII, credentials).
- Sandboxing: Code execution tools must always run in isolated environments (Docker containers).
Testing Strategy
Testing agent systems is more complex than traditional software testing. Due to the non-deterministic output of LLMs, the same input can produce different results.
- Unit tests: Test each tool function independently. Validate tool input/output without involving agents.
- Integration tests: Use mock LLM responses to verify agent tool-calling patterns.
- Evaluation (Eval): Use the LLM-as-Judge pattern to automatically evaluate the quality of agent final outputs. Build evaluation pipelines using LangSmith's evaluation features or a custom setup.
- Load tests: Measure system performance under varying concurrent user counts and request frequencies. Pay special attention to LLM API rate limits.
Version Management and Migration
All three frameworks are evolving rapidly, and API changes are frequent. AutoGen in particular underwent a major architectural overhaul from v0.2 to v0.4 (AgentChat), and a future transition to the Microsoft Agent Framework has been announced. It is safest to pin framework versions and perform migration testing in a separate branch for major updates.
Conclusion
There is no silver bullet when choosing an LLM agent framework. CrewAI shines in "build it fast and show it" scenarios, AutoGen is well-suited when rich conversational interactions between agents are central, and LangGraph is optimal for complex workflows requiring production-grade reliability and control.
The recommended approach in practice is as follows: First, quickly build a prototype with CrewAI to validate the value of the agent system. Once validated, consider reimplementing with LangGraph based on production requirements (state management, resumability, observability, Human-in-the-loop). If conversational agents are central, choose AutoGen, but formulate a transition plan to the Microsoft Agent Framework alongside it.
Regardless of which framework you choose, remember that the success of an agent system depends less on the framework itself and more on the quality of prompt engineering, the robustness of tool design, and thorough preparation for failure modes.
References
- AutoGen GitHub Repository - Microsoft - Official AutoGen repository and documentation
- CrewAI Official Documentation - CrewAI quickstart guide and API reference
- LangGraph GitHub Repository - LangChain - Official LangGraph repository
- CrewAI vs LangGraph vs AutoGen: Choosing the Right Multi-Agent AI Framework (DataCamp) - DataCamp's three-framework comparison tutorial
- AutoGen vs LangGraph vs CrewAI: Which Agent Framework Actually Holds Up in 2026? (DEV Community) - A practical comparison from a 2026 perspective
- LangGraph Tutorial: Complete Guide to Building AI Workflows (Codecademy) - Comprehensive LangGraph guide
- Open Source AI Agent Frameworks Compared (OpenAgents) - Comprehensive open-source agent framework comparison (2026)
- AutoGen v0.4 AgentChat Official Documentation - AutoGen AgentChat tutorial
Quiz
Q1: What is the main topic covered in "Comparing LLM Agent Frameworks: AutoGen vs CrewAI vs LangGraph — A Practical Selection Guide"?
A comprehensive comparison guide for three LLM agent frameworks (AutoGen, CrewAI, LangGraph). Covers architecture and design philosophy, multi-agent orchestration patterns, tool integration, memory management, production deployment strategies, and practical selection criteria — a...
Q2: Why You Need an Agent Framework?
Limitations of a Single LLM Call Simple LLM API calls have the following limitations: One-shot responses: A single call cannot complete complex multi-step tasks. Chains of research, analysis, execution, and verification require orchestrating multiple calls.
Q3: Describe the Architecture Comparison.
AutoGen: Conversation-Centric Multi-Agent AutoGen's core design philosophy is "problem-solving through inter-agent conversation." All interactions are message-based, and the speaking order and content of agents are determined by conversation patterns.
Q4: What are the key aspects of Practical Code Examples for Each Framework?
AutoGen: Building a Research Agent Team An example of a multi-agent research system using the AutoGen v0.4 AgentChat API. CrewAI: Content Creation Pipeline An example of an automated blog post generation pipeline using CrewAI.
Q5: How does Tool Integration Comparison work?
This section compares how each framework integrates external tools. Tool Definition Pattern Comparison Key Differences AutoGen uses the most concise approach by registering Python functions directly as tools.