
Picking an AI Agent Framework in 2026
A hands-on comparison of nine Python frameworks — with code, data, and no marketing spin.
Why I wrote this
I've spent the last year and half building POC or production AI agents. I've shipped pipelines that run inside real business workflows, I've thrown away prototypes that looked great in a notebook and fell apart under load, and I've argued with colleagues about whether we even need a framework at all. This post is the reference I kept wishing existed: honest, grounded in actual use, with real code samples and no agenda.
I'm going to cover nine Python frameworks as of March 2026. Not because these are the only options, but because they're the ones showing up in architecture discussions, job postings, and production repositories.
Note on methodology: All GitHub star counts and growth data were collected in March 2026 via GitHub's API and the OSS Insight API. Code samples are functional as of this writing. My opinions are my own — I work in the AWS ecosystem and I'll call that out where it's relevant.
The Landscape at a Glance
Here's the raw data before I get into opinions. Absolute star counts are a popularity contest, not a quality signal. The six-month growth column tells a more useful story — it shows which frameworks developers are actively discovering and adopting right now.
| Framework | Stars | Age | 6-month Growth | Best For |
|---|---|---|---|---|
| AutoGen | 55.4K | 31 mo | +1,982 | Conversational multi-agent teams |
| CrewAI | 45.7K | 28 mo | +2,540 | Role-based collaborative workflows |
| LlamaIndex | 47.6K | 40 mo | +1,183 | RAG pipelines & data retrieval |
| LangGraph | 26.1K | 31 mo | +2,351 | Complex stateful graphs |
| Smolagents | 25.9K | 15 mo | +1,018 | Lightweight code-first agents |
| OpenAI SDK | 19.7K | 12 mo | +1,554 | Simple OpenAI-first agents |
| Google ADK | 18.3K | 11 mo | +1,900 | Google Cloud ecosystem |
| Pydantic AI | 15.4K | 21 mo | +996 | Structured, validated outputs |
| Strands Agents | 5.3K | 10 mo | +618 | Model-driven, AWS-native |
GitHub data collected March 2026. 'Age' measured from first stargazer event.
A few things jump out. CrewAI and LangGraph are still pulling the most new interest in absolute terms. Google ADK is remarkable for something less than a year old. Strands has the smallest community but it grew through AWS product adoption rather than viral open-source traction — it's already running inside Amazon Q, Kiro, and AWS Glue.
Also worth noting: almost every framework shows the same pattern in monthly star activity. A massive launch spike, then convergence to a steady trickle of 200–600 new stars per month. The hype fades. What you see afterward is genuine, organic adoption. On that measure, the field is far more level than the headline numbers suggest.
Different Philosophies, Different Trade-offs
These aren't interchangeable tools. They reflect genuinely different ideas about how agents should be built.
Scaffold-heavy
LangGraph and AutoGen give you explicit control over execution flow. You define graphs, nodes, edges, and state transitions. You get predictability at the cost of boilerplate. These frameworks assume you don't fully trust the model to decide what comes next, and they're often right.
Model-driven
Strands Agents and Smolagents take the opposite stance. Give the model tools, give it a goal, get out of the way. Less scaffolding, more trust in the model's reasoning. This approach has become more viable because the models themselves have gotten dramatically better at tool use and multi-step planning.
Role-based
CrewAI models the problem as a team of specialists with defined roles, backstories, and goals. Intuitive for business workflows — a researcher, a writer, an editor working in sequence. Can feel constraining when you need fine-grained execution control.
Type-safe
Pydantic AI focuses on structured, validated outputs. Less about orchestration, more about guaranteeing that the LLM returns exactly what your code expects. I use this one constantly as a layer inside larger pipelines.
Vendor-optimized
OpenAI Agents SDK and Google ADK are tuned for their respective ecosystems but generally work with other providers. The depth of integration with their native platform is noticeably smoother than going cross-provider.
The Same Thing, Nine Ways
The fastest way to understand a framework is to see the same minimal example written in each. Here's the pattern: create an agent with one tool, run it. The tool rolls a dice. The differences are immediately visible.
Strands Agents
Three imports, a decorated function, two lines to run. The
@tool decorator pulls schema from the docstring and type hints. Sensible defaults, no ceremony.1
2
3
4
5
6
7
8
9
10
11
12
13
14
from strands import Agent, tool
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides.
Args:
sides: Number of sides on the dice
"""
return f"You rolled a {random.randint(1, sides)}"
agent = Agent(tools=[roll_dice])
response = agent("Roll a 20-sided dice for me")
print(response)
LangGraph
The prebuilt
create_react_agent keeps simple cases manageable. Real power — and real complexity — shows up when you build custom graphs with nodes and edges, which is what most production teams end up doing.1
2
3
4
5
6
7
8
9
10
11
12
13
14
from langchain.chat_models import init_chat_model
from langgraph.prebuilt import create_react_agent
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides."""
return f"You rolled a {random.randint(1, sides)}"
model = init_chat_model("anthropic:claude-sonnet-4-20250514")
agent = create_react_agent(model, tools=[roll_dice])
result = agent.invoke(
{"messages": [{"role": "user", "content": "Roll a 20-sided dice for me"}]}
)
print(result["messages"][-1].content)
CrewAI
More ceremony. You define an agent with a role, backstory, and goal, then wrap the interaction in a Task and a Crew. This overhead starts making sense when you have multiple specialized agents. For a single tool call, it feels like a lot.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from crewai import Agent, Task, Crew
from crewai.tools import tool
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides."""
return f"You rolled a {random.randint(1, sides)}"
roller = Agent(
role="Dice Roller",
goal="Roll dice accurately when asked",
backstory="You are a tabletop gaming assistant",
tools=[roll_dice],
)
task = Task(
description="Roll a 20-sided dice for me",
expected_output="A dice roll result",
agent=roller,
)
crew = Crew(agents=[roller], tasks=[task])
result = crew.kickoff()
print(result)
OpenAI Agents SDK
Dead simple.
@function_tool, an Agent definition, Runner.run_sync. Built-in tracing and session persistence (SQLite) are nice extras you don't have to configure.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from agents import Agent, Runner, function_tool
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides."""
return f"You rolled a {random.randint(1, sides)}"
agent = Agent(
name="Dice Agent",
instructions="You help roll dice when asked.",
tools=[roll_dice],
)
result = Runner.run_sync(agent, "Roll a 20-sided dice for me")
print(result.final_output)
Google ADK
More verbose for basic cases, but the multi-agent composition primitives —
SequentialAgent, ParallelAgent, LoopAgent — are clean and explicit. The dev UI (adk web) is genuinely excellent for testing before you ship.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from google.adk.agents import Agent
from google.adk.runners import InMemoryRunner
from google.genai.types import Content, Part
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides."""
return f"You rolled a {random.randint(1, sides)}"
root_agent = Agent(
name="dice_agent",
model="gemini-2.5-flash",
instruction="You help roll dice when asked.",
tools=[roll_dice],
)
runner = InMemoryRunner(agent=root_agent, app_name="dice_app")
session = await runner.session_service.create_session(app_name="dice_app", user_id="user1")
response = runner.run(user_id="user1", session_id=session.id,
new_message=Content(parts=[Part(text="Roll a 20-sided dice for me")]))
async for event in response:
if event.content and event.content.parts:
print(event.content.parts[0].text)
Pydantic AI
Where this framework shines is structured output. Define a Pydantic model as the return type and the framework guarantees the LLM's response validates against it, retrying with error context if it doesn't. This eliminates an entire class of production bugs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from pydantic_ai import Agent
import random
agent = Agent(
"openai:gpt-4o",
system_prompt="You help roll dice when asked.",
)
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides."""
return f"You rolled a {random.randint(1, sides)}"
result = agent.run_sync("Roll a 20-sided dice for me")
print(result.output)
Smolagents
The
CodeAgent writes Python code to orchestrate tool calls instead of using JSON. More token-efficient in some scenarios, and lets the agent compose operations in ways JSON tool calls can't easily express. Be aware of CVE-2025-9959 — it's patched in 1.21.0+, but code-executing agents need serious sandboxing.1
2
3
4
5
6
7
8
9
10
11
12
13
14
from smolagents import CodeAgent, InferenceClientModel, tool
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides.
Args:
sides: Number of sides on the dice
"""
return f"You rolled a {random.randint(1, sides)}"
model = InferenceClientModel()
agent = CodeAgent(tools=[roll_dice], model=model)
agent.run("Roll a 20-sided dice for me")
AutoGen
Async-first and event-driven. The Team abstraction is where it gets interesting. Worth noting: there was a major 0.2-to-0.4 rewrite that left a lot of outdated tutorials floating around online — if you're debugging, check the version of the example you're reading.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides."""
return f"You rolled a {random.randint(1, sides)}"
model = OpenAIChatCompletionClient(model="gpt-4o")
agent = AssistantAgent("dice_agent", model_client=model, tools=[roll_dice])
async def main():
await Console(agent.run_stream(task="Roll a 20-sided dice for me"))
import asyncio
asyncio.run(main())
LlamaIndex
The agent loop itself is not LlamaIndex's main event. What you're really buying is 160+ data connectors, the RAG pipeline, and evaluation modules. The agent is the orchestration layer on top of serious retrieval infrastructure.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from llama_index.core.agent.workflow import FunctionAgent
from llama_index.core.tools import FunctionTool
from llama_index.llms.openai import OpenAI
import random
def roll_dice(sides: int) -> str:
"""Roll a dice with the given number of sides."""
return f"You rolled a {random.randint(1, sides)}"
tool = FunctionTool.from_defaults(fn=roll_dice)
agent = FunctionAgent(
tools=[tool],
llm=OpenAI(model="gpt-4o"),
system_prompt="You help roll dice when asked.",
)
async def main():
response = await agent.run("Roll a 20-sided dice for me")
print(response.response.content)
import asyncio
asyncio.run(main())
What Actually Matters When Choosing
Model lock-in
The single most important architectural decision. Do you want to be free to swap providers in 12 months?
- Model-agnostic (no friction): Strands, LangGraph, Smolagents, Pydantic AI, LlamaIndex
- Provider-first but open: Google ADK, OpenAI Agents SDK, CrewAI
- No framework is truly locked, but integration depth varies enormously. Strands on Bedrock and ADK on Vertex are each smoother in their native cloud than they are fighting against the grain.
MCP support
Model Context Protocol has become the standard for connecting agents to external tools and data sources. Where things stand today:
- First-class native: Strands Agents, CrewAI, Smolagents, Pydantic AI
- Built-in via integrations: LangGraph (added in 1.0), OpenAI Agents SDK, Google ADK
- Built-in: AutoGen
- Emerging/partial: LlamaIndex
Deployment story
This is where the field really splits. Some frameworks are Python libraries that stop at the process boundary. Others have a real answer for getting your agent into production at scale.
- Full deployment story: Strands (Bedrock AgentCore, Lambda, Fargate), LangGraph (LangGraph Cloud), Google ADK (Cloud Run, Vertex AI)
- SDK + guidance: Pydantic AI, CrewAI (optional Agent Management Platform), OpenAI Agents SDK
- SDK only: Smolagents, AutoGen, LlamaIndex — deployment is your responsibility
Observability
You cannot debug what you cannot see, and agents are notoriously hard to debug. LangGraph with LangSmith is the most mature story. Pydantic AI with Logfire is good. OpenAI Agents SDK has built-in tracing. Strands uses OpenTelemetry, which plugs into AWS X-Ray and CloudWatch cleanly. For the others, you're bringing your own stack.
The Honest Take on Each
LangGraph
The good: Swiss Army knife. Broad integrations, mature tooling, LangSmith observability, durable execution for long-running agents. Battle-tested in production.
The honest: Heavy. Abstraction layers on abstraction layers. Documentation is extensive but scattered, and it's not always clear which version you're reading. The pricing for LangGraph Cloud can surprise you at scale. There's a vocal developer backlash around over-abstraction and API churn — worth reading before you commit.
Use it if: You need durable execution, complex stateful workflows, and you're willing to invest in learning the ecosystem.
CrewAI
The good: Fastest path from idea to multi-agent prototype. The role/backstory/goal pattern clicks immediately. Massive, active community. MCP and A2A support are first-class.
The honest: The abstraction can fight you when you need fine-grained control. Multiple teams report hitting a ceiling at 6–12 months and rewriting to LangGraph or custom code. Infinite loops and limited built-in observability are recurring complaints.
Use it if: You're building collaborative multi-agent workflows and want fast prototyping. The crew metaphor should fit your problem.
OpenAI Agents SDK
The good: Zero to working agent in under an hour. Built-in tracing, guardrails, session persistence. Massive PyPI download numbers suggest broad real-world adoption.
The honest: Works best with OpenAI models. Limited semantic memory layer. The handoff pattern for multi-agent is simple but not as powerful as graph-based approaches.
Use it if: You're an OpenAI shop and want the path of least resistance.
Google ADK
The good: Clean multi-agent composition primitives. Excellent dev UI. Multi-language support (Python, TypeScript, Go, Java) — more language breadth than any other framework here.
The honest: Gemini-first. Other providers work but aren't as polished. Google's reputation for sunsetting products makes some developers cautious about deep investment.
Use it if: You're in the Google ecosystem or the SequentialAgent/ParallelAgent composition model fits your architecture.
AutoGen
The good: Strong academic backing. Interesting Team abstraction for conversational multi-agent. Built-in code execution. Well-designed event-driven architecture.
The honest: The 0.2-to-0.4 rewrite created a documentation minefield. Microsoft is steering investment toward Semantic Kernel. Token consumption and circular conversation loops are persistent community complaints.
Use it if: You need conversational multi-agent systems or code execution as a core capability.
Strands Agents
The good: Minimal boilerplate, broad provider support (12+ providers), first-class MCP integration, three distinct multi-agent patterns (Graph, Swarm, Workflow), A2A protocol support. The
@tool decorator is dead simple. The deepest AWS deployment story via Bedrock AgentCore. 14 million downloads — it's not a side project.The honest: Newer than most frameworks here. Smaller community. Some features are still experimental. Lambda deployments don't support streaming — you need AgentCore Runtime or Fargate for that. If you're not on AWS, you miss the main deployment advantage, though the SDK works with any provider.
Use it if: You want model-driven agents with minimal scaffolding. You're on AWS or plan to be. You want to go from prototype to production without switching frameworks.
Pydantic AI
The good: Nothing else comes close for structured output. Type-safe approach catches errors that would otherwise surface in production. Logfire integration is solid. Low lock-in.
The honest: Often used as a validation layer alongside other frameworks rather than standalone. The multi-agent graph is newer and less battle-tested than LangGraph.
Use it if: Output structure is critical to your application or you're building API-driven agents with strict contracts.
Smolagents
The good: True to its name. The CodeAgent pattern is genuinely interesting for token-efficient tool orchestration. Model-agnostic. Push-to-Hub makes sharing agents easy.
The honest: CVE-2025-9959 was a sandbox escape (patched in 1.21.0+). More experimental than production-hardened. Deployment requires careful sandboxing configuration.
Use it if: You want a small, code-first library for research or prototyping.
LlamaIndex
The good: Unmatched retrieval infrastructure. 160+ data connectors, evaluation modules, a mature RAG pipeline. If your agent's primary job is querying documents and data sources, this is the most complete solution.
The honest: The agent loop is less sophisticated than dedicated agent frameworks. You're buying the retrieval stack and getting an agent as the orchestration layer on top.
Use it if: Your agent is fundamentally a retrieval/RAG system that also needs to reason and act.
The Cheat Sheet
| If you need… | Use |
|---|---|
| Complex, stateful workflows with maximum control | LangGraph |
| A multi-agent crew for content/research pipelines | CrewAI |
| The simplest possible thing that works | OpenAI Agents SDK |
| Structured, validated outputs | Pydantic AI |
| Document and data source querying | LlamaIndex |
| Google Cloud ecosystem | Google ADK |
| Code-generating agents for research | Smolagents |
| Conversational multi-agent teams | AutoGen |
| Model-driven agents with minimal scaffolding | Strands Agents |
| Something in production on AWS next month | Strands + Bedrock AgentCore |
Where This Is All Going
A year ago the question was which framework to learn. Now I hear a different question more often: do I even need a framework?
That's not a fringe view. There's a growing movement of developers who stripped out the frameworks and went back to raw Python loops against the provider API. The models have gotten good enough at tool use that a direct approach can get you surprisingly far. Vendor lock-in anxiety is real — major players formed the Agentic AI Foundation (AAIF) to promote interoperability and open standards. Watch that space.
The frameworks that will survive are the ones providing genuine value beyond what you'd write yourself: durable execution, managed deployment, observability, multi-agent coordination, structured outputs. These are hard problems worth depending on a library to solve. Everything else is scaffolding around a model that's increasingly capable of scaffolding itself.
The direction is clear. Less framework, more model. The best frameworks on this list know that. They're getting thinner, not thicker. They trust the model more and engineer the prompt less. Strands calls it model-driven. Smolagents calls it barebones. Pydantic AI calls it the Pydantic way. The label doesn't matter. The direction does.
Pick the framework that fits your cloud, your team, and your problem. Don't pick the one with the most stars.
"inspired by the conversation around agentic frameworks"
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article