How do I see one whole conversation in an agent built with CrewAI?
Rius shows one whole CrewAI conversation as a session: wrap each kickoff in rius.session() with your own conversation id, and every trace from that conversation shares it. The integration records the model calls on its own, and you add observe wrappers for the run and its tools. You then open the conversation from the session link in a trace header, or by pasting the id into the search box on the Spans tab.
How it works
Each crew.kickoff() produces one trace. With no session id, every trace is its own session, so a multi-turn conversation appears as separate, unrelated traces. A session id on each kickoff ties them together.
What Rius records on its own
- Every model call CrewAI makes arrives as an
LLMgeneration with model, tokens, cost and time to first token. CrewAI drives the OpenAI client, so theopenaiintegration captures these calls. If LiteLLM is installed, CrewAI uses it instead, and thelitellmintegration captures them. - Prompts and replies are captured by default, so the conversation text is on each generation.
What needs something of your own
- The run itself: wrap your entrypoint around
crew.kickoff()withobserveandkind=SpanKind.AGENT. That becomes the root span of the trace. - Each tool: put
observewithkind=SpanKind.TOOLon the tool body, under@tool. These show up asTOOLspans. - The session: scope each kickoff with
rius.session(), reusing your own conversation id. - Crews and tasks produce no spans of their own, so which task a generation belongs to is not visible. Wrap per-task entrypoints with
observeif you need that.
Generation spans are all named ChatCompletion, so tell them apart by position and content, not by name.
Set it up
- Install the SDK with the
openaiextra next to CrewAI. The full setup is on the CrewAI integration page, and the Integrations overview covers the other frameworks.
pip install "glassflow-rius[openai]" crewai- Initialise Rius, trace your tools with the TOOL kind, and wrap the entrypoint as an
AGENTspan.@toolstays outermost andobservegoes on the function body.
import rius
from crewai import Agent, Crew, Task
from crewai.tools import tool
from rius import SpanKind
rius.init(api_key="ri_...", 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())- Call
handle_ticketinsiderius.session()for every turn of the same conversation, using the same id each time. That id is what groups the traces. - Set
CREWAI_TRACING_ENABLED=falsewhere no terminal is attached, such as CI or containers, because CrewAI asks about its own tracing on first run. - If you stream calls, enable usage on the underlying client, or the generations carry no token counts.
What you get
Run a few turns of one conversation, then open the console and read it in these places. The Exploring traces page covers each view in full.
- Session link. Each trace header shows its session. The link opens the Spans tab filtered to that session, so you see every span from every turn.
- Search by id. Paste the conversation id into the search box on the Spans tab to list every span in that session.
- Waterfall. Each turn is a tree: your
AGENTroot,LLMgenerations with tokens inline, and your namedTOOLspans. A run that calls a tool reads as an LLM call, the tool, then a second LLM call that answers. - Input / Output tab. Prompt and completion for each generation. Since previous llm call shows what each step added to the prompt, so a dropped turn or a rewritten system prompt stands out.
- Cost and tokens. Each generation has its tokens and a list-price cost estimate in US dollars, and the trace header sums them.
To watch conversations across your whole agent fleet, see Monitor AI agents in production.
When something else fits
- If you need to know which crew task a model call belongs to, add an
observewrapper per task, because the framework’s crews and tasks produce no spans. - If a turn is a single run with no follow-ups, you do not need a session id, since one trace already shows the whole run.