Agent Frameworks
AutoGen & CrewAI
LangGraph organises around a state machine. These two organise around people.
JrCodex·7 min read
Jr Codex Agentic AI Notes
Level: Intermediate Prerequisites: Chapter 3: LangGraph Time to complete: ~25 minutes
Table of Contents
- A Different Organising Metaphor
- AutoGen — Agents in Conversation
- Termination and Control in AutoGen
- CrewAI — Roles and Tasks
- What the Metaphor Hides
- When Each Fits
- Summary & Next Steps
1. A Different Organising Metaphor
LangGraph organises around a state machine. These two organise around people.
Three Metaphors
─────────────────────────────────────────
LANGGRAPH a state machine
"nodes update state; edges route"
Control flow is explicit and yours.
AUTOGEN a conversation
"agents talk until the problem is
solved"
Control flow emerges from who speaks
next.
CREWAI an org chart
"roles are assigned tasks, with a
process"
Control flow follows the task list.
─────────────────────────────────────────
Why the Metaphor Matters
─────────────────────────────────────────
It determines what is easy and what is
impossible.
A conversation is easy to start and hard to bound.
A state machine is verbose to write and easy to
bound.
Choose the metaphor that matches how much control
you need — Section 6.
─────────────────────────────────────────
2. AutoGen — Agents in Conversation
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.teams import RoundRobinGroupChat
from autogen_agentchat.conditions import TextMentionTermination, MaxMessageTermination
from autogen_ext.models.openai import OpenAIChatCompletionClient
model = OpenAIChatCompletionClient(model="gpt-4o")
analyst = AssistantAgent(
name="analyst",
model_client=model,
tools=[query_database],
system_message="You query data and report findings. State the SQL you ran. "
"Do not interpret business meaning — that is the strategist's job.",
)
strategist = AssistantAgent(
name="strategist",
model_client=model,
system_message="You interpret the analyst's findings and recommend actions. "
"If you need more data, ask the analyst for it specifically. "
"When the recommendation is complete, reply with APPROVED.",
)
team = RoundRobinGroupChat(
[analyst, strategist],
termination_condition=(TextMentionTermination("APPROVED") # a semantic stop
| MaxMessageTermination(20)), # AND a hard stop
)
result = await team.run(task="Why did Q3 enterprise churn increase?")The Model
─────────────────────────────────────────
Agents post messages into a shared conversation.
Each sees the whole transcript. A speaker-selection
policy decides who goes next:
RoundRobinGroupChat in turn
SelectorGroupChat an LLM picks the next
speaker by relevance
Swarm agents hand off explicitly
Termination is a separate, composable condition —
which is the part to get right.
─────────────────────────────────────────
3. Termination and Control in AutoGen
The Central Risk
─────────────────────────────────────────
Two polite agents will agree with each other
forever.
analyst: "Here are the numbers."
strategist: "Thank you, that is helpful."
analyst: "Happy to help. Anything else?"
strategist: "That covers it, thank you."
...
Each message costs money. Nothing is being
produced. Nothing in the conversation metaphor
stops this.
─────────────────────────────────────────
termination = (
TextMentionTermination("APPROVED") # the intended, semantic ending
| MaxMessageTermination(20) # the backstop
| TokenUsageTermination(max_total_token=50_000) # the cost ceiling
)Always Compose Three Conditions
─────────────────────────────────────────
SEMANTIC the ending you actually want
COUNT messages, so a chat cannot run away
COST tokens, because message count and cost
are not proportional
This is Module 4, Chapter 4's bounding layer,
expressed in AutoGen's vocabulary. The framework
supplies the mechanism; it does not choose the
bounds for you.
─────────────────────────────────────────
from autogen_agentchat.agents import UserProxyAgent
# A human as a participant — AutoGen's human-in-the-loop.
human = UserProxyAgent(name="reviewer", input_func=input)
team = RoundRobinGroupChat([analyst, strategist, human], termination_condition=termination)4. CrewAI — Roles and Tasks
from crewai import Agent, Task, Crew, Process
researcher = Agent(
role="Market Researcher",
goal="Find accurate, current competitor pricing",
backstory="You are meticulous and always cite your sources.",
tools=[web_search, scrape_page],
allow_delegation=False, # keep it focused on its own job
max_iter=8, # per-agent step bound
)
writer = Agent(
role="Business Analyst",
goal="Turn research into a clear one-page brief for executives",
backstory="You write plainly and never pad.",
allow_delegation=False,
)
research_task = Task(
description="Find published pricing for {competitors}. Note the tier structure.",
expected_output="A bullet list per competitor: tier name, price, key limits.",
agent=researcher,
)
writing_task = Task(
description="Write a one-page brief comparing the pricing found.",
expected_output="Markdown, under 400 words, with a comparison table.",
agent=writer,
context=[research_task], # EXPLICIT dependency — Module 4, Chapter 1
)
crew = Crew(agents=[researcher, writer], tasks=[research_task, writing_task],
process=Process.sequential, verbose=True)
result = crew.kickoff(inputs={"competitors": "Acme, Globex, Initech"})The Model
─────────────────────────────────────────
Agents are ROLES with a goal and a backstory.
Tasks are units of work assigned to a role, with
an EXPECTED OUTPUT.
A process (sequential or hierarchical) orders
them.
`expected_output` is the most useful field in the
library — it is a per-task acceptance criterion,
which is exactly what Module 4, Chapter 3 asked
for.
─────────────────────────────────────────
The Backstory Field, Honestly
─────────────────────────────────────────
`backstory` is prompt text. "You are a meticulous
researcher with 20 years of experience" is a
persona, and personas have a modest, real effect
on tone and thoroughness.
It is not a capability. An agent with an
impressive backstory and no search tool cannot
research anything.
Judge these frameworks by their tools and
termination conditions, not by how the roles read.
─────────────────────────────────────────
5. What the Metaphor Hides
The Shared Weakness
─────────────────────────────────────────
Both make it EASY to build something that appears
to work and hard to see what it cost.
A five-agent crew that produces a decent brief may
have made sixty LLM calls, forty of them agents
restating each other's work.
The conversation and org-chart metaphors are
natural to humans precisely because they hide
coordination cost — which is the thing you most
need to see.
─────────────────────────────────────────
# Instrument before you trust the output.
import time
class CostTracker:
def __init__(self): self.calls, self.tokens, self.t0 = 0, 0, time.time()
def record(self, usage):
self.calls += 1
self.tokens += usage.total_tokens
def report(self):
print(f"{self.calls} calls | {self.tokens:,} tokens | "
f"{time.time() - self.t0:.1f}s")
# Then ask the question that matters:
# would ONE agent with the same tools have produced this,
# at a fifth of the cost?The Question to Ask Every Time
─────────────────────────────────────────
"Would a single agent with all these tools have
done this?"
Very often, yes. Module 6, Chapter 5 covers why
multi-agent systems underperform single agents
more often than the literature suggests, and how
to tell in advance.
─────────────────────────────────────────
6. When Each Fits
Decision Guide
─────────────────────────────────────────
Need precise control, durable state, resumable
runs, production reliability
──► LANGGRAPH. Verbose, and it does what you
told it.
Exploring whether agents debating a problem
produces better answers; research and
prototyping
──► AUTOGEN. The conversation abstraction is
genuinely good for this.
A well-understood pipeline with clear roles and
a fixed task list
──► CREWAI. Fastest to a working prototype
when the process is already known.
Not sure yet
──► one agent, your own loop (Chapter 1),
plus the tools. Add coordination only
when you can name what it buys.
─────────────────────────────────────────
7. Summary & Next Steps
Key Takeaways
- AutoGen organises around a conversation and CrewAI around an org chart; each metaphor determines what is easy and what is impossible to control.
- AutoGen conversations do not stop on their own — always compose a semantic, a message-count and a token-cost termination condition.
- CrewAI's
expected_outputis a per-task acceptance criterion and its most valuable field;backstoryis prompt text affecting tone, not capability. - Both metaphors hide coordination cost, so instrument calls and tokens before trusting the result, and ask whether one agent with the same tools would have sufficed.
Concept Check
- Why do two AutoGen agents left with only a semantic termination condition risk running indefinitely?
- Which CrewAI field corresponds to the verification criteria from Module 4, Chapter 3, and why does that matter?
- What does the conversation metaphor make easy, and what does it make hard to see?
Next Chapter
→ Chapter 5: Choosing a Framework
Jr Codex — 1-on-1 Personalized Coaching | Back to Module Index | Back to Agentic AI Index