LangGraph vs CrewAI: Choosing a Multi-Agent Framework

LangGraph gives you a graph over shared state and leaves every decision to you. CrewAI gives you a team of role-based agents and an event-driven flow around them. How each models the work, keeps state, and pauses for a person, with code for both.

7 min read

LangGraph and CrewAI both run several model-driven steps and agents toward one result, but they start from opposite ends. LangGraph is a low-level runtime: you draw the work as a graph of nodes over a shared state, and it gives you checkpointing, pauses for a person, and resuming after a failure. CrewAI is a higher-level Python framework: you describe agents by role and goal, give them tasks, and wrap them in Flows, an event-driven layer that holds state and control logic. Choose LangGraph when you want to decide every step and pause anywhere, in Python or JavaScript. Choose CrewAI when the work reads like a team with deliverables and you want working agents with less code, in Python. Both can run fixed steps beside agentic ones, both persist state, both pause for a person, and both use MCP tools.

This page compares the two on three things: the model, state, and human in the loop. The wider field, and when you need no framework at all, is in AI agent frameworks compared. How each names agents and tasks is in agents vs tasks in multi-agent frameworks.

The model: a graph, or a crew inside a flow

LangChain’s LangGraph overview (opens in a new tab) calls it a low-level orchestration framework and runtime for long-running, stateful agents, focused entirely on orchestration. You define a state schema, add nodes, which are plain functions that read the state and return updates, and connect them with edges, including conditional ones. A node can call a model, run a tool, or do ordinary deterministic work, and LangChain presents that mix as a core strength. It does not supply roles or prompts; it expects you to build the agent, or to start from LangChain’s prebuilt agents and move to LangGraph when you need the control.

CrewAI has two layers. A crew is a set of agents, each with a role, a goal and a backstory, working through tasks with an expected output, in sequence or under a manager. A flow is the structure around it: CrewAI’s Flows documentation (opens in a new tab) describes methods marked @start() and @listen(), a @router() for branching, or_ and and_ for joins, and state shared between methods. CrewAI’s own guidance is to use crews where you want agents to collaborate with some autonomy, flows where you need precise, predictable steps, and both together for most real applications.

The same two-step job in each (Python, trimmed)
# LangGraph: nodes over shared state
from langgraph.graph import StateGraph, START, END
g = StateGraph(ReleaseState)
g.add_node("draft", draft_notes)        # calls a model
g.add_node("check", check_links)        # plain code
g.add_edge(START, "draft")
g.add_edge("draft", "check")
g.add_edge("check", END)
graph = g.compile()

# CrewAI: a flow whose first step runs a crew
from crewai.flow.flow import Flow, start, listen
class ReleaseFlow(Flow[ReleaseState]):
    @start()
    def draft(self):
        self.state.notes = NotesCrew().crew().kickoff().raw
    @listen(draft)
    def check(self):
        self.state.ok = check_links(self.state.notes)

The shape is similar; the level is not. In LangGraph the model call inside draft_notes is yours to write. In CrewAI the crew brings agents, prompts and a loop, and you tune them through their fields.

State: what is kept, and how you resume

In LangGraph, state is the typed object every node reads and updates. Compile the graph with a checkpointer and it saves a checkpoint of that state at each step, per thread; the thread_id you pass is the pointer that lets you resume, replay or fork a run. The checkpointers page (opens in a new tab) lists SQLite for local work and Postgres or MongoDB for production, and three durability modes: exit saves only when the run ends or pauses, async saves while the next step runs, and sync saves before every step at some cost in speed. Separate stores hold long-term memory across threads.

In CrewAI, a flow’s state is either a plain dictionary or a Pydantic model, and every run gets a unique ID. The @persist decorator saves it, to SQLite by default, so a flow survives a restart, and restore_from_state_id starts a new run from an old one’s state. Separately, CrewAI’s checkpointing (opens in a new tab) saves a whole crew, flow or agent mid-run, by default once per completed task, so a failed run resumes without redoing finished tasks. A unified Memory class covers what agents remember between runs.

The difference in practice: LangGraph checkpoints every step of the graph as a matter of course, which is what makes pausing anywhere possible. CrewAI saves at the boundaries you choose, flow methods or finished tasks, which is less to think about and coarser.

Human in the loop

LangGraph’s mechanism is one function. According to its interrupts documentation (opens in a new tab), calling interrupt() inside a node pauses the graph and hands a JSON-serializable payload to the caller; the checkpointer saves the state; later you run the graph again on the same thread with Command(resume=...), and that value becomes the return value of interrupt(). It needs a checkpointer and a thread ID. One trap: on resume the node restarts from its beginning, so anything before the interrupt() call runs twice. Make side effects before it idempotent, or move them after it or into their own node.

CrewAI has three levels. On a task, human_input=True asks a person for feedback before the agent gives its final answer. In a flow, the @human_feedback decorator, added in CrewAI 1.8.0, pauses after a method, shows its output for review, and with emit uses a model to turn the reviewer’s free-text reply into an outcome such as approved or rejected that other methods listen for. CrewAI’s human feedback guide (opens in a new tab) notes that it waits for console input by default; for production you give it a provider that notifies Slack, email or a webhook, kickoff() returns a pending result with the state saved automatically, and you resume the flow when the answer arrives. CrewAI’s hosted platform adds webhook-based review for deployed crews.

An approval gate in each
# LangGraph
from langgraph.types import interrupt, Command
def approve(state):
    ok = interrupt({"question": "Publish these notes?", "notes": state["notes"]})
    return {"approved": ok}
graph = builder.compile(checkpointer=checkpointer)
cfg = {"configurable": {"thread_id": "release-42"}}
graph.invoke({"notes": "..."}, cfg)          # pauses at interrupt()
graph.invoke(Command(resume=True), cfg)      # resumes; interrupt() returns True

# CrewAI
from crewai.flow.flow import Flow, start, listen
from crewai.flow.human_feedback import human_feedback
class ReleaseFlow(Flow):
    @start()
    @human_feedback(message="Publish these notes?",
                    emit=["approved", "rejected"], llm="gpt-4o-mini")
    def draft(self):
        return "..."
    @listen("approved")
    def publish(self, result):
        ...

LangGraph gives you a precise primitive and leaves the interface to you. CrewAI gives you a review step with routing built in, at the cost of a model call to interpret the answer. Where people should step in at all is covered in human in the loop for AI agents.

The two side by side

  • Level: LangGraph is a low-level runtime you build agents on. CrewAI is a framework that arrives with agents, tasks and processes.
  • Languages: LangGraph in Python and JavaScript. CrewAI in Python.
  • Model of the work: LangGraph, nodes and edges over a typed state. CrewAI, role-based crews inside event-driven flows.
  • State: LangGraph checkpoints each step per thread, with durability modes and cross-thread stores. CrewAI persists flow state with @persist and checkpoints crews and flows per task.
  • Pausing for a person: LangGraph, interrupt() anywhere in a node, resumed with Command(resume=...). CrewAI, human_input on a task and @human_feedback on a flow method, with async providers for production.
  • MCP: both load MCP tools, LangGraph through LangChain’s MCP support and CrewAI through an agent’s mcps field or MCPServerAdapter; see MCP vs LangChain tools.
  • Tracing and hosting: LangSmith for LangGraph, CrewAI’s own platform for CrewAI. Both frameworks run without them.

How to choose

  1. If you ship in TypeScript, the choice is made: LangGraph.
  2. If the work is a known pipeline with a few model calls, either works; pick the one your team reads more easily, and consider no framework at all.
  3. If you need to pause at arbitrary points, for hours, and resume exactly where you stopped, LangGraph’s per-step checkpoints and interrupt() are built for that.
  4. If the work reads like jobs for colleagues, a researcher, a writer, a reviewer, CrewAI’s crews get you there with less code.
  5. If you want both, CrewAI’s flows give you deterministic steps around crews, and LangGraph lets you call any agent inside a node. Prototype the hardest step in each before committing.

Where the approval and the result are recorded

A framework’s pause lasts as long as its thread or flow state, and the reviewer sees it in whatever interface you built. The decision to run the job, and what it produced, belongs somewhere people already look. fenbs is an MCP server at https://fenbs.ai/api/mcp, so a LangGraph node or a CrewAI agent can reach it like any other server, signing in with a token you issue under Settings with a name, scopes and an optional expiry. One pattern keeps the human gate outside the code: a person presses “Let AI do this” on a task with a plan; the job calls fenbs_next_approved_task to take the most urgent approved one; when it finishes it comments with what it did, sets the test status and notes, and moves the task along the lanes, To Do, Next Up, In Progress and Completed. History records each change under the assistant’s name. fenbs does not resume your graph or flow for you; it is the record of what was asked and what was done.

Related

The other frameworks: AI agent frameworks compared, OpenAI Agents SDK and Claude Agent SDK. Several agents on one job: multi-agent workflows. The tools a board gives an agent: the MCP docs.

Questions people ask.

Is LangGraph better than CrewAI?

Neither is better in general. LangGraph suits teams that want explicit control of every step, per-step checkpoints and pauses anywhere, in Python or JavaScript. CrewAI suits teams that think in roles and deliverables and want working agents with less code, in Python.

Does CrewAI use LangGraph or LangChain?

No. CrewAI is its own framework with its own agents, tasks, crews and flows. It can call models from many providers and load MCP tools, but it does not run on LangGraph.

How does human in the loop work in LangGraph?

Call interrupt() inside a node. The graph pauses, the checkpointer saves the state, and the payload goes to the caller. Run the graph again on the same thread ID with Command(resume=value) and interrupt() returns that value. The node restarts from its beginning, so keep side effects after the interrupt.

Can a CrewAI flow wait for a person without blocking?

Yes. By default the human_feedback decorator waits for console input, but with a custom feedback provider kickoff() returns a pending result, the flow state is saved automatically, and you resume the flow when the reply arrives from Slack, email or a webhook.

Start with one thing.

There is nothing to set up first. Write one line and you’ve started.