Introduction: Navigating the Agentic Landscape

Welcome back, intrepid AI architects! In previous chapters, we’ve explored the foundational concepts of AI agents: their ability to perceive, plan, act, and leverage tools and memory to achieve complex goals. We’ve seen how a single agent can tackle a task, but the real power often emerges when multiple specialized agents collaborate.

As of March 20, 2026, the AI agent ecosystem is vibrant and rapidly evolving, offering a diverse array of frameworks designed to streamline the development of these sophisticated systems. This chapter is your guide to navigating this exciting landscape. We’ll embark on a “framework face-off,” comparing some of the most prominent agentic architectures: LangGraph, AutoGen, CrewAI, and Semantic Kernel.

Our goal isn’t just to list features, but to help you understand the core philosophies, architectural patterns, strengths, and ideal use cases for each. By the end of this chapter, you’ll be equipped with the knowledge to confidently choose the right agentic framework for your next multi-agent application, ensuring your project is built on a solid and suitable foundation. Ready to become a master agent orchestrator? Let’s dive in!

Core Concepts: What Makes an Agentic Framework Tick?

Before we pit frameworks against each other, let’s quickly recap the essential components that any robust agentic framework must address, and which will serve as our comparison criteria:

  1. Orchestration Pattern: How do agents interact and coordinate? Is it a predefined graph, a free-form conversation, a task-driven crew, or a planner-driven sequence?
  2. State Management: How is the progress and context of a multi-step workflow tracked and maintained across agent interactions?
  3. Tool Usage/Function Calling: How easily can agents integrate and use external functions, APIs, or custom code to perform actions beyond their LLM capabilities?
  4. Memory Management: How do agents retain information from past interactions (short-term) and access persistent knowledge (long-term)?
  5. Ease of Use & Development Experience: How quickly can you get started? How steep is the learning curve for complex scenarios?
  6. Extensibility & Flexibility: How easy is it to customize agent behavior, integrate new LLMs, or swap out components?
  7. Community & Ecosystem: How active is the community? What resources (docs, examples) are available?

Understanding these criteria will help us dissect each framework’s unique approach.

The Underlying Principles of Agentic AI

Before we delve into specific frameworks, let’s solidify our understanding of the fundamental principles that power all intelligent agents. These principles, often inspired by cognitive science, guide how agents interact with their environment and achieve their goals.

1. Goal-Oriented Behavior

At its core, an AI agent is designed to achieve specific goals. Whether it’s answering a question, generating code, or making a financial recommendation, every action an agent takes is ultimately directed towards fulfilling its objective. This goal-oriented nature distinguishes agents from simple chatbots or single-turn LLM calls. The framework you choose will provide mechanisms to define these goals and track progress towards them.

2. Perception-Action Loop

Agents operate within a continuous cycle:

  • Perception: The agent observes its environment. This could be reading user input, fetching data from a tool, or receiving messages from other agents.
  • Reasoning/Planning: Based on its perception and internal state, the agent processes the information, updates its understanding, and plans its next action. This is often where the LLM’s intelligence comes into play, deciding what to do next.
  • Action: The agent executes the planned action. This might involve calling a tool, generating a response, or sending a message to another agent.
  • Feedback: The environment changes as a result of the action, which then feeds back into the next perception phase, closing the loop.

This loop allows agents to adapt and course-correct, making them dynamic problem-solvers. Different frameworks implement this loop with varying degrees of explicitness and control.

3. Planning and Strategic Thinking

Complex goals rarely have a single, direct path to completion. Agents often need to break down large goals into smaller, manageable sub-goals and sequence actions strategically. This is where planning comes in:

  • Task Decomposition: Breaking a complex problem into smaller, interdependent tasks.
  • Action Selection: Choosing the most appropriate tool or internal thought process for each sub-task.
  • Sequencing: Determining the order in which actions should be performed.
  • Conditional Logic: Adapting the plan based on intermediate results or environmental changes.

Frameworks provide different ways to facilitate this planning, from explicit state machine definitions (like LangGraph) to emergent planning through conversation (like AutoGen) or dedicated planner components (like Semantic Kernel).

4. Memory and Context Management

To engage in multi-step interactions and learn from experience, agents need memory:

  • Short-Term Memory (Context): This is the immediate context of the current interaction, often the conversation history with an LLM. It’s crucial for maintaining coherence and relevance.
  • Long-Term Memory (Knowledge Base): This refers to persistent information, facts, or past experiences that the agent can retrieve and leverage across different sessions or tasks. This often involves vector databases and retrieval-augmented generation (RAG).

Effective memory management prevents agents from “forgetting” crucial information and allows them to build on past interactions.

5. Tool Usage and External Interaction

The world of AI agents extends far beyond just text generation. Agents become truly powerful when they can interact with the real world through tools:

  • Function Calling: The ability of an LLM to identify when a function needs to be called and to generate the correct parameters to call it.
  • API Integration: Connecting to external services, databases, or web resources.
  • Code Execution: Running local code to perform calculations, data manipulation, or system commands.

Tools enable agents to gather up-to-date information, perform precise computations, and execute actions that impact the external environment.

These principles form the bedrock of agentic AI. As we explore each framework, observe how they provide different abstractions and mechanisms to implement these core ideas.

The Contenders: A Quick Overview

Let’s meet our frameworks, each bringing a distinct flavor to agent orchestration. Note that specific version numbers can vary rapidly, but as of early 2026, these are the generally stable and recommended installation methods:

  • LangGraph (Part of LangChain): A library for building robust and stateful multi-actor applications with LLMs, inspired by state machines.
    • Installation: pip install langgraph langchain_openai (ensure langchain is also installed, usually pulled as a dependency).
  • AutoGen (Microsoft Research): Enables the development of multi-agent conversations with customizable and conversational agents.
    • Installation: pip install pyautogen openai
  • CrewAI: Focuses on creating crews of AI agents that collaborate on complex tasks, emphasizing roles, tasks, and process management.
    • Installation: pip install crewai crewai_tools openai
  • Semantic Kernel (Microsoft): A lightweight SDK that lets you easily combine AI services with conventional programming languages, with a strong emphasis on “planners” and “skills.”
    • Installation: pip install semantic-kernel openai (for Python, also supports C#).

Framework Deep Dive: Architectures and Strengths

Now, let’s explore each framework in more detail, highlighting their unique architectural patterns, ideal use cases, and specific considerations for prompt engineering and tool design.

1. LangGraph: State Machines for Structured Workflows

Core Philosophy: LangGraph, an extension of LangChain, is built around the concept of creating stateful, cyclic graphs for agentic workflows. Think of it like defining a finite state machine where each node in the graph represents an agent, a tool call, or a decision point, and edges define transitions between these states. This provides a highly deterministic and auditable flow, excellent for complex, iterative processes.

Orchestration Pattern: Graph-based. You explicitly define the flow using nodes and edges. This allows for highly deterministic and auditable workflows, including loops for iterative refinement or conditional branching.

State Management: Its strength lies in its explicit state management. The graph’s state is passed between nodes, allowing agents to react to the evolving context. This makes it excellent for workflows requiring precise control over information flow and iterative processes. You define a State object (often a TypedDict) that holds all the relevant information for your workflow.

Tool Usage/Function Calling: Seamlessly integrates with LangChain’s extensive tool ecosystem, allowing agents to call external functions, APIs, or other custom tools defined as part of a LangChain Runnable. Tools are typically defined as Python functions decorated with @tool or by inheriting from BaseTool.

Memory Management: Leverages LangChain’s memory modules. You can integrate various memory types (e.g., ConversationBufferMemory, VectorStoreRetrieverMemory) into your graph’s state or as part of a specific agent’s configuration within a node.

Prompt Engineering in LangGraph: Prompts in LangGraph are typically defined within the Runnable (e.g., ChatPromptTemplate) that an LLM node executes. Since the state is explicit, you can design prompts to directly reference and utilize specific parts of the current graph state. For example, if your state contains current_task and previous_output, your prompt can guide the LLM to use these specific variables for its reasoning. Clarity and explicit instruction are key, as the LLM’s role is often to process inputs from the state and update it.

Tool Design in LangGraph: Tools are standard LangChain tools. When designing them, ensure they are atomic, have clear descriptions, and well-defined input schemas. The LLM agent within a LangGraph node will use these descriptions to decide when and how to call a tool. The output of a tool call will then typically update the graph’s state for subsequent nodes to use.

Pros:

  • High Control: Offers granular control over workflow logic, state transitions, and loops.
  • Determinism: Easier to debug and reason about complex, multi-step processes.
  • Flexibility: Can model almost any workflow, from simple sequences to complex, self-correcting loops.
  • LangChain Ecosystem: Benefits from LangChain’s rich set of integrations (LLMs, tools, retrievers).

Cons:

  • Steeper Learning Curve: Defining graphs and state transitions can be more complex than simpler, declarative approaches.
  • Boilerplate: Can require more code for simple linear workflows compared to other frameworks.

Ideal Use Cases:

  • Complex, multi-step data processing pipelines.
  • Workflows requiring iterative refinement (e.g., code generation and testing loops).
  • Autonomous agents needing precise control over their decision-making process.
  • Systems where auditability and clear flow visualization are critical.

Visualizing a LangGraph Workflow

Imagine an agent that generates code, tests it, and refines it if tests fail. This iterative process is perfectly suited for LangGraph:

graph TD A[Start: Receive Request] --> B{Code Generation Agent} B --> C[Generate Code] C --> D{Test Agent} D --> E[Run Tests] E --> F{Tests Pass?} F -->|No| B F -->|Yes| G[Return Final Code] G --> H[End]

Figure 11.1: A simplified LangGraph workflow for iterative code generation and testing.

LangGraph Step-by-Step Example: Simple Stock Analysis

Let’s build a small LangGraph workflow that fetches stock information and then summarizes it.

First, ensure you have the necessary libraries installed and your OpenAI API key set up:

pip install langgraph==0.0.30 langchain_openai==0.1.10 pydantic==2.7.1
# Ensure you have your OpenAI API key set as an environment variable:
# export OPENAI_API_KEY="YOUR_API_KEY"

Now, let’s create our LangGraph application. We’ll start by defining our graph’s state and a simple tool.

Step 1: Define the Graph State The AgentState defines what information our graph will carry and update as it executes.

# filename: langgraph_example.py
from typing import TypedDict, Annotated, List
from langchain_core.messages import BaseMessage
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
import os

# Set up OpenAI LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# 1. Define the graph state
class AgentState(TypedDict):
    """
    Represents the state of our graph.
    - messages: A list of messages passed between nodes.
    - stock_data: Stores fetched stock information.
    - summary: Stores the LLM's summary of the stock.
    """
    messages: Annotated[List[BaseMessage], lambda x, y: x + y]
    stock_data: str
    summary: str
    user_query: str # To hold the initial query

Explanation:

  • AgentState is a TypedDict that defines the schema for our graph’s state.
  • messages: A list of LangChain BaseMessage objects, useful for conversational context. The Annotated type with a lambda function ensures new messages are appended.
  • stock_data: A string to store the raw data fetched by our tool.
  • summary: A string to store the LLM-generated summary.
  • user_query: To hold the initial request for context.

Step 2: Define a Tool We’ll create a dummy tool to simulate fetching stock data. In a real application, this would call a financial API.

# Add to langgraph_example.py
@tool
def get_stock_info(ticker: str) -> str:
    """Fetches dummy stock information for a given ticker symbol."""
    if ticker.upper() == "AAPL":
        return "AAPL: Apple Inc. is a tech giant. Current price: $170. Market Cap: $2.6T. P/E Ratio: 28. Recent news: Strong Q4 earnings, new Vision Pro launch."
    elif ticker.upper() == "GOOG":
        return "GOOG: Alphabet Inc. (Google) is a multinational tech company. Current price: $155. Market Cap: $1.9T. P/E Ratio: 25. Recent news: AI advancements, regulatory scrutiny."
    else:
        return f"No detailed info for {ticker}. Assume basic info: Price $100, Market Cap $100B."

# List of tools available to agents
tools = [get_stock_info]

Explanation:

  • The @tool decorator from langchain_core.tools turns our get_stock_info function into a LangChain-compatible tool.
  • It takes a ticker and returns a string with simulated stock data.
  • tools list holds our available tools.

Step 3: Define Graph Nodes Each node in our graph will perform a specific action.

# Add to langgraph_example.py
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnableLambda

# Node 1: Fetch Stock Data
def fetch_stock_node(state: AgentState) -> AgentState:
    """
    Fetches stock data using the get_stock_info tool based on the user query.
    """
    print(f"\n--- Entering Fetch Stock Node ---")
    user_query = state["user_query"]
    # Simple regex to extract ticker, or more sophisticated parsing in a real app
    import re
    match = re.search(r'\b([A-Z]{2,5})\b', user_query) # Look for 2-5 uppercase letters
    ticker = match.group(1) if match else "AAPL" # Default to AAPL if no ticker found

    print(f"Attempting to fetch data for ticker: {ticker}")
    stock_info = get_stock_info.invoke({"ticker": ticker})
    print(f"Fetched stock data: {stock_info[:50]}...") # Print first 50 chars

    return {"stock_data": stock_info, "messages": []} # Clear messages for next step context

Explanation:

  • fetch_stock_node is a function that takes the current AgentState as input.
  • It extracts a ticker from the user_query (a simple regex for demonstration).
  • It invokes our get_stock_info tool with the extracted ticker.
  • It returns a dictionary to update the AgentState, specifically setting stock_data.
# Add to langgraph_example.py
# Node 2: Analyze Stock Data and Summarize
def analyze_stock_node(state: AgentState) -> AgentState:
    """
    Uses an LLM to analyze the fetched stock data and generate a summary.
    """
    print(f"\n--- Entering Analyze Stock Node ---")
    stock_data = state["stock_data"]
    user_query = state["user_query"]

    # Prompt for the LLM to summarize the stock data
    prompt = ChatPromptTemplate.from_messages([
        ("system", "You are an expert financial analyst. Summarize the provided stock data concisely and answer the user's original query if applicable."),
        ("human", f"Original query: {user_query}\n\nStock Data:\n{stock_data}\n\nProvide a concise summary and answer the original query.")
    ])

    # Create an LLM chain
    llm_chain = prompt | llm

    print(f"Analyzing stock data with LLM...")
    summary_response = llm_chain.invoke({"stock_data": stock_data, "user_query": user_query})
    summary = summary_response.content
    print(f"Generated summary: {summary[:100]}...") # Print first 100 chars

    return {"summary": summary, "messages": []}

Explanation:

  • analyze_stock_node takes AgentState as input.
  • It constructs a ChatPromptTemplate using the stock_data and user_query from the state.
  • It creates an llm_chain by piping the prompt to our llm.
  • It invokes the chain to get a summary and updates the summary field in the AgentState.

Step 4: Build the Graph Now we connect our nodes with edges to define the workflow.

# Add to langgraph_example.py
# 4. Build the graph
workflow = StateGraph(AgentState)

# Add nodes
workflow.add_node("fetch_stock", fetch_stock_node)
workflow.add_node("analyze_stock", analyze_stock_node)

# Set entry point
workflow.set_entry_point("fetch_stock")

# Define edges
workflow.add_edge("fetch_stock", "analyze_stock")
workflow.add_edge("analyze_stock", END)

# Compile the graph
app = workflow.compile()

# 5. Run the graph
print("--- Running LangGraph Example ---")
final_state = app.invoke(
    {"user_query": "What's the latest on AAPL?", "stock_data": "", "summary": "", "messages": []}
)

print("\n--- Final LangGraph Output ---")
print(f"User Query: {final_state['user_query']}")
print(f"Stock Data: {final_state['stock_data']}")
print(f"Summary: {final_state['summary']}")

Explanation:

  • StateGraph(AgentState) initializes our graph with the defined state.
  • add_node: Registers our functions as nodes in the graph.
  • set_entry_point: Specifies where the graph execution begins.
  • add_edge: Connects nodes sequentially. END signifies the end of the workflow.
  • compile(): Finalizes the graph into a runnable application.
  • app.invoke(): Runs the graph with an initial state, which includes our user_query.

To run this example, save the code as langgraph_example.py and execute python langgraph_example.py in your terminal. You’ll see the print statements indicating the flow and the final summarized output.

2. AutoGen: Conversational Agents for Collaborative Problem Solving

Core Philosophy: AutoGen, developed by Microsoft Research, excels at enabling multi-agent conversations. Its strength lies in defining agents that can converse with each other, often simulating human-like collaboration to solve tasks. It focuses on making agents autonomous and capable of initiating and responding to messages, including directly executing code.

Orchestration Pattern: Conversational. Agents exchange messages, execute code, and respond based on their predefined roles and capabilities. The flow is less explicitly defined than LangGraph; instead, it emerges from the agents’ interactions.

State Management: Context is primarily managed through the conversation history. Each agent maintains its own view of the conversation, and the framework ensures messages are routed appropriately. For more complex state tracking, agents can be programmed to summarize conversations or store specific findings in external memory.

Tool Usage/Function Calling: Agents can execute code, call functions, and even interact with human users directly within the conversation. This is powerful for dynamic problem-solving where agents might need to try different approaches. AutoGen agents can be configured to use a “code interpreter” to execute Python code, or to call pre-registered functions.

Memory Management: Conversation history serves as short-term memory. For long-term memory, you’d typically integrate external solutions (e.g., vector databases) that agents can query as part of their conversational turns, often by defining a tool that accesses this memory.

Prompt Engineering in AutoGen: AutoGen’s prompt engineering is centered around the system_message provided to each agent. This message defines the agent’s persona, capabilities, and instructions on how to interact. For example, a Researcher agent’s system_message might instruct it to use search tools and summarize findings, while a Coder agent’s message might tell it to write and test Python code. The conversational nature means prompts are implicitly built from the ongoing dialogue.

Tool Design in AutoGen: Tools in AutoGen are typically Python functions that you register with a UserProxyAgent. When an AssistantAgent decides it needs a tool, it will generate a function call in its response, which the UserProxyAgent then executes on its behalf. Tool descriptions are crucial for the LLM to understand when and how to use them.

Pros:

  • Highly Flexible Conversations: Excellent for scenarios where the exact steps aren’t known beforehand and agents need to explore solutions.
  • Human-Agent Collaboration: Seamlessly integrates human input into multi-agent workflows.
  • Code Execution: Agents can directly execute Python code, making them powerful for development and data analysis tasks.
  • Simplicity for Conversational Flows: Relatively easy to set up basic conversational agents.

Cons:

  • Less Deterministic: Debugging complex conversational loops can be challenging due to the emergent nature of interactions.
  • Context Window Management: Long conversations can quickly hit LLM context window limits, requiring careful prompt engineering.
  • Less Structured State: If you need very precise state tracking beyond conversation history, you might need to build custom solutions.

Ideal Use Cases:

  • Automated code generation, debugging, and refactoring with human oversight.
  • Multi-agent research assistants that discuss findings.
  • Complex problem-solving requiring dynamic exploration of solutions.
  • Interactive simulations or role-playing scenarios.

Visualizing an AutoGen Conversation

graph TD User[Human User] -->|\1 Query| Manager[User Proxy Agent] Manager -->|\2 Query| Research[Research Agent] Research -->|\3 Tool Call| Tool[Stock Data Tool] Tool -->|\4 Data| Research Research -->|\5 Data| Analyst[Analyst Agent] Analyst -->|\6 Report Draft| Manager Manager -->|\7 Feedback| Research Research -->|\8 Refine| Analyst Analyst -->|\9 Final Report| Manager Manager -->|\10 Final Report| User

Figure 11.2: An AutoGen multi-agent conversation flow, including human interaction and tool usage.

AutoGen Step-by-Step Example: Collaborative Weather Report

Let’s create two AutoGen agents: a UserProxyAgent (representing our human interaction and tool executor) and an AssistantAgent (our LLM-powered agent) to fetch and report weather.

First, install AutoGen and OpenAI:

pip install pyautogen==0.2.22 openai==1.17.1
# Ensure you have your OpenAI API key set as an environment variable:
# export OPENAI_API_KEY="YOUR_API_KEY"

Now, let’s define our agents and a tool.

Step 1: Define a Tool Function We’ll create a simple Python function that simulates fetching weather data.

# filename: autogen_example.py
import autogen
import os

# Set up OpenAI LLM configuration
config_list = [
    {
        "model": "gpt-4o",
        "api_key": os.environ.get("OPENAI_API_KEY"),
    }
]

# 1. Define a tool function
def get_current_weather(location: str, unit: str = "celsius") -> str:
    """
    Get the current weather in a given location.
    :param location: The city and state, e.g., "San Francisco, CA"
    :param unit: The unit of temperature, e.g., "celsius" or "fahrenheit"
    :return: A string describing the weather.
    """
    if "london" in location.lower():
        return "It's 10 degrees Celsius and cloudy in London."
    elif "new york" in location.lower():
        return "It's 20 degrees Fahrenheit and sunny in New York."
    else:
        return f"Weather data not available for {location}. Try London or New York."

Explanation:

  • get_current_weather is a regular Python function. AutoGen agents can call such functions if they are registered.
  • config_list is used to configure the LLM models for AutoGen agents.

Step 2: Define AutoGen Agents We’ll create a UserProxyAgent to handle tool execution and a AssistantAgent to reason and generate responses.

# Add to autogen_example.py
# 2. Define AutoGen agents
# UserProxyAgent: Acts as a proxy for the human user, can execute code/tools
user_proxy = autogen.UserProxyAgent(
    name="Admin",
    system_message="A human admin. Interact with the Assistant to get weather reports. You can execute code.",
    human_input_mode="NEVER", # Set to "ALWAYS" or "TERMINATE" for human interaction
    max_consecutive_auto_reply=10,
    is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    code_execution_config={"work_dir": "weather_reports", "use_docker": False}, # Set to True for sandboxed execution
    function_map={"get_current_weather": get_current_weather}, # Register the tool
)

# AssistantAgent: An LLM-based agent that can write code or call functions
assistant = autogen.AssistantAgent(
    name="Weather_Reporter",
    llm_config={"config_list": config_list},
    system_message="You are a helpful AI assistant that can fetch weather information using the provided tools. "
                    "When you have the weather, present it clearly and ask if there's anything else needed. "
                    "Reply TERMINATE when the task is done and you have provided the answer.",
)

Explanation:

  • user_proxy:
    • system_message: Defines its role.
    • human_input_mode="NEVER": For fully automated execution. Change to "ALWAYS" to prompt the user for input.
    • is_termination_msg: A lambda function to determine when the conversation should end.
    • code_execution_config: Allows the agent to execute code; work_dir specifies where temporary files go.
    • function_map: Crucially, this registers our get_current_weather function as a tool available to the assistant agent.
  • assistant:
    • llm_config: Specifies the LLM model to use.
    • system_message: Guides the LLM’s behavior, persona, and how to use tools.

Step 3: Initiate the Conversation We start the conversation by sending a message from the user_proxy to the assistant.

# Add to autogen_example.py
# 3. Initiate the conversation
print("\n--- Running AutoGen Example ---")
user_proxy.initiate_chat(
    assistant,
    message="What's the weather like in London and New York?",
)
print("\n--- AutoGen Conversation Ended ---")

Explanation:

  • user_proxy.initiate_chat() starts the multi-agent conversation.
  • The initial message acts as the first prompt to the assistant.
  • The agents will then converse, with the assistant deciding to call get_current_weather via the user_proxy as needed, until the is_termination_msg condition is met.

To run this example, save the code as autogen_example.py and execute python autogen_example.py. You will see the agents interacting, with the Weather_Reporter agent asking Admin (the user_proxy) to run the get_current_weather function, and then providing the report.

3. CrewAI: Role-Playing Agents for Task-Driven Collaboration

Core Philosophy: CrewAI focuses on defining a “crew” of agents, each with a specific role, goal, and backstory. These agents are assigned tasks and follow a defined process to achieve a collective goal. It’s inspired by organizational structures where specialized team members collaborate, making it intuitive for building multi-agent systems that mirror human teams.

Orchestration Pattern: Task-driven and role-playing. You define agents with distinct personalities and responsibilities, then assign them tasks. The framework handles the delegation and execution based on a specified process (e.g., sequential, hierarchical).

State Management: State is managed through the tasks. Each task has inputs and outputs, and agents collaborate to complete their assigned tasks, passing results between them. The overall crew maintains the context of the collective goal. The agent objects themselves can also hold some state or memory.

Tool Usage/Function Calling: Agents are equipped with a list of tools relevant to their role. When processing a task, an agent intelligently decides which tools to use. CrewAI provides crewai_tools for common functionalities like web searching, file reading, etc., and also allows custom tools.

Memory Management: Agents in CrewAI can have individual memory, and the crew itself maintains a shared context. This allows for both specialized knowledge retention and collective understanding. This often comes through the verbose mode of agents and tasks, allowing them to remember previous outputs.

Prompt Engineering in CrewAI: Prompt engineering is highly declarative in CrewAI. The role, goal, and backstory of each Agent are crucial system messages that define their persona and capabilities. Task descriptions also act as prompts, guiding the agent on what to achieve. You can also define specific expected_output for tasks, which acts as a strong constraint for the LLM. The framework then combines these elements to construct the final prompt sent to the LLM.

Tool Design in CrewAI: Tools are provided to agents as a list during their definition. crewai_tools offers pre-built tools (e.g., SerperDevTool for web search, FileReadTool). For custom tools, you typically wrap a Python function into a BaseTool or a custom crewai_tool. Ensure tool descriptions are clear so agents know when to use them.

Pros:

  • Intuitive Role-Based Design: Very natural way to think about multi-agent systems, mirroring human teams.
  • Strong Task Management: Clear definition of tasks, inputs, and outputs makes complex workflows manageable.
  • Built-in Processes: Provides structured ways for agents to collaborate (sequential, hierarchical).
  • Focus on Collaboration: Designed from the ground up for agents to work together effectively.

Cons:

  • Less Granular Control: While powerful for collaboration, it might offer less explicit control over individual agent’s internal reasoning steps compared to LangGraph.
  • Specific Paradigm: Best suited for problems that map well to a “team of experts” metaphor.

Ideal Use Cases:

  • Automated content creation (e.g., a researcher, writer, and editor crew).
  • Customer support systems with specialized agents (billing, technical, sales).
  • Project management assistants that delegate and track tasks.
  • Financial analysis with agents specializing in market research, report generation, and risk assessment.

Visualizing a CrewAI Process

graph TD subgraph Crew_FinancialAnalysis["Financial Analysis Crew"] Researcher[Research Analyst] DataAnalyst[Data Scientist] ReportWriter[Report Writer] end Start[Client Request] --> Task1[Task: Gather Market Data] Task1 --> Researcher Researcher --> Task2[Task: Process and Analyze Data] Task2 --> DataAnalyst DataAnalyst --> Task3[Task: Draft Investment Report] Task3 --> ReportWriter ReportWriter --> End[Deliver Final Report] Task1 -.->|Uses| ToolA[Web Search Tool] Task2 -.->|Uses| ToolB[Data Processing Library] Task3 -.->|Uses| ToolC[Reporting Template Tool]

Figure 11.3: A CrewAI workflow illustrating agents collaborating on tasks with specific tools.

CrewAI Step-by-Step Example: Simple Research and Writing Crew

Let’s build a small crew with a Researcher and a Writer to research a topic and generate a short article.

First, install CrewAI and crewai_tools:

pip install crewai==0.35.3 crewai_tools==0.2.0 openai==1.17.1
# Ensure you have your OpenAI API key set as an environment variable:
# export OPENAI_API_KEY="YOUR_API_KEY"
# For web search, you might also need a SERPER_API_KEY or similar
# export SERPER_API_KEY="YOUR_SERPER_API_KEY"

Now, let’s define our agents, tools, tasks, and the crew.

Step 1: Define Tools We’ll use a SerperDevTool for web searching.

# filename: crewai_example.py
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool
import os

# Set up OpenAI as the LLM
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY")
os.environ["OPENAI_MODEL_NAME"] = "gpt-4o" # Or gpt-3.5-turbo

# Optional: Set up Serper API for web search (replace with your key)
# os.environ["SERPER_API_KEY"] = os.getenv("SERPER_API_KEY") # Get your key from https://serper.dev/

# 1. Define Tools
search_tool = SerperDevTool()

Explanation:

  • SerperDevTool() initializes a web search tool. Make sure SERPER_API_KEY is set in your environment for it to work. If not, the agent might ‘hallucinate’ search results or fail.
  • We also set OPENAI_MODEL_NAME for the LLM.

Step 2: Define Agents We’ll create a Researcher and a Writer agent, each with a distinct role, goal, and tools.

# Add to crewai_example.py
# 2. Define Agents
researcher = Agent(
    role='Senior Research Analyst',
    goal='Uncover interesting facts and data about the specified topic.',
    backstory="""You are a seasoned research analyst with a knack for finding hidden gems of information.
                You're meticulous and thorough, ensuring all facts are accurate and well-sourced.""",
    verbose=True,
    allow_delegation=False,
    tools=[search_tool], # Assign the search tool to the researcher
    llm=None # Will use the default LLM set by os.environ["OPENAI_MODEL_NAME"]
)

writer = Agent(
    role='Professional Content Writer',
    goal='Craft compelling and informative articles based on research findings.',
    backstory="""You are a renowned content writer known for your engaging prose and ability to
                transform complex information into easily digestible content.""",
    verbose=True,
    allow_delegation=False,
    llm=None
)

Explanation:

  • Each Agent is defined with a role, goal, and backstory which act as its system prompt.
  • verbose=True shows the agent’s thought process.
  • tools=[search_tool] assigns the web search capability only to the researcher.
  • llm=None means it will use the LLM specified in the environment variable.

Step 3: Define Tasks We’ll define two tasks: one for research and one for writing, and assign them to the respective agents.

# Add to crewai_example.py
# 3. Define Tasks
research_task = Task(
    description=(
        "Research the latest advancements in AI agent frameworks as of March 2026. "
        "Focus on key features, unique architectural patterns, and real-world applications."
        "Identify at least 3-5 significant trends or breakthroughs."
    ),
    expected_output='A detailed bulleted list of the latest AI agent framework advancements and trends.',
    agent=researcher # Assign this task to the researcher
)

write_task = Task(
    description=(
        "Write a short, engaging blog post (around 300 words) summarizing the research findings "
        "on AI agent framework advancements. The tone should be informative and forward-looking. "
        "Include a compelling introduction and conclusion."
    ),
    expected_output='A 300-word blog post in markdown format, summarizing AI agent framework advancements.',
    agent=writer, # Assign this task to the writer
    context=[research_task] # The writer's task depends on the researcher's output
)

Explanation:

  • Task objects have a description (the prompt for the agent), expected_output (guides the agent on the desired format and content), and an agent assigned to it.
  • context=[research_task] is crucial for orchestration: it tells CrewAI that write_task should only start after research_task is completed, and the output of research_task will be available as context for the writer agent.

Step 4: Form the Crew and Run Finally, we assemble the crew with our agents and tasks, and define the process.

# Add to crewai_example.py
# 4. Form the Crew and Run
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
    process=Process.sequential, # Tasks run one after another
    verbose=2 # Shows more detailed logs
)

print("\n--- Running CrewAI Example ---")
result = crew.kickoff()

print("\n--- CrewAI Final Output ---")
print(result)

Explanation:

  • Crew takes a list of agents and tasks.
  • process=Process.sequential means tasks will run in the order they are defined in the tasks list. Other options like hierarchical are available.
  • verbose=2 provides a detailed log of agent thoughts and actions.
  • crew.kickoff() starts the process. The result will be the output of the final task.

To run this example, save the code as crewai_example.py and execute python crewai_example.py. You’ll observe the Researcher using the search tool, then the Writer taking the research output and crafting the blog post.

4. Semantic Kernel: Skills and Planners for Enterprise Integration

Core Philosophy: Semantic Kernel (SK), another offering from Microsoft, is a lightweight SDK designed to integrate AI capabilities (especially LLMs) with conventional programming languages (Python, C#). Its core concepts are “Skills” (collections of functions/prompts) and “Planners” (AI components that chain skills together to achieve a goal). It focuses on making AI capabilities composable and reusable within existing application logic.

Orchestration Pattern: Planner-driven. A “planner” agent observes the user’s goal and available skills, then generates a sequence of skill calls (a “plan”) to achieve that goal. This approach emphasizes composability and reusability of AI functions, making it excellent for integrating AI into existing software systems.

State Management: State is often managed within the execution context of the planner and individual skill calls. It’s less about a persistent, evolving graph state and more about passing context between chained functions. The Kernel object itself holds configuration and registered skills.

Tool Usage/Function Calling: Tools are explicitly defined as “Skills” (native code functions or semantic functions/prompts). The planner’s job is to orchestrate these skills. This makes it very strong for integrating AI into existing applications with well-defined APIs or business logic.

Memory Management: SK has a robust memory system, allowing you to store and retrieve information using vector embeddings, which can be integrated into skills. This enables agents to access and leverage long-term, semantic memory.

Prompt Engineering in Semantic Kernel: Semantic Kernel defines “semantic functions” (also called “prompts”) as a core type of skill. These are typically simple text files or strings that contain a prompt template. The key is to make these prompts concise, focused on a single task, and parameterized so they can accept inputs from the planner or other skills. For example, a SummarizeSkill would have a prompt like: “Summarize the following text: {{$input}}”. The planner then combines these small, focused prompts.

Tool Design in Semantic Kernel: Tools are exposed as “Native Skills” (regular C# or Python functions) or “Semantic Skills” (prompts). When designing native skills, ensure they have clear descriptions and input parameters. The planner uses these descriptions to understand the skill’s purpose and how to chain it. For semantic skills, focus on making the prompt’s input and output clear, allowing it to act as a modular function.

Pros:

  • Enterprise Integration: Designed for seamless integration into existing C# and Python applications.
  • Composability: Skills are modular and reusable, promoting cleaner code and easier maintenance.
  • Planner-Driven: The planner intelligently orchestrates skills, reducing the need for explicit workflow definition for many tasks.
  • Strong Memory System: Built-in support for semantic memory using vector stores.

Cons:

  • Less Focus on Multi-Agent Conversations: While you can build multi-agent systems, it’s not its primary design strength like AutoGen or CrewAI.
  • Planner Limitations: Planners can sometimes struggle with highly ambiguous or open-ended tasks, requiring careful skill design.

Ideal Use Cases:

  • Adding AI capabilities (summarization, sentiment analysis, code generation) to existing line-of-business applications.
  • Building intelligent plugins or extensions for enterprise software.
  • Automating tasks where a clear sequence of API calls or functions can achieve a goal.
  • Creating chatbots or virtual assistants that leverage a wide array of backend services.

Visualizing a Semantic Kernel Planner

graph TD UserRequest[User Request Summarize recent sales report] --> Planner[Planner Agent] Planner -->|Plan| Plan[Plan Call GetReport then SummarizeText] Plan --> Skill1[Skill GetReport] Skill1 -->|Context| Skill2[Skill SummarizeText] Skill2 -->|Result| UserResponse[Return Summary] subgraph Skills_Available["Available Skills"] Skill1_def[GetReport Fetch report from DB] Skill2_def[SummarizeText Summarize given text] Skill3_def[AnalyzeSentiment Determine text sentiment] end

Figure 11.4: A Semantic Kernel planner orchestrating skills to fulfill a user request.

Semantic Kernel Step-by-Step Example: Planning with Skills

Let’s demonstrate Semantic Kernel by creating a simple kernel with a “Text” skill (semantic function for summarization) and a “Web” skill (native function to fetch content from a URL), then using a SequentialPlanner to combine them.

First, install Semantic Kernel and OpenAI:

pip install semantic-kernel==0.9.1b1 openai==1.17.1
# Ensure you have your OpenAI API key set as an environment variable:
# export OPENAI_API_KEY="YOUR_API_KEY"

Now, let’s set up the kernel, define skills, and use a planner.

Step 1: Initialize the Kernel and LLM The Kernel is the central orchestrator.

# filename: semantic_kernel_example.py
import semantic_kernel as sk
from semantic_kernel.connectors.ai.openai import OpenAIChatCompletion
from semantic_kernel.planners import SequentialPlanner
import os
import requests

# 1. Initialize the Kernel and LLM
kernel = sk.Kernel()

# Configure OpenAI chat completion
api_key = os.getenv("OPENAI_API_KEY")
if not api_key:
    raise ValueError("OPENAI_API_KEY environment variable not set.")

kernel.add_service(
    OpenAIChatCompletion(service_id="chat-gpt", ai_model_id="gpt-4o", api_key=api_key),
)

Explanation:

  • sk.Kernel() creates the kernel instance.
  • OpenAIChatCompletion is added as the AI service, specifying the model and API key.

Step 2: Define Skills (Native and Semantic) We’ll create a native skill to fetch web content and a semantic skill for summarization.

# Add to semantic_kernel_example.py
# 2. Define Skills

# Native Skill: WebFetcher
class WebSkill:
    @sk.function(description="Fetches content from a given URL.")
    def fetch_url_content(self, url: str) -> str:
        """Fetches the text content from a given URL."""
        try:
            response = requests.get(url, timeout=10)
            response.raise_for_status()  # Raise an exception for HTTP errors
            return response.text[:2000] # Return first 2000 chars to avoid large context
        except requests.exceptions.RequestException as e:
            return f"Error fetching URL {url}: {e}"

# Import the native skill into the kernel
web_skill = kernel.import_plugin_from_object(WebSkill(), plugin_name="WebSkill")

# Semantic Skill: Summarizer
# This is a prompt defined directly in Python, but can also be loaded from a file.
summarize_prompt = """
Summarize the following text concisely and clearly, focusing on the main points.
TEXT:
{{$input}}
"""

summarize_skill = kernel.create_function_from_prompt(
    prompt=summarize_prompt,
    function_name="SummarizeContent",
    description="Summarizes provided text.",
    plugin_name="TextSkill"
)

Explanation:

  • WebSkill is a Python class containing fetch_url_content, decorated with @sk.function to make it a kernel function. It uses requests to fetch content.
  • kernel.import_plugin_from_object registers WebSkill with the kernel.
  • summarize_prompt defines our semantic function. {{$input}} is a placeholder for the text to be summarized.
  • kernel.create_function_from_prompt registers this prompt as a semantic skill named SummarizeContent within the TextSkill plugin.

Step 3: Use the SequentialPlanner The SequentialPlanner will automatically generate a plan to achieve a goal by chaining our registered skills.

# Add to semantic_kernel_example.py
# 3. Use the SequentialPlanner
planner = SequentialPlanner(kernel=kernel)

async def main():
    user_goal = "Summarize the content of the Semantic Kernel overview page from Microsoft Learn."
    print(f"User Goal: {user_goal}")

    # The planner needs context to find the URL
    context = kernel.create_new_context()
    context["input"] = "https://learn.microsoft.com/en-us/semantic-kernel/overview/"

    print("\n--- Creating a plan ---")
    plan = await planner.create_plan(goal=user_goal, context=context)
    print(f"Plan created:\n{plan.generated_plan}")

    print("\n--- Executing the plan ---")
    result = await plan.invoke(kernel)

    print("\n--- Semantic Kernel Final Output ---")
    print(result.value)

if __name__ == "__main__":
    import asyncio
    asyncio.run(main())

Explanation:

  • SequentialPlanner(kernel=kernel) initializes the planner.
  • planner.create_plan() is an asynchronous call that asks the planner to figure out the steps (skills) needed to achieve the user_goal. We provide a context with the URL.
  • plan.invoke(kernel) then executes the generated plan. The output of one skill becomes the input for the next.
  • The result.value will contain the final summarized content.

To run this example, save the code as semantic_kernel_example.py and execute python semantic_kernel_example.py. You’ll see the planner’s generated plan (it should identify WebSkill.fetch_url_content then TextSkill.SummarizeContent) and then the final summary.

Framework Comparison Table

Let’s consolidate our findings into a handy comparison table:

Feature / FrameworkLangGraphAutoGenCrewAISemantic Kernel
OrchestrationGraph-based (State Machine)ConversationalRole/Task-basedPlanner-driven (Skills)
State ManagementExplicit Graph StateConversation HistoryTask Inputs/OutputsContext/Skill Parameters
Tool UsageLangChain RunnablesCode Execution, FunctionsAgent-assigned ToolsSkills (Native/Semantic)
MemoryLangChain Memory ModulesConversation HistoryAgent & Crew MemoryVector Store Memory
Primary FocusDeterministic, stateful workflows with loopsMulti-agent conversations, human-in-the-loopCollaborative teams with roles & tasksIntegrating AI with existing apps via skills
Ease of UseIntermediate-AdvancedBeginner-IntermediateIntermediateIntermediate
FlexibilityHigh (any graph)High (dynamic conversations)Moderate (role/task paradigm)High (skill composability)
Ideal ForIterative processes, complex pipelinesDynamic problem-solving, human-AI devTeam-based automation, project managementEnterprise integration, AI plugins

Choosing the Right Framework: A Decision Path

With a clearer understanding of each framework, how do you decide which one is right for your project? Here’s a guided thought process:

  1. What’s the core interaction pattern?

    • Do agents need to engage in free-form, dynamic conversations, potentially with human intervention, to explore solutions? Consider AutoGen. Its strength is emergent dialogue and flexible problem-solving.
    • Do you need a highly structured, auditable workflow with explicit states, conditional logic, and potential loops for iterative refinement? Consider LangGraph. It provides maximum control over the flow.
    • Are you modeling a “team” where specialized agents with distinct roles need to collaborate on a series of well-defined tasks to achieve a common goal? Consider CrewAI. It excels at defining clear responsibilities and collaborative processes.
    • Are you primarily looking to integrate AI capabilities (like summarization, data extraction, or complex function calls) into an existing application, where AI acts as an intelligent orchestrator of predefined functions/APIs? Consider Semantic Kernel. Its planner and skill system are perfect for composable AI functions within traditional codebases.
  2. How critical is state management and determinism?

    • If you need precise control over the flow of information and want to easily debug step-by-step state changes, LangGraph’s explicit graph state is a huge advantage.
    • If the emergent behavior of a conversation is acceptable and you’re comfortable with less explicit state, AutoGen might be sufficient.
    • CrewAI provides good task-level state, suitable for tracking progress within a collaborative team.
    • Semantic Kernel’s planner handles state implicitly through skill execution, which works well for function chaining.
  3. What kind of “tools” will your agents use?

    • If your tools are primarily Python functions or web APIs that can be wrapped as LangChain Runnables, LangGraph and CrewAI fit well.
    • If agents need to dynamically execute code snippets or interact with a shell, AutoGen is very powerful.
    • If your tools are existing enterprise APIs or functions that you want to expose as modular “skills” for an AI planner to orchestrate, Semantic Kernel shines.
  4. What’s your preferred development paradigm?

    • Developers who enjoy defining explicit graphs and state machines will appreciate LangGraph.
    • Those who prefer a more declarative, conversational approach will find AutoGen intuitive.
    • If thinking in terms of roles, goals, and tasks resonates with you, CrewAI will feel natural.
    • Engineers wanting to deeply embed AI into traditional application logic using familiar programming constructs (like functions/methods) will lean towards Semantic Kernel.

There’s no single “best” framework; the ideal choice is always context-dependent. Sometimes, you might even find yourself combining aspects or insights from multiple frameworks for a truly unique solution!

Mini-Challenge: Architectural Architect

It’s time to put your newfound knowledge to the test! No coding this time, but a critical thinking exercise.

Challenge: Imagine you need to build an “Automated Financial Advisor” application. This application should:

  1. Take a user’s financial goals (e.g., “save for retirement,” “invest in tech stocks,” “pay off debt”).
  2. Gather current market data, news, and the user’s existing portfolio details.
  3. Analyze the data and generate a personalized investment recommendation report, including risk assessment.
  4. Allow the user to ask follow-up questions about the report and refine their goals.

Your Task: Based on the requirements above, which two frameworks would you consider as the primary candidates for building this system, and why? Justify your choices by referring to their strengths, orchestration patterns, and how they would handle the key requirements (data gathering, analysis, report generation, follow-up questions).

Hint: Think about the different stages of the process and how each framework’s core design philosophy aligns with those stages. Consider which frameworks excel at structured workflows versus dynamic interactions.

What to Observe/Learn: This challenge helps you practice mapping real-world problems to the architectural strengths of different agentic frameworks, a crucial skill for any AI engineer.

Common Pitfalls & Troubleshooting in Framework Selection

Choosing an agentic framework is a significant decision. Here are some common pitfalls and tips to avoid them:

  1. Over-engineering with the Wrong Framework: Don’t pick the most complex framework for a simple problem. If a linear sequence of tool calls suffices, you might not need a full graph or multi-agent conversation. Start simple. For instance, a basic RAG application might not need an entire multi-agent crew.
  2. Underestimating Learning Curve: Each framework has its own idioms and patterns. Don’t jump into a complex framework like LangGraph without dedicating time to understand its core concepts (nodes, edges, state). Similarly, AutoGen’s conversational dynamics can be tricky to control initially.
  3. Ignoring Ecosystem Fit: Consider your existing tech stack and team’s familiarity. If your team is heavily invested in LangChain, LangGraph might be a natural extension. If you’re a C# shop, Semantic Kernel might be more appealing. Leverage existing knowledge where possible.
  4. Premature Optimization/Complexity: Don’t try to build the most robust, self-healing, infinitely scalable system on day one. Start with a Minimum Viable Product (MVP) using the most straightforward framework that meets core requirements, then iterate. You can always refactor or switch frameworks later if the initial choice proves limiting.
  5. Forgetting Human-in-the-Loop: Many complex agentic workflows benefit from human oversight or intervention. Ensure your chosen framework allows for easy integration of human feedback if needed (AutoGen excels here with human_input_mode). For critical tasks, manual review stages are often essential.
  6. Neglecting Cost and Performance: Different orchestration patterns can lead to vastly different numbers of LLM calls, impacting cost and latency. A highly chatty AutoGen conversation might be more expensive than a tightly controlled LangGraph workflow for the same outcome. Analyze the expected number of LLM interactions for your chosen pattern.

When troubleshooting issues during development, always refer to the official documentation. The agentic AI space is evolving rapidly, and the latest best practices are usually found there. The communities around these frameworks (GitHub issues, Discord channels) are also invaluable resources.

Summary

Phew! That was a deep dive into the fascinating world of AI agent frameworks. We’ve explored:

  • The critical criteria for evaluating agentic frameworks: orchestration, state, tools, memory, ease of use, extensibility, and community.
  • The underlying principles of agentic AI: goal-oriented behavior, perception-action loops, planning, memory, and tool usage.
  • The unique architectural patterns and strengths of LangGraph (stateful graphs), AutoGen (conversational agents), CrewAI (role-playing teams), and Semantic Kernel (skills and planners).
  • Practical, runnable code examples for each framework, demonstrating their core concepts in action.
  • Specific considerations for prompt engineering and tool design within each framework.
  • A practical decision-making process to help you choose the most suitable framework for your specific project needs.
  • Common pitfalls to avoid when embarking on your agentic development journey.

The choice of framework significantly impacts the design, development, and maintainability of your AI agent applications. By understanding the nuances of each, you’re now better prepared to make informed decisions and build truly intelligent and effective systems.

What’s next? In the final chapter, we’ll wrap up our journey by discussing advanced topics, future trends, and ethical considerations in AI agent development, preparing you for the exciting road ahead!

References


This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.