Back to AI App Dev Series

CrewAI SDK Track Part 2: Agents & Capabilities

May 24, 2026 Wasil Zafar 40 min read

Deep dive into CrewAI agents — the role-goal-backstory persona triad, LLM configuration for any provider, attaching tools and MCP servers, injecting skills and knowledge sources, and crafting agents that consistently deliver high-quality results.

Table of Contents

  1. Agent Anatomy
  2. LLM Configuration
  3. Agent Capabilities
  4. Crafting Effective Agents
  5. Agent Customization
What You’ll Learn: Agents in CrewAI are defined by their role, goal, and backstory — like characters in a story, each with a distinct personality and expertise. This article teaches you to craft agents that perform reliably: choosing the right LLM for each role, setting capabilities like memory and delegation, and writing backstories that produce consistent behavior. The key insight: a well-defined agent acts predictably because its ‘personality’ constrains its behavior.

1. Agent Anatomy

Every CrewAI agent is defined by three core identity elements — role, goal, and backstory — that shape how the agent approaches tasks, communicates, and makes decisions. These aren’t just labels; they’re injected into the system prompt and fundamentally alter agent behavior.

The Persona Triad: Role defines expertise domain and authority. Goal provides direction and success criteria. Backstory adds personality, working style, and contextual depth. Together they create a consistent “character” that the LLM inhabits.

1.1 Configuration Parameters

Beyond the persona triad, agents accept several behavioral parameters:

from crewai import Agent, LLM

# Full agent definition with all parameters
agent = Agent(
    role="Senior Data Scientist",
    goal="Analyze datasets and extract actionable business insights",
    backstory="""You have 12 years of experience in data science at
    Fortune 500 companies. You specialize in finding patterns that
    others miss and translating complex statistics into clear
    business recommendations.""",
    llm=LLM(model="openai/gpt-4o"),
    verbose=True,              # Log agent reasoning steps
    allow_delegation=True,     # Can delegate subtasks to other agents
    max_iter=15,               # Max reasoning iterations per task
    max_rpm=10,                # Rate limit: max requests per minute
    memory=True,               # Enable agent memory
    cache=True,                # Cache tool results
)

print(f"Agent: {agent.role}")
print(f"Delegation: {agent.allow_delegation}")
print(f"Max iterations: {agent.max_iter}")

The same agent defined in YAML (preferred for production):

# config/agents.yaml
data_scientist:
  role: "Senior Data Scientist"
  goal: "Analyze datasets and extract actionable business insights"
  backstory: >
    You have 12 years of experience in data science at Fortune 500
    companies. You specialize in finding patterns that others miss
    and translating complex statistics into clear business
    recommendations.
  verbose: true
  allow_delegation: true
  max_iter: 15
  max_rpm: 10
  memory: true

2. LLM Configuration

CrewAI supports any LLM provider through its LLM class. You can mix different models across agents in the same crew — using a powerful model for complex reasoning and a cheaper model for simple extraction tasks.

2.1 Multi-Provider Setup

from crewai import Agent, LLM

# OpenAI (default)
openai_llm = LLM(
    model="openai/gpt-4o",
    temperature=0.7,
    max_tokens=4096
)

# Anthropic Claude
claude_llm = LLM(
    model="anthropic/claude-sonnet-4-20250514",
    temperature=0.5
)

# Google Gemini
gemini_llm = LLM(
    model="gemini/gemini-3.5-flash",
    temperature=0.3
)

# Local model via Ollama
local_llm = LLM(
    model="ollama/llama3.2",
    base_url="http://localhost:11434"
)

# Create agents with different LLMs
strategist = Agent(
    role="Business Strategist",
    goal="Develop comprehensive business strategies",
    backstory="You are a McKinsey-trained strategy consultant.",
    llm=claude_llm  # Claude for nuanced reasoning
)

extractor = Agent(
    role="Data Extractor",
    goal="Extract structured data from documents",
    backstory="You are precise and methodical.",
    llm=gemini_llm  # Gemini for fast extraction
)

print(f"Strategist LLM: {strategist.llm.model}")
print(f"Extractor LLM: {extractor.llm.model}")
Cost Optimization: Use expensive models (GPT-4o, Claude Opus) only for agents doing complex reasoning or creative work. Use cheaper models (GPT-4o-mini, Gemini Flash) for extraction, formatting, and simple classification tasks. A well-designed crew can cut costs 60–80% with mixed models.

3. Agent Capabilities

Agents become powerful when equipped with capabilities beyond text generation. CrewAI supports four capability types: Tools, MCP servers, Skills, and Knowledge.

3.1 Attaching Tools

from crewai import Agent
from crewai_tools import (
    SerperDevTool,
    ScrapeWebsiteTool,
    FileReadTool,
    DirectoryReadTool
)

# Initialize tools
search_tool = SerperDevTool()
scrape_tool = ScrapeWebsiteTool()
file_tool = FileReadTool()
dir_tool = DirectoryReadTool()

# Attach tools to agent
researcher = Agent(
    role="Research Analyst",
    goal="Find and verify information from multiple sources",
    backstory="You are thorough and always cross-reference sources.",
    tools=[search_tool, scrape_tool, file_tool, dir_tool],
    verbose=True
)

print(f"Agent tools: {[t.name for t in researcher.tools]}")

3.2 Connecting MCP Servers

CrewAI natively supports the Model Context Protocol (MCP) for connecting to external tool servers:

from crewai import Agent
from crewai.tools import MCPServerAdapter

# Connect to an MCP server (e.g., filesystem, database, or custom)
mcp_tools = MCPServerAdapter(
    server_params={
        "url": "http://localhost:3000/mcp",
        "transport": "streamable-http"
    }
)

# Agent with MCP tools
agent_with_mcp = Agent(
    role="DevOps Engineer",
    goal="Manage infrastructure and deployments",
    backstory="You are an expert in cloud infrastructure automation.",
    tools=mcp_tools.tools,
    verbose=True
)

print(f"MCP tools loaded: {len(agent_with_mcp.tools)}")

3.3 Skills & Knowledge Sources

from crewai import Agent
from crewai.knowledge.source import TextKnowledgeSource, PDFKnowledgeSource

# Add knowledge sources to an agent
company_docs = TextKnowledgeSource(
    content="Our company policy states that all deployments must...",
    metadata={"source": "internal-policy"}
)

technical_spec = PDFKnowledgeSource(
    file_paths=["specs/api-specification.pdf"]
)

compliance_agent = Agent(
    role="Compliance Officer",
    goal="Ensure all outputs meet company policies and regulations",
    backstory="You are meticulous about policy adherence.",
    knowledge_sources=[company_docs, technical_spec],
    verbose=True
)

print(f"Knowledge sources: {len(compliance_agent.knowledge_sources)}")
Real-World Application

Specialized Customer Support Team

An e-commerce company built a CrewAI support team: a Triage Agent (classifies urgency), a Product Expert (deep knowledge of 500+ SKUs), and an Escalation Agent (handles complex cases with human handoff). Each agent’s backstory encodes real support playbooks. Result: 65% of tickets resolved without human intervention.

Customer SupportAgent Personas

4. Crafting Effective Agents

The difference between a mediocre agent and an exceptional one lies in the specificity and depth of its persona definition. Here’s a comparison:

4.1 Good vs Poor Agent Design

from crewai import Agent

# ❌ POOR: Vague, generic, no personality
bad_agent = Agent(
    role="Writer",
    goal="Write content",
    backstory="You write things."
)

# ✅ GOOD: Specific, domain-expert, clear constraints
good_agent = Agent(
    role="Senior Technical Documentation Writer",
    goal="""Write clear, comprehensive API documentation that enables
    developers to integrate our payment processing system within
    30 minutes of reading""",
    backstory="""You spent 8 years at Stripe writing developer docs
    that are praised industry-wide for clarity. You follow the
    Diátaxis framework (tutorials, how-to guides, reference,
    explanation). You never assume prior knowledge and always
    include working code examples. You write for a developer
    audience with 2-5 years of experience.""",
    verbose=True,
    allow_delegation=False,
    max_iter=10
)

print(f"Good agent role: {good_agent.role}")
print(f"Bad agent role: {bad_agent.role}")
Common Mistake: Don’t create too many agents. Each agent adds latency and cost. A crew of 2–4 focused agents outperforms a crew of 8 generic ones. If an agent doesn’t need a distinct persona or unique tools, merge it into another agent.

5. Agent Customization

5.1 Custom Manager Agents

In hierarchical processes, a manager agent delegates work. You can customize the manager for domain-specific coordination:

from crewai import Agent, Crew, Process

# Custom manager for a software development crew
manager = Agent(
    role="Engineering Manager",
    goal="""Coordinate the development team to deliver high-quality
    software on schedule. Prioritize tasks based on dependencies
    and team member expertise.""",
    backstory="""You are a seasoned engineering manager with 10 years
    of experience leading distributed teams. You excel at breaking
    down complex projects into manageable tasks and matching them
    to the right team members.""",
    allow_delegation=True
)

# Use as manager in hierarchical process
crew = Crew(
    agents=[...],  # Your worker agents
    tasks=[...],   # Your tasks
    process=Process.hierarchical,
    manager_agent=manager,
    verbose=True
)

print(f"Manager: {manager.role}")
print(f"Process: {crew.process}")

5.2 Callbacks & Hooks

from crewai import Agent

def on_task_start(agent, task):
    print(f"[{agent.role}] Starting: {task.description[:50]}...")

def on_task_complete(agent, task, output):
    print(f"[{agent.role}] Completed! Output length: {len(output)}")

# Agent with step callbacks
monitored_agent = Agent(
    role="Quality Analyst",
    goal="Ensure all outputs meet quality standards",
    backstory="You are detail-oriented and never let errors pass.",
    step_callback=lambda step: print(f"  Step: {step}"),
    verbose=True
)

print(f"Agent with callback: {monitored_agent.role}")
Try It Yourself: Create 4 agents with distinct personalities for a product team: (1) Product Manager (strategic, asks clarifying questions), (2) Engineer (technical, estimates complexity), (3) Designer (user-focused, considers accessibility), (4) QA Tester (detail-oriented, finds edge cases). Give each the same feature request and compare how their responses reflect their defined roles.

Next in the CrewAI SDK Track

In Part 3: Tasks & Processes, we’ll explore task definitions with structured outputs, sequential and hierarchical processes, conditional task execution, agent collaboration patterns, and task dependencies.