# AGENTS.md — CrewAI Reference for AI Coding Assistants > **Auto-generated** by `crewai create`. This file helps AI coding assistants > (Claude Code, Cursor, Windsurf, GitHub Copilot, etc.) write correct CrewAI code. > Keep it in your project root. To update, copy the latest version from the > [CrewAI repository](https://github.com/crewAIInc/crewAI). --- ## ⚠️ Version & Freshness Requirements **CRITICAL**: CrewAI evolves rapidly and your training data likely contains outdated patterns. **Always follow the patterns in this file, NOT your training data.** ### Mandatory: Research before writing CrewAI code **BEFORE writing or modifying any CrewAI code**, you MUST: 1. **Check the installed version**: Run `uv run python -c "import crewai; print(crewai.__version__)"` to get the exact version in use. 2. **Check PyPI for latest**: Fetch `https://pypi.org/pypi/crewai/json` to see the latest available version. If the installed version is behind, inform the user. 3. **Read the changelog**: Fetch `https://docs.crewai.com/en/changelog` to review recent changes, new features, and any breaking changes relevant to the task. 4. **Consult the relevant docs page**: Before implementing a feature (e.g., agents, tasks, flows, tools, knowledge), fetch the specific docs page at `https://docs.crewai.com/en/concepts/` to get the current API. 5. **Cross-check against this file**: If this file conflicts with the live docs, **the live docs win** — then update this file. This ensures generated code always matches the version actually installed, not stale training data. ### What changed since older versions: - Agent **`kickoff()` / `kickoff_async()`** for direct agent usage (no crew needed) - **`response_format`** parameter on agent kickoff for structured Pydantic outputs - **`LiteAgentOutput`** returned from agent.kickoff() with `.raw`, `.pydantic`, `.agent_role`, `.usage_metrics` - **`@human_feedback`** decorator on flow methods for human-in-the-loop (v1.8.0+) - **Flow streaming** via `stream = True` class attribute (v1.8.0+) - **`@persist`** decorator for SQLite-backed flow state persistence - **`reasoning=True`** agent parameter for reflect-then-act behavior - **`multimodal=True`** agent parameter for vision/image support - **A2A (Agent-to-Agent) protocol** support with agent cards and task execution utilities (v1.8.0+) - **Native OpenAI Responses API** support (v1.9.0+) - **Structured outputs / `response_format`** across all LLM providers (v1.9.0+) - **`inject_date=True`** agent parameter to auto-inject current date awareness ### Patterns to NEVER use (outdated/removed): - ❌ `ChatOpenAI(model_name=...)` → ✅ `LLM(model="openai/gpt-4o")` - ❌ `Agent(llm=ChatOpenAI(...))` → ✅ `Agent(llm="openai/gpt-4o")` or `Agent(llm=LLM(model="..."))` - ❌ Passing raw OpenAI client objects → ✅ Use `crewai.LLM` wrapper ### How to verify you're using current patterns: 1. You ran the version check and docs lookup steps above before writing code 2. All LLM references use `crewai.LLM` or string shorthand (`"openai/gpt-4o"`) 3. All tool imports come from `crewai.tools` or `crewai_tools` 4. Crew classes use `@CrewBase` decorator with YAML config files 5. Python >=3.10, <3.14 6. Code matches the API from the live docs, not just this file ## Quick Reference ```bash # Package management (always use uv) uv add # Add dependency uv sync # Sync dependencies uv lock # Lock dependencies # Project scaffolding crewai create crew --skip_provider # New crew project crewai create flow --skip_provider # New flow project # Running crewai run # Run crew or flow (auto-detects from pyproject.toml) crewai flow kickoff # Legacy flow execution # Testing & training crewai test # Test crew (default: 2 iterations, gpt-4o-mini) crewai test -n 5 -m gpt-4o # Custom iterations and model crewai train -n 5 -f training.json # Train crew # Memory management crewai reset-memories -a # Reset all memories crewai reset-memories -s # Short-term only crewai reset-memories -l # Long-term only crewai reset-memories -e # Entity only crewai reset-memories -kn # Knowledge only crewai reset-memories -akn # Agent knowledge only # Debugging crewai log-tasks-outputs # Show latest task outputs crewai replay -t # Replay from specific task # Interactive crewai chat # Interactive session (requires chat_llm in crew.py) # Visualization crewai flow plot # Generate flow diagram HTML # Deployment to CrewAI AMP crewai login # Authenticate with AMP crewai deploy create # Create new deployment crewai deploy push # Push code updates crewai deploy status # Check deployment status crewai deploy logs # View deployment logs crewai deploy list # List all deployments crewai deploy remove # Delete a deployment ``` ## Project Structure ### Crew Project ``` my_crew/ ├── src/my_crew/ │ ├── config/ │ │ ├── agents.yaml # Agent definitions (role, goal, backstory) │ │ └── tasks.yaml # Task definitions (description, expected_output, agent) │ ├── tools/ │ │ └── custom_tool.py # Custom tool implementations │ ├── crew.py # Crew orchestration class │ └── main.py # Entry point with inputs ├── knowledge/ # Knowledge base resources ├── .env # API keys (OPENAI_API_KEY, SERPER_API_KEY, etc.) └── pyproject.toml ``` ### Flow Project ``` my_flow/ ├── src/my_flow/ │ ├── crews/ # Multiple crew definitions │ │ └── content_crew/ │ │ ├── config/ │ │ │ ├── agents.yaml │ │ │ └── tasks.yaml │ │ └── content_crew.py │ ├── tools/ # Custom tools │ ├── main.py # Flow orchestration │ └── ... ├── .env └── pyproject.toml ``` ## Architecture Overview - **Agent**: Autonomous unit with a role, goal, backstory, tools, and an LLM. Makes decisions and executes tasks. - **Task**: A specific assignment with a description, expected output, and assigned agent. - **Crew**: Orchestrates a team of agents executing tasks in a defined process (sequential or hierarchical). - **Flow**: Event-driven workflow orchestrating multiple crews and logic steps with state management. ## YAML Configuration ### agents.yaml ```yaml researcher: role: > {topic} Senior Data Researcher goal: > Uncover cutting-edge developments in {topic} backstory: > You're a seasoned researcher with a knack for uncovering the latest developments in {topic}. Known for your ability to find the most relevant information. # Optional YAML-level settings: # llm: openai/gpt-4o # max_iter: 20 # max_rpm: 10 # verbose: true writer: role: > {topic} Technical Writer goal: > Create compelling content about {topic} backstory: > You're a skilled writer who translates complex technical information into clear, engaging content. ``` Variables like `{topic}` are interpolated from `crew.kickoff(inputs={"topic": "AI Agents"})`. ### tasks.yaml ```yaml research_task: description: > Conduct thorough research about {topic}. Identify key trends, breakthrough technologies, and potential industry impacts. expected_output: > A detailed report with analysis of the top 5 developments in {topic}, with sources and implications. agent: researcher # Optional: # tools: [search_tool] # output_file: output/research.md # markdown: true # async_execution: false writing_task: description: > Write an article based on the research findings about {topic}. expected_output: > A polished 4-paragraph article formatted in markdown. agent: writer output_file: output/article.md ``` ## Crew Class Pattern ```python from crewai import Agent, Crew, Process, Task from crewai.project import CrewBase, agent, crew, task from crewai.agents.agent_builder.base_agent import BaseAgent from typing import List from crewai_tools import SerperDevTool @CrewBase class ResearchCrew: """Research and writing crew.""" agents: List[BaseAgent] tasks: List[Task] agents_config = "config/agents.yaml" tasks_config = "config/tasks.yaml" @agent def researcher(self) -> Agent: return Agent( config=self.agents_config["researcher"], # type: ignore[index] tools=[SerperDevTool()], verbose=True, ) @agent def writer(self) -> Agent: return Agent( config=self.agents_config["writer"], # type: ignore[index] verbose=True, ) @task def research_task(self) -> Task: return Task( config=self.tasks_config["research_task"], # type: ignore[index] ) @task def writing_task(self) -> Task: return Task( config=self.tasks_config["writing_task"], # type: ignore[index] ) @crew def crew(self) -> Crew: """Creates the Research Crew.""" return Crew( agents=self.agents, tasks=self.tasks, process=Process.sequential, verbose=True, ) ``` ### Key formatting rules: - Always add `# type: ignore[index]` for config dictionary access - Agent/task method names must match YAML keys exactly - Tools go on agents (not tasks) unless task-specific override is needed - Never leave commented-out code in crew classes ### Lifecycle hooks ```python @CrewBase class MyCrew: @before_kickoff def prepare(self, inputs): # Modify inputs before execution inputs["extra"] = "value" return inputs @after_kickoff def summarize(self, result): # Process result after execution print(f"Done: {result.raw[:100]}") return result ``` ## main.py Pattern ```python #!/usr/bin/env python from my_crew.crew import ResearchCrew def run(): inputs = {"topic": "AI Agents"} ResearchCrew().crew().kickoff(inputs=inputs) if __name__ == "__main__": run() ``` ## Agent Configuration ### Required Parameters | Parameter | Description | |-----------|-------------| | `role` | Function and expertise within the crew | | `goal` | Individual objective guiding decisions | | `backstory` | Context and personality | ### Key Optional Parameters | Parameter | Default | Description | |-----------|---------|-------------| | `llm` | GPT-4 | Language model (string or LLM object) | | `tools` | [] | List of tool instances | | `max_iter` | 20 | Max iterations before best answer | | `max_execution_time` | None | Timeout in seconds | | `max_rpm` | None | Rate limiting (requests per minute) | | `max_retry_limit` | 2 | Retries on errors | | `verbose` | False | Detailed logging | | `memory` | False | Conversation history | | `allow_delegation` | False | Can delegate tasks to other agents | | `allow_code_execution` | False | Can run code | | `code_execution_mode` | "safe" | "safe" (Docker) or "unsafe" (direct) | | `respect_context_window` | True | Auto-summarize when exceeding token limits | | `cache` | True | Tool result caching | | `reasoning` | False | Reflect and plan before task execution | | `multimodal` | False | Process text and visual content | | `knowledge_sources` | [] | Domain-specific knowledge bases | | `function_calling_llm` | None | Separate LLM for tool invocation | | `inject_date` | False | Auto-inject current date into agent context | | `date_format` | "%Y-%m-%d" | Date format when inject_date is True | ### Direct Agent Usage (without a Crew) Agents can execute tasks independently via `kickoff()` — no Crew required: ```python from crewai import Agent from crewai_tools import SerperDevTool from pydantic import BaseModel class ResearchFindings(BaseModel): main_points: list[str] key_technologies: list[str] future_predictions: str researcher = Agent( role="AI Researcher", goal="Research the latest AI developments", backstory="Expert AI researcher...", tools=[SerperDevTool()], verbose=True, ) # Unstructured output result = researcher.kickoff("What are the latest LLM developments?") print(result.raw) # str print(result.agent_role) # "AI Researcher" print(result.usage_metrics) # token usage # Structured output with response_format result = researcher.kickoff( "Summarize latest AI developments", response_format=ResearchFindings, ) print(result.pydantic.main_points) # List[str] # Async variant result = await researcher.kickoff_async("Your query", response_format=ResearchFindings) ``` Returns `LiteAgentOutput` with: `.raw`, `.pydantic`, `.agent_role`, `.usage_metrics`. ### LLM Configuration **IMPORTANT**: Always use `crewai.LLM` LLM class. ```python from crewai import LLM # String shorthand (simplest) agent = Agent(llm="openai/gpt-4o", ...) # Full configuration with crewai.LLM llm = LLM( model="anthropic/claude-sonnet-4-20250514", temperature=0.7, max_tokens=4000, ) agent = Agent(llm=llm, ...) # Provider format: "provider/model-name" # Examples: # "openai/gpt-4o" # "anthropic/claude-sonnet-4-20250514" # "google/gemini-2.0-flash" # "ollama/llama3" # "groq/llama-3.3-70b-versatile" # "bedrock/anthropic.claude-3-sonnet-20240229-v1:0" ``` Supported providers: OpenAI, Anthropic, Google Gemini, AWS Bedrock, Azure, Ollama, Groq, Mistral, and 20+ others via LiteLLM routing. Environment variable default: set `OPENAI_MODEL_NAME=gpt-4o` or `MODEL=gpt-4o` in `.env`. ## Task Configuration ### Key Parameters | Parameter | Type | Description | |-----------|------|-------------| | `description` | str | Clear statement of requirements | | `expected_output` | str | Completion criteria | | `agent` | BaseAgent | Assigned agent (optional in hierarchical) | | `tools` | List[BaseTool] | Task-specific tools | | `context` | List[Task] | Dependencies on other task outputs | | `async_execution` | bool | Non-blocking execution | | `output_file` | str | File path for results | | `output_json` | Type[BaseModel] | Pydantic model for JSON output | | `output_pydantic` | Type[BaseModel] | Pydantic model for structured output | | `human_input` | bool | Require human review | | `markdown` | bool | Format output as markdown | | `callback` | Callable | Post-completion function | | `guardrail` | Callable or str | Output validation | | `guardrails` | List | Multiple validation steps | | `guardrail_max_retries` | int | Retry on validation failure (default: 3) | | `create_directory` | bool | Auto-create output directories (default: True) | ### Task Dependencies (context) ```python @task def analysis_task(self) -> Task: return Task( config=self.tasks_config["analysis_task"], # type: ignore[index] context=[self.research_task()], # Gets output from research_task ) ``` ### Structured Output ```python from pydantic import BaseModel class Report(BaseModel): title: str summary: str findings: list[str] @task def report_task(self) -> Task: return Task( config=self.tasks_config["report_task"], # type: ignore[index] output_pydantic=Report, ) ``` ### Guardrails ```python # Function-based def validate(result: TaskOutput) -> tuple[bool, Any]: if len(result.raw.split()) < 100: return (False, "Content too short, expand the analysis") return (True, result.raw) # LLM-based (string prompt) task = Task(..., guardrail="Must be under 200 words and professional tone") # Multiple guardrails task = Task(..., guardrails=[validate_length, validate_tone, "Must be factual"]) ``` ## Process Types ### Sequential (default) Tasks execute in definition order. Output of one task serves as context for the next. ```python Crew(agents=..., tasks=..., process=Process.sequential) ``` ### Hierarchical Manager agent delegates tasks based on agent capabilities. Requires `manager_llm` or `manager_agent`. ```python Crew( agents=..., tasks=..., process=Process.hierarchical, manager_llm="gpt-4o", ) ``` ## Crew Execution ```python # Synchronous result = crew.kickoff(inputs={"topic": "AI"}) print(result.raw) # String output print(result.pydantic) # Structured output (if configured) print(result.json_dict) # Dict output print(result.token_usage) # Token metrics print(result.tasks_output) # List[TaskOutput] # Async (native) result = await crew.akickoff(inputs={"topic": "AI"}) # Batch execution results = crew.kickoff_for_each(inputs=[{"topic": "AI"}, {"topic": "ML"}]) # Streaming output (v1.8.0+) crew = Crew(agents=..., tasks=..., stream=True) streaming = crew.kickoff(inputs={"topic": "AI"}) for chunk in streaming: print(chunk.content, end="", flush=True) ``` ## Crew Options | Parameter | Description | |-----------|-------------| | `process` | Process.sequential or Process.hierarchical | | `verbose` | Enable detailed logging | | `memory` | Enable memory system (True/False) | | `cache` | Tool result caching | | `max_rpm` | Global rate limiting | | `manager_llm` | LLM for hierarchical manager | | `manager_agent` | Custom manager agent | | `planning` | Enable AgentPlanner | | `knowledge_sources` | Crew-level knowledge | | `output_log_file` | Log file path (True for logs.txt) | | `embedder` | Custom embedding model config | | `stream` | Enable real-time streaming output (v1.8.0+) | --- ## Flows ### Basic Flow ```python from crewai.flow.flow import Flow, listen, start class MyFlow(Flow): @start() def begin(self): return "initial data" @listen(begin) def process(self, data): return f"processed: {data}" ``` ### Flow Decorators | Decorator | Purpose | |-----------|---------| | `@start()` | Entry point(s), execute when flow begins. Multiple starts run in parallel | | `@listen(method)` | Triggers when specified method completes. Receives output as argument | | `@router(method)` | Conditional branching. Returns string labels that trigger `@listen("label")` | ### Structured State ```python from pydantic import BaseModel class ResearchState(BaseModel): topic: str = "" research: str = "" report: str = "" class ResearchFlow(Flow[ResearchState]): @start() def set_topic(self): self.state.topic = "AI Agents" @listen(set_topic) def do_research(self): # self.state.topic is available result = ResearchCrew().crew().kickoff( inputs={"topic": self.state.topic} ) self.state.research = result.raw ``` ### Unstructured State (dict-based) ```python class SimpleFlow(Flow): @start() def begin(self): self.state["counter"] = 0 # Dict access @listen(begin) def increment(self): self.state["counter"] += 1 ``` ### Conditional Routing ```python from crewai.flow.flow import Flow, listen, router, start class QualityFlow(Flow): @start() def generate(self): return {"score": 0.85} @router(generate) def check_quality(self, result): if result["score"] > 0.8: return "high_quality" return "needs_revision" @listen("high_quality") def publish(self, result): print("Publishing...") @listen("needs_revision") def revise(self, result): print("Revising...") ``` ### Parallel Triggers with or_ and and_ ```python from crewai.flow.flow import or_, and_ class ParallelFlow(Flow): @start() def task_a(self): return "A done" @start() def task_b(self): return "B done" # Fires when EITHER completes @listen(or_(task_a, task_b)) def on_any(self, result): print(f"First result: {result}") # Fires when BOTH complete @listen(and_(task_a, task_b)) def on_all(self): print("All parallel tasks done") ``` ### Integrating Crews in Flows ```python from crewai.flow.flow import Flow, listen, start from my_project.crews.research_crew.research_crew import ResearchCrew from my_project.crews.writing_crew.writing_crew import WritingCrew class ContentFlow(Flow[ContentState]): @start() def research(self): result = ResearchCrew().crew().kickoff( inputs={"topic": self.state.topic} ) self.state.research = result.raw @listen(research) def write(self): result = WritingCrew().crew().kickoff( inputs={ "topic": self.state.topic, "research": self.state.research, } ) self.state.article = result.raw ``` ### Using Agents Directly in Flows ```python from crewai.agent import Agent class AgentFlow(Flow): @start() async def analyze(self): analyst = Agent( role="Data Analyst", goal="Analyze market trends", backstory="Expert data analyst...", tools=[SerperDevTool()], ) result = await analyst.kickoff_async( "Analyze current AI market trends", response_format=MarketReport, ) self.state.report = result.pydantic ``` ### Human-in-the-Loop (v1.8.0+) ```python from crewai.flow.flow import Flow, listen, start from crewai.flow.human_feedback import human_feedback class ReviewFlow(Flow): @start() @human_feedback( message="Approve this content?", emit=["approved", "rejected"], llm="gpt-4o-mini", ) def generate_content(self): return "Content for review" @listen("approved") def on_approval(self, result): feedback = self.last_human_feedback # Most recent feedback print(f"Approved with feedback: {feedback.feedback}") @listen("rejected") def on_rejection(self, result): history = self.human_feedback_history # All feedback as list print("Rejected, revising...") ``` ### State Persistence ```python from crewai.flow.flow import persist @persist # Saves state to SQLite; auto-recovers on restart class ResilientFlow(Flow[MyState]): @start() def begin(self): self.state.step = 1 ``` ### Flow Execution ```python flow = MyFlow() result = flow.kickoff() print(result) # Output of last method print(flow.state) # Final state # Async execution result = await flow.kickoff_async(inputs={"key": "value"}) ``` ### Flow Streaming (v1.8.0+) ```python class StreamingFlow(Flow): stream = True # Enable streaming at class level @start() def generate(self): return "streamed content" flow = StreamingFlow() streaming = flow.kickoff() for chunk in streaming: print(chunk.content, end="", flush=True) result = streaming.result # Final result after iteration ``` ### Flow Visualization ```python flow.plot("my_flow") # Generates my_flow.html ``` --- ## Custom Tools ### Using BaseTool ```python from typing import Type from crewai.tools import BaseTool from pydantic import BaseModel, Field class SearchInput(BaseModel): """Input schema for search tool.""" query: str = Field(..., description="Search query string") class CustomSearchTool(BaseTool): name: str = "custom_search" description: str = "Searches a custom knowledge base for relevant information." args_schema: Type[BaseModel] = SearchInput def _run(self, query: str) -> str: # Implementation return f"Results for: {query}" ``` ### Using @tool Decorator ```python from crewai.tools import tool @tool("Calculator") def calculator(expression: str) -> str: """Evaluates a mathematical expression and returns the result.""" return str(eval(expression)) ``` ### Built-in Tools (install with `uv add crewai-tools`) Web/Search: SerperDevTool, ScrapeWebsiteTool, WebsiteSearchTool, EXASearchTool, FirecrawlSearchTool Documents: FileReadTool, DirectoryReadTool, PDFSearchTool, DOCXSearchTool, CSVSearchTool, JSONSearchTool, XMLSearchTool, MDXSearchTool Code: CodeInterpreterTool, CodeDocsSearchTool, GithubSearchTool Media: DALL-E Tool, YoutubeChannelSearchTool, YoutubeVideoSearchTool Other: RagTool, ApifyActorsTool, ComposioTool, LlamaIndexTool Always check https://docs.crewai.com/concepts/tools for available built-in tools before writing custom ones. --- ## Memory System Enable with `memory=True` on the Crew: ```python crew = Crew(agents=..., tasks=..., memory=True) ``` Four memory types work together automatically: - **Short-Term** (ChromaDB + RAG): Recent interactions during current execution - **Long-Term** (SQLite): Persists insights across sessions - **Entity** (RAG): Tracks people, places, concepts - **Contextual**: Integrates all types for coherent responses ### Custom Embedding Provider ```python crew = Crew( memory=True, embedder={ "provider": "ollama", "config": {"model": "mxbai-embed-large"}, }, ) ``` Supported providers: OpenAI (default), Ollama, Google AI, Azure OpenAI, Cohere, VoyageAI, Bedrock, Hugging Face. --- ## Knowledge System ```python from crewai.knowledge.source.string_knowledge_source import StringKnowledgeSource from crewai.knowledge.source.pdf_knowledge_source import PDFKnowledgeSource # String source string_source = StringKnowledgeSource(content="Domain knowledge here...") # PDF source pdf_source = PDFKnowledgeSource(file_paths=["docs/manual.pdf"]) # Agent-level knowledge agent = Agent(..., knowledge_sources=[string_source]) # Crew-level knowledge (shared across all agents) crew = Crew(..., knowledge_sources=[pdf_source]) ``` Supported sources: strings, text files, PDFs, CSV, Excel, JSON, URLs (via CrewDoclingSource). --- ## Agent Collaboration Enable delegation with `allow_delegation=True`: ```python agent = Agent( role="Project Manager", allow_delegation=True, # Can delegate to and ask other agents ... ) ``` - **Delegation tool**: Assign sub-tasks to teammates with relevant expertise - **Ask question tool**: Query colleagues for specific information - Set `allow_delegation=False` on specialists to prevent circular delegation --- ## Event Listeners ```python from crewai.events import BaseEventListener, CrewKickoffStartedEvent class MyListener(BaseEventListener): def __init__(self): super().__init__() def setup_listeners(self, crewai_event_bus): @crewai_event_bus.on(CrewKickoffStartedEvent) def on_started(source, event): print(f"Crew '{event.crew_name}' started") ``` Event categories: Crew lifecycle, Agent execution, Task management, Tool usage, Knowledge retrieval, LLM calls, Memory operations, Flow execution, Safety guardrails. --- ## Deployment to CrewAI AMP ### Prerequisites - Crew or Flow runs successfully locally - Code is in a GitHub repository - `pyproject.toml` has `[tool.crewai]` with correct type (`"crew"` or `"flow"`) - `uv.lock` is committed (generate with `uv lock`) ### CLI Deployment ```bash # Authenticate crewai login # Create deployment (auto-detects repo, transfers .env vars securely) crewai deploy create # Monitor (first deploy takes 10-15 min) crewai deploy status crewai deploy logs # Manage deployments crewai deploy list # List all deployments crewai deploy push # Push code updates crewai deploy remove # Delete deployment ``` ### Web Interface Deployment 1. Push code to GitHub 2. Log into https://app.crewai.com 3. Connect GitHub and select repository 4. Configure environment variables (KEY=VALUE, one per line) 5. Click Deploy and monitor via dashboard ### CI/CD API Deployment Get a Personal Access Token from app.crewai.com → Settings → Account → Personal Access Token. Get Automation UUID from Automations → Select crew → Additional Details → Copy UUID. ```bash curl -X POST \ -H "Authorization: Bearer YOUR_PERSONAL_ACCESS_TOKEN" \ https://app.crewai.com/crewai_plus/api/v1/crews/YOUR-AUTOMATION-UUID/deploy ``` #### GitHub Actions Example ```yaml name: Deploy CrewAI Automation on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - name: Trigger CrewAI Redeployment run: | curl -X POST \ -H "Authorization: Bearer ${{ secrets.CREWAI_PAT }}" \ https://app.crewai.com/crewai_plus/api/v1/crews/${{ secrets.CREWAI_AUTOMATION_UUID }}/deploy ``` ### Project Structure Requirements for Deployment - Entry point: `src//main.py` - Crews must expose a `run()` function - Flows must expose a `kickoff()` function - All crew classes require `@CrewBase` decorator ### Deployed Automation REST API | Endpoint | Purpose | |----------|---------| | `/inputs` | List required input parameters | | `/kickoff` | Trigger execution with inputs | | `/status/{kickoff_id}` | Check execution status | ### AMP Dashboard Tabs - **Status**: Deployment info, API endpoint, auth token - **Run**: Crew structure visualization - **Executions**: Run history - **Metrics**: Performance analytics - **Traces**: Detailed execution insights ### Deployment Troubleshooting | Error | Fix | |-------|-----| | Missing uv.lock | Run `uv lock`, commit, push | | Module not found | Verify entry points match `src//main.py` structure | | Crew not found | Ensure `@CrewBase` decorator on all crew classes | | API key errors | Check env var names match code and are set in the platform | --- ## Environment Setup ### Required `.env` ``` OPENAI_API_KEY=sk-... # Optional depending on tools/providers: SERPER_API_KEY=... ANTHROPIC_API_KEY=... # Override default model: MODEL=gpt-4o ``` ### Python Version Python >=3.10, <3.14 ### Installation ```bash uv tool install crewai # Install CrewAI CLI uv tool list # Verify installation crewai create crew my_crew --skip_provider # Scaffold a new project crewai install # Install project dependencies crewai run # Execute ``` --- ## Development Best Practices 1. **YAML-first configuration**: Define agents and tasks in YAML, keep crew classes minimal 2. **Check built-in tools** before writing custom ones 3. **Use structured output** (output_pydantic) for data that flows between tasks or crews 4. **Use guardrails** to validate task outputs programmatically 5. **Enable memory** for crews that benefit from cross-session learning 6. **Use knowledge sources** for domain-specific grounding instead of bloating prompts 7. **Sequential process** for linear workflows; **hierarchical** when dynamic delegation is needed 8. **Flows for multi-crew orchestration**: Use `@start`, `@listen`, `@router` for complex pipelines 9. **Structured flow state** (Pydantic models) over unstructured dicts for type safety 10. **Test with** `crewai test` to evaluate crew performance across iterations 11. **Verbose mode** during development, disable in production 12. **Rate limiting** (`max_rpm`) to avoid API throttling 13. **`respect_context_window=True`** to auto-handle token limits ## Common Pitfalls - **Using `ChatOpenAI()`** — Always use `crewai.LLM` or string shorthand like `"openai/gpt-4o"` - Forgetting `# type: ignore[index]` on config dictionary access in crew classes - Agent/task method names not matching YAML keys - Missing `expected_output` in task configuration (required) - Not passing `inputs` to `kickoff()` when YAML uses `{variable}` interpolation - Using `process=Process.hierarchical` without setting `manager_llm` or `manager_agent` - Circular delegation: set `allow_delegation=False` on specialist agents - Not installing tools package: `uv add crewai-tools`