LabHub

Blog

Multi-Agent Systems Compared: AutoGen vs CrewAI vs LangGraph — Which Should You Choose?

한국어English日本語

I get asked "should I use a multi-agent system?" fairly often. The honest answer is: usually no. A single agent handles most cases just fine. But there are specific situations where multi-agent is genuinely the right call — and when that happens, your framework choice matters a lot.

When Do You Actually Need Multi-Agent?

Three situations where single-agent genuinely falls short:

Situation 1: The task is too large to fit in one context window

Refactoring 10,000 lines of code? The entire codebase won't fit in one agent's context window. Multiple agents each handling a module solves this cleanly.

Situation 2: You need distinct expertise

"Collect news articles, analyze them, write a report." A search-optimized agent, an analysis-focused agent, and a writing-specialized agent — each with different system prompts and tools — naturally outperforms a single agent trying to do everything.

Situation 3: You want to parallelize for speed

Researching 10 markets simultaneously? Ten agents running in parallel, each covering one market, is vastly faster than one agent doing them sequentially.

If none of these apply, stick with a single agent. Don't add complexity you don't need.

Framework 1: Microsoft AutoGen

AutoGen is Microsoft's multi-agent framework. Its core abstraction is conversation — agents solve problems by talking to each other.

Core Concept

AutoGen's philosophy: agents collaborate like team members in a chat. They exchange messages to arrive at a solution.

import autogen

llm_config = {
    "model": "gpt-4",
    "api_key": "your-api-key"
}

# Define agents
coder = autogen.AssistantAgent(
    name="Coder",
    llm_config=llm_config,
    system_message=(
        "You are a Python expert. Write clean, well-tested code. "
        "Always include error handling and type hints. "
        "When you finish, say 'TERMINATE'."
    )
)

reviewer = autogen.AssistantAgent(
    name="Reviewer",
    llm_config=llm_config,
    system_message=(
        "You are a senior software engineer. Review code for: "
        "1. Bugs and edge cases "
        "2. Security vulnerabilities "
        "3. Performance issues "
        "4. Code style and maintainability "
        "Provide specific, actionable feedback."
    )
)

# UserProxyAgent handles actual code execution
user_proxy = autogen.UserProxyAgent(
    name="User",
    human_input_mode="NEVER",           # fully automated
    max_consecutive_auto_reply=10,
    code_execution_config={
        "work_dir": "coding",
        "use_docker": False             # use True in production
    },
    is_termination_msg=lambda x: "TERMINATE" in x.get("content", "")
)

# Kick off the conversation
user_proxy.initiate_chat(
    coder,
    message="Write a Python script that fetches weather data and plots it with matplotlib"
)

GroupChat for Multiple Agents

groupchat = autogen.GroupChat(
    agents=[user_proxy, coder, reviewer],
    messages=[],
    max_round=20,
    speaker_selection_method="auto"  # LLM decides who speaks next
)

manager = autogen.GroupChatManager(
    groupchat=groupchat,
    llm_config=llm_config
)

user_proxy.initiate_chat(
    manager,
    message="Design and implement a REST API client library"
)

AutoGen Pros and Cons

Pros:

Cons:

Framework 2: CrewAI

CrewAI is built around the idea of an "AI team." Agents have explicit roles and goals; tasks have explicit dependencies.

Core Concept

CrewAI's philosophy: structure your AI system like a company with clear roles and responsibilities.

from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool, WebsiteSearchTool

search_tool = SerperDevTool()
web_tool = WebsiteSearchTool()

# Agents have roles and goals
researcher = Agent(
    role="Research Analyst",
    goal="Find accurate, comprehensive, and up-to-date information on any topic",
    backstory=(
        "You are an expert researcher with 10 years of experience. "
        "You always verify information from multiple sources and cite your findings."
    ),
    tools=[search_tool, web_tool],
    llm="gpt-4",
    verbose=True
)

analyst = Agent(
    role="Data Analyst",
    goal="Analyze information and identify key trends and insights",
    backstory=(
        "You are a data analyst who excels at finding patterns and drawing "
        "actionable conclusions from complex information."
    ),
    llm="gpt-4",
    verbose=True
)

writer = Agent(
    role="Technical Writer",
    goal="Write clear, engaging, and well-structured content",
    backstory=(
        "You are a technical writer who makes complex topics accessible. "
        "You write for engineers who value precision and clarity."
    ),
    llm="gpt-4",
    verbose=True
)

# Tasks with explicit dependencies
research_task = Task(
    description=(
        "Research the top 5 current trends in AI agent development. "
        "For each trend, include concrete examples and credible sources."
    ),
    agent=researcher,
    expected_output="5 trends, each with a 2-3 sentence description and source"
)

analysis_task = Task(
    description=(
        "Analyze the researched trends and rank them by impact for engineers. "
        "Explain the practical implications of each trend."
    ),
    agent=analyst,
    expected_output="Ranked trend analysis with practical implications for each",
    context=[research_task]     # depends on research_task output
)

writing_task = Task(
    description=(
        "Write a 1,000-word technical blog post based on the analysis. "
        "Focus on actionable insights engineers can use immediately."
    ),
    agent=writer,
    expected_output="Markdown blog post with title, headers, and code examples",
    context=[research_task, analysis_task]
)

crew = Crew(
    agents=[researcher, analyst, writer],
    tasks=[research_task, analysis_task, writing_task],
    process=Process.sequential,     # or Process.hierarchical
    verbose=True
)

result = crew.kickoff()
print(result)

CrewAI Pros and Cons

Pros:

Cons:

Framework 3: LangGraph

LangGraph is built by the LangChain team. It models agent workflows as directed graphs. The most flexible option, but with a steeper learning curve.

Core Concept

LangGraph's philosophy: model your agent system as a directed acyclic graph. Nodes are functions, edges are flow control. State is explicit and typed.

from langgraph.graph import StateGraph, END
from typing import TypedDict, List, Annotated
import operator

# Typed state shared across the entire graph
class ResearchState(TypedDict):
    messages: Annotated[List[str], operator.add]   # messages accumulate
    research_done: bool
    analysis_done: bool
    draft: str
    final_report: str

workflow = StateGraph(ResearchState)

# Node functions — pure functions that return state updates
def research_node(state: ResearchState) -> dict:
    results = search_web(state["messages"][-1])
    return {
        "messages": [f"Research results: {results}"],
        "research_done": True
    }

def analysis_node(state: ResearchState) -> dict:
    research = [m for m in state["messages"] if "Research results:" in m]
    analysis = analyze_data(research)
    return {
        "messages": [f"Analysis: {analysis}"],
        "analysis_done": True
    }

def writing_node(state: ResearchState) -> dict:
    all_context = "\n".join(state["messages"])
    draft = write_report(all_context)
    return {"draft": draft}

def review_node(state: ResearchState) -> dict:
    reviewed = review_and_improve(state["draft"])
    return {"final_report": reviewed}

# Conditional routing function
def route_after_research(state: ResearchState) -> str:
    if len(state["messages"]) > 3:
        return "analysis"
    else:
        return "research"    # loop back for more research

# Add nodes
workflow.add_node("research", research_node)
workflow.add_node("analysis", analysis_node)
workflow.add_node("writing", writing_node)
workflow.add_node("review", review_node)

# Add edges — this is where flow control lives
workflow.set_entry_point("research")
workflow.add_conditional_edges(
    "research",
    route_after_research,
    {
        "analysis": "analysis",
        "research": "research"    # self-loop
    }
)
workflow.add_edge("analysis", "writing")
workflow.add_edge("writing", "review")
workflow.add_edge("review", END)

app = workflow.compile()

result = app.invoke({
    "messages": ["Research the latest AI agent trends"],
    "research_done": False,
    "analysis_done": False,
    "draft": "",
    "final_report": ""
})
print(result["final_report"])

Checkpointing and Streaming

Where LangGraph genuinely differentiates itself:

from langgraph.checkpoint.sqlite import SqliteSaver

# Checkpointing: save and restore intermediate state
memory = SqliteSaver.from_conn_string(":memory:")
app = workflow.compile(checkpointer=memory)

# Thread IDs enable resumable conversations
config = {"configurable": {"thread_id": "session-123"}}

# First run
result = app.invoke(initial_state, config=config)

# Continue the same thread later
follow_up = app.invoke(
    {"messages": ["Add more detail to the analysis section"]},
    config=config
)

Visualize Your Graph

# LangGraph can emit Mermaid diagrams
print(app.get_graph().draw_mermaid())

# Example output:
# graph TD
#     __start__ --> research
#     research -->|need more| research
#     research -->|sufficient| analysis
#     analysis --> writing
#     writing --> review
#     review --> __end__

LangGraph Pros and Cons

Pros:

Cons:

Comparison Table

PropertyAutoGenCrewAILangGraph
Learning curveLowLowHigh
FlexibilityMediumMediumHigh
State managementBasicBasicPowerful
Production readinessMediumMediumHigh
Built-in code executionYesNo (separate setup)No
Community sizeLargeFast-growingGrowing
Best forCode generation workflowsRole-based team tasksComplex workflows

Decision Guide

Choose AutoGen when:

Choose CrewAI when:

Choose LangGraph when:

Production Pitfalls (Framework-Agnostic)

These problems hit you regardless of which framework you pick.

1. Cost explosions

Multiple agents each making LLM calls adds up fast. Always track costs.

import tiktoken

def estimate_cost(messages, model="gpt-4"):
    enc = tiktoken.encoding_for_model(model)
    total_tokens = sum(len(enc.encode(m["content"])) for m in messages)
    cost_per_1k = 0.03  # GPT-4 input pricing
    return (total_tokens / 1000) * cost_per_1k

2. Context not passing between agents

When one agent's output doesn't reach the next agent correctly, the entire pipeline breaks silently. Always verify context handoffs explicitly.

3. No pipeline-level timeouts

An individual agent can run indefinitely. Set a hard timeout on the entire pipeline.

import asyncio

async def run_with_timeout(crew, timeout=300):
    try:
        return await asyncio.wait_for(
            asyncio.to_thread(crew.kickoff),
            timeout=timeout
        )
    except asyncio.TimeoutError:
        raise RuntimeError(f"Crew timed out after {timeout}s")

4. Not monitoring intermediate results

In a 5-agent pipeline, if agent 2 produces garbage, agents 3-5 amplify that garbage. Log intermediate outputs for every node.

Wrapping Up

My recommendation: start with CrewAI. It's intuitive and you'll get results fast. Move to LangGraph when you need production reliability or complex conditional flows. Use AutoGen specifically for code generation workflows.

Regardless of the framework: always try a single agent first. Add multi-agent complexity only when you genuinely hit its limits.

Next: Tool Calling in Practice — how LLMs actually interact with external tools, common pitfalls, and patterns that hold up in production.

Comments

No comments yet.

Sign in to leave a comment