crewaiinc/crewai

Framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks.

55,831 stars Python 8 components

10 hidden assumptions · 9-stage pipeline · 8 components

Like any codebase, this ml inference makes assumptions it never checks — most are routine. The ones worth your attention are below, in plain language with what to do about each.

Orchestrates teams of AI agents to complete complex tasks collaboratively

Execution begins when a user calls Crew.kickoff(inputs) or Flow.kickoff(inputs). For a Crew: inputs are interpolated into task description templates, then the Crew iterates through tasks sequentially (or routes them via a manager agent in hierarchical mode). For each task, the assigned agent's reasoning loop starts: ContextualMemory assembles a context block from memory stores, the LLM generates a thought and selects a tool call (structured as a function-calling JSON schema), the tool executes and returns a string observation, and the loop repeats until the LLM produces a 'Final Answer'. That answer is parsed into a TaskOutput and stored; subsequent tasks receive prior TaskOutputs as context. After all tasks finish, a CrewOutput is returned. For a Flow: decorated step methods execute in event-order — each method's return value is emitted as an event that triggers listening methods, with shared FlowState threaded through. Checkpoints are written after each step. If a step embeds a Crew, the Crew runs synchronously and its CrewOutput is returned to the Flow step.

Under the hood, the system uses 4 feedback loops, 5 data pools, 5 control points to manage its runtime behavior.

A 8-component ml inference. 1272 files analyzed. Data flows through 9 distinct pipeline stages.

Hidden Assumptions

Most of what this code assumes is routine. These 2 are the ones most likely to cause trouble here — in plain terms, with what to do about each. The rest are minor; they're under "Show everything".

Worth your attention first

The system never checks that your AI provider account credentials (like your OpenAI or Anthropic key) are actually set before starting a run. It only discovers the key is missing when it tries to contact the AI for the very first time — which can be several seconds into a run after a lot of setup work. The error message that appears is technical and doesn't clearly say 'your key is missing.'

What to do: Before starting a long crew run, confirm your AI provider key is set in your environment or config — a quick check in a terminal saves a confusing mid-run failure.

Worth your attention first

When you tell the system you want the AI's answer in a specific structured format (like a neat list or a form with named fields), the system just asks the AI nicely and hopes it complies. If the AI returns a rambling paragraph instead of the structured format, the system quietly records 'nothing' for the structured part and moves on. Anything downstream that expected the structured data gets empty-handed — and the run still looks like it succeeded.

What to do: If your workflow depends on structured output from a task, check that the final result actually contains the data you expected before treating the run as successful — and consider adding a validation step or human review of structured outputs.

Show everything (8 more)
Contract

If you accidentally set up a task to use the results of another task that hasn't run yet (because it comes later in the list), the system won't warn you. It will just give the agent an empty or broken context block and the agent will try to work with nothing, potentially producing completely wrong results that look plausible.

What to do: When defining tasks that depend on each other's results, double-check that any task listed as a dependency always appears earlier in your task list than the task that needs it.

lib/crewai/src/crewai/crew.py:Crew
Environment

When you turn on memory for a crew, the system assumes it can quietly set up a local database and connect to an embedding service (often a separate paid API endpoint) without ever checking first. If something is wrong — wrong permissions on your computer, a missing key, or a full disk — you won't find out until the crew is already running and hits an opaque technical error.

What to do: If you enable memory, do a short test run first to confirm the memory system initializes cleanly before committing to a long production run.

lib/crewai/src/crewai/memory/contextual/contextual_memory.py:ContextualMemory.build_context_for_task
Scale

Each time an AI agent takes an action and observes a result, those results pile up in the conversation the AI is tracking. The system doesn't monitor how large this pile gets. If the agent uses tools that return a lot of text — like web pages or large files — the pile can overflow the AI's memory limit before it finishes the task, causing a crash with a technical error message.

What to do: For tasks that involve tools returning large amounts of text (web scraping, file reading), reduce the number of allowed agent iterations or choose a model with a larger context window.

lib/crewai/src/crewai/agent.py:Agent
Temporal

When a flow is interrupted and you later resume it from a saved checkpoint, the system just restores the saved data and keeps going — it doesn't check whether you changed the workflow definition in the meantime. If you modified the steps or renamed fields, the resumed run can behave incorrectly or crash, possibly without a clear error message.

What to do: After changing a flow's structure or field names, delete any saved checkpoints for that flow and start fresh rather than resuming from an old save.

lib/crewai/src/crewai/flow/flow.py:FlowCheckpointManager
Contract

When an AI agent uses a tool, it generates the tool's input from scratch based on the tool description. If the AI uses a slightly different word for a field name than the tool expects, the tool either fails silently (using a blank default) or errors — and either way the agent may never realize it got bad data, continuing to reason from a wrong result.

What to do: When writing custom tools, use extremely unambiguous field names in the tool schema and include clear descriptions, to reduce the chance the AI guesses a slightly different name.

lib/crewai/src/crewai/tools/base_tool.py:BaseTool.run
Ordering

If the same crew is accidentally started twice — for example, in an automated retry after a failure — the system doesn't notice and just runs again, potentially mixing up results and memory from the two runs. The output might look fine but quietly reflect a mix of two different attempts.

What to do: When building retry logic around a crew run, create a fresh crew instance for each attempt rather than re-using and re-starting the same one.

lib/crewai/src/crewai/crew.py:Crew.kickoff
Domain

Task descriptions can contain fill-in-the-blank slots (like a topic name) that get replaced with values you provide when starting the crew. If you forget to provide a value for one of those slots — or spell it differently — the whole run crashes immediately with a technical key error, without telling you which task had the problem.

What to do: Before a full run, compare the placeholder names in your task descriptions with the keys you're passing in to make sure every slot has an exact matching value.

lib/crewai/src/crewai/crew.py:Crew
Resource

One of the built-in data-fetching tools can wait silently for up to 10 minutes for an external service to prepare data. If your code is running in a cloud environment with a shorter timeout limit (many serverless platforms cut off after 1–5 minutes), the whole run gets killed while waiting, with no warning and nothing saved.

What to do: If you use this data-fetching tool in a cloud environment, make sure your execution environment allows at least 10 minutes per task, or configure a shorter timeout on the tool itself.

lib/crewai-tools/src/crewai_tools/tools/brightdata_tool/brightdata_dataset.py:BrightDataDatasetTool

Open the standalone hidden-assumptions report for crewai →

How Data Flows Through the System

Execution begins when a user calls Crew.kickoff(inputs) or Flow.kickoff(inputs). For a Crew: inputs are interpolated into task description templates, then the Crew iterates through tasks sequentially (or routes them via a manager agent in hierarchical mode). For each task, the assigned agent's reasoning loop starts: ContextualMemory assembles a context block from memory stores, the LLM generates a thought and selects a tool call (structured as a function-calling JSON schema), the tool executes and returns a string observation, and the loop repeats until the LLM produces a 'Final Answer'. That answer is parsed into a TaskOutput and stored; subsequent tasks receive prior TaskOutputs as context. After all tasks finish, a CrewOutput is returned. For a Flow: decorated step methods execute in event-order — each method's return value is emitted as an event that triggers listening methods, with shared FlowState threaded through. Checkpoints are written after each step. If a step embeds a Crew, the Crew runs synchronously and its CrewOutput is returned to the Flow step.

  1. Receive kickoff inputs and interpolate templates — Crew.kickoff(inputs: dict) receives a dictionary of named values (e.g. {'topic': 'AI'}). Each Task's description and expected_output strings are scanned for {placeholder} tokens and substituted with the matching input values. Flow.kickoff() initializes FlowState (empty dict or Pydantic model with defaults) and merges any provided inputs. [dict (user-supplied inputs) → Task (with interpolated description strings)] (config: process (sequential|hierarchical), topic, outline)
  2. Build agent prompt with memory context — Before invoking the agent, ContextualMemory.build_context_for_task() queries four stores: ShortTermMemory (recent conversation chunks from the current run stored in a ChromaDB collection), LongTermMemory (SQLite table of past task summaries keyed by embedding similarity), EntityMemory (ChromaDB collection of entity facts extracted during prior runs), and optionally UserMemory (Mem0 API). Retrieved chunks are formatted into a 'Relevant Context' block appended to the task prompt. [Task → Formatted context string] (config: memory, embedder.provider, embedder.model)
  3. Agent reasoning loop: LLM decides next action — The AgentExecutor sends the combined prompt (system role/goal/backstory + task description + available tool schemas + memory context + prior observations) to the LLM via the LLM wrapper's call() method, which routes through litellm to the configured provider. If the model supports function calling, tools are passed as JSON schemas; otherwise ReAct-format text parsing is used. The LLM returns either a tool call (with arguments) or a Final Answer string. [LLM → Tool call JSON or Final Answer string] (config: llm (model name), temperature, max_tokens +2)
  4. Tool execution and observation injection — When the LLM selects a tool, BaseTool.run() is called with the model-validated arguments (the tool's args_schema Pydantic model is used to validate the JSON the LLM produced). The tool's _run() method executes (e.g. a web scrape, code sandbox execution, vector search). The string result is injected back into the agent's context as an 'Observation', and the reasoning loop continues. [BaseTool args_schema → Tool observation string] (config: cache_function, max_retry_limit)
  5. Parse Final Answer into TaskOutput — When the LLM produces a Final Answer, the Task parses the raw text. If the task declared output_pydantic (a Pydantic model class), the framework attempts JSON extraction and model validation. If output_json was declared, it extracts a JSON dict. Otherwise the raw string is kept. The result is wrapped in a TaskOutput(raw, pydantic, json_dict, agent, output_format) and stored on the Task object. [Final Answer string → TaskOutput] (config: output_pydantic, output_json, output_file)
  6. Memory save: store task result in long-term and entity stores — After a task completes, the Crew saves the TaskOutput to LongTermMemory (embedding the result summary into SQLite) and runs an entity extraction LLM call to identify named entities mentioned in the output, storing them in EntityMemory's ChromaDB collection. ShortTermMemory is updated with the full conversation turn. [TaskOutput → RAGChunk (written to memory stores)] (config: memory, long_term_memory, entity_memory)
  7. Pass context to next task — In sequential process mode, each Task's TaskOutput is appended to a running context list. Tasks that declare context=[prior_task] explicitly receive those TaskOutputs formatted as 'Context from prior task' blocks in their prompt. In hierarchical mode, the manager agent decides which tasks to assign and what context to provide, making delegation decisions dynamically. [TaskOutput → Task (updated with context for next agent)] (config: process (sequential|hierarchical), manager_llm, manager_agent)
  8. Assemble CrewOutput and return — After all tasks complete, Crew assembles a CrewOutput from the list of TaskOutputs, setting raw to the final task's raw output, and aggregating token_usage metrics across all LLM calls. If an evaluator is configured, it calls BaseEvaluator.evaluate() to score each agent's performance on metrics like goal_achievement and tool_usage, storing EvaluationResults. [TaskOutput → CrewOutput] (config: full_output, output_log_file)
  9. Flow checkpoint and event routing — In Flow mode, after each decorated step method returns, FlowCheckpointManager serializes the current FlowState to a JSON file or SQLite row under .checkpoints/ (or .checkpoints.db). The return value is emitted as an event; the Flow router checks all @listen decorators for matching conditions and queues the next methods. @router() methods return a string that selects among named branches. [FlowState → FlowState (updated checkpoint)] (config: topic, outline, draft +1)

Data Models

The data structures that flow between stages — the contracts that hold the system together.

Agent lib/crewai/src/crewai/agent.py
Pydantic BaseModel with role: str, goal: str, backstory: str, tools: list[BaseTool], llm: LLM | str, memory: bool, max_iter: int, allow_delegation: bool, verbose: bool, agent_executor: AgentExecutor (set at runtime)
Defined statically in a crew Python file, instantiated when the Crew is created, then given tasks to execute — the agent_executor is built lazily on first use and destroyed when the crew run ends.
Task lib/crewai/src/crewai/task.py
Pydantic BaseModel with description: str, expected_output: str, agent: Agent | None, tools: list[BaseTool], context: list[Task] (tasks whose output feeds this one), output_file: str | None, human_input: bool, output: TaskOutput | None (set after execution)
Declared in the crew definition, queued for execution in the order determined by the process strategy, then populated with a TaskOutput after the assigned agent finishes.
TaskOutput lib/crewai/src/crewai/tasks/task_output.py
Pydantic BaseModel with description: str, raw: str (raw LLM response text), pydantic: BaseModel | None (structured output if output_pydantic set), json_dict: dict | None, agent: str, output_format: OutputFormat
Created by the Task after the agent's final answer is parsed, optionally coerced into a Pydantic model or JSON dict, then stored on the Task and passed as context to downstream tasks.
CrewOutput lib/crewai/src/crewai/crews/crew_output.py
Pydantic BaseModel with raw: str (last task's raw output), pydantic: BaseModel | None, json_dict: dict | None, tasks_output: list[TaskOutput], token_usage: UsageMetrics
Assembled by Crew after all tasks complete, aggregating each TaskOutput and the final task's formatted result, then returned from Crew.kickoff() to the caller or Flow step.
FlowState lib/crewai/src/crewai/flow/flow.py
Either a plain dict or a user-defined Pydantic BaseModel subclass; holds named fields that persist across Flow method calls (e.g. topic: str, outline: str, draft: str, final_post: str in the blog-post template)
Initialized (empty or with defaults) when Flow.kickoff() is called, mutated in-place by each step method that writes to self.state, persisted to a checkpoint file/DB between steps, and read by subsequent steps.
LLM lib/crewai/src/crewai/llm.py
Class wrapping litellm with model: str (e.g. 'gpt-4o', 'anthropic/claude-3-5-sonnet'), temperature: float, max_tokens: int | None, api_key: str | None, base_url: str | None, callbacks: list, supports_function_calling: bool (computed)
Created per-agent (or shared if the same model string is reused), used to make every LLM API call during the agent's reasoning loop, emitting LLMCallStarted/Completed events around each call.
RAGChunk lib/crewai-tools/src/crewai_tools/rag/core.py
Pydantic BaseModel with id: str (UUID), content: str, metadata: dict[str, Any], data_type: DataType (enum: TEXT, TABLE, IMAGE)
Created by a loader (e.g. PDFLoader, CSVLoader) when a knowledge source is ingested, embedded into a vector, stored in the configured vector store (default: ChromaDB), and retrieved at query time to augment agent prompts.
EvaluationResult lib/crewai/src/crewai/experimental/evaluation/base_evaluator.py
Pydantic BaseModel with agent_id: str, task_id: str, metrics: dict[MetricCategory, EvaluationScore] where MetricCategory includes goal_achievement, tool_usage, reasoning_quality
Generated by a BaseEvaluator subclass after each task completes, collected across all tasks for a crew run, and aggregated into an AgentEvaluationResult with mean/min/max scores per metric category.

System Behavior

How the system operates at runtime — where data accumulates, what loops, what waits, and what controls what.

Data Pools

LongTermMemory SQLite store (database)
Stores past task summaries as text embeddings in a SQLite database under ~/.crewai/. Each row contains the task description, agent role, quality score, and metadata. Retrieved by cosine similarity at the start of each new task to give agents relevant historical context.
EntityMemory ChromaDB collection (index)
A vector index of named entities (people, organizations, concepts) extracted from task outputs via an LLM call. Enables agents in later tasks to recall structured facts about entities mentioned earlier in the same crew run.
Flow checkpoint store (checkpoint)
Either a directory of JSON files (./.checkpoints/) or a SQLite database (./.checkpoints.db), one entry per flow run step. Stores the serialized FlowState so a failed or interrupted flow can be resumed from the last successful step without re-running all prior steps.
Tool result cache (cache)
An optional in-memory or persistent cache keyed by (tool_name, arguments_hash). When cache_function is enabled on a BaseTool, identical repeated tool calls within a crew run return the cached observation rather than re-executing the tool, reducing LLM round-trips and external API costs.
CrewAI AMP cloud state (database)
The remote CrewAI AMP platform stores deployment records, execution logs, and organization settings. The PlusAPIClient in crewai-core sends HTTP requests to create/read/update these records. Auth tokens and org UUIDs are cached locally in ~/.crewai/settings.json.

Feedback Loops

Delays

Control Points

Technology Stack

litellm (library)
Routes all LLM API calls to 100+ providers (OpenAI, Anthropic, Bedrock, Vertex, Ollama, etc.) behind a single interface — every agent's reasoning call goes through this
LangChain (framework)
Provides the AgentExecutor that implements the think-act-observe loop and the document loading/splitting utilities used in knowledge ingestion
Pydantic v2 (library)
Defines all data contracts — tool input schemas, agent/task configs, task outputs, memory items — and validates LLM-generated JSON before execution
ChromaDB (database)
Default vector store for short-term and entity memory — stores text embeddings and supports cosine-similarity retrieval for contextual memory lookups
SQLite (via sqlite3) (database)
Stores long-term memory summaries and flow execution checkpoints locally; accessed directly via sqlite3, not an ORM
Click + Rich (framework)
Click handles CLI command parsing and argument validation; Rich renders colored output, tables, and progress indicators in the terminal
httpx (library)
HTTP client used by PlusAPIClient to communicate with the CrewAI AMP REST API and by several tools (BrightData, enterprise config) to call external services
OpenTelemetry (infra)
Provides distributed tracing — crew runs emit spans and attributes that can be exported to any OTLP-compatible backend for production observability

Key Components

Explore the interactive analysis

See the full architecture map, data flow, and code patterns visualization.

Analyze on CodeSea

Related Ml Inference Repositories

Frequently Asked Questions

What is crewAI used for?

Orchestrates teams of AI agents to complete complex tasks collaboratively crewaiinc/crewai is a 8-component ml inference written in Python. Data flows through 9 distinct pipeline stages. The codebase contains 1272 files.

How is crewAI architected?

crewAI is organized into 5 architecture layers: CLI & Scaffolding, Agent Orchestration Core, Memory & Knowledge, Tools Ecosystem, and 1 more. Data flows through 9 distinct pipeline stages. This layered structure keeps concerns separated and modules independent.

How does data flow through crewAI?

Data moves through 9 stages: Receive kickoff inputs and interpolate templates → Build agent prompt with memory context → Agent reasoning loop: LLM decides next action → Tool execution and observation injection → Parse Final Answer into TaskOutput → .... Execution begins when a user calls Crew.kickoff(inputs) or Flow.kickoff(inputs). For a Crew: inputs are interpolated into task description templates, then the Crew iterates through tasks sequentially (or routes them via a manager agent in hierarchical mode). For each task, the assigned agent's reasoning loop starts: ContextualMemory assembles a context block from memory stores, the LLM generates a thought and selects a tool call (structured as a function-calling JSON schema), the tool executes and returns a string observation, and the loop repeats until the LLM produces a 'Final Answer'. That answer is parsed into a TaskOutput and stored; subsequent tasks receive prior TaskOutputs as context. After all tasks finish, a CrewOutput is returned. For a Flow: decorated step methods execute in event-order — each method's return value is emitted as an event that triggers listening methods, with shared FlowState threaded through. Checkpoints are written after each step. If a step embeds a Crew, the Crew runs synchronously and its CrewOutput is returned to the Flow step. This pipeline design reflects a complex multi-stage processing system.

What technologies does crewAI use?

The core stack includes litellm (Routes all LLM API calls to 100+ providers (OpenAI, Anthropic, Bedrock, Vertex, Ollama, etc.) behind a single interface — every agent's reasoning call goes through this), LangChain (Provides the AgentExecutor that implements the think-act-observe loop and the document loading/splitting utilities used in knowledge ingestion), Pydantic v2 (Defines all data contracts — tool input schemas, agent/task configs, task outputs, memory items — and validates LLM-generated JSON before execution), ChromaDB (Default vector store for short-term and entity memory — stores text embeddings and supports cosine-similarity retrieval for contextual memory lookups), SQLite (via sqlite3) (Stores long-term memory summaries and flow execution checkpoints locally; accessed directly via sqlite3, not an ORM), Click + Rich (Click handles CLI command parsing and argument validation; Rich renders colored output, tables, and progress indicators in the terminal), and 2 more. A focused set of dependencies that keeps the build manageable.

What system dynamics does crewAI have?

crewAI exhibits 5 data pools (LongTermMemory SQLite store, EntityMemory ChromaDB collection), 4 feedback loops, 5 control points, 3 delays. The feedback loops handle recursive and recursive. These runtime behaviors shape how the system responds to load, failures, and configuration changes.

What design patterns does crewAI use?

5 design patterns detected: Pydantic-first data contracts, Event-driven observability bus, Decorator-driven Flow graph construction, Layered tool registry with fallback, Provider-agnostic LLM adapter via litellm.

Analyzed on July 20, 2026 by CodeSea. Written by .