Skip to content
Blog

The Agent SDK Landscape: PydanticAI vs. LangGraph vs. CrewAI

Stop choosing an agent framework at random. Compare PydanticAI, LangGraph, and CrewAI on statefulness, multi-agent design, type safety, and operational control — then pick the right tool per job.

Published on August 8, 2026

AI Assistant

Pick the framework that fits the shape of your problem, not the buzzword of the week. The 2026 landscape has quietly consolidated: the serious Python choices are PydanticAI, LangGraph, and CrewAI. They have different mental models, so the decision is structural, not academic.

The three mental models

  • PydanticAI treats an agent as a typed function: LLM in, validated structured output out. It is the lightest, a single-agent library from the Pydantic team, modelled after FastAPI.
  • LangGraph treats an agent as a graph. Nodes are functions, edges are conditional transitions, every step is checkpointed, and you can pause, replay, or branch mid-run.
  • CrewAI treats an agent as a role on a team. Agents carry a role, goal, and backstory, and a Crew orchestrates tasks.

These are genuinely different patches to your architecture, not marketing variations.

When nothing would be better: stick to plain code

The strongest recommendation in 2026 is to start with no framework at all. A main() loop that calls a model, checks a condition, and calls another model is fully deterministic, costs nothing, and has no supply chain to maintain. Reach for a framework when you hit a concrete need: state that outlives one call, retries and branching, or multi-agent coordination.

PydanticAI — the typed boundary layer

If your job is “call model, guarantee the JSON shape”, this is the cleanest tool.

from pydantic import BaseModel
from pydantic_ai import Agent

class Ticket(BaseModel):
    summary: str
    severity: int
    assignee: str | None

agent = Agent(
    "gemini-2.5-flash",
    result_type=Ticket,
    system_prompt="Extract a support ticket from the user text.",
)

result = agent.run_sync(
    "Networking bug 402 — payment page hangs on checkout, blocks us from releasing tonight."
)
print(result.data)  # Ticket(summary=..., severity=..., ...)

Winning properties:

  • Structured output is non-negotiable. The model returns result.data as a validated Ticket; a malformed response becomes a typed error, not a silent string.
  • Deps injection, isolation, and testability. Every tool signature is typed; it feels like FastAPI.
  • Usage limits are baked in — request tokens, response tokens, total, and tool calls. Budget control in the config file is rare and valuable.

The cost: it is deliberately single-agent. No first-class team/handoff/handler patterns; you compose multi-agent behavior in your own code (register& run agent A’s result as a tool inside agent B).

LangGraph — the state machine

LangGraph turns a workflow into an explicit graph. Nodes mutate a TypedDict state, edges decide what runs next. Its durable-execution story is the industry’s best.

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

class State(TypedDict):
    facts: list[str]
    draft: str
    verdict: str

def research(state: State) -> State:
    return {"facts": get_facts(state.get("prompt", ""))}

def writer(state: State) -> State:
    return {"draft": write_draft(state["facts"])}

def critic(state: State) -> State:
    state["verdict"] = grade(state["draft"])
    return state

graph = StateGraph(State)
graph.add_node("research", research)
graph.add_node("writer", writer)
graph.add_node("critic", critic)
graph.add_edge("research", "writer")
graph.add_edge("writer", "critic")
graph.add_conditional_edges("critic", lambda s: "RESUME" if s["verdict"] != "OK" else "END", {
    "RESUME": "writer", "END": END,
})
app = graph.compile()

Why it wins:

  • Checkpointed execution. Every transition is recorded; a crash or human interruption resumes from the last checkpoint, not from zero. The killer feature for overnight pipelines.
  • Human-in-the-loop is native: pause at a node, wait days, resume with edited state.
  • Explicit branching. The conditional edge means you decide flow, not the model — traceable, testable, and token-frugal.

The cost: verbosity and a real learning curve. And debug posture: connect it to LangSmith tracing if you want per-step telemetry.

CrewAI — the team builder

You define a Crew and it does task delegation behind the scenes. The developer experience is the fastest to smoke: 45 minutes to a working multi-agent demo, roles, tools, and memory built in.

from crewai import Agent, Task, Crew, Process

researcher = Agent(
    role="Research Analyst",
    goal="Gather up-to-date facts on the topic",
    backstory="Prepares research briefs for writers",
)
writer = Agent(
    role="Staff Writer",
    goal="Write a crisp technical blog post",
    backstory="Translates research into readable prose",
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[
        Task(description="Research the topic and return 5 facts", agent=researcher),
        Task(description="Write a 300-word post from the facts", agent=writer),
    ],
    process=Process.sequential,
)
result = crew.kickoff()

Why it shines:

  • Role-based mental model — stakeholders read the crew, understand it, and suggest roles.
  • 30+ built-in tools and MCP server support and the largest community. For prototyping a “research → analyze → write → review” pipeline, it’s the least ceremony.
  • Flows (deterministic layer) were added for production-grade orchestration when pure autonomy gets unduable.

The costs are real: delegation loops are a known reliability issue at scale, observability is thin compared to LangGraph, and token spend tends higher (crew overhead) — the details have hit $414 on an uncapped run. Set max_iters and budget caps before deploy.

The comparison

DimensionPydanticAILangGraphCrewAI
Core modelTyped agentStateful graphRole-based crew
Structured outputBest (built-in)Manual/typed edgesVia Pydantic
State / multi-stepMinimal (single-agent)Best (checkpointed)Good (crews/flows)
Human-in-the-loopDeferred toolsFirst-class interruptsHooks
ObservabilityOTel / LogfireLangSmith/StudioInternal logs
Time to prototype1.5h3h45min
Token efficiency2,9122,8474,216
Cost controlUsage limits built-inCode-levelmax_iters (advisory)
Debug-at-2AM score8/109/105/10

The decision

  • Mostly linear typed pipeline, you own the Python → PydanticAI. Validation, low LOC, token caps in one place.
  • Branching, retries, human approvals, long-running jobs → LangGraph. The checkpointing is the difference between a bot and a product.
  • Role-based team prototype in a week → CrewAI. Fast, readable, team-shaped. Plan to add hard budgets (especially token limits), and consider a deterministic Flow layer in production.
  • Don’t over-index on the framework — prompt quality and tool design change output more. Mixing is fine: LangGraph as the orchestration spine, PydanticAI as the typed boundary, both calling the same model.

Conclusion & Next Steps

Pick the mental model that matches your pain: typed output (PydanticAI), explicit control flow (LangGraph), or fast role teams (CrewAI). Next: add tracing to whichever you chose, cap token spend empirically, and stress-test the protocol — most frameworks beat in production because observability and guardrails were afterthoughts, not because the loop was slow.

References / Sources