| name | crewai |
| description | [Applies to: **/*.py] Definitive guidelines for building robust, maintainable, and scalable multi-agent systems using CrewAI, focusing on modularity, clear role definition, and disciplined LLM configuration. |
| source | cursor_mdc |
CrewAI Best Practices
This document outlines the definitive best practices for developing with CrewAI, ensuring your multi-agent systems are maintainable, performant, and production-ready. Adhere to these guidelines for all new and existing CrewAI projects.
1. Code Organization and Structure
Always modularize your agents, tasks, and crews into dedicated Python modules. This promotes reusability, version control, and clear separation of concerns.
1.1. Dedicated Modules for Agents, Tasks, and Crews
Always separate definitions. Agents belong in agents.py, tasks in tasks.py, and the crew orchestration in crew.py (or main.py).
❌ BAD: Monolithic file
from crewai import Agent, Task, Crew
researcher = Agent(role='Researcher', ...)
research_task = Task(description='Research topic', agent=researcher, ...)
crew = Crew(agents=[researcher], tasks=[research_task], ...)
✅ GOOD: Modular structure
from crewai import Agent
from tools import search_tool
class ResearchAgents:
def senior_researcher(self) -> Agent:
return Agent(
role='Senior Researcher',
goal='Uncover critical insights and data points.',
backstory='Expert in data analysis and synthesis.',
verbose=True, allow_delegation=False, tools=[search_tool]
)
from crewai import Task
from agents import ResearchAgents
class ResearchTasks:
def __init__(self):
self.agents = ResearchAgents()
def research_topic(self, topic: str) -> Task:
return Task(
description=f"Conduct comprehensive research on '{topic}'.",
expected_output='A detailed report summarizing key findings.',
agent=self.agents.senior_researcher(),
async_execution=False
)
from crewai import Crew, Process
from agents import ResearchAgents
from tasks import ResearchTasks
class MyCrew:
def __init__(self, topic: str):
self.topic = topic
self.agents = ResearchAgents()
self.tasks = ResearchTasks()
def run(self):
crew = Crew(
agents=[self.agents.senior_researcher()],
tasks=[self.tasks.research_topic(self.topic)],
process=Process.sequential,
verbose=2
)
return crew.kickoff()
if __name__ == "__main__":
result = MyCrew("AI Agent Frameworks in 2025").run()
print(result)
1.2. Clear Agent Role Definition
Every agent must have explicit role, goal, and backstory. These are the primary drivers of agent behavior.
❌ BAD: Vague agent
researcher = Agent(role='Researcher')
✅ GOOD: Detailed agent
researcher = Agent(
role='Senior Market Analyst',
goal='Provide actionable insights on emerging AI trends.',
backstory='With years of experience in tech market analysis, I excel at identifying key drivers and potential disruptors.',
verbose=True, allow_delegation=False
)
2. LLM Selection and Configuration
Match LLM capabilities to agent requirements. Always externalize LLM configuration.
2.1. Strategic LLM Selection
Choose LLMs based on task complexity, cost, and latency. For complex reasoning, use powerful models (e.g., GPT-4o, Claude Opus). For simple, high-volume tasks, use faster, cheaper models.
❌ BAD: Arbitrary LLM choice
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(model_name="gpt-3.5-turbo")
✅ GOOD: Purpose-driven LLM choice
from langchain_openai import ChatOpenAI
complex_llm = ChatOpenAI(model_name="gpt-4o", temperature=0.2)
fast_llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.7)
researcher = Agent(llm=complex_llm, ...)
summarizer = Agent(llm=fast_llm, ...)
2.2. Externalized LLM Configuration
Never hardcode API keys or sensitive configurations. Use environment variables.
❌ BAD: Hardcoded API key
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(openai_api_key="sk-YOUR_HARDCODED_KEY")
✅ GOOD: Environment variables (.env)
OPENAI_API_KEY="sk-YOUR_ACTUAL_API_KEY"
import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
load_dotenv()
def get_openai_llm(model: str = "gpt-4o", temperature: float = 0.2) -> ChatOpenAI:
return ChatOpenAI(
model_name=model,
temperature=temperature,
api_key=os.getenv("OPENAI_API_KEY")
)
from config import get_openai_llm
complex_llm = get_openai_llm("gpt-4o")
3. Common Patterns and Anti-patterns
3.1. Agent Collaboration with context
Leverage context in tasks to enable agents to build upon each other's work. This is fundamental to multi-agent systems.
❌ BAD: Isolated tasks
task1 = Task(description='Research X', agent=researcher)
task2 = Task(description='Summarize Y', agent=summarizer)
✅ GOOD: Chained tasks with context
research_task = Task(
description='Research quantum computing trends.',
expected_output='A comprehensive report.', agent=researcher
)
analysis_task = Task(
description='Analyze the research report and identify key investment opportunities.',
expected_output='A list of 3-5 high-potential investment opportunities.',
agent=analyst,
context=[research_task]
)
3.2. Effective Tool Usage
Equip agents with specific tools for specific jobs. Agents should not "hallucinate" tool usage.
❌ BAD: Agent without necessary tools
researcher = Agent(role='Researcher', goal='Find data', ...)
✅ GOOD: Agent with relevant tools
from crewai_tools import SerperDevTool
search_tool = SerperDevTool()
researcher = Agent(
role='Data Gatherer',
goal='Collect up-to-date information on market shifts.',
backstory='My expertise lies in rapidly extracting relevant data from the web.',
tools=[search_tool],
verbose=True
)
4. Performance Considerations
Optimize for cost and speed by managing verbosity, process flow, and asynchronous execution.
4.1. Controlled Verbosity
Use verbose=1 for production, verbose=2 for development/debugging. Excessive verbosity impacts performance.
❌ BAD: Always verbose=2 in production
crew = Crew(..., verbose=2)
✅ GOOD: Conditional verbosity
import os
is_dev = os.getenv("ENV", "dev") == "dev"
crew = Crew(..., verbose=2 if is_dev else 1)
4.2. Process Flow and Asynchronous Tasks
Choose Process.sequential for dependent tasks and Process.hierarchical for complex, multi-layered coordination. Use async_execution=True for tasks that can run in parallel.
❌ BAD: Defaulting to sequential for independent tasks
task1 = Task(description='Scrape website A', agent=scraper)
task2 = Task(description='Scrape website B', agent=scraper)
crew = Crew(tasks=[task1, task2], process=Process.sequential)
✅ GOOD: Parallel execution for independent tasks
task1 = Task(description='Scrape website A', agent=scraper, async_execution=True)
task2 = Task(description='Scrape website B', agent=scraper, async_execution=True)
crew = Crew(tasks=[task1, task2], process=Process.sequential)
5. Common Pitfalls and Gotchas
5.1. Agent Drift and Hallucination
Agents can stray from their goal or invent information. Mitigate this with clear goal, backstory, specific tools, and allow_delegation=False where appropriate.
❌ BAD: Agent frequently goes off-topic
agent = Agent(role='Generalist', goal='Explore AI', temperature=0.8)
✅ GOOD: Focused agent
agent = Agent(
role='Specific Data Extractor',
goal='Extract financial data from quarterly reports.',
backstory='I am precise and only focus on numerical data.',
tools=[pdf_reader_tool],
allow_delegation=False,
temperature=0.1
)
5.2. Infinite Loops
Agents can get stuck in repetitive cycles. Set max_rpm (requests per minute