> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://respan.ai/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://respan.ai/_mcp/server.

# CrewAI (tracing)

> Trace CrewAI workflows with canonical task, agent, tool, and chat spans, including model and token usage.

[CrewAI](https://www.crewai.com/) is a framework for orchestrating role-playing autonomous AI agents. Respan's first-party `respan-instrumentation-crewai` listener subscribes to CrewAI's official lifecycle events and emits the canonical workflow, task, agent, tool, and chat span hierarchy directly. Chat spans include the model, provider, prompt, completion, and provider-reported token usage.

**Complete first-party tracing requires:** `respan-instrumentation-crewai` 0.2.0 or later and `crewai` 1.10.1 or later. Earlier 0.1.x instrumentation releases can export workflow structure without a chat child span or token usage.

#### Set up Respan

Create an account at [platform.respan.ai](https://platform.respan.ai) and grab an [API key](https://platform.respan.ai/platform/api/api-keys).

Run `npx @respan/cli setup` to set up with your coding agent.

#### Use Respan Gateway

See [CrewAI gateway setup](/docs/gateway/crewai) to route this integration through the Respan gateway.

#### Example projects

* [Python examples](https://github.com/respanai/respan-example-projects/tree/main/python/tracing/crewai)

## Setup

#### Install packages

```bash
pip install respan-ai "respan-instrumentation-crewai>=0.2.0" "crewai>=1.10.1"
```

#### Set environment variables

```bash
export OPENAI_API_KEY="YOUR_OPENAI_API_KEY"
export RESPAN_API_KEY="YOUR_RESPAN_API_KEY"
```

`OPENAI_API_KEY` is used for LLM requests. `RESPAN_API_KEY` is used to export traces to Respan.

#### Initialize and run

Initialize Respan before importing CrewAI so the listener is active before CrewAI emits lifecycle events.

```python
from respan import Respan
from respan_instrumentation_crewai import CrewAIInstrumentor

respan = Respan(instrumentations=[CrewAIInstrumentor()])

from crewai import Agent, Crew, Task

researcher = Agent(
    role="Researcher",
    goal="Research and summarize the latest AI trends",
    backstory="You are a senior AI researcher with years of experience.",
)

writer = Agent(
    role="Writer",
    goal="Write a concise report based on the research",
    backstory="You are a technical writer who excels at clear communication.",
)

research_task = Task(
    description="Research the latest trends in AI agent frameworks.",
    expected_output="A summary of key trends and developments.",
    agent=researcher,
)

write_task = Task(
    description="Write a brief report based on the research findings.",
    expected_output="A well-structured report in markdown format.",
    agent=writer,
)

crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task],
)

result = crew.kickoff()
print(result)
```

#### View your trace

Open the [Traces page](https://platform.respan.ai/platform/traces) to see your CrewAI workflow with agent spans, task execution, tool usage, and LLM calls with provider-reported token usage.

The coordinated backend release includes CrewAI LLM spans classified as `chat` in `llm_call_count`. Historical materialized aggregate rows are not backfilled, so traces aggregated before that release can retain their previous summary count; the LLM child span and its model, prompt, completion, and token fields remain the source of truth.

## Configuration

| Parameter             | Type           | Default | Description                                                          |
| --------------------- | -------------- | ------- | -------------------------------------------------------------------- |
| `api_key`             | `str \| None`  | `None`  | Falls back to `RESPAN_API_KEY` env var.                              |
| `base_url`            | `str \| None`  | `None`  | Falls back to `RESPAN_BASE_URL` env var.                             |
| `instrumentations`    | `list`         | `[]`    | Plugin instrumentations to activate, such as `CrewAIInstrumentor()`. |
| `customer_identifier` | `str \| None`  | `None`  | Default customer identifier for all spans.                           |
| `metadata`            | `dict \| None` | `None`  | Default metadata attached to all spans.                              |
| `environment`         | `str \| None`  | `None`  | Environment tag (e.g. `"production"`).                               |

`CrewAIInstrumentor()` takes no configuration. Activation and deactivation are idempotent, and the listener uses CrewAI's official event bus to emit canonical spans directly.

## LLM usage and cost

On the supported baseline, CrewAI tracing records the model, prompt, completion, and provider-reported prompt, completion, and total token counts. The instrumentation does not emit a direct cost. Respan can derive cost when the model is in its pricing catalog; custom or unknown models can show zero cost until custom pricing is configured.

## Attributes

### In Respan()

Set defaults at initialization. These apply to all spans.

```python
from respan import Respan
from respan_instrumentation_crewai import CrewAIInstrumentor

respan = Respan(
    instrumentations=[CrewAIInstrumentor()],
    customer_identifier="user_123",
    metadata={"service": "crew-api", "version": "1.0.0"},
)
```

### With propagate\_attributes

Override per-request using a context scope.

```python
from respan import Respan, propagate_attributes
from respan_instrumentation_crewai import CrewAIInstrumentor

respan = Respan(instrumentations=[CrewAIInstrumentor()])

from crewai import Agent, Crew, Task

def handle_request(user_id: str, topic: str):
    with propagate_attributes(
        customer_identifier=user_id,
        thread_identifier="conv_abc_123",
        metadata={"plan": "pro"},
    ):
        researcher = Agent(
            role="Researcher",
            goal=f"Research {topic}",
            backstory="Expert researcher.",
        )
        task = Task(
            description=f"Research {topic}",
            expected_output="Summary",
            agent=researcher,
        )
        crew = Crew(agents=[researcher], tasks=[task])
        print(crew.kickoff())
```

| Attribute             | Type   | Description                                           |
| --------------------- | ------ | ----------------------------------------------------- |
| `customer_identifier` | `str`  | Identifies the end user in Respan analytics.          |
| `thread_identifier`   | `str`  | Groups related messages into a conversation.          |
| `metadata`            | `dict` | Custom key-value pairs. Merged with default metadata. |

## Decorators (optional)

Decorators are not required. On the supported baseline, each CrewAI kickoff automatically emits a workflow with task, agent, tool, and chat descendants. Use `@workflow` and `@task` to add structure when you want to group related crews into a named workflow with nested tasks.

```python
from respan import Respan, workflow, task
from respan_instrumentation_crewai import CrewAIInstrumentor

respan = Respan(instrumentations=[CrewAIInstrumentor()])

from crewai import Agent, Crew, Task

@task(name="run_research_crew")
def run_research_crew(topic: str) -> str:
    researcher = Agent(
        role="Researcher",
        goal=f"Research {topic}",
        backstory="Expert.",
    )
    research_task = Task(
        description=f"Research {topic}",
        expected_output="Findings.",
        agent=researcher,
    )
    crew = Crew(agents=[researcher], tasks=[research_task])
    return str(crew.kickoff())

@workflow(name="content_pipeline")
def pipeline(topic: str):
    findings = run_research_crew(topic)
    print(findings)

pipeline("AI agent frameworks")
```

## Examples

### Tool calls

Tool calls are automatically captured as spans with inputs, outputs, and timing.

```python
from respan import Respan
from respan_instrumentation_crewai import CrewAIInstrumentor

respan = Respan(instrumentations=[CrewAIInstrumentor()])

from crewai import Agent, Crew, Task
from crewai.tools import tool

@tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    return f"Sunny, 22C in {city}"

researcher = Agent(
    role="City Researcher",
    goal="Gather weather data for a city",
    backstory="You collect city data using available tools.",
    tools=[get_weather],
)

task = Task(
    description="Research the weather in Paris.",
    expected_output="Weather data for Paris.",
    agent=researcher,
)

crew = Crew(agents=[researcher], tasks=[task])
print(crew.kickoff())
```