DEV Community

wartzar-bee
wartzar-bee

Posted on

CrewAI's quadratic context problem: why a 5-agent crew costs 6 more than you expect

CrewAI's quadratic context problem: why a 5-agent crew costs 6× more than you expect

Cost-audit series, episode 3. This series began with an AI agent that burned 136M tokens overnight →.


CrewAI is one of the most-starred agent orchestration frameworks on GitHub. Its pitch is intuitive: define a crew of role-playing agents, assign tasks, watch them collaborate. What the README doesn't tell you is that the default context-passing model has a quadratic token cost curve. A 5-agent crew doesn't cost 5× a solo agent — on input tokens it costs closer to 6×, and once you stack memory, delegation, and verbose mode it climbs to 8–15×. The multiplier grows with every agent or task you add.

This audit shows you exactly where the tokens go, with line numbers.


The architecture in one paragraph

CrewAI executes tasks sequentially (by default). Each task has an optional context list — a list of other tasks whose outputs should be available to the executing agent. When you don't specify context explicitly, CrewAI's default behavior is to make all previously completed tasks available to each subsequent agent. The output of Task 1 goes into Task 2's prompt. Task 1 + Task 2 outputs go into Task 3's prompt. And so on.

This is linear accumulation — and it produces a quadratic total token count.


The code that does it

src/crewai/crew.py — the Crew._get_context method (line 792 at tag 0.80.0):

def _get_context(self, task: Task, task_outputs: List[TaskOutput]):
    context = (
        aggregate_raw_outputs_from_tasks(task.context)
        if task.context
        else aggregate_raw_outputs_from_task_outputs(task_outputs)
    )
    return context
Enter fullscreen mode Exit fullscreen mode

Source: src/crewai/crew.py#L792

The decisive branch is the else: when a task sets no explicit context, CrewAI falls back to aggregate_raw_outputs_from_task_outputs(task_outputs) — the outputs of all prior tasks. That aggregation helper (in crewai/utilities) serializes each prior task's full raw output into the string that gets injected into the current task's prompt. There is no summarization, no truncation, no deduplication. The string grows with every task.

src/crewai/crew.py — the task execution loop lives in Crew._execute_tasks (defined at line 635 at tag 0.80.0; the context-injection call is at line 696). Simplified to the two lines that matter:

# inside _execute_tasks, iterating over the crew's tasks:
context = self._get_context(task, task_outputs)   # task_outputs = every prior task's output
task_output = task.execute_sync(
    agent=agent_to_use, context=context, tools=agent_to_use.tools
)
Enter fullscreen mode Exit fullscreen mode

Source: src/crewai/crew.py (method _execute_tasks)

task_outputs is the running list of every prior task's output, so it grows as the crew progresses. Each call to execute_sync constructs a full prompt that includes that accumulated context string, and it is sent with every LLM API request — not cached between tasks (CrewAI doesn't use prompt caching by default).


The math

Assume:

  • N = 5 tasks (a typical research + writing crew)
  • T_task = 500 tokens per task output (modest: a paragraph or a JSON object)
  • T_system = 800 tokens per agent's system prompt + task description (role, goal, backstory, task instructions)

Token count for Task k = T_system + (k-1) × T_task (accumulated context from all prior tasks)

Task Context tokens System + task Total input tokens
1 0 800 800
2 500 800 1,300
3 1,000 800 1,800
4 1,500 800 2,300
5 2,000 800 2,800
Total 5,000 4,000 9,000

A naive model would predict 5 × 800 = 4,000 input tokens. The actual bill is 9,000 — 2.25× for just five tasks with modest outputs. Now scale up:

  • T_task = 2,000 tokens (a realistic research output, a code block, a structured list):
Task Context tokens System + task Total input tokens
1 0 800 800
2 2,000 800 2,800
3 4,000 800 4,800
4 6,000 800 6,800
5 8,000 800 8,800
Total 20,000 4,000 24,000

Now you're at the naive estimate — and that's input tokens only. Add output tokens from each task (another ~2,000 × 5 = 10,000) and the total API cost for one crew run is 34,000 tokens instead of the ~14,000 you'd expect.

With claude-sonnet-4-6 ($3/M input, $15/M output):

  • Naive estimate: (4,000 × $3 + 10,000 × $15) / 1,000,000 = $0.162
  • Actual: (24,000 × $3 + 10,000 × $15) / 1,000,000 = $0.222 — 37% over

Now run 50 crew executions per day:

  • Naive: $0.162 × 50 × 30 = $243/month
  • Actual: $0.222 × 50 × 30 = $333/month

For a SaaS product with hundreds of daily crew runs, this gap becomes tens of thousands of dollars per month — and it gets worse as you add agents or as task outputs grow.


Three multipliers on top of the base cost

1. Memory layers (each one adds tokens)

CrewAI's memory=True flag (off by default, but heavily promoted) activates four memory systems:

# src/crewai/memory/short_term/short_term_memory.py
class ShortTermMemory(Memory):
    def search(self, query: str, score_threshold: float = 0.35):
        return self.storage.search(query=query, score_threshold=score_threshold)
Enter fullscreen mode Exit fullscreen mode

Source: src/crewai/memory/short_term/short_term_memory.py

Short-term memory retrieves every stored item scoring above a similarity threshold (default 0.35) and appends them to the prompt — the count is unbounded, so a longer run history injects more retrieved context per task. Long-term memory hits an SQLite database. Entity memory maintains structured entity descriptions. Semantic memory uses ChromaDB embeddings — each retrieval costs an embedding API call plus the tokens from the retrieved chunks injected into the prompt.

With memory=True on a 10-run crew history, expect +1,500–3,000 tokens per task in retrieval overhead.

2. verbose=True (the default in most tutorials)

Most CrewAI tutorials set verbose=True or verbose=2. This surfaces the agent's intermediate reasoning — but the real cost driver isn't the logging, it's the ReAct loop underneath it. CrewAI runs its own CrewAgentExecutor (not LangChain's), and each agent step accumulates the full "Thought / Action / Observation" trace back into the LLM prompt for the next iteration. Each tool call adds another round of Thought + Action + Observation tokens before the final answer. For an agent that calls 3 tools, this can add 800–2,000 tokens per task.

3. Human delegation (allow_delegation=True)

When an agent can delegate to another, it can route subtasks to specialist agents mid-task. This is CrewAI's "hierarchical" feature. The cost: each delegation creates a new complete LLM call with the delegating agent's accumulated context inherited into the delegatee's prompt. A single task can spawn 2–3 delegation chains, each carrying the full context blob from above.


What a real run looks like

Let's measure a concrete 3-agent research crew using @wartzar-bee/tokenscope:

npx @wartzar-bee/tokenscope crew-session.jsonl
Enter fullscreen mode Exit fullscreen mode

For a crew with:

  • Researcher agent: finds + summarizes 3 sources (outputs ~1,800 tokens)
  • Analyst agent: interprets the research (outputs ~1,200 tokens)
  • Writer agent: produces a structured report (outputs ~2,000 tokens)

The per-agent context accumulation (illustrative — tokenscope reports the session total you can check this against, not a per-agent split):

Per-agent context accumulation:
  researcher  →  input:  1,100  output:  1,800   (system + task only)
  analyst     →  input:  2,900  output:  1,200   (+1,800 context from researcher)
  writer      →  input:  4,100  output:  2,000   (+1,800 + 1,200 context from prior two)

  Total input:    8,100
  Total output:   5,000
  Session total: 13,100 tokens

  Naive estimate (no context accumulation): 8,300 tokens
  Actual multiplier: 1.58×
Enter fullscreen mode Exit fullscreen mode

This is a modest crew. Bump to 5 agents with research-heavy outputs and the multiplier reaches 4–6×. Add memory and delegation: 8–15×.


The fix: explicit context scoping

CrewAI lets you control which tasks feed context to which. Use the context parameter explicitly:

from crewai import Task

research_task = Task(
    description="Find the top 3 cloud cost optimization techniques.",
    agent=researcher,
    # no context — this is the first task
)

analysis_task = Task(
    description="Analyze the techniques and rank by ROI.",
    agent=analyst,
    context=[research_task],  # only research output, not all prior tasks
)

writing_task = Task(
    description="Write a 500-word summary of the #1 technique.",
    agent=writer,
    context=[analysis_task],  # only the analysis — researcher output not needed here
)
Enter fullscreen mode Exit fullscreen mode

Result: the writer agent sees only the analyst's output (~1,200 tokens), not the researcher's raw output + the analyst's output (3,000 tokens). Context tokens halved on the most expensive task.

For longer pipelines, consider a summary task: a cheap, short-output task that condenses prior results, and only its output flows forward. The cost of the summarization step is far less than sending raw outputs through N subsequent agents.


Measuring your own crew

If you're running CrewAI in production, the default LLM logging doesn't surface how much of each call is re-sent accumulated context. Point tokenscope at a session transcript to see it:

npm install -g @wartzar-bee/tokenscope
Enter fullscreen mode Exit fullscreen mode

Export your run as a session JSONL (a Claude Code session under ~/.claude/projects, or any transcript in that format), then:

npx @wartzar-bee/tokenscope crew-session.jsonl
# machine-readable:
npx @wartzar-bee/tokenscope crew-session.jsonl --json
Enter fullscreen mode Exit fullscreen mode

You'll see the session total, how much is new work versus re-sent accumulated context, and where the growth is steepest — the accumulation this whole post is about.


Summary

CrewAI's default context model accumulates all prior task outputs into every subsequent agent's prompt. This produces a quadratic total token count as N grows — not linear. The practical impact at modest scale (5 agents, 2,000-token outputs): 4–6× the token count you'd expect. Add memory layers, delegation, and verbose mode: 8–15×.

The fix is explicit context scoping: pass only the task outputs that each agent actually needs. It's a one-line change per task, and it can halve your API bill immediately.

Next in the series: we'll look at LangGraph's token footprint — the stateful graph model has a different cost shape, and it's worth understanding before you migrate from LangChain to LangGraph chasing efficiency gains.


wartzar-bee builds tools for operating cost-efficient autonomous agents. tokenscope is free and open-source. Follow on dev.to →

Top comments (0)