Summary
If you are building an AI agent in Python in 2026, four frameworks come up in almost every architecture discussion: LangGraph, CrewAI, the OpenAI Agents SDK and Pydantic AI. All four can call tools, use an LLM to decide what to do next and return structured results. Where they differ is in how much control you get over the flow, how they handle long-running work and human approval, how tied they are to one model provider, and how easy they are to debug when an agent goes wrong in production.
The short version:
- LangGraph is the best fit for long-running, stateful workflows with explicit branching, checkpoints and human approval steps.
- Pydantic AI is the best fit for typed, validated tool calls and outputs, especially for teams already using Pydantic and FastAPI.
- OpenAI Agents SDK is the quickest path for simple agents and handoffs when OpenAI is your model provider.
- CrewAI is the fastest way to prototype role-based multi-agent systems, with Flows for more controlled, event-driven pipelines.
This guide explains how each one works, shows a small code example for each, and gives a practical decision guide for CTOs and product teams who need an agent that survives real users, not just a demo.
What an AI agent framework actually does
An "agent" is an LLM running in a loop: it reads the conversation and current state, decides whether to call a tool (a database query, an API call, a search), reads the tool result and repeats until it can give an answer. You can write that loop yourself in around a hundred lines of Python. Frameworks earn their place by handling the parts that get hard in production:
- Tool definitions and validation – turning Python functions into tool schemas and validating the arguments the model sends.
- Structured output – making the final result match a schema your backend can trust.
- State and persistence – saving progress so a run can pause, survive a crash and resume.
- Human-in-the-loop – stopping before risky actions (refunds, emails, deletes) until a person approves.
- Multi-agent orchestration – routing work between specialised agents.
- Observability – tracing every model call and tool call so you can see why an agent did something.
Keep this list in mind. The right framework is the one that handles the parts your product cannot afford to get wrong.
LangGraph: explicit graphs for stateful, long-running agents
LangGraph, from LangChain, Inc., is a low-level orchestration runtime. You define your agent as a graph: nodes are steps (an LLM call, a tool call, a validation) and edges decide what runs next. State is a typed dictionary passed between nodes, and a checkpointer saves that state after every step, with in-memory, SQLite and Postgres options. LangChain's higher-level agent helpers are built on top of it, and it is also available for JavaScript and TypeScript.
Its standout feature is interrupt(): a node can pause the graph, wait for a human decision (minutes or days later) and resume from exactly that point.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
from langgraph.checkpoint.memory import InMemorySaver
from langgraph.types import interrupt, Command
class State(TypedDict):
ticket: str
draft: str
approved: bool
def draft_reply(state: State) -> dict:
# call your LLM here
return {"draft": f"Suggested refund reply for: {state['ticket']}"}
def human_approval(state: State) -> dict:
decision = interrupt({"draft": state["draft"]}) # pauses the run
return {"approved": decision == "approve"}
builder = StateGraph(State)
builder.add_node("draft_reply", draft_reply)
builder.add_node("human_approval", human_approval)
builder.add_edge(START, "draft_reply")
builder.add_edge("draft_reply", "human_approval")
builder.add_edge("human_approval", END)
graph = builder.compile(checkpointer=InMemorySaver()) # use Postgres in production
config = {"configurable": {"thread_id": "ticket-1042"}}
graph.invoke({"ticket": "Customer wants a refund"}, config) # stops at interrupt
graph.invoke(Command(resume="approve"), config) # resumes later
Strengths: full control of the flow, easy to see which node an agent was in when it went wrong, resumable runs, mature human-in-the-loop support and a large integration ecosystem through LangChain. Tracing and evaluation are tightly integrated with LangSmith.
Trade-offs: more code and concepts than a simple tool loop needs, and the layering between LangChain and LangGraph has a learning curve. Durability comes from LangGraph's own checkpointers rather than an external workflow engine.
Choose it for: approval workflows, back-office automation, multi-step research or document processing, and anything regulated where you must explain and replay what happened. We covered how teams use LangChain and LangGraph with retrieval in RAG and LangChain in production.
Pydantic AI: type-safe agents for Python teams
Pydantic AI comes from the team behind Pydantic, the validation library under FastAPI and much of the Python AI ecosystem. Instead of a graph, you define an Agent with an output type, a dependency type and tools. Tool arguments and final outputs are validated Pydantic models, so a malformed response is caught and retried rather than passed to your database. It supports many model providers, can use tools from MCP servers, and emits OpenTelemetry traces (with Pydantic Logfire as an optional hosted backend).
from dataclasses import dataclass
from pydantic import BaseModel
from pydantic_ai import Agent, RunContext
@dataclass
class Deps:
customer_id: int
db: "OrderRepository" # your own data access class
class RefundDecision(BaseModel):
approve: bool
amount: float
reason: str
agent = Agent(
"openai:gpt-4o",
deps_type=Deps,
output_type=RefundDecision,
instructions="Decide refunds using the order history. Never exceed the order total.",
)
@agent.tool
async def order_total(ctx: RunContext[Deps], order_id: str) -> float:
return await ctx.deps.db.order_total(ctx.deps.customer_id, order_id)
result = await agent.run("Refund order A-1042, item arrived broken", deps=Deps(42, repo))
decision: RefundDecision = result.output # validated, typed
Strengths: the result has a guaranteed shape, editors and type checkers understand your agent, dependency injection makes testing straightforward, and it feels like ordinary FastAPI code. For durable, long-running runs it integrates with workflow engines such as Temporal, DBOS and Prefect instead of inventing its own.
Trade-offs: there is less built-in multi-agent structure than CrewAI, and for complex branching you either compose agents in plain Python or use its graph module. The ecosystem of ready-made integrations is smaller than LangChain's.
Choose it for: agents embedded in a SaaS backend where the output feeds another system: classification, extraction, triage, pricing suggestions, support actions and internal tools.
OpenAI Agents SDK: minimal primitives, fastest start
The OpenAI Agents SDK is deliberately small: agents, handoffs (one agent passing control to another), guardrails (checks on input and output), sessions for conversation memory, and built-in tracing. It is open source (MIT) and available for Python and TypeScript.
from agents import Agent, Runner, function_tool
@function_tool
def lookup_order(order_id: str) -> str:
"""Return the status of an order."""
return "Shipped on 3 Nov, refund not yet requested"
billing_agent = Agent(
name="Billing",
instructions="Answer refund and invoice questions. Use tools for facts.",
tools=[lookup_order],
)
triage_agent = Agent(
name="Triage",
instructions="Route billing questions to the Billing agent.",
handoffs=[billing_agent],
)
result = Runner.run_sync(triage_agent, "Where is my refund for order A-1042?")
print(result.final_output)
Strengths: very little to learn, tight integration with OpenAI's models, realtime voice and tracing dashboard, tool approval support and documented integrations with durable execution engines.
Trade-offs: it is designed around OpenAI's APIs. Other providers work through adapters, and tracing uploads to OpenAI by default, so check that fits your data policy. For complex, auditable workflows you will end up writing your own control flow.
Choose it for: customer support assistants, internal copilots and single-agent tool use where you are already committed to OpenAI.
CrewAI: role-based multi-agent teams
CrewAI models agents as team members. Each agent has a role, a goal and a backstory; you give a crew a list of tasks and it works through them, sequentially or with a manager agent delegating. CrewAI also offers Flows, an event-driven layer for more deterministic pipelines that can include crews as steps.
from crewai import Agent, Task, Crew, Process
researcher = Agent(
role="Market researcher",
goal="Find recent, verifiable facts about {topic}",
backstory="You check sources and never guess numbers.",
)
writer = Agent(
role="B2B copywriter",
goal="Turn research into a clear one-page brief",
backstory="You write for busy CTOs.",
)
research = Task(description="Research {topic}", expected_output="Bullet list of facts with sources", agent=researcher)
brief = Task(description="Write the brief", expected_output="A 300-word brief", agent=writer)
crew = Crew(agents=[researcher, writer], tasks=[research, brief], process=Process.sequential)
result = crew.kickoff(inputs={"topic": "EV fleet charging software"})
Strengths: the quickest route from idea to a working multi-agent prototype, readable configuration, many pre-built tools and a large community.
Trade-offs: when agents delegate to each other freely, it is harder to explain exactly why a decision was made, and costs can grow as agents talk to each other. For production, teams usually lean on Flows or keep crews small and well-bounded.
Choose it for: research, content and analysis pipelines, internal automation and proofs of concept where speed matters more than strict control.
Side-by-side comparison
| Need | LangGraph | Pydantic AI | OpenAI Agents SDK | CrewAI |
|---|---|---|---|---|
| Control over flow | Highest (explicit graph) | High (plain Python) | Medium (handoffs) | Lower in Crews, higher in Flows |
| Typed, validated output | Yes (Pydantic models) | Core design goal | Yes (output_type) | Yes (Pydantic output on tasks) |
| Pause and resume / human approval | Built-in checkpoints and interrupts | Via durable engines and tool approval | Tool approval, serialisable run state | Human input on tasks, Flows state |
| Model provider flexibility | Broad | Broad | OpenAI-first | Broad |
| Observability | LangSmith | OpenTelemetry / Logfire | OpenAI tracing, custom processors | Built-in and third-party |
| Learning curve | Steepest | Low for Pydantic users | Lowest | Low |
| Best for | Long, auditable workflows | Agents inside SaaS backends | Simple OpenAI agents | Fast multi-agent prototypes |
These frameworks are not mutually exclusive. A common production pattern is a LangGraph workflow where individual nodes are small, typed Pydantic AI agents.
What matters more than the framework in production
In our experience building AI features for SaaS products, agents rarely fail because of the framework choice. They fail because of missing engineering around the model. Whatever you choose, plan for these:
1. Limit what tools can do
Give each agent the smallest set of tools it needs, scope every tool to the current user or tenant on the server side, and require human approval for anything irreversible, such as payments, deletions or outbound emails. Treat any text the agent reads from the web, documents or emails as untrusted, because prompt injection can make it call tools you did not intend.
2. Validate everything that leaves the model
Use structured output and validate it before writing to your database or calling another API. Retrying on a validation error is cheap; cleaning bad data out of production is not.
3. Make runs durable
Agent runs can take minutes, call slow APIs and hit rate limits. Run them in background workers or a workflow engine, persist state, and make tools idempotent so a retry does not charge a card twice.
4. Trace and evaluate
Log every model call, tool call, token count and latency. Build a small evaluation set from real requests and run it on every prompt or model change, so you know whether a change helped or hurt.
5. Control cost
Multi-step agents multiply token usage. Cap the number of steps, use smaller models for routing and classification, and cache where you can. Our guide to reducing LLM API costs in SaaS covers this in detail.
6. Expose tools in a standard way
All four frameworks can consume tools from Model Context Protocol (MCP) servers. If your product's capabilities are behind an MCP server, you can switch agent frameworks later without rewriting your integrations. See how to build an MCP server for your SaaS.
Decision guide: which one should you pick?
- You need approvals, branching and the ability to replay what happened: LangGraph.
- Your agent lives inside a Python/FastAPI backend and its output feeds other systems: Pydantic AI.
- You are all-in on OpenAI and the flow is simple: OpenAI Agents SDK.
- You want to test a multi-agent idea this week: CrewAI, then harden with Flows or move critical paths to a stricter framework.
- Your main app is Laravel or Node.js: run the agent as a separate Python service behind an internal API or queue, or use a framework's TypeScript version, and keep authentication and permissions in your main app.
Start from the failure you can least afford. If a wrong agent decision means a compliance issue or money moving incorrectly, choose explicit control. If the risk is mainly bad data, choose strict validation. If the risk is spending months before learning whether users want the feature, choose the fastest prototype and plan a rewrite of the critical path.
FAQ
Is LangGraph better than CrewAI?
Neither is better in general. LangGraph gives you explicit control, checkpoints and human approval, which suits production workflows that must be auditable. CrewAI is faster for prototyping role-based multi-agent systems. Many teams prototype in CrewAI and build critical workflows in LangGraph.
Can I use the OpenAI Agents SDK with Claude, Gemini or open-source models?
It can work with other providers through adapters, but its main path is OpenAI's API and its tracing defaults to OpenAI's dashboard. If you need to switch models often, LangGraph or Pydantic AI usually fit better.
Do I need a framework at all to build an AI agent?
Not always. A single agent with a few tools can be a short, well-tested loop in your own code. A framework becomes worth it when you need persistence, human approval, multi-agent routing, validated output and tracing without building them yourself.
Which framework is best for a SaaS product built on Laravel or Node.js?
Keep your main app as the source of truth for users, permissions and billing, and run the agent as a separate service. Python teams often choose Pydantic AI or LangGraph for that service; JavaScript teams can use the TypeScript versions of LangGraph or the OpenAI Agents SDK.
How much does it cost to build a production AI agent?
It depends on the number of tools and integrations, data security requirements, approval workflows and evaluation needs. A focused single-agent feature is a much smaller project than a multi-agent system connected to several business systems. The best way to estimate is a short discovery phase that defines tools, data access and success measures.
How do we stop an AI agent from taking harmful actions?
Restrict tools to the minimum, enforce permissions on the server rather than in the prompt, validate outputs, cap steps and spending, and require human approval for irreversible actions. Monitor traces and run evaluations before each release.
Written by
Prakash Tank
Founder & Full-Stack Architect, DayDecoder
Prakash started as a certified Laravel developer and grew into a full-stack architect who owns a project end to end: requirements, architecture, development, deployment, and support. He designs event-driven systems with...
More about Prakash