crewaiinc/crewai
Framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks.
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".
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.
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)
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
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
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
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
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
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
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
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.
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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)
- 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.
lib/crewai/src/crewai/agent.pyPydantic 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.
lib/crewai/src/crewai/task.pyPydantic 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.
lib/crewai/src/crewai/tasks/task_output.pyPydantic 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.
lib/crewai/src/crewai/crews/crew_output.pyPydantic 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.
lib/crewai/src/crewai/flow/flow.pyEither 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.
lib/crewai/src/crewai/llm.pyClass 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.
lib/crewai-tools/src/crewai_tools/rag/core.pyPydantic 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.
lib/crewai/src/crewai/experimental/evaluation/base_evaluator.pyPydantic 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
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.
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.
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.
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.
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
- Agent think-act-observe loop (recursive, balancing) — Trigger: A task is assigned to an agent and agent_executor.invoke() is called. Action: The agent sends its current prompt+observations to the LLM, receives a tool call or Final Answer, executes the tool (if applicable), appends the observation to the conversation, and loops. Exit: The LLM produces a 'Final Answer' string, OR the iteration count reaches max_iter (default 20), OR a StopIteration/force_final_answer condition fires.
- Hierarchical manager delegation loop (recursive, balancing) — Trigger: Crew.process == Process.hierarchical — the manager agent is invoked with the full task list. Action: The manager agent decides which worker agent to assign the next sub-task to, that agent executes it, the result is returned to the manager, and the manager decides the next delegation. Exit: The manager agent produces a Final Answer synthesizing all sub-task results.
- Flow event routing loop (polling, balancing) — Trigger: Flow.kickoff() begins; start methods are queued. Action: Each completed step's return value is emitted as an event; the router checks @listen/@router decorators for matching conditions and enqueues matching methods; the loop processes the queue until empty. Exit: No more methods are enqueued (all listeners have fired or no listeners match the final event).
- LLM rate-limit retry (retry, balancing) — Trigger: LLM call raises a rate-limit or transient error (HTTP 429/503). Action: litellm's built-in retry logic waits with exponential backoff and re-sends the same prompt. Exit: Successful response received, or max retry count exceeded (raises exception).
Delays
- Human input pause (async-processing, ~Indefinite (waits for user keyboard input)) — When a Task has human_input=True, the agent's proposed output is printed to the console and execution blocks at input() until the user accepts or provides corrections. The agent then re-runs with the feedback.
- BrightData dataset polling (batch-window, ~Up to DEFAULT_TIMEOUT=600 seconds, polling every DEFAULT_POLLING_INTERVAL=1 second) — When an agent triggers a BrightData dataset snapshot, the tool blocks (polling the BrightData API every second) until the dataset is ready or timeout is reached. This can hold the agent reasoning loop for up to 10 minutes.
- OAuth2 device authorization polling (polling, ~Polls every ~5 seconds until user completes browser login (typically <2 minutes)) — During `crewai login`, the CLI prints a device code URL and polls the OAuth provider until the user authenticates in their browser. The CLI is blocked during this time.
Control Points
- process (sequential | hierarchical) (architecture-switch) — Controls: Determines whether tasks are executed in a fixed linear order (sequential: each agent does its task, output passed forward) or via a manager agent that dynamically delegates sub-tasks to worker agents (hierarchical). Changes the entire execution topology.. Default: Process.sequential (default)
- llm (model string) (architecture-switch) — Controls: Selects which LLM provider and model each agent uses (e.g. 'gpt-4o', 'anthropic/claude-3-5-sonnet', 'ollama/llama3'). Different models have different reasoning capabilities, context windows, and function-calling support — fundamentally changing agent behavior.
- memory (bool) (feature-flag) — Controls: Enables or disables the full memory subsystem (short-term, long-term, entity, contextual). When False, agents have no cross-task recall; when True, ContextualMemory queries all stores before each agent invocation.. Default: False (default)
- max_iter (int) (hyperparameter) — Controls: Maximum number of think-act-observe iterations the agent can perform before being forced to produce a Final Answer. Low values save tokens but risk incomplete reasoning; high values allow more thorough tool use but can loop.. Default: 20 (default)
- verbose (bool) (runtime-toggle) — Controls: When True, prints every LLM call, tool invocation, and observation to stdout using Rich formatting. Useful for debugging but noisy in production. Also gates whether the AgentExecutor logs intermediate steps.. Default: False (default)
Technology Stack
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
Provides the AgentExecutor that implements the think-act-observe loop and the document loading/splitting utilities used in knowledge ingestion
Defines all data contracts — tool input schemas, agent/task configs, task outputs, memory items — and validates LLM-generated JSON before execution
Default vector store for short-term and entity memory — stores text embeddings and supports cosine-similarity retrieval for contextual memory lookups
Stores long-term memory summaries and flow execution checkpoints locally; accessed directly via sqlite3, not an ORM
Click handles CLI command parsing and argument validation; Rich renders colored output, tables, and progress indicators in the terminal
HTTP client used by PlusAPIClient to communicate with the CrewAI AMP REST API and by several tools (BrightData, enterprise config) to call external services
Provides distributed tracing — crew runs emit spans and attributes that can be exported to any OTLP-compatible backend for production observability
Key Components
- Crew (orchestrator) — The central runtime coordinator. Crew.kickoff() validates the agent/task configuration, builds execution context, and then drives agents through tasks either sequentially (each agent executes its task in order, passing output as context) or hierarchically (a manager agent delegates sub-tasks). It also wires memory initialization, telemetry, and post-run evaluation.
lib/crewai/src/crewai/crew.py - Flow (orchestrator) — An event-driven pipeline engine. Developers decorate Python methods with @start(), @listen(event), @router() to define a directed graph of steps. Flow.kickoff() runs the start methods, then routes execution based on emitted events — when a method returns, its return value is emitted as an event and any method decorated with @listen for that event is queued next. State is shared across all steps via self.state.
lib/crewai/src/crewai/flow/flow.py - Agent (executor) (executor) — Wraps a LangChain AgentExecutor to implement the think→act→observe reasoning loop. On each invocation, the agent receives a prompt combining its role/goal/backstory, the task description, available tools, and memory context. It iterates: calling the LLM to decide an action, invoking the chosen tool, observing the result, and repeating until it produces a Final Answer — or until max_iter is reached.
lib/crewai/src/crewai/agent.py - LLM (adapter) — A thin wrapper around the litellm library that normalizes calls to 100+ LLM providers (OpenAI, Anthropic, Bedrock, Vertex, Ollama, etc.) behind a single call() interface. It handles function-calling schemas, streaming, retry on rate limits, and emits structured LLMCallStarted / LLMCallCompleted / LLMStreamChunk events for observability.
lib/crewai/src/crewai/llm.py - BaseTool (adapter) — The contract that every tool (both built-in and user-defined) must implement. Defines a Pydantic args_schema so the LLM receives a typed JSON schema for calling the tool, and a _run() method that executes the actual action. The framework calls run() (which validates inputs, handles caching, and catches errors) rather than _run() directly.
lib/crewai/src/crewai/tools/base_tool.py - ContextualMemory (resolver) — Before each agent invocation, ContextualMemory.build_context_for_task() queries short-term memory (recent conversation chunks), long-term memory (SQLite embedding store of past task summaries), entity memory (structured facts about named entities), and user memory (Mem0-backed personal context), then formats and injects the retrieved facts into the agent's prompt as an additional context block.
lib/crewai/src/crewai/memory/contextual/contextual_memory.py - DeployCommand (gateway) — Handles the full deploy lifecycle: runs pre-deploy validation (checks for lockfile, env vars, syntax), zips the project directory, and either pushes to GitHub (if a git remote exists) or uploads the ZIP directly to the CrewAI AMP REST API. Returns a deployment URL that the user can visit to monitor the run.
lib/cli/src/crewai_cli/deploy/main.py - FlowCheckpointManager (store) — Persists and restores FlowState between step executions. After each step completes, the current state is serialized (JSON or Pydantic model_dump) and written to either a directory of JSON files or a SQLite 'checkpoints' table, keyed by a flow-run UUID. On resume, the latest checkpoint is loaded to skip already-completed steps.
lib/crewai/src/crewai/flow/flow.py
Explore the interactive analysis
See the full architecture map, data flow, and code patterns visualization.
Analyze on CodeSeaRelated 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 Karolina Sarna.