← Back to DevBytes

Building a Research Team with CrewAI: Researchers, Writers, Editors

What Is a Research Team in CrewAI?

CrewAI is an open-source framework for orchestrating autonomous AI agents that collaborate to accomplish complex tasks. A research team built with CrewAI typically consists of three core roles: Researchers who gather and synthesize information, Writers who transform raw research into polished content, and Editors who review, refine, and ensure quality standards are met. These agents work together in a sequential or hierarchical pipeline, passing outputs from one to the next, much like a human editorial team.

Each agent in CrewAI is powered by a large language model (LLM) and can be equipped with custom tools such as web scrapers, search APIs, or internal knowledge bases. The framework handles task delegation, context sharing, and output validation, allowing developers to build sophisticated multi-agent workflows with minimal boilerplate code.

Core Components of a CrewAI Research Team

Why Building a Research Team Matters

Manual research-to-content pipelines are slow, inconsistent, and difficult to scale. A single developer writing a comprehensive report must juggle source discovery, fact-checking, drafting, and polishing — often leading to bottlenecks and uneven quality. By distributing these responsibilities across specialized AI agents, CrewAI enables:

For organizations producing technical reports, market analyses, or documentation, a CrewAI research team transforms a linear human-driven process into an automated, reliable pipeline that runs on demand.

Setting Up Your Environment

Before building the team, install CrewAI and its dependencies. You'll need Python 3.10+ and API keys for your chosen LLM provider (OpenAI, Anthropic, Azure OpenAI, or local models via Ollama).

# Create a virtual environment
python -m venv crewai-env
source crewai-env/bin/activate  # On Windows: crewai-env\Scripts\activate

# Install CrewAI with tools package
pip install crewai crewai-tools

# Set your API key
export OPENAI_API_KEY="your-api-key-here"  # Or use a .env file

Create a project structure to keep your code organized:

research_team/
├── main.py              # Entry point for running the crew
├── agents.py            # Agent definitions
├── tasks.py             # Task definitions
├── tools.py             # Custom tool implementations
└── .env                 # Environment variables (API keys)

Defining the Research Team Agents

Each agent needs a distinct role, goal, and backstory. The backstory is particularly important — it shapes the agent's tone, decision-making style, and how it interprets instructions. Let's define three agents in agents.py:

from crewai import Agent
from crewai_tools import SerperDevTool, ScrapeWebsiteTool

# Shared tools
search_tool = SerperDevTool()          # Google search via Serper API
scrape_tool = ScrapeWebsiteTool()      # Extract content from URLs

# --- RESEARCHER AGENT ---
researcher = Agent(
    role="Senior Research Analyst",
    goal="Uncover deep, accurate, and well-sourced information on the given topic",
    backstory=(
        "You are a veteran research analyst with 15 years of experience at a top-tier "
        "think tank. You excel at finding primary sources, cross-referencing facts, "
        "and identifying emerging trends. You distrust superficial summaries and always "
        "dig at least three layers deep. Your reports are structured, citation-heavy, "
        "and free of fluff."
    ),
    tools=[search_tool, scrape_tool],
    verbose=True,
    allow_delegation=False,  # Researcher focuses solely on research
    llm="gpt-4o"             # Use a capable model for deep reasoning
)

# --- WRITER AGENT ---
writer = Agent(
    role="Technical Content Writer",
    goal="Transform research findings into clear, engaging, and well-structured prose",
    backstory=(
        "You are an award-winning technical writer who specializes in making complex "
        "topics accessible. You've written for publications like Nature, Wired, and MIT "
        "Technology Review. You know how to craft compelling narratives, use analogies "
        "effectively, and maintain a consistent voice. You always adapt your style to "
        "the target audience specified in the task."
    ),
    tools=[],  # Writer works from research output, doesn't need external tools
    verbose=True,
    allow_delegation=False,
    llm="gpt-4o"
)

# --- EDITOR AGENT ---
editor = Agent(
    role="Chief Editor and Quality Assurance Lead",
    goal="Ensure content is factually accurate, grammatically flawless, and aligned with editorial standards",
    backstory=(
        "You are the chief editor at a prestigious publishing house with a reputation "
        "for uncompromising quality. You've edited over 500 published works across "
        "technical and scientific domains. You catch subtle errors others miss, enforce "
        "style guide consistency, and provide constructive, actionable feedback. You "
        "never let a piece go to print unless it meets your exacting standards."
    ),
    tools=[search_tool],  # Editor can fact-check independently
    verbose=True,
    allow_delegation=True,  # Editor can delegate corrections back to writer
    llm="gpt-4o"
)

Agent Design Principles

Defining Tasks with Context and Expected Output

Tasks in CrewAI carry context from preceding tasks, enabling the writer to receive the researcher's findings and the editor to receive the writer's draft. Each task specifies an expected_output that guides the agent and serves as a validation checkpoint. Create tasks.py:

from crewai import Task
from datetime import datetime

# --- RESEARCH TASK ---
research_task = Task(
    description=(
        "Conduct a comprehensive investigation into the topic: {topic}\n\n"
        "Your research should cover:\n"
        "1. Historical context and evolution of the topic\n"
        "2. Current state-of-the-art, key players, and recent developments\n"
        "3. Competing perspectives or controversies\n"
        "4. Quantitative data, statistics, and trends (with sources)\n"
        "5. Future outlook and predictions from credible experts\n\n"
        "Use at least 8 distinct sources. For each major claim, provide a citation "
        "or link to the original source. Structure your findings as a detailed "
        "research brief with labeled sections."
    ),
    expected_output=(
        "A structured research brief (minimum 1500 words) with sections: "
        "Executive Summary, Historical Context, Current Landscape, Data & Statistics, "
        "Key Controversies, Future Outlook, and Source List with URLs. "
        "Every factual claim must have a corresponding source reference."
    ),
    agent=researcher,
    async_execution=False  # Run synchronously to ensure research is complete before writing
)

# --- WRITING TASK ---
writing_task = Task(
    description=(
        "Using the research brief provided as context, write a polished, publication-ready "
        "article on the topic: {topic}\n\n"
        "Target audience: {audience}\n"
        "Desired tone: {tone}\n"
        "Target length: {length} words\n\n"
        "Requirements:\n"
        "- Open with a compelling hook that draws readers in\n"
        "- Use the research brief's structure as scaffolding but adapt it narratively\n"
        "- Include concrete examples, data points, and quotes from sources\n"
        "- Maintain the specified tone consistently throughout\n"
        "- End with a thought-provoking conclusion that ties back to the hook"
    ),
    expected_output=(
        "A complete article draft in markdown format matching the target length, "
        "with a title, introduction, body sections, and conclusion. All statistics "
        "and claims should be properly attributed to sources from the research brief."
    ),
    agent=writer,
    context=[research_task],  # Writer receives the researcher's output
    async_execution=False
)

# --- EDITING TASK ---
editing_task = Task(
    description=(
        "You have received a draft article that needs thorough editing before publication. "
        "Perform the following quality assurance steps:\n\n"
        "1. Fact-check every statistic, date, name, and claim against the original "
        "research brief. Flag any discrepancies.\n"
        "2. Review grammar, punctuation, and adherence to the specified tone ({tone}).\n"
        "3. Assess structural flow — does the article build logically? Are transitions smooth?\n"
        "4. Verify that all sources are properly cited and attribution is correct.\n"
        "5. Provide a final edited version with your changes incorporated.\n"
        "6. Append an editor's note summarizing the changes made and any concerns that "
        "require further review."
    ),
    expected_output=(
        "A final, edited article in markdown format with all corrections applied, "
        "followed by a short editor's note (in italics) detailing changes made, "
        "factual corrections, and a quality rating out of 5."
    ),
    agent=editor,
    context=[research_task, writing_task],  # Editor sees both research and draft
    async_execution=False,
    output_file="final_article.md"  # Automatically saves the final output
)

Task Context Chaining

The context parameter is what makes CrewAI powerful. By passing [research_task] to the writing task, CrewAI automatically feeds the researcher's completed output into the writer's prompt. For the editor, passing both [research_task, writing_task] gives access to the source material and the draft, enabling independent fact-checking against original research.

Assembling and Running the Crew

The Crew class ties agents and tasks together and manages execution. Create main.py:

from crewai import Crew, Process
from agents import researcher, writer, editor
from tasks import research_task, writing_task, editing_task

def run_research_pipeline(topic, audience="technical professionals", tone="authoritative", length=2000):
    """Run the complete research → writing → editing pipeline."""
    
    # Interpolate variables into task descriptions
    # CrewAI uses {variable} syntax for interpolation
    crew = Crew(
        agents=[researcher, writer, editor],
        tasks=[research_task, writing_task, editing_task],
        process=Process.sequential,  # Research → Write → Edit
        verbose=True,                # Logs agent actions and reasoning
        memory=True,                 # Enables cross-task memory for the crew
        planning=True,               # Allows agents to plan their approach before executing
        # Optional: set manager LLM for hierarchical process
        # manager_llm="gpt-4o",
    )
    
    result = crew.kickoff(inputs={
        "topic": topic,
        "audience": audience,
        "tone": tone,
        "length": str(length)
    })
    
    return result

if __name__ == "__main__":
    topic = "The impact of large language models on enterprise software development"
    final_output = run_research_pipeline(
        topic=topic,
        audience="software engineering managers and CTOs",
        tone="analytical and forward-looking",
        length=2500
    )
    print("\n=== FINAL OUTPUT ===\n")
    print(final_output)

Execution Process Explained

When crew.kickoff() is called, CrewAI executes the pipeline in sequential order:

  1. Planning phase: If planning=True, each agent analyzes its task and formulates an internal plan before acting
  2. Research execution: The researcher uses SerperDevTool to search, ScrapeWebsiteTool to extract content from promising URLs, then compiles the research brief
  3. Context injection: The completed research brief is passed into the writer's task context
  4. Writing execution: The writer crafts the article using the research as source material
  5. Editing execution: The editor receives both the research brief and the draft, performs fact-checking and polishing, and outputs the final article plus an editor's note
  6. Output collection: The final task's output is returned and saved to final_article.md

Adding Custom Tools for Enhanced Research

While CrewAI ships with built-in tools, custom tools allow you to integrate proprietary data sources, internal wikis, or specialized APIs. Here's how to build a custom tool that queries an internal document store:

from crewai_tools import BaseTool
from typing import Optional
import json

class InternalKnowledgeBaseTool(BaseTool):
    name: str = "Internal Knowledge Base Search"
    description: str = (
        "Searches the company's internal knowledge base for documents, "
        "reports, and technical specifications. Returns relevant excerpts "
        "with document IDs for reference."
    )
    
    def _run(self, query: str) -> str:
        """
        Execute the search against the internal knowledge base.
        In production, this would query a vector database or enterprise search API.
        """
        # Simulated response — replace with actual API call
        # Example: results = vector_db.similarity_search(query, k=5)
        simulated_results = [
            {
                "doc_id": "KB-2024-089",
                "title": "LLM Adoption in Enterprise: Q3 2024 Survey",
                "excerpt": "72% of surveyed enterprises report at least one LLM-powered feature in production...",
                "relevance_score": 0.94
            },
            {
                "doc_id": "KB-2024-102",
                "title": "Security Considerations for AI-Generated Code",
                "excerpt": "Static analysis tools catch 34% more vulnerabilities in AI-generated code versus human-written...",
                "relevance_score": 0.87
            }
        ]
        return json.dumps(simulated_results, indent=2)

# Add the custom tool to the researcher agent
researcher_with_internal_kb = Agent(
    role="Senior Research Analyst",
    goal="Uncover deep, accurate information using both public and internal sources",
    backstory="...",  # Same backstory as before
    tools=[
        SerperDevTool(),
        ScrapeWebsiteTool(),
        InternalKnowledgeBaseTool()  # Custom tool added here
    ],
    verbose=True,
    allow_delegation=False,
    llm="gpt-4o"
)

Tool Implementation Best Practices

Switching to Hierarchical Process for Complex Projects

For larger projects — such as researching multiple subtopics simultaneously or coordinating a team of 5+ agents — switch to the hierarchical process. This introduces a manager agent (powered by a separate LLM) that dynamically allocates tasks and synthesizes results.

# Hierarchical crew with a manager
crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, writing_task, editing_task],
    process=Process.hierarchical,  # Manager coordinates agents
    manager_llm="gpt-4o",          # Dedicated LLM for the manager
    verbose=True,
    memory=True,
    planning=True
)

result = crew.kickoff(inputs={
    "topic": "Quantum computing applications in financial services",
    "audience": "investment analysts and portfolio managers",
    "tone": "educational yet sophisticated",
    "length": "3000"
})

In hierarchical mode, the manager agent can decide to spin up additional research threads, request revisions from the writer before passing to the editor, or even loop back if quality thresholds aren't met. This adds flexibility at the cost of higher token consumption and slightly less predictable execution paths.

Handling Variables and Dynamic Inputs

CrewAI supports {variable} interpolation in both task descriptions and expected outputs. When you call crew.kickoff(inputs={...}), all occurrences of {variable_name} are replaced with the provided values. This makes your crew reusable across different topics, audiences, and style requirements without code changes.

# Define a task with multiple variables
research_task = Task(
    description=(
        "Research topic: {topic}\n"
        "Focus areas: {focus_areas}\n"
        "Geographic scope: {region}\n"
        "Time period: {time_period}\n"
        "Minimum sources: {min_sources}"
    ),
    expected_output="A research brief covering all specified focus areas and scope.",
    agent=researcher
)

# Run with different parameters
result = crew.kickoff(inputs={
    "topic": "Renewable energy adoption in manufacturing",
    "focus_areas": "solar thermal, heat pumps, green hydrogen",
    "region": "European Union",
    "time_period": "2020-2025",
    "min_sources": "12"
})

Best Practices for Production Research Teams

1. Design Tasks as Self-Contained Units

Each task should have a clear, self-contained description that an agent can understand without relying on implicit knowledge. Include explicit formatting requirements, citation standards, and word counts in the expected output. Ambiguous tasks produce inconsistent results across runs.

2. Implement Validation Gateways

Add lightweight validation between stages. For example, check that the research brief actually contains URLs before passing it to the writer. CrewAI's output_file parameter saves intermediate outputs — use these to build automated quality checks:

# Example: Validate research output before proceeding
def validate_research_output(output: str) -> bool:
    """Check that research output meets minimum quality bars."""
    checks = [
        "http" in output.lower(),          # Contains URLs/sources
        len(output.split()) >= 800,        # Minimum word count
        "executive summary" in output.lower() or "findings" in output.lower()
    ]
    return all(checks)

# In your pipeline, after research task completes:
# (This would be part of a custom callback or post-processing step)

3. Use Memory and Planning Judiciously

memory=True enables cross-task context retention, which is essential for the writer to recall specific research details. planning=True adds a planning step where agents outline their approach before acting — this improves output quality for complex tasks but increases token usage. For simple, well-defined tasks, consider disabling planning to reduce costs.

4. Match Models to Role Complexity

Not every role needs the most powerful (and expensive) model. A typical optimization:

researcher = Agent(..., llm="gpt-4o")           # Deep reasoning needed
writer = Agent(..., llm="gpt-4o-mini")           # Creative but less intensive
editor = Agent(..., llm="gpt-4o")               # Critical quality gate

Test different model combinations — sometimes a cheaper model performs adequately for writing when given excellent research input.

5. Build Retry and Fallback Logic

API failures, rate limits, and tool errors happen. Wrap your crew execution in robust error handling:

import time
from crewai import Crew

def run_with_retry(crew: Crew, inputs: dict, max_retries: int = 3):
    """Execute crew with exponential backoff on failure."""
    for attempt in range(max_retries):
        try:
            result = crew.kickoff(inputs=inputs)
            return result
        except Exception as e:
            print(f"Attempt {attempt + 1} failed: {e}")
            if attempt < max_retries - 1:
                wait_time = 2 ** attempt * 10  # 10s, 20s, 40s
                print(f"Retrying in {wait_time} seconds...")
                time.sleep(wait_time)
            else:
                raise
    return None

6. Log and Monitor Agent Behavior

Enable verbose=True during development to see agent reasoning traces. For production, capture these logs to a monitoring system to track performance, identify failure patterns, and optimize prompts over time. CrewAI's verbose output includes thought processes, tool calls, and inter-agent communication.

7. Iterate on Prompts Through Backstories

The agent backstory is effectively a system prompt. Invest time in crafting it — include specific examples of desired behavior, anti-patterns to avoid, and stylistic preferences. Run the same task with different backstory formulations and compare outputs to find the most effective phrasing.

8. Handle Large Research Scopes with Task Decomposition

For broad topics, break research into multiple focused tasks rather than one monolithic task:

# Instead of one giant research task, decompose:
market_research_task = Task(
    description="Research market size, competitors, and industry trends for {topic}",
    agent=market_analyst  # Specialized agent
)

technical_research_task = Task(
    description="Research technical specifications, architectures, and benchmarks for {topic}",
    agent=technical_analyst  # Another specialized agent
)

# Then a synthesis task combines both streams
synthesis_task = Task(
    description="Combine market and technical research into a unified brief",
    agent=lead_researcher,
    context=[market_research_task, technical_research_task]
)

Complete Working Example

Below is a consolidated, runnable example combining all concepts. Save this as main.py and run it after installing dependencies:

# main.py — Complete Research Team Pipeline
import os
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool, ScrapeWebsiteTool

# Configure API keys (set in environment or .env file)
os.environ["SERPER_API_KEY"] = os.getenv("SERPER_API_KEY", "your-serper-key")
os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY", "your-openai-key")

# --- TOOLS ---
search_tool = SerperDevTool()
scrape_tool = ScrapeWebsiteTool()

# --- AGENTS ---
researcher = Agent(
    role="Senior Research Analyst",
    goal="Produce thorough, well-sourced research briefs on any given topic",
    backstory=(
        "You are a meticulous research analyst with deep expertise in technology, "
        "business, and science. You have access to web search and scraping tools. "
        "You always cite sources, cross-reference claims, and structure findings "
        "in clear, skimmable formats. You never plagiarize — you synthesize."
    ),
    tools=[search_tool, scrape_tool],
    verbose=True,
    allow_delegation=False,
    llm="gpt-4o"
)

writer = Agent(
    role="Senior Writer and Content Strategist",
    goal="Craft compelling, accurate, and audience-tailored content from research materials",
    backstory=(
        "You are a seasoned writer with a knack for turning dense research into "
        "engaging narratives. You adapt tone effortlessly — from academic to casual — "
        "and always structure articles for readability with clear headings, "
        "logical flow, and memorable conclusions."
    ),
    verbose=True,
    allow_delegation=False,
    llm="gpt-4o"
)

editor = Agent(
    role="Lead Editor",
    goal="Ensure every piece of content meets the highest standards of accuracy and clarity",
    backstory=(
        "You are a detail-oriented editor who has polished thousands of articles "
        "for major publications. You catch factual errors, improve sentence flow, "
        "enforce style consistency, and provide transparent edit notes."
    ),
    tools=[search_tool],
    verbose=True,
    allow_delegation=True,
    llm="gpt-4o"
)

# --- TASKS ---
research_task = Task(
    description=(
        "Research the following topic thoroughly: {topic}\n\n"
        "Cover these dimensions:\n"
        "- Origins and historical development\n"
        "- Current landscape: major players, technologies, adoption rates\n"
        "- Key statistics and data points (with sources)\n"
        "- Expert opinions and competing viewpoints\n"
        "- Future projections and emerging trends\n\n"
        "Use at least 6 distinct, credible sources. Structure output as a "
        "detailed research brief with labeled sections and inline citations."
    ),
    expected_output=(
        "A comprehensive research brief (1500+ words) with sections: Overview, "
        "Historical Context, Current State, Key Data & Statistics, Expert Perspectives, "
        "Future Outlook, and References (with URLs)."
    ),
    agent=researcher,
    async_execution=False
)

writing_task = Task(
    description=(
        "Write a polished article based on the research brief provided in context.\n\n"
        "Topic: {topic}\n"
        "Target audience: {audience}\n"
        "Desired tone: {tone}\n"
        "Target length: approximately {length} words\n\n"
        "Requirements:\n"
        "- Engaging title and opening hook\n"
        "- Clear section structure with descriptive headings\n"
        "- Concrete data points and examples from the research\n"
        "- Consistent tone matched to the target audience\n"
        "- Compelling conclusion with a forward-looking perspective"
    ),
    expected_output=(
        "A complete article draft in markdown, meeting the specified length, "
        "with title, introduction, body sections, and conclusion."
    ),
    agent=writer,
    context=[research_task],
    async_execution=False
)

editing_task = Task(
    description=(
        "Edit the draft article provided in context for publication readiness.\n\n"
        "Steps:\n"
        "1. Verify all factual claims against the original research brief\n"
        "2. Correct grammar, punctuation, and stylistic issues\n"
        "3. Improve clarity, flow, and transitions where needed\n"
        "4. Ensure consistent tone ({tone}) for audience ({audience})\n"
        "5. Output the final edited article\n"
        "6. Add an editor's note summarizing changes made"
    ),
    expected_output=(
        "Final edited article in markdown, followed by an italicized editor's note "
        "listing changes, corrections, and a quality assessment."
    ),
    agent=editor,
    context=[research_task, writing_task],
    async_execution=False,
    output_file="final_article.md"
)

# --- CREW ---
crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, writing_task, editing_task],
    process=Process.sequential,
    verbose=True,
    memory=True,
    planning=True
)

# --- EXECUTION ---
if __name__ == "__main__":
    result = crew.kickoff(inputs={
        "topic": "The rise of AI-powered coding assistants and their impact on software developer productivity",
        "audience": "software engineering directors and VP-level technical leaders",
        "tone": "insightful and data-driven",
        "length": "2200"
    })
    
    print("\n" + "="*60)
    print("RESEARCH TEAM PIPELINE COMPLETE")
    print("="*60)
    print(f"\nFinal article saved to 'final_article.md'")
    print(f"\nPreview of final output:\n{result[:500]}...")

Conclusion

Building a research team with CrewAI transforms how you produce high-quality, well-sourced content. By distributing the research, writing, and editing responsibilities across specialized AI agents, you create a pipeline that is consistent, auditable, and scalable. The framework's support for tool integration, context chaining, and flexible process modes gives you fine-grained control while handling the orchestration complexity automatically.

The key to success lies in thoughtful agent design — crafting detailed backstories, assigning appropriate tools, and structuring tasks with clear expected outputs. Start with the sequential process to establish a reliable baseline, then experiment with hierarchical execution for more complex projects. Invest in validation checkpoints, model optimization, and robust error handling as you move toward production. With these patterns in place, your CrewAI research team becomes a reliable engine for turning raw information into polished, publication-ready content.

— Ad —

Google AdSense will appear here after approval

← Back to all articles