CrewAICrewAI 1.15 · Python 3.10 to 3.13
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
32 small wins to finish your pathNext lesson

Watching a crew work

verbose=True prints a panel for each step of a run. An event listener lets your own code react to the same steps, which is how you trace a crew once it grows.

A crew with one task hides little. From lesson 9 on, agents call tools and pass work to each other, and you want to see each step. CrewAI gives two ways.

Examplecrew.py, as it stands
from crewai import Agent, Crew, Task
from shop_llm import ShopLLM

agent = Agent(
    role="Support agent",
    goal="Answer customers of a small online shop",
    backstory="You have worked the shop's support desk for years.",
    llm=ShopLLM(model="shop"),
)
task = Task(
    description="Answer the customer: {question}",
    expected_output="One short, friendly sentence.",
    agent=agent,
)
crew = Crew(agents=[agent], tasks=[task])

The same crew, now taking the question as an input rather than holding it in the task.

verbose

Example
crew = Crew(agents=[agent], tasks=[task], verbose=True)
crew.kickoff(inputs={"question": "Where is my order A17?"})

Each panel is one step: the crew starting, the task starting, the agent at work, its final answer, the task completing, the crew completing. The ids change on every run. Verbose output is for reading; code cannot use it.

An event listener

CrewAI emits an event at each of those steps on a shared event bus. A listener is a class that subscribes to the events it cares about.

Examplewatch.py
from crewai.events import BaseEventListener, TaskCompletedEvent, TaskStartedEvent


class Watch(BaseEventListener):
    def setup_listeners(self, bus):
        @bus.on(TaskStartedEvent)
        def started(source, event):
            print("task started:", event.task.description)

        @bus.on(TaskCompletedEvent)
        def finished(source, event):
            print("task finished:", event.output.raw)

setup_listeners receives the bus, and @bus.on registers a function for one event type. Each function gets the object that emitted the event and the event itself, which carries the task, its output, and more.

Example
watch = Watch()
crew.kickoff(inputs={"question": "Where is my order A17?"})
crew.kickoff(inputs={"question": "Is C40 in stock?"})

Creating an instance is what registers it, and it stays registered for every crew in the program. Both runs are traced. There are dozens of event types, for tools, flows, memory and more, each carrying the details of its step.

Try it yourself
  • Import CrewKickoffCompletedEvent, subscribe to it and print event.output.raw.
  • Print event.task.agent.role in started.
  • Set agent.verbose = True as well as the crew's and compare the output.

You understood something today that you didn't yesterday.