Thomas Piacentini
← Atelier

Fiche

Agent Frameworks Compared

Texte original · anglais

Pick LangGraph, CrewAI, AutoGen, Agno, Mastra, an agent SDK or plain Python by problem shape, state and routing cost.

Orchestration frameworks side by side

FactorLangGraphCrewAIAutoGenAgno
Core abstractionStateGraphCrewConversableAgent pairAgent
Mental modelState machineTeam of specialistsChatAgent with batteries included
StateTyped state, checkpointerStrings through contextChat historySession store through storage=
Who branchesYou, with conditional edgesManager, or you at build timeAgents, through chatThe next tool call
Routing tokensOnly where you write llm.invokeHierarchical manager spends tokensGroupChatManager spends tokensThin single-agent path
Best fitTyped state, human interruptsShort role-driven plansProposer-critic dialogueSingle agent, sessions, memory
Worst fitUnknown topologyComplex branchingKnown DAG, durable stateDeep branched graphs
Sources

Agent SDKs and runtimes side by side

FactorOpenAI Agents SDKClaude Agent SDKAutoGen v0.4Mastra
PrimitivesAgent, Handoff, Guardrail, Session, TracingTools, MCP, hooks, subagents, session storeCore, AgentChat, ExtensionsAgents, Tools, Workflows
DelegationTool named transfer_to_<agent_name>Subagents with their own contextMessages to an actor inboxWorkflow steps
StateSession in SQLite, Redis, customappend, load, delete with cascadePrivate actor stateComposite storage
TracingOn by defaultW3C trace context into the CLIA span for each messageMastra Studio
Use forOpenAI-first productsClaude-first productsActor concurrency, fault isolationTypeScript, Next.js or Vercel
Sources

Use X when

LangGraph
typed state, approvals, resume, Send fanout.
CrewAI
a Flow that calls Crew.kickoff() in a step.
AutoGen
proposer-critic dialogue.
Agno
many short-lived Python agents, FastAPI.
Mastra
TypeScript, Next.js or Vercel, Zod-typed tools.
Plain Python
two LLM calls and a tool.
Sources

Decision tree: recommend(p)

  1. plain python total_llm_calls 2 or fewer. No roles, dialogue, resume, fan-out or human interrupt.
  2. langgraph needs_resume, needs_human_interrupt or has_parallel_fanout.
  3. autogen has_dialogue without has_typed_state.
  4. crewai has_roles without has_typed_state.
  5. agno needs_session_memory, no roles, no dialogue.
  6. langgraph has_typed_state, and the fallback.
Sources

Actor runtime (AutoGen v0.4 shape)

sendstartreviewreviewreview_resultcrash_me raises__user__sends startclientRuntime queueMessage dequequeueChecklistAgentsends three reviewsprocessdead_lettersfailed messagesstoreReviewerAgentsends review_resultprocess
Nodes and connections
NodeRoleKind
__user__sends startclient
Runtime queueMessage dequequeue
ChecklistAgentsends three reviewsprocess
dead_lettersfailed messagesstore
ReviewerAgentsends review_resultprocess
FromToConnection
__user__Runtime queuesend
Runtime queueChecklistAgentstart
ChecklistAgentRuntime queuereview
Runtime queueReviewerAgentreview
ReviewerAgentRuntime queuereview_result
ReviewerAgentdead_letterscrash_me raises
Sources

SDK tasks in code

TaskCode
Ask the recommenderpython main.py --ask
Hand off to an agenttransfer_to_<agent_name>
Turn off tracingOPENAI_AGENTS_DISABLE_TRACING=1
Fan spans to your backendadd_trace_processor(processor)
Run with stored historyRunner.run(agent, input, session=session)
Force a typed task outputoutput_pydantic=MyModel
List subagent sessionslist_subkeys(session_id)
Sources

Suspend and resume in Mastra

const workflow = createWorkflow({ id: "ticket-triage", inputSchema: ticketSchema, outputSchema: resultSchema })
.then(classifyStep)intent read or write
.then(planStep)lookup or update action
.then(executeStep)suspends before an update
.commit();
if (!resumeData?.approved)
return suspend({ ticketId: plan.ticket.id, planHash: digest });digest = planHash(plan)
const { workflow, storage } = await persistentWorkflow(tool, "file:" + resolve(args["--db"]));
const run = await workflow.createRun({ runId: args["--resume"] });fresh process, same file
result = await run.resume({ step: "execute", resumeData });approved, ticketId, planHash
Sources

Run the projects

CommandWhat it does
python3 scripts/project_test.py <project> --stage 1 --path <dir> --strictGrade one stage
python3 scripts/project_test.py <project> --all --solution --optional --strictRun the real SDK tests. A SKIP fails
node --experimental-strip-types cli.ts --ticket fixtures/ticket.json --out workflow-outputScratch workflow. Writes a checkpoint when approval is required
node --experimental-strip-types optional-mastra/cli.ts --ticket fixtures/ticket.json --db ./runs.db --out pending-outputDurable Mastra run. Reports a runId
python3 main.py --ticket fixtures/ticket.json --out support-outputOffline support review in index.html
main.py --ticket YOUR_TICKET.json --adk --out support-adk-outputSame ticket through real ADK agents
Sources

What the projects build and check

Mastra builds
a typed approval workflow. It suspends before an update and resumes from SQLite.
Mastra output
workflow.json, plus approval.json with approved=false.
Mastra check
matching approval gives one tool call. A wrong hash gives zero calls.
ADK builds
a redacted support reply or a human escalation, then the same rules in real ADK agents.
ADK output
an HTML review page and support.json.
ADK check
invoice login ties billing and access, so it escalates.
Sources

Defaults and limits

SettingDefaultNote
Runner max_hops3Hop counter against handoff drift
Runtime max_messages100Messages for each run_until_idle
HierarchicalCrew max_steps5Manager rounds before stop
Guardrail modeParallelLower tail latency, wastes tokens on a trip
executePlan maxCalls3Allowed range 1 to 100
executePlan maxAttempts2Allowed range 1 to 10
Sources

Troubleshooting

SymptomCauseFix
A hands off to B, B hands back to AHandoff driftAdd a hop counter
Built-in tool skips the checkTool guardrails fire on function tools onlyAdd a separate policy
Task N+1 ad-libs on free textBrittle handoffsSet output_pydantic on Task N
Crew costs too many tokensManager-LLM token taxSwitch to Sequential
Context gone before the first tool callLong backstoriesKeep backstories under 200 words
Overhead dominates tiny tasksSubagent over-spawnBatch the tasks
Sessions grow without limitSession bloatUse list_sessions and an expiry policy
Resume fails before the toolWrong ticket or digest in approvalKeep ticketId and planHash unchanged
Sources

Questions

Why is a handoff a tool?
The model calls transfer_to_billing_agent. The runtime switches agents.
Why do actors isolate faults?
send(agent_b, msg) only queues. The runtime catches handler crashes.
Can the model change the ADK route?
No. It raises ValueError.
Sources
Original source
---
id: agent-frameworks-compared
title: Agent Frameworks Compared
code: AC
subtitle: Pick LangGraph, CrewAI, AutoGen, Agno, Mastra, an agent SDK or plain Python by problem shape, state and routing cost.
hue: 280
sources: phases/11-llm-engineering/17-agent-framework-tradeoffs, phases/14-agent-engineering/14-autogen-actor-model, phases/14-agent-engineering/15-crewai-role-based-crews, phases/14-agent-engineering/16-openai-agents-sdk, phases/14-agent-engineering/17-claude-agent-sdk, phases/14-agent-engineering/18-agno-and-mastra-runtimes, projects/typed-workflow-agent-with-mastra, projects/support-agent-with-google-adk
source_hash: e7088d4e33659396
---

## Orchestration frameworks side by side
type: comparison
span: 2
from: phases/11-llm-engineering/17-agent-framework-tradeoffs/docs/en.md

| Factor | LangGraph | CrewAI | AutoGen | Agno |
|---|---|---|---|---|
| Core abstraction | `StateGraph` | `Crew` | `ConversableAgent` pair | `Agent` |
| Mental model | State machine | Team of specialists | Chat | Agent with batteries included |
| State | Typed state, checkpointer | Strings through `context` | Chat history | Session store through `storage=` |
| Who branches | You, with conditional edges | Manager, or you at build time | Agents, through chat | The next tool call |
| Routing tokens | Only where you write `llm.invoke` | Hierarchical manager spends tokens | `GroupChatManager` spends tokens | Thin single-agent path |
| Best fit | Typed state, human interrupts | Short role-driven plans | Proposer-critic dialogue | Single agent, sessions, memory |
| Worst fit | Unknown topology | Complex branching | Known DAG, durable state | Deep branched graphs |

## Agent SDKs and runtimes side by side
type: comparison
span: 2
from: phases/14-agent-engineering/14-autogen-actor-model/docs/en.md, phases/14-agent-engineering/16-openai-agents-sdk/docs/en.md, phases/14-agent-engineering/17-claude-agent-sdk/docs/en.md, phases/14-agent-engineering/18-agno-and-mastra-runtimes/docs/en.md

| Factor | OpenAI Agents SDK | Claude Agent SDK | AutoGen v0.4 | Mastra |
|---|---|---|---|---|
| Primitives | Agent, Handoff, Guardrail, Session, Tracing | Tools, MCP, hooks, subagents, session store | Core, AgentChat, Extensions | Agents, Tools, Workflows |
| Delegation | Tool named `transfer_to_<agent_name>` | Subagents with their own context | Messages to an actor inbox | Workflow steps |
| State | `Session` in SQLite, Redis, custom | `append`, `load`, `delete` with cascade | Private actor state | Composite storage |
| Tracing | On by default | W3C trace context into the CLI | A span for each message | Mastra Studio |
| Use for | OpenAI-first products | Claude-first products | Actor concurrency, fault isolation | TypeScript, Next.js or Vercel |

## Use X when
type: concepts
from: phases/11-llm-engineering/17-agent-framework-tradeoffs/docs/en.md, phases/14-agent-engineering/15-crewai-role-based-crews/docs/en.md, phases/14-agent-engineering/18-agno-and-mastra-runtimes/docs/en.md

- **LangGraph**: typed state, approvals, resume, `Send` fanout.
- **CrewAI**: a Flow that calls `Crew.kickoff()` in a step.
- **AutoGen**: proposer-critic dialogue.
- **Agno**: many short-lived Python agents, FastAPI.
- **Mastra**: TypeScript, Next.js or Vercel, Zod-typed tools.
- **Plain Python**: two LLM calls and a tool.

## Decision tree: recommend(p)
type: flow
from: phases/11-llm-engineering/17-agent-framework-tradeoffs/code/main.py

1. **plain python**: `total_llm_calls` 2 or fewer. No roles, dialogue, resume, fan-out or human interrupt.
2. **langgraph**: `needs_resume`, `needs_human_interrupt` or `has_parallel_fanout`.
3. **autogen**: `has_dialogue` without `has_typed_state`.
4. **crewai**: `has_roles` without `has_typed_state`.
5. **agno**: `needs_session_memory`, no roles, no dialogue.
6. **langgraph**: `has_typed_state`, and the fallback.

## Actor runtime (AutoGen v0.4 shape)
type: architecture
from: phases/14-agent-engineering/14-autogen-actor-model/code/main.py, phases/14-agent-engineering/14-autogen-actor-model/docs/en.md

```arch
node | user | 1,1 | __user__ | sends start | client
node | queue | 2,1 | Runtime queue | Message deque | queue
node | check | 3,1 | ChecklistAgent | sends three reviews | process
node | dlq | 1,2 | dead_letters | failed messages | store
node | rev | 3,2 | ReviewerAgent | sends review_result | process
edge | user | queue | send
edge | queue | check | start
edge | check | queue | review
edge | queue | rev | review
edge | rev | queue | review_result
edge | rev | dlq | crash_me raises
```

## SDK tasks in code
type: task-table
from: phases/11-llm-engineering/17-agent-framework-tradeoffs/code/main.py, phases/14-agent-engineering/15-crewai-role-based-crews/docs/en.md, phases/14-agent-engineering/16-openai-agents-sdk/docs/en.md, phases/14-agent-engineering/17-claude-agent-sdk/docs/en.md

| Task | Code |
|---|---|
| Ask the recommender | `python main.py --ask` |
| Hand off to an agent | `transfer_to_<agent_name>` |
| Turn off tracing | `OPENAI_AGENTS_DISABLE_TRACING=1` |
| Fan spans to your backend | `add_trace_processor(processor)` |
| Run with stored history | `Runner.run(agent, input, session=session)` |
| Force a typed task output | `output_pydantic=MyModel` |
| List subagent sessions | `list_subkeys(session_id)` |

## Suspend and resume in Mastra
type: annotated-code
lang: typescript
from: projects/typed-workflow-agent-with-mastra/solution/optional-mastra/adapter.ts, projects/typed-workflow-agent-with-mastra/solution/optional-mastra/cli.ts

```typescript
const workflow = createWorkflow({ id: "ticket-triage", inputSchema: ticketSchema, outputSchema: resultSchema })
  .then(classifyStep)                    // intent read or write
  .then(planStep)                        // lookup or update action
  .then(executeStep)                     // suspends before an update
  .commit();
if (!resumeData?.approved)
  return suspend({ ticketId: plan.ticket.id, planHash: digest });  // digest = planHash(plan)
const { workflow, storage } = await persistentWorkflow(tool, "file:" + resolve(args["--db"]));
const run = await workflow.createRun({ runId: args["--resume"] });  // fresh process, same file
result = await run.resume({ step: "execute", resumeData });  // approved, ticketId, planHash
```

## Run the projects
type: command-table
span: 2
from: projects/typed-workflow-agent-with-mastra/README.md, projects/support-agent-with-google-adk/README.md

| Command | What it does |
|---|---|
| `python3 scripts/project_test.py <project> --stage 1 --path <dir> --strict` | Grade one stage |
| `python3 scripts/project_test.py <project> --all --solution --optional --strict` | Run the real SDK tests. A SKIP fails |
| `node --experimental-strip-types cli.ts --ticket fixtures/ticket.json --out workflow-output` | Scratch workflow. Writes a checkpoint when approval is required |
| `node --experimental-strip-types optional-mastra/cli.ts --ticket fixtures/ticket.json --db ./runs.db --out pending-output` | Durable Mastra run. Reports a runId |
| `python3 main.py --ticket fixtures/ticket.json --out support-output` | Offline support review in index.html |
| `main.py --ticket YOUR_TICKET.json --adk --out support-adk-output` | Same ticket through real ADK agents |

## What the projects build and check
type: concepts
from: projects/typed-workflow-agent-with-mastra/README.md, projects/typed-workflow-agent-with-mastra/project.json, projects/typed-workflow-agent-with-mastra/solution/main.ts, projects/support-agent-with-google-adk/README.md, projects/support-agent-with-google-adk/project.json, projects/support-agent-with-google-adk/solution/intake.py

- **Mastra builds**: a typed approval workflow. It suspends before an update and resumes from SQLite.
- **Mastra output**: `workflow.json`, plus `approval.json` with approved=false.
- **Mastra check**: matching approval gives one tool call. A wrong hash gives zero calls.
- **ADK builds**: a redacted support reply or a human escalation, then the same rules in real ADK agents.
- **ADK output**: an HTML review page and `support.json`.
- **ADK check**: invoice login ties billing and access, so it escalates.

## Defaults and limits
type: defaults
from: phases/14-agent-engineering/14-autogen-actor-model/code/main.py, phases/14-agent-engineering/15-crewai-role-based-crews/code/main.py, phases/14-agent-engineering/16-openai-agents-sdk/code/main.py, phases/14-agent-engineering/16-openai-agents-sdk/docs/en.md, projects/typed-workflow-agent-with-mastra/solution/main.ts

| Setting | Default | Note |
|---|---|---|
| Runner `max_hops` | 3 | Hop counter against handoff drift |
| Runtime `max_messages` | 100 | Messages for each `run_until_idle` |
| HierarchicalCrew `max_steps` | 5 | Manager rounds before stop |
| Guardrail mode | Parallel | Lower tail latency, wastes tokens on a trip |
| `executePlan` `maxCalls` | 3 | Allowed range 1 to 100 |
| `executePlan` `maxAttempts` | 2 | Allowed range 1 to 10 |

## Troubleshooting
type: troubleshooting
from: phases/14-agent-engineering/15-crewai-role-based-crews/docs/en.md, phases/14-agent-engineering/16-openai-agents-sdk/docs/en.md, phases/14-agent-engineering/17-claude-agent-sdk/docs/en.md, phases/14-agent-engineering/18-agno-and-mastra-runtimes/docs/en.md, projects/typed-workflow-agent-with-mastra/README.md

| Symptom | Cause | Fix |
|---|---|---|
| A hands off to B, B hands back to A | Handoff drift | Add a hop counter |
| Built-in tool skips the check | Tool guardrails fire on function tools only | Add a separate policy |
| Task N+1 ad-libs on free text | Brittle handoffs | Set `output_pydantic` on Task N |
| Crew costs too many tokens | Manager-LLM token tax | Switch to Sequential |
| Context gone before the first tool call | Long backstories | Keep backstories under 200 words |
| Overhead dominates tiny tasks | Subagent over-spawn | Batch the tasks |
| Sessions grow without limit | Session bloat | Use `list_sessions` and an expiry policy |
| Resume fails before the tool | Wrong ticket or digest in approval | Keep ticketId and planHash unchanged |

## Questions
type: qa
from: phases/14-agent-engineering/14-autogen-actor-model/docs/en.md, phases/14-agent-engineering/16-openai-agents-sdk/docs/en.md, phases/14-agent-engineering/18-agno-and-mastra-runtimes/docs/en.md, phases/11-llm-engineering/17-agent-framework-tradeoffs/docs/en.md, projects/support-agent-with-google-adk/solution/adk_adapter.py

- **Why is a handoff a tool?** The model calls `transfer_to_billing_agent`. The runtime switches agents.
- **Why do actors isolate faults?** `send(agent_b, msg)` only queues. The runtime catches handler crashes.
- **Can the model change the ADK route?** No. It raises `ValueError`.
Code et livrables 1
Ce que vous en retenez

Enregistré dans votre carnet sur cet appareil.

Sources et limites

Les repères de lecture sont déclaratifs. Les résultats de tests sont enregistrés séparément ; ils valent pour les cas exécutés.

AI Engineering from Scratch · 3f9715c · MIT

Télécharger ce fichier ↓