← Back to DevBytes

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

Building a Research Team with CrewAI

CrewAI is an open-source framework that orchestrates multiple AI agents to collaborate on complex tasks. Instead of relying on a single LLM to do everything, you assemble a team of specialized agents — each with a distinct role, goal, and backstory — and let them work together. This tutorial walks you through building a complete research team composed of Researchers, Writers, and Editors using CrewAI, from initial setup to production-ready patterns.

What Is a Research Team in CrewAI?

A research team in CrewAI is a crew of AI agents that collectively tackle knowledge-intensive projects. Each agent is configured with:

When you create a crew, you define a process (sequential or hierarchical) that governs how agents interact and hand off work. For a research team, the typical flow is: Researcher gathers data → Writer synthesizes findings → Editor refines the final output.

Why a Multi-Agent Research Team Matters

Single-agent prompts often fail on deep research tasks because one model must simultaneously plan, search, synthesize, and polish — leading to shallow coverage, hallucinations, or inconsistent tone. Splitting these responsibilities across specialized agents brings several advantages:

The result is higher-quality deliverables, better factuality, and a dramatically lower cognitive load on any single LLM call.

Prerequisites and Installation

Before building the team, install CrewAI and its dependencies. You'll also need an API key for your chosen LLM provider (OpenAI, Anthropic, Ollama, etc.).

pip install crewai crewai-tools

Set your API key as an environment variable:

export OPENAI_API_KEY="sk-your-key-here"

For web search capabilities, install the Serper tool (requires a free API key from serper.dev):

pip install 'crewai[tools]'

Step 1: Define the Researcher Agent

The Researcher is responsible for gathering factual information, verifying sources, and compiling raw findings. It needs search tools and a curious, analytical backstory.

from crewai import Agent
from crewai_tools import SerperDevTool

# Give the researcher a search tool
search_tool = SerperDevTool()

researcher = Agent(
    role="Senior Research Analyst",
    goal="Uncover cutting-edge information and verify facts about the given topic",
    backstory=(
        "You are a veteran research analyst with 20 years of experience at a top-tier think tank. "
        "You are skeptical by nature and always cross-reference multiple sources before accepting a claim. "
        "Your reports are dense, thorough, and never leave a question unanswered. "
        "You despise vague statements and demand concrete data points, citations, and examples."
    ),
    tools=[search_tool],
    llm="gpt-4-turbo",
    verbose=True,
    allow_delegation=False  # Researchers don't delegate — they gather
)

Step 2: Define the Writer Agent

The Writer takes the Researcher's raw findings and transforms them into engaging, well-structured prose. It does not need search tools — it relies entirely on the Researcher's output.

writer = Agent(
    role="Senior Content Writer",
    goal="Transform raw research findings into compelling, accessible, and well-structured prose",
    backstory=(
        "You are an award-winning science and technology writer who has written for "
        "publications like The New Yorker, Wired, and The Atlantic. "
        "You excel at taking dense, technical material and making it engaging for a general audience "
        "without sacrificing accuracy. You structure articles with clear introductions, logical flow, "
        "and memorable conclusions. You never plagiarize and always synthesize information in your own voice."
    ),
    tools=[],  # Writer relies on researcher output, no external tools needed
    llm="gpt-4-turbo",
    verbose=True,
    allow_delegation=False
)

Step 3: Define the Editor Agent

The Editor reviews the Writer's draft for factual accuracy, grammar, tone consistency, and adherence to style guidelines. It acts as the final quality gate.

editor = Agent(
    role="Chief Editor and Fact-Checker",
    goal="Ensure the final article is factually accurate, grammatically flawless, and stylistically consistent",
    backstory=(
        "You are the chief editor at a prestigious publishing house with a reputation for "
        "meticulous accuracy. You have caught errors that others missed for 30 years. "
        "You review every sentence for factual correctness, logical coherence, and adherence "
        "to the house style guide. You are ruthless about cutting fluff, fixing passive voice, "
        "and ensuring every claim is backed by a source. You provide constructive, specific feedback."
    ),
    tools=[search_tool],  # Editor can independently verify facts
    llm="gpt-4-turbo",
    verbose=True,
    allow_delegation=True  # Editor can send work back if needed
)

Step 4: Create Tasks with Clear Deliverables

Tasks in CrewAI define the work each agent performs. Each task has a description, an expected output, and is assigned to a specific agent. Tasks flow sequentially by default.

from crewai import Task

research_task = Task(
    description=(
        "Conduct a comprehensive research investigation on the topic: {topic}\n\n"
        "Your research MUST cover:\n"
        "1. The historical background and context of the topic\n"
        "2. Key facts, statistics, and data points from credible sources\n"
        "3. Current developments and recent news (within the last 6 months)\n"
        "4. Expert opinions and differing perspectives\n"
        "5. Potential future implications or trends\n\n"
        "Compile your findings into a structured research brief with clear sections "
        "and source attributions. Include URLs where appropriate."
    ),
    expected_output=(
        "A detailed research brief organized into 5 clearly labeled sections: "
        "Background, Key Facts, Current Developments, Expert Perspectives, and Future Outlook. "
        "Each section should contain bullet points with source attributions. "
        "The brief should be 1500-2000 words of dense, factual content."
    ),
    agent=researcher
)

writing_task = Task(
    description=(
        "Using the research brief provided by the Senior Research Analyst, "
        "write a polished, engaging article for a general audience.\n\n"
        "Guidelines:\n"
        "- Open with a compelling hook that draws readers in\n"
        "- Structure the article with clear subheadings\n"
        "- Explain complex concepts in accessible language\n"
        "- Maintain a neutral, informative tone\n"
        "- Include specific data points and examples from the research\n"
        "- End with a thoughtful conclusion\n"
        "- Target length: 1200-1500 words"
    ),
    expected_output=(
        "A complete, publication-ready article draft with an engaging title, "
        "introduction, body sections with subheadings, and a conclusion. "
        "The article should be 1200-1500 words, written in clear English, "
        "with all factual claims traceable to the research brief."
    ),
    agent=writer,
    context=[research_task]  # Writer receives the researcher's output
)

editing_task = Task(
    description=(
        "Review the article draft provided by the Senior Content Writer. "
        "Your job is to:\n\n"
        "1. Fact-check every claim against the original research brief and external sources\n"
        "2. Correct any grammatical errors, typos, or awkward phrasing\n"
        "3. Ensure the tone is consistent and appropriate for the target audience\n"
        "4. Verify that all statistics and data points are accurately transcribed\n"
        "5. Flag any sections that need rewriting and provide specific feedback\n"
        "6. Produce the final, polished version of the article\n\n"
        "If you find major issues, use your delegation authority to request revisions."
    ),
    expected_output=(
        "A final, publication-ready article that is factually verified, "
        "grammatically perfect, and stylistically consistent. "
        "Include a brief editor's note summarizing the changes made. "
        "If significant revisions were needed, describe them in detail."
    ),
    agent=editor,
    context=[research_task, writing_task]  # Editor has access to both prior outputs
)

Step 5: Assemble and Run the Crew

Bring the agents and tasks together into a Crew. The process type determines execution order. For research teams, sequential is the most reliable pattern.

from crewai import Crew, Process

research_crew = Crew(
    agents=[researcher, writer, editor],
    tasks=[research_task, writing_task, editing_task],
    process=Process.sequential,
    verbose=True,
    memory=True  # Retains context across the full pipeline
)

# Execute the crew with a specific topic
result = research_crew.kickoff(inputs={"topic": "The impact of quantum computing on cybersecurity"})

print("=== FINAL OUTPUT ===")
print(result)

The memory=True flag is critical — it ensures the crew retains intermediate outputs and can reference them across tasks. Without it, later agents may lose access to earlier findings.

Complete End-to-End Script

Here's the full script combining everything above. Save it as research_team.py and run it directly.

# research_team.py
import os
from crewai import Agent, Task, Crew, Process
from crewai_tools import SerperDevTool

# --- Configuration ---
os.environ["OPENAI_API_KEY"] = "sk-your-key-here"
os.environ["SERPER_API_KEY"] = "your-serper-key-here"

search_tool = SerperDevTool()

# --- Agents ---
researcher = Agent(
    role="Senior Research Analyst",
    goal="Uncover cutting-edge information and verify facts about the given topic",
    backstory=(
        "You are a veteran research analyst with 20 years of experience at a top-tier think tank. "
        "You are skeptical by nature and always cross-reference multiple sources before accepting a claim. "
        "Your reports are dense, thorough, and never leave a question unanswered. "
        "You despise vague statements and demand concrete data points, citations, and examples."
    ),
    tools=[search_tool],
    llm="gpt-4-turbo",
    verbose=True,
    allow_delegation=False
)

writer = Agent(
    role="Senior Content Writer",
    goal="Transform raw research findings into compelling, accessible, and well-structured prose",
    backstory=(
        "You are an award-winning science and technology writer who has written for "
        "publications like The New Yorker, Wired, and The Atlantic. "
        "You excel at taking dense, technical material and making it engaging for a general audience "
        "without sacrificing accuracy. You structure articles with clear introductions, logical flow, "
        "and memorable conclusions. You never plagiarize and always synthesize information in your own voice."
    ),
    tools=[],
    llm="gpt-4-turbo",
    verbose=True,
    allow_delegation=False
)

editor = Agent(
    role="Chief Editor and Fact-Checker",
    goal="Ensure the final article is factually accurate, grammatically flawless, and stylistically consistent",
    backstory=(
        "You are the chief editor at a prestigious publishing house with a reputation for "
        "meticulous accuracy. You have caught errors that others missed for 30 years. "
        "You review every sentence for factual correctness, logical coherence, and adherence "
        "to the house style guide. You are ruthless about cutting fluff, fixing passive voice, "
        "and ensuring every claim is backed by a source. You provide constructive, specific feedback."
    ),
    tools=[search_tool],
    llm="gpt-4-turbo",
    verbose=True,
    allow_delegation=True
)

# --- Tasks ---
research_task = Task(
    description=(
        "Conduct a comprehensive research investigation on the topic: {topic}\n\n"
        "Your research MUST cover:\n"
        "1. The historical background and context of the topic\n"
        "2. Key facts, statistics, and data points from credible sources\n"
        "3. Current developments and recent news (within the last 6 months)\n"
        "4. Expert opinions and differing perspectives\n"
        "5. Potential future implications or trends\n\n"
        "Compile your findings into a structured research brief with clear sections "
        "and source attributions. Include URLs where appropriate."
    ),
    expected_output=(
        "A detailed research brief organized into 5 clearly labeled sections: "
        "Background, Key Facts, Current Developments, Expert Perspectives, and Future Outlook. "
        "Each section should contain bullet points with source attributions. "
        "The brief should be 1500-2000 words of dense, factual content."
    ),
    agent=researcher
)

writing_task = Task(
    description=(
        "Using the research brief provided by the Senior Research Analyst, "
        "write a polished, engaging article for a general audience.\n\n"
        "Guidelines:\n"
        "- Open with a compelling hook that draws readers in\n"
        "- Structure the article with clear subheadings\n"
        "- Explain complex concepts in accessible language\n"
        "- Maintain a neutral, informative tone\n"
        "- Include specific data points and examples from the research\n"
        "- End with a thoughtful conclusion\n"
        "- Target length: 1200-1500 words"
    ),
    expected_output=(
        "A complete, publication-ready article draft with an engaging title, "
        "introduction, body sections with subheadings, and a conclusion. "
        "The article should be 1200-1500 words, written in clear English, "
        "with all factual claims traceable to the research brief."
    ),
    agent=writer,
    context=[research_task]
)

editing_task = Task(
    description=(
        "Review the article draft provided by the Senior Content Writer. "
        "Your job is to:\n\n"
        "1. Fact-check every claim against the original research brief and external sources\n"
        "2. Correct any grammatical errors, typos, or awkward phrasing\n"
        "3. Ensure the tone is consistent and appropriate for the target audience\n"
        "4. Verify that all statistics and data points are accurately transcribed\n"
        "5. Flag any sections that need rewriting and provide specific feedback\n"
        "6. Produce the final, polished version of the article\n\n"
        "If you find major issues, use your delegation authority to request revisions."
    ),
    expected_output=(
        "A final, publication-ready article that is factually verified, "
        "grammatically perfect, and stylistically consistent. "
        "Include a brief editor's note summarizing the changes made. "
        "If significant revisions were needed, describe them in detail."
    ),
    agent=editor,
    context=[research_task, writing_task]
)

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

# --- Run ---
topic = "The impact of quantum computing on cybersecurity"
result = crew.kickoff(inputs={"topic": topic})

print("\n" + "="*80)
print("FINAL ARTICLE")
print("="*80)
print(result)

Best Practices for Research Crews

1. Write Detailed Backstories

The backstory is not just flavor text — it heavily influences the agent's tone, reasoning style, and output quality. Be specific about the agent's experience level, personality traits, and pet peeves. An agent told it "despises vague statements" will consistently push for concrete data.

2. Use Context Chaining Explicitly

Always pass prior task outputs to downstream tasks using the context parameter. This ensures the Writer actually receives the Researcher's brief rather than just a summary. The Editor should receive both the research brief and the article draft to cross-reference effectively.

writing_task = Task(
    # ...
    context=[research_task]  # Writer sees researcher output
)

editing_task = Task(
    # ...
    context=[research_task, writing_task]  # Editor sees everything
)

3. Set Clear, Measurable Expected Outputs

Vague expected outputs like "a good article" lead to inconsistent results. Instead, specify word counts, required sections, tone requirements, and formatting. The agents use these as concrete targets.

4. Enable Memory for Long Pipelines

The memory=True flag on the Crew stores intermediate outputs and allows agents to reference them across multiple steps. Without it, context can be lost, especially when using stateless LLM calls.

5. Choose the Right Process Type

For most research teams, sequential is simpler and more predictable. Use hierarchical only when you need parallel research on distinct subtopics.

6. Give the Editor Teeth

Set allow_delegation=True on the Editor so it can send work back for revision. Without this, the Editor may produce a final article that includes a note saying "the draft had issues" but without actually fixing them. Delegation enables a genuine review cycle.

7. Match Tools to Roles

Give search tools to the Researcher (primary fact-gatherer) and optionally to the Editor (for verification). The Writer typically doesn't need tools — it works from the research brief. Over-tooling agents increases token usage and can confuse the LLM about which tool to use when.

8. Use Different LLMs Per Agent

You can assign different models to different agents based on their needs. The Researcher might use a model with a large context window for handling many sources, while the Writer might use a more creative model.

researcher = Agent(
    # ...
    llm="gpt-4-turbo"  # Strong reasoning for research
)

writer = Agent(
    # ...
    llm="claude-3-opus"  # Excellent prose generation
)

editor = Agent(
    # ...
    llm="gpt-4-turbo"  # Precise for fact-checking
)

9. Iterate on Task Descriptions

Task descriptions are prompts. Treat them like code — version them, test variations, and refine based on output quality. If the Writer consistently produces articles that are too short, adjust the expected_output word count and add more structural guidance to the description.

10. Handle Errors Gracefully

CrewAI operations can fail due to API rate limits, tool errors, or malformed outputs. Wrap your crew execution in try-except blocks and consider implementing retry logic for production systems.

import time

def run_crew_with_retry(topic, max_retries=3):
    for attempt in range(max_retries):
        try:
            return crew.kickoff(inputs={"topic": topic})
        except Exception as e:
            print(f"Attempt {attempt + 1} failed: {e}")
            if attempt < max_retries - 1:
                time.sleep(5 * (attempt + 1))  # Exponential backoff
            else:
                raise

Extending the Team: Adding Specialists

Once the core Researcher → Writer → Editor pipeline works, you can extend it with additional specialists:

Each new agent follows the same pattern: define role, goal, backstory, tools, and create a task with context linking. The sequential process naturally accommodates additional stages.

Conclusion

Building a research team with CrewAI transforms a single-prompt workflow into a disciplined, multi-stage pipeline that produces higher-quality, better-verified content. The Researcher digs deep, the Writer crafts compelling narrative, and the Editor ensures accuracy and polish — each doing what it does best. By following the patterns in this tutorial — detailed backstories, explicit context chaining, clear expected outputs, and role-appropriate tool assignment — you can build research crews that consistently deliver publication-ready work. Start with the three-agent core, iterate on your task descriptions, and expand with specialists as your needs grow. The result is a repeatable, scalable content engine powered by collaborative AI agents.

— Ad —

Google AdSense will appear here after approval

← Back to all articles