Everyone has an opinion on agent frameworks. "LangChain is bloated." "CrewAI is magical." "Just write raw API calls." "AutoGen is the future."
Here's the truth: every framework does the same thing β LLM + tools + loop. What differs is how much abstraction they add, how they handle multi-agent orchestration, and how much they fight you when you need something custom.
Let's open the hood on each one.
What Agent Frameworks Actually Do
Strip away the branding and docs, and every framework provides:
Tool registration β Define functions the LLM can call
The agent loop β Run think β act β observe repeatedly
Message management β Handle conversation history
Error handling β Retry failed tool calls, handle parsing errors
Orchestration β Coordinate multiple agents (some frameworks)
That's it. If you can build those five things yourself in 100 lines of Python, do you need a framework? Maybe. Depends on how complex your agents get.
LangChain
The OG. The one everyone has opinions about.
LangChain was the first major agent framework (2022). It tries to be everything β chains, agents, memory, retrieval, output parsing, callbacks, streaming. This ambition is both its strength and its biggest criticism.
Architecture
LangChain Stack:
βββ langchain-core # Base abstractions (Runnables, messages)
βββ langchain # Chains, agents, retrieval
βββ langchain-community # Third-party integrations
βββ langchain-openai # OpenAI provider
βββ langchain-anthropic # Anthropic provider
βββ langgraph # Graph-based agent orchestration
βββ langsmith # Observability & tracing
A LangChain Agent
python
from langchain_openai import ChatOpenAI
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate
Define tools
@tool
def search(query: str) -> str:
"""Search the web for information."""
return web_search(query)@tool
def calculate(expression: str) -> str:
"""Evaluate a mathematical expression."""
return str(eval(expression))Create agent
llm = ChatOpenAI(model="gpt-4o")
prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful research assistant."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}")])
agent = create_tool_calling_agent(llm, [search, calculate], prompt)
executor = AgentExecutor(agent=agent, tools=[search, calculate], verbose=True)Run
result = executor.invoke({"input": "What's India's GDP per capita in USD?"})LangGraph (The Real Power)
LangChain's newer LangGraph is where it gets interesting. Instead of a linear agent loop, you define a graph of states and transitions:
python
from langgraph.graph import StateGraph, MessagesState
def research_node(state: MessagesState):
"""Agent does research"""
response = llm.invoke(state["messages"])
return {"messages": [response]}def review_node(state: MessagesState):
"""Agent reviews research"""
response = reviewer_llm.invoke(state["messages"])
return {"messages": [response]}def should_continue(state: MessagesState):
last = state["messages"][-1]
if last.tool_calls:
return "tools" # Continue to tool execution
return "review" # Move to reviewBuild the graph
graph = StateGraph(MessagesState)graph.add_node("research", research_node)
graph.add_node("review", review_node)
graph.add_node("tools", tool_node)
graph.add_edge("start", "research")
graph.add_conditional_edges("research", should_continue)
graph.add_edge("tools", "research")
graph.add_edge("review", "end")
app = graph.compile()Verdict
Pros Cons
| Massive ecosystem, integrations for everything | Abstraction overload β too many layers |
|---|---|
| LangGraph is genuinely powerful for complex workflows | API changes frequently, docs lag |
| LangSmith tracing is excellent for debugging | Simple tasks feel over-engineered |
| Largest community, most tutorials | Learning curve is steep |
Use when: You need complex, stateful workflows with many integrations. LangGraph is the right choice for production-grade multi-step agents.
Skip when: You're building a simple single-agent tool caller. The overhead isn't worth it.
π§
CrewAI
Multi-agent made simple.
CrewAI's pitch: define agents as "crew members" with roles, give them a mission, and let them collaborate. It's the most intuitive multi-agent framework.
Architecture
CrewAI Concepts:
βββ Agent β A persona with role, goal, backstory, tools
βββ Task β A specific assignment for an agent
βββ Crew β A team of agents working on tasks
βββ Process β How tasks are executed (sequential, hierarchical)
βββ Tools β Functions agents can use
A CrewAI Crew
python
from crewai import Agent, Task, Crew, Process
Define agents
researcher = Agent(
role="Senior Research Analyst",
goal="Find comprehensive, accurate information on the given topic",
backstory="You're a seasoned researcher with 15 years of experience "
"in technology analysis. You're thorough and fact-driven.",
tools=[search_tool, scrape_tool],
llm="gpt-4o")
writer = Agent(
role="Technical Content Writer",
goal="Write engaging, technically accurate blog posts",
backstory="You're a developer-turned-writer who makes complex topics "
"accessible. Your writing is concise, uses code examples, "
"and avoids fluff.",
tools=[],
llm="gpt-4o")
Define tasks
research_task = Task(
description="Research the current state of AI agents in 2025. "
"Focus on frameworks, patterns, and production use cases.",
expected_output="Detailed research notes with sources",
agent=researcher)
writing_task = Task(
description="Write a 1500-word blog post based on the research. "
"Include code examples and practical takeaways.",
expected_output="Complete blog post in markdown",
agent=writer)
Create and run crew
crew = Crew(
agents=[researcher, writer],
tasks=[research_task, writing_task],
process=Process.sequential, # Tasks run in order
verbose=True)
result = crew.kickoff()Verdict
Pros Cons
| Intuitive role-based design | Less flexible than LangGraph for custom flows |
|---|---|
| Easy multi-agent setup | Limited control over agent communication |
| Great for content pipelines | Can be expensive (multiple agents = multiple LLM calls) |
| Fast to prototype | "Backstory" adds tokens but unclear value |
Use when: You have clear roles and sequential/hierarchical tasks. Content creation, research pipelines, analysis workflows.
Skip when: You need fine-grained control over agent state, custom routing, or complex conditional logic.
π
AutoGen (Microsoft)The academic powerhouse.
AutoGen is Microsoft's framework focused on multi-agent conversations. Its key idea: agents communicate through natural language conversations, and you define the rules for who speaks when.
Architecture
AutoGen Concepts:
βββ ConversableAgent β Base agent that can chat
βββ AssistantAgent β LLM-powered agent
βββ UserProxyAgent β Human-in-the-loop or auto-executor
βββ GroupChat β Multi-agent conversation
βββ GroupChatManager β Controls turn-taking in group chat
An AutoGen Conversation
python
from autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager
Define agents
coder = AssistantAgent(
name="Coder",
system_message="You write Python code. Only respond with code blocks.",
llm_config={"model": "gpt-4o"})
reviewer = AssistantAgent(
name="Reviewer",
system_message="You review code for bugs, security issues, and style. "
"Be critical but constructive.",
llm_config={"model": "gpt-4o"})
executor = UserProxyAgent(
name="Executor",
human_input_mode="NEVER", # Auto-execute, no human approval
code_execution_config={"work_dir": "coding"})
Set up group chat
group_chat = GroupChat(
agents=[coder, reviewer, executor],
messages=[],
max_round=10)
manager = GroupChatManager(
groupchat=group_chat,
llm_config={"model": "gpt-4o"})
Start the conversation
executor.initiate_chat(
manager,
message="Write a Python script that scrapes HackerNews top stories "
"and saves them to a CSV file.")
The agents literally have a conversation β Coder writes code, Executor runs it, Reviewer critiques, Coder revises. The GroupChatManager decides who speaks next.
Verdict
Pros Cons
| Natural conversation-based collaboration | Conversations can meander and waste tokens |
|---|---|
| Strong code execution support | Complex setup for simple tasks |
| Academic rigor, well-researched | Turn-taking can be unpredictable |
| Good for iterative refinement | Less intuitive than CrewAI's role model |
Use when: Tasks benefit from iterative discussion β code review, collaborative writing, complex problem-solving where multiple perspectives matter.
Skip when: You want predictable, fast pipelines. AutoGen conversations can be verbose and expensive.
Smolagents (Hugging Face)
The minimalist.
Smolagents is Hugging Face's answer to framework bloat. Its philosophy: agents should be simple, transparent, and easy to understand. The entire agent loop is ~200 lines of code.
Architecture
Smolagents Concepts:
βββ ToolCallingAgent β Standard tool-calling agent
βββ CodeAgent β Writes and executes Python code as actions
βββ Tool β Simple function wrapperβββ ManagedAgent β Agent that can be used as a tool by another agent
A Smolagent
python
from smolagents import CodeAgent, Tool, HfApiModel
Define a tool
class SearchTool(Tool):
name = "web_search"
description = "Search the web for information"
inputs = {"query": {"type": "string", "description": "Search query"}}
output_type = "string"def forward(self, query: str) -> str:
return web_search(query)Create agent
agent = CodeAgent(
tools=[SearchTool()],
model=HfApiModel("Qwen/Qwen2.5-72B-Instruct"),
max_steps=10)
Run
result = agent.run("Find the latest Python version and its release date")The CodeAgent Difference
Smolagents' CodeAgent is unique β instead of generating JSON tool calls, it writes Python code as its action:
python
Standard tool calling (other frameworks):
Action: search(query="latest Python version")
Smolagents CodeAgent:
The agent writes actual Python code:
results = web_search("latest Python version 2025")
version = results[0]["title"].split("Python ")[1]
print(f"Latest version: {version}")This is more flexible β the agent can use variables, loops, conditionals, and string manipulation directly. No need to create a tool for every little operation.
Verdict
Pros Cons
| Dead simple, easy to understand | Fewer integrations |
|---|---|
| CodeAgent is genuinely innovative | Smaller community |
| Minimal dependencies | Less battle-tested in production |
| Transparent β you can read the entire source | Limited orchestration for multi-agent |
Use when: You want simplicity and transparency. Great for learning, prototyping, and when you want to understand exactly what your agent is doing.
Skip when: You need complex multi-agent orchestration or extensive third-party integrations.
Head-to-Head Comparison
Feature LangChain/LangGraph CrewAI AutoGen Smolagents
Complexity High Medium Medium-High Low
| Multi-agent | LangGraph (powerful) | Built-in (simple) | GroupChat (flexible) | ManagedAgent (basic) |
|---|---|---|---|---|
| Learning curve | Steep | Gentle | Moderate | Minimal |
| Integrations | 700+ | 50+ | 30+ | 20+ |
| Best for | Complex workflows | Role-based pipelines | Iterative discussion | Simple agents, learning |
| Code execution | Via tools | Via tools | Built-in sandbox | CodeAgent (native) |
| Observability | LangSmith | Basic logging | Basic logging | Basic logging |
| Production ready | Yes | Growing | Yes | Early |
| Stars (GitHub) | 105K+ | 25K+ | 40K+ | 15K+ |
π¬
When to Skip Frameworks Entirely
Sometimes the best framework is no framework:
python
A complete agent in 40 lines, no framework
from openai import OpenAI
import json
client = OpenAI()
tools = [...] # Your tool definitions
def run_agent(task, max_steps=15):
messages = [
{"role": "system", "content": "You are a helpful agent. Use tools to complete tasks."},
{"role": "user", "content": task}
]
for _ in range(max_steps):
response = client.chat.completions.create(
model="gpt-4o", messages=messages, tools=tools
)
msg = response.choices[0].message
messages.append(msg)
if not msg.tool_calls:
return msg.content
for tc in msg.tool_calls:
result = execute_tool(tc.function.name, json.loads(tc.function.arguments))
messages.append({"role": "tool", "tool_call_id": tc.id, "content": str(result)})
return "Max steps reached"Use raw API when:
Your agent is simple (1-3 tools, single agent)
You want full control over every decision
Framework abstractions are getting in your way
You're optimizing for cost or latency
Use a framework when:
Multi-agent coordination
Complex state management
You need built-in error handling and retries
Your team needs shared conventions
π‘οΈ
Practical Takeaways
All frameworks do the same thing β LLM + tools + loop, with varying abstraction levels
LangGraph for complex workflows β the most powerful orchestration, but steep learning curve
CrewAI for role-based teams β intuitive, fast to prototype, great for content pipelines
AutoGen for iterative discussion β agents that argue and refine, good for code review
Smolagents for simplicity β minimal, transparent, great for learning
Raw API for simple agents β 40 lines of code beats a framework when your needs are simple
π¦
What's Next?
Episode 35: Agent Failure Modes β Infinite loops, cost explosions, hallucinated actions, tool abuse. Agents can go wrong in spectacular ways. We'll catalog the failure modes, how to detect them, and how to build guardrails that keep your agent (and your wallet) safe.
β Previous
Ep 33: Agent Memory
Next β
Ep 35: Agent Failure Modes
Next: Episode 35 β Agent Failure Modes
A chatbot that gives wrong answers is annoying. An agent that takes wrong actions is dangerous. Here's how they fail.
This is part of a 98-episode series covering AI engineering from tokens to production deployment.