Skip to Content

CrewAI

Trace crews built with CrewAI  with Rius: the model calls the framework makes arrive as generations with tokens and cost, and observe wrappers give the run and its tools their shape. This guide covers the Python framework; the runnable example behind it lives in agent-observability-examples .

How coverage works

CrewAI 1.x drives the OpenAI client natively (it is a hard dependency), so the openai integration captures its model calls. LiteLLM is an optional fallback the framework uses only when it is installed; in that setup, use the litellm integration instead.

As with other frameworks covered through a client-library integration, the crew structure runs inside the framework where auto-instrumentation cannot see it: wrap the entrypoint and the tool bodies with observe.

CrewAI also ships its own tracing product and asks about it interactively on first run; set CREWAI_TRACING_ENABLED=false to keep it off non-interactively (CI, containers).

Setup

pip install "glassflow-rius[openai]" crewai
import rius from crewai import Agent, Crew, Task from crewai.tools import tool from rius import SpanKind rius.init(api_key="gf_...", service_name="my-agent") @tool("account_status") @rius.observe(kind=SpanKind.TOOL) def account_status(email: str) -> str: """Fetch a customer's account standing by email.""" return lookup(email) support_agent = Agent( role="Support specialist", goal="Answer support tickets accurately and concisely.", backstory="You resolve product and account questions.", tools=[account_status], llm="gpt-5-mini", ) @rius.observe(name="handle-ticket", kind=SpanKind.AGENT) def handle_ticket(ticket: str) -> str: task = Task( description=f"Resolve this support ticket: {ticket}", expected_output="A short, direct answer.", agent=support_agent, ) return str(Crew(agents=[support_agent], tasks=[task]).kickoff())

The decorator order on tools matters: @tool outermost, observe on the function body. The TOOL span parents under the run through ambient context.

What lands where

In the frameworkIn the trace
Your entrypoint around crew.kickoff()The root AGENT span (your observe wrapper)
A model callAn LLM generation with model, tokens, cost, TTFT
A toolA TOOL span (your observe wrapper on the body)
Crews and tasksNo spans of their own; see blind spots

A run that calls a tool looks like this (real trace from the example, crewai 1.15.18):

handle-ticket AGENT 8.4s ├─ ChatCompletion LLM 3.9s agent decides to call the tool ├─ search_knowledge_base TOOL 0ms └─ ChatCompletion LLM 4.3s agent answers

Blind spots

Verified against crewai 1.15.18:

  • Crew and task structure produces no spans. Which task a generation belongs to is not visible from the trace; wrap per-task entrypoints with observe if that matters to you.
  • Generation span names are generic (ChatCompletion).
  • First run prompts interactively about CrewAI’s own tracing; set CREWAI_TRACING_ENABLED=false where no terminal is attached.

Sessions

One conversation or job is one session: scope each kickoff with rius.session(), reusing your own id.

Verify

Run one ticket that calls a tool, then open the trace in the console: the root AGENT span, the LLM generations with model, tokens, and cost, and your named TOOL spans should all be there.

Streamed calls need usage enabled on the underlying client to carry token counts; see the OpenAI integration for the include_usage note.

Last updated on