LangGraph vs AutoGen

Workflow orchestration vs conversation-centric programming

Overview

Workflow orchestration vs conversation-centric programming

Verdict

Workflow orchestration vs conversation-centric programming

Details

LangGraph vs AutoGen

Overview

A detailed comparison of two popular multi-agent frameworks: LangGraph (by LangChain) and AutoGen (by Microsoft). Both enable building sophisticated AI agent systems, but with fundamentally different approaches.

Quick Comparison Table

FeatureLangGraphAutoGen
MaintainerLangChainMicrosoft
ParadigmStateful workflows (graph-based)Conversation-centric
State ManagementExplicit TypedDict stateMessage-based conversation
Control FlowExplicit edges and conditionsImplicit via conversation
Human-in-the-LoopBuilt-in interrupt/resumeBuilt-in human proxy
PersistenceCheckpointers (memory, SQLite, Postgres)Conversation history
StreamingNative streaming supportStreaming support
Multi-AgentGraph nodes as agentsAgent-to-agent conversation
Learning CurveModerate (graph concepts)Low (conversation model)
Best ForComplex workflows, pipelinesMulti-agent conversations

Philosophy

LangGraph

"Build stateful, multi-actor applications with LLMs"

LangGraph treats agent systems as graphs:

  • Nodes represent actions (LLM calls, tools, etc.)
  • Edges define control flow
  • State flows through the graph
  • Explicit, deterministic execution
from langgraph.graph import StateGraph, END

class AgentState(TypedDict):
    messages: list[BaseMessage]
    context: dict
    iteration: int

builder = StateGraph(AgentState)
builder.add_node("research", research_node)
builder.add_node("write", write_node)
builder.add_edge("research", "write")
builder.set_entry_point("research")
builder.add_edge("write", END)

workflow = builder.compile()

AutoGen

"Build multi-agent conversation systems"

AutoGen treats agent systems as conversations:

  • Agents have roles and personalities
  • Agents converse with each other
  • Messages flow between agents
  • Implicit, emergent behavior
from autogen import ConversableAgent, UserProxyAgent

researcher = ConversableAgent(
    name="Researcher",
    system_message="You are a research assistant.",
    llm_config=llm_config,
)

writer = ConversableAgent(
    name="Writer",
    system_message="You are a content writer.",
    llm_config=llm_config,
)

researcher.initiate_chat(
    writer,
    message="Research and write about AI agents",
)

State Management

LangGraph

Explicit, typed state:

from typing import TypedDict, Annotated, List
from langgraph.graph import add_messages
from langchain_core.messages import BaseMessage

class AgentState(TypedDict):
    messages: Annotated[List[BaseMessage], add_messages]
    context: dict
    iteration: int
    status: str

def research_node(state: AgentState) -> dict:
    # Access state
    messages = state["messages"]
    context = state["context"]
    
    # Return updates
    return {
        "messages": [new_message],
        "context": {"research": results},
        "iteration": state["iteration"] + 1,
    }

Benefits:

  • Type-safe state definition
  • Explicit state transitions
  • Easy to debug and test
  • State persists across checkpoints

AutoGen

Message-based state:

researcher = ConversableAgent(
    name="Researcher",
    system_message="You are a research assistant.",
    llm_config=llm_config,
)

writer = ConversableAgent(
    name="Writer",
    system_message="You are a content writer.",
    llm_config=llm_config,
)

# State is implicit in conversation history
researcher.initiate_chat(
    writer,
    message="Research and write about AI agents",
)

# Access conversation history
print(researcher.chat_messages)
print(writer.chat_messages)

Benefits:

  • Natural conversation flow
  • Less boilerplate
  • Emergent behavior
  • Easy to add/remove agents

Control Flow

LangGraph

Explicit control flow:

from langgraph.graph import StateGraph, END
from typing import Literal

def should_continue(state: AgentState) -> Literal["write", "finalize"]:
    if state["iteration"] >= 3:
        return "finalize"
    return "write"

builder = StateGraph(AgentState)
builder.add_node("research", research_node)
builder.add_node("write", write_node)
builder.add_node("finalize", finalize_node)

builder.add_edge("research", "write")
builder.add_conditional_edges(
    "write",
    should_continue,
    {"write": "write", "finalize": "finalize"},
)
builder.add_edge("finalize", END)

workflow = builder.compile()

Features:

  • Conditional edges
  • Parallel execution
  • Loops and cycles
  • Human interrupts

AutoGen

Implicit control flow:

researcher = ConversableAgent(
    name="Researcher",
    system_message="You are a research assistant. TERMINATE when done.",
    llm_config=llm_config,
    max_consecutive_auto_reply=3,
)

writer = ConversableAgent(
    name="Writer",
    system_message="You are a content writer. TERMINATE when done.",
    llm_config=llm_config,
    max_consecutive_auto_reply=3,
)

user = UserProxyAgent(
    name="User",
    human_input_mode="TERMINATE",
    is_termination_msg=lambda msg: "TERMINATE" in msg.get("content", ""),
)

# Control via system messages and termination conditions
user.initiate_chat(
    researcher,
    message="Research and write about AI agents",
    max_turns=10,
)

Features:

  • Termination messages
  • Max turns limit
  • Human proxy for control
  • Group chat for multi-agent

Human-in-the-Loop

LangGraph

Built-in interrupt and resume:

from langgraph.checkpoint.memory import MemorySaver

checkpointer = MemorySaver()
workflow = builder.compile(checkpointer=checkpointer)

# Run with interrupt
config = {"configurable": {"thread_id": "session-1"}}
workflow.invoke(input_data, config=config)

# Interrupt before node
builder.interrupt_before("review")

# Resume after human input
snapshot = workflow.get_state(config)
if snapshot.next:
    # Human reviewed, resume
    workflow.invoke(None, config=config)

AutoGen

Human proxy agent:

user_proxy = UserProxyAgent(
    name="Human",
    human_input_mode="ALWAYS",  # Always ask for input
    is_termination_msg=lambda msg: "TERMINATE" in msg.get("content", ""),
)

# Human participates in conversation
researcher.initiate_chat(
    user_proxy,
    message="Research about AI agents",
)

# Or human-in-the-loop for specific agents
assistant = ConversableAgent(
    name="Assistant",
    system_message="You are helpful.",
    human_input_mode="TERMINATE",  # Ask until TERMINATE
)

Multi-Agent Patterns

LangGraph

Nodes as specialized agents:

from langgraph.graph import StateGraph

class MultiAgentState(TypedDict):
    messages: list[BaseMessage]
    research: dict
    draft: str
    review: dict

builder = StateGraph(MultiAgentState)

# Specialized agent nodes
builder.add_node("researcher", researcher_node)
builder.add_node("writer", writer_node)
builder.add_node("editor", editor_node)
builder.add_node("manager", manager_node)

# Define workflow
builder.add_edge("researcher", "writer")
builder.add_edge("writer", "editor")
builder.add_edge("editor", "manager")
builder.add_conditional_edges(
    "manager",
    should_loop,
    {"writer": "writer", "finalize": "finalize"},
)

workflow = builder.compile()

AutoGen

Agents in conversation:

from autogen import GroupChat, GroupChatManager

researcher = ConversableAgent(name="Researcher", ...)
writer = ConversableAgent(name="Writer", ...)
editor = ConversableAgent(name="Editor", ...)
manager = ConversableAgent(name="Manager", ...)

groupchat = GroupChat(
    agents=[researcher, writer, editor, manager],
    messages=[],
    max_round=12,
    speaker_selection_method="auto",
)

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

researcher.initiate_chat(
    manager,
    message="Create a blog post about AI agents",
)

Persistence

LangGraph

Multiple checkpointers:

# In-memory (development)
from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()

# SQLite (persistent)
from langgraph.checkpoint.sqlite import SqliteSaver
checkpointer = SqliteSaver.from_conn_string("checkpoints.db")

# PostgreSQL (production)
from langgraph.checkpoint.postgres import PostgresSaver
checkpointer = PostgresSaver(conn)

workflow = builder.compile(checkpointer=checkpointer)

# Time travel
history = workflow.get_state_history(config)
for checkpoint in history:
    print(checkpoint.values)

AutoGen

Conversation history:

# Access conversation history
print(agent.chat_messages)

# Save/restore conversations
import json
history = json.dumps(agent.chat_messages)

# Restore
agent.chat_messages = json.loads(history)

Streaming

LangGraph

Native streaming:

async for event in workflow.astream(input_data, config=config):
    for node_name, node_output in event.items():
        print(f"[{node_name}] {node_output}")
        yield node_output

# Stream state
async for state in workflow.astream(input_data, config=config, stream_mode="values"):
    print(f"Step: {state['iteration']}")

AutoGen

Streaming chat:

# Stream chat completion
for chunk in llm_client.stream(messages):
    print(chunk.choices[0].delta.content, end='', flush=True)

# Agent streaming
async for message in agent.a_generate_reply_stream(messages):
    print(message.content, end='', flush=True)

Error Handling

LangGraph

Node-level error handling:

from langgraph.errors import NodeInterrupt

async def resilient_node(state: AgentState) -> dict:
    try:
        result = await perform_action()
        return {"result": result}
    except Exception as e:
        # Log and continue
        return {"error": str(e), "result": None}
        # Or raise to stop
        # raise NodeInterrupt(f"Failed: {e}")

AutoGen

Conversation-level error handling:

try:
    agent.initiate_chat(
        other_agent,
        message="Do something",
    )
except Exception as e:
    print(f"Conversation error: {e}")
    # Handle or retry

Best Use Cases

Choose LangGraph When:

  • Complex workflows: Multi-step processes with clear stages
  • State management: Need explicit state tracking
  • Persistence: Need to save/resume workflows
  • Human-in-the-loop: Need interrupts and approvals
  • Parallel execution: Need concurrent branches
  • Testing: Need deterministic, testable workflows

Choose AutoGen When:

  • Multi-agent conversations: Natural conversation flows
  • Rapid prototyping: Quick setup and experimentation
  • Emergent behavior: Let agents figure out the flow
  • Human participation: Humans as conversation participants
  • Group dynamics: Multi-agent group discussions
  • Flexibility: Less rigid structure

Migration Guide

From LangGraph to AutoGen

# LangGraph
builder = StateGraph(AgentState)
builder.add_node("research", research_node)
builder.add_node("write", write_node)
builder.add_edge("research", "write")

# AutoGen
researcher = ConversableAgent(name="Researcher", ...)
writer = ConversableAgent(name="Writer", ...)
researcher.initiate_chat(writer, message="Research and write")

From AutoGen to LangGraph

# AutoGen
researcher.initiate_chat(writer, message="Research and write")

# LangGraph
builder = StateGraph(AgentState)
builder.add_node("researcher", researcher_node)
builder.add_node("writer", writer_node)
builder.add_edge("researcher", "writer")
workflow = builder.compile()
result = workflow.invoke({"messages": [HumanMessage(content="Research and write")]})

Conclusion

Both frameworks are powerful tools for building AI agent systems. The choice depends on your needs:

  • LangGraph excels at structured workflows with explicit state, persistence, and human-in-the-loop support.
  • AutoGen excels at multi-agent conversations with natural interaction patterns and emergent behavior.

For production systems requiring reliability and control, I recommend LangGraph. For rapid prototyping and conversational applications, AutoGen is excellent.

Resources


Last updated: May 2026