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.dataas a validatedTicket; 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
| Dimension | PydanticAI | LangGraph | CrewAI |
|---|---|---|---|
| Core model | Typed agent | Stateful graph | Role-based crew |
| Structured output | Best (built-in) | Manual/typed edges | Via Pydantic |
| State / multi-step | Minimal (single-agent) | Best (checkpointed) | Good (crews/flows) |
| Human-in-the-loop | Deferred tools | First-class interrupts | Hooks |
| Observability | OTel / Logfire | LangSmith/Studio | Internal logs |
| Time to prototype | 1.5h | 3h | 45min |
| Token efficiency | 2,912 | 2,847 | 4,216 |
| Cost control | Usage limits built-in | Code-level | max_iters (advisory) |
| Debug-at-2AM score | 8/10 | 9/10 | 5/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
- PydanticAI docs. https://ai.pydantic.dev
- LangGraph docs. https://langchain-ai.github.io/langgraph/
- CrewAI docs. https://docs.crewai.com
- A practical 2026 comparison of the three. https://subagentic.ai/howtos/2026-ai-agent-framework-decision-guide/