Become an Agentic Architect

Assembling Agents into a Crew

A crew in CrewAI is a collaborative group of agents working together to complete one or more tasks following a defined process. As an Agentic Architect, you design the crew so that roles, tasks, and execution flow match a real business workflow.


1. What a Crew Is

  • A crew bundles three things: a list of agents, a list of tasks, and a process that defines how those tasks run (e.g., sequential or hierarchical).
  • The crew is the unit you kick off to run an entire workflow end‑to‑end, and its final output is the output of the last task.
  • Crews can also share memory, tools, and logging, so you manage behavior at the team level, not just at the single‑agent level.

2. Core Crew Attributes

When you instantiate a Crew, you combine agents and tasks plus several optional controls.

Key attributes:

  • tasks: List of Task objects the crew will execute.
  • agents: List of agents that belong to this crew.
  • process(optional): Execution strategy, usually Process.sequential or Process.hierarchical (default is sequential).
  • verbose(optional): Enables detailed logs during execution.
  • manager_llm / manager_agent (optional): Required for hierarchical crews, used by the manager to delegate and validate tasks.
  • max_rpm (optional): Rate limit for the whole crew.
  • memory(optional): Shared memory (short‑term, long‑term, entity) across tasks.
  • cache(optional): Whether to cache tool results for efficiency (default True).
  • embedder(optional): Shared embedder config, mainly for memory.
  • step_callback / task_callback (optional): Hooks called after each agent step or after each task; useful for logging, analytics, or side‑effects.
  • knowledge_sources (optional): Shared knowledge sources accessible to all agents in the crew.
  • stream(optional): Enable streaming output for real‑time updates during execution.
  • output_log_file (optional): Save logs as .txt or .json for later inspection.
  • planning / planning_llm (optional): Enable an AgentPlanner to create a plan across tasks before each iteration.

These parameters let you move from a simple demo crew to a production‑ready team with observability and controls.


3. Assembling a Crew with CrewBase and Decorators (YAML‑First)

The recommended pattern is to keep agents and tasks in YAML, then assemble them in a Python class that inherits from CrewBase.

3.1 Crew class structure

# Python Code

from crewai import Agent, Crew, Task, Process
from crewai.project import CrewBase, agent, task, crew, before_kickoff, after_kickoff
from crewai.agents.agent_builder.base_agent import BaseAgent
from typing import List

@CrewBase
class YourCrewName:
    """Description of your crew"""

    agents: List[BaseAgent]
    tasks: List[Task]

    # YAML configuration files
    agents_config = "config/agents.yaml"
    tasks_config = "config/tasks.yaml"

    @before_kickoff
    def prepare_inputs(self, inputs):
        inputs["additional_data"] = "Some extra information"
        return inputs

    @after_kickoff
    def process_output(self, output):
        output.raw += "\nProcessed after kickoff."
        return output

    @agent
    def agent_one(self) -> Agent:
        return Agent(
            config=self.agents_config["agent_one"],
            verbose=True
        )

    @agent
    def agent_two(self) -> Agent:
        return Agent(
            config=self.agents_config["agent_two"],
            verbose=True
        )

    @task
    def task_one(self) -> Task:
        return Task(
            config=self.tasks_config["task_one"]
        )

    @task
    def task_two(self) -> Task:
        return Task(
            config=self.tasks_config["task_two"]
        )

    @crew
    def crew(self) -> Crew:
        return Crew(
            agents=self.agents,   # auto‑collected from @agent
            tasks=self.tasks,     # auto‑collected from @task
            process=Process.sequential,
            verbose=True,
        )

Then you run:

YourCrewName().crew().kickoff(inputs={"any": "input here"})

Key ideas:

  • @CrewBase turns the class into a crew container with automatic wiring.
  • @agent methods define agents and are automatically collected into self.agents.
  • @task methods define tasks and are automatically collected into self.tasks.
  • @crew returns the final Crew object using those agents and tasks.
  • @before_kickoff and @after_kickoff provide hooks to pre‑process inputs and post‑process outputs without changing task logic.

4. Assembling a Crew Directly in Code (Alternative)

You can also define everything imperatively in plain Python.

from crewai import Agent, Crew, Task, Process
from crewai_tools import YourCustomTool

class YourCrewName:
    def agent_one(self) -> Agent:
        return Agent(
            role="Data Analyst",
            goal="Analyze data trends in the market",
            backstory="An experienced data analyst with a background in economics",
            verbose=True,
            tools=[YourCustomTool()]
        )

    def agent_two(self) -> Agent:
        return Agent(
            role="Market Researcher",
            goal="Gather information on market dynamics",
            backstory="A diligent researcher with a keen eye for detail",
            verbose=True
        )

    def task_one(self) -> Task:
        return Task(
            description="Collect recent market data and identify trends.",
            expected_output="A report summarizing key trends in the market.",
            agent=self.agent_one()
        )

    def task_two(self) -> Task:
        return Task(
            description="Research factors affecting market dynamics.",
            expected_output="An analysis of factors influencing the market.",
            agent=self.agent_two()
        )

    def crew(self) -> Crew:
        return Crew(
            agents=[self.agent_one(), self.agent_two()],
            tasks=[self.task_one(), self.task_two()],
            process=Process.sequential,
            verbose=True
        )

# Run the crew
YourCrewName().crew().kickoff(inputs={})

Use this approach when

  • You want maximum control in code (dynamic agents/tasks).
  • You’re rapidly prototyping before extracting things to YAML.

5. Running, Observing, and Replaying a Crew

Once agents and tasks are assembled into a crew, you manage execution and inspection via the CrewOutput, logs, metrics, and replay tools.

5.1 Kicking off a crew

  • kickoff(): Run once with a single inputs dict.
  • kickoff_for_each(): Run the same crew for a list of inputs (e.g., multiple topics).
  • Async variants: akickoff, akickoff_for_each, kickoff_async, kickoff_for_each_async for high‑concurrency workloads.

Example:

# Python Code

result = my_crew.kickoff()
print(result)  # CrewOutput

5.2 Reading CrewOutput

CrewOutput gives you several views of the result:

  • raw (str): Final output text (from the last task).
  • json_dict (dict, optional): JSON representation if structured output is enabled.
  • pydantic (BaseModel, optional): Pydantic object when using Pydantic output.
  • tasks_output (list): Individual TaskOutput objects for each task in the crew.
  • token_usage (dict): Token‑usage stats for the whole run.

You can also call json() or to_dict() for serialization.

5.3 Logs, metrics and streaming

  • output_log_file=True or a filename saves crew logs to logs.txt or a custom .txt / .json file.
  • usage_metrics attribute on the crew shows aggregated LLM usage across tasks.
  • stream=True enables streaming; you iterate over chunks and then access streaming.result for the final CrewOutput.

5.4 Replay from a specific task

  • CLI commands:

    • crewai log-tasks-outputs – list task IDs from the latest run.
    • crewai replay -t <task_id> – replay from that task while preserving previous context

Replay is extremely valuable in a course setting to show debugging and iterative refinement of a single step in a complex workflow.


6. Design Checklist: Assembling a Good Crew

Before you consider a crew “ready,” verify:

  1. Agents: Each important skill is covered by a distinct agent role/goal/backstory.
  2. Tasks: Every major step in the workflow is captured as a task with clear description and expected_output.
  3. Process: Process.sequential vs Process.hierarchical matches how decisions should flow. (see Lesson 6)
  4. Hooks: before_kickoff / after_kickoff are used if you need input enrichment or output post‑processing.
  5. Observability: verbose, output_log_file, usage_metrics, and optionally stream are configured for debugging and teaching.

This is the mental model you want your learners to internalize: agents are specialists, tasks are assignments, and the crew is the team plus its playbook for execution.