diff --git a/crewai_trading/.gitignore b/crewai_trading/.gitignore new file mode 100644 index 0000000..7279347 --- /dev/null +++ b/crewai_trading/.gitignore @@ -0,0 +1,3 @@ +.env +__pycache__/ +.DS_Store diff --git a/crewai_trading/AGENTS.md b/crewai_trading/AGENTS.md new file mode 100644 index 0000000..887dbc6 --- /dev/null +++ b/crewai_trading/AGENTS.md @@ -0,0 +1,1017 @@ +# 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 +│ │ └── poem_crew/ +│ │ ├── config/ +│ │ │ ├── agents.yaml +│ │ │ └── tasks.yaml +│ │ └── poem_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` diff --git a/crewai_trading/README.md b/crewai_trading/README.md new file mode 100644 index 0000000..aa2b8f7 --- /dev/null +++ b/crewai_trading/README.md @@ -0,0 +1,54 @@ +# CrewaiTrading Crew + +Welcome to the CrewaiTrading Crew project, powered by [crewAI](https://crewai.com). This template is designed to help you set up a multi-agent AI system with ease, leveraging the powerful and flexible framework provided by crewAI. Our goal is to enable your agents to collaborate effectively on complex tasks, maximizing their collective intelligence and capabilities. + +## Installation + +Ensure you have Python >=3.10 <3.14 installed on your system. This project uses [UV](https://docs.astral.sh/uv/) for dependency management and package handling, offering a seamless setup and execution experience. + +First, if you haven't already, install uv: + +```bash +pip install uv +``` + +Next, navigate to your project directory and install the dependencies: + +(Optional) Lock the dependencies and install them by using the CLI command: +```bash +crewai install +``` +### Customizing + +**Add your `OPENAI_API_KEY` into the `.env` file** + +- Modify `src/crewai_trading/config/agents.yaml` to define your agents +- Modify `src/crewai_trading/config/tasks.yaml` to define your tasks +- Modify `src/crewai_trading/crew.py` to add your own logic, tools and specific args +- Modify `src/crewai_trading/main.py` to add custom inputs for your agents and tasks + +## Running the Project + +To kickstart your crew of AI agents and begin task execution, run this from the root folder of your project: + +```bash +$ crewai run +``` + +This command initializes the crewai_trading Crew, assembling the agents and assigning them tasks as defined in your configuration. + +This example, unmodified, will run the create a `report.md` file with the output of a research on LLMs in the root folder. + +## Understanding Your Crew + +The crewai_trading Crew is composed of multiple AI agents, each with unique roles, goals, and tools. These agents collaborate on a series of tasks, defined in `config/tasks.yaml`, leveraging their collective skills to achieve complex objectives. The `config/agents.yaml` file outlines the capabilities and configurations of each agent in your crew. + +## Support + +For support, questions, or feedback regarding the CrewaiTrading Crew or crewAI. +- Visit our [documentation](https://docs.crewai.com) +- Reach out to us through our [GitHub repository](https://github.com/joaomdmoura/crewai) +- [Join our Discord](https://discord.com/invite/X4JWnZnxPb) +- [Chat with our docs](https://chatg.pt/DWjSBZn) + +Let's create wonders together with the power and simplicity of crewAI. diff --git a/crewai_trading/knowledge/user_preference.txt b/crewai_trading/knowledge/user_preference.txt new file mode 100644 index 0000000..dd63a17 --- /dev/null +++ b/crewai_trading/knowledge/user_preference.txt @@ -0,0 +1,4 @@ +User name is John Doe. +User is an AI Engineer. +User is interested in AI Agents. +User is based in San Francisco, California. diff --git a/crewai_trading/pyproject.toml b/crewai_trading/pyproject.toml new file mode 100644 index 0000000..ff862bb --- /dev/null +++ b/crewai_trading/pyproject.toml @@ -0,0 +1,24 @@ +[project] +name = "crewai_trading" +version = "0.1.0" +description = "crewai_trading using crewAI" +authors = [{ name = "Your Name", email = "you@example.com" }] +requires-python = ">=3.10,<3.14" +dependencies = [ + "crewai[tools]==1.11.0" +] + +[project.scripts] +crewai_trading = "crewai_trading.main:run" +run_crew = "crewai_trading.main:run" +train = "crewai_trading.main:train" +replay = "crewai_trading.main:replay" +test = "crewai_trading.main:test" +run_with_trigger = "crewai_trading.main:run_with_trigger" + +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.crewai] +type = "crew" diff --git a/crewai_trading/src/crewai_trading/__init__.py b/crewai_trading/src/crewai_trading/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/crewai_trading/src/crewai_trading/config/agents.yaml b/crewai_trading/src/crewai_trading/config/agents.yaml new file mode 100644 index 0000000..72ed693 --- /dev/null +++ b/crewai_trading/src/crewai_trading/config/agents.yaml @@ -0,0 +1,19 @@ +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 and present it in a clear and concise manner. + +reporting_analyst: + role: > + {topic} Reporting Analyst + goal: > + Create detailed reports based on {topic} data analysis and research findings + backstory: > + You're a meticulous analyst with a keen eye for detail. You're known for + your ability to turn complex data into clear and concise reports, making + it easy for others to understand and act on the information you provide. \ No newline at end of file diff --git a/crewai_trading/src/crewai_trading/config/tasks.yaml b/crewai_trading/src/crewai_trading/config/tasks.yaml new file mode 100644 index 0000000..44bd898 --- /dev/null +++ b/crewai_trading/src/crewai_trading/config/tasks.yaml @@ -0,0 +1,17 @@ +research_task: + description: > + Conduct a thorough research about {topic} + Make sure you find any interesting and relevant information given + the current year is {current_year}. + expected_output: > + A list with 10 bullet points of the most relevant information about {topic} + agent: researcher + +reporting_task: + description: > + Review the context you got and expand each topic into a full section for a report. + Make sure the report is detailed and contains any and all relevant information. + expected_output: > + A fully fledged report with the main topics, each with a full section of information. + Formatted as markdown without '```' + agent: reporting_analyst diff --git a/crewai_trading/src/crewai_trading/crew.py b/crewai_trading/src/crewai_trading/crew.py new file mode 100644 index 0000000..75cacd3 --- /dev/null +++ b/crewai_trading/src/crewai_trading/crew.py @@ -0,0 +1,63 @@ +from crewai import Agent, Crew, Process, Task +from crewai.project import CrewBase, agent, crew, task +from crewai.agents.agent_builder.base_agent import BaseAgent +# If you want to run a snippet of code before or after the crew starts, +# you can use the @before_kickoff and @after_kickoff decorators +# https://docs.crewai.com/concepts/crews#example-crew-class-with-decorators + +@CrewBase +class CrewaiTrading(): + """CrewaiTrading crew""" + + agents: list[BaseAgent] + tasks: list[Task] + + # Learn more about YAML configuration files here: + # Agents: https://docs.crewai.com/concepts/agents#yaml-configuration-recommended + # Tasks: https://docs.crewai.com/concepts/tasks#yaml-configuration-recommended + + # If you would like to add tools to your agents, you can learn more about it here: + # https://docs.crewai.com/concepts/agents#agent-tools + @agent + def researcher(self) -> Agent: + return Agent( + config=self.agents_config['researcher'], # type: ignore[index] + verbose=True + ) + + @agent + def reporting_analyst(self) -> Agent: + return Agent( + config=self.agents_config['reporting_analyst'], # type: ignore[index] + verbose=True + ) + + # To learn more about structured task outputs, + # task dependencies, and task callbacks, check out the documentation: + # https://docs.crewai.com/concepts/tasks#overview-of-a-task + @task + def research_task(self) -> Task: + return Task( + config=self.tasks_config['research_task'], # type: ignore[index] + ) + + @task + def reporting_task(self) -> Task: + return Task( + config=self.tasks_config['reporting_task'], # type: ignore[index] + output_file='report.md' + ) + + @crew + def crew(self) -> Crew: + """Creates the CrewaiTrading crew""" + # To learn how to add knowledge sources to your crew, check out the documentation: + # https://docs.crewai.com/concepts/knowledge#what-is-knowledge + + return Crew( + agents=self.agents, # Automatically created by the @agent decorator + tasks=self.tasks, # Automatically created by the @task decorator + process=Process.sequential, + verbose=True, + # process=Process.hierarchical, # In case you wanna use that instead https://docs.crewai.com/how-to/Hierarchical/ + ) diff --git a/crewai_trading/src/crewai_trading/main.py b/crewai_trading/src/crewai_trading/main.py new file mode 100644 index 0000000..ceb8ea6 --- /dev/null +++ b/crewai_trading/src/crewai_trading/main.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +import sys +import warnings + +from datetime import datetime + +from crewai_trading.crew import CrewaiTrading + +warnings.filterwarnings("ignore", category=SyntaxWarning, module="pysbd") + +# This main file is intended to be a way for you to run your +# crew locally, so refrain from adding unnecessary logic into this file. +# Replace with inputs you want to test with, it will automatically +# interpolate any tasks and agents information + +def run(): + """ + Run the crew. + """ + inputs = { + 'topic': 'AI LLMs', + 'current_year': str(datetime.now().year) + } + + try: + CrewaiTrading().crew().kickoff(inputs=inputs) + except Exception as e: + raise Exception(f"An error occurred while running the crew: {e}") + + +def train(): + """ + Train the crew for a given number of iterations. + """ + inputs = { + "topic": "AI LLMs", + 'current_year': str(datetime.now().year) + } + try: + CrewaiTrading().crew().train(n_iterations=int(sys.argv[1]), filename=sys.argv[2], inputs=inputs) + + except Exception as e: + raise Exception(f"An error occurred while training the crew: {e}") + +def replay(): + """ + Replay the crew execution from a specific task. + """ + try: + CrewaiTrading().crew().replay(task_id=sys.argv[1]) + + except Exception as e: + raise Exception(f"An error occurred while replaying the crew: {e}") + +def test(): + """ + Test the crew execution and returns the results. + """ + inputs = { + "topic": "AI LLMs", + "current_year": str(datetime.now().year) + } + + try: + CrewaiTrading().crew().test(n_iterations=int(sys.argv[1]), eval_llm=sys.argv[2], inputs=inputs) + + except Exception as e: + raise Exception(f"An error occurred while testing the crew: {e}") + +def run_with_trigger(): + """ + Run the crew with trigger payload. + """ + import json + + if len(sys.argv) < 2: + raise Exception("No trigger payload provided. Please provide JSON payload as argument.") + + try: + trigger_payload = json.loads(sys.argv[1]) + except json.JSONDecodeError: + raise Exception("Invalid JSON payload provided as argument") + + inputs = { + "crewai_trigger_payload": trigger_payload, + "topic": "", + "current_year": "" + } + + try: + result = CrewaiTrading().crew().kickoff(inputs=inputs) + return result + except Exception as e: + raise Exception(f"An error occurred while running the crew with trigger: {e}") diff --git a/crewai_trading/src/crewai_trading/tools/__init__.py b/crewai_trading/src/crewai_trading/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/crewai_trading/src/crewai_trading/tools/custom_tool.py b/crewai_trading/src/crewai_trading/tools/custom_tool.py new file mode 100644 index 0000000..154beae --- /dev/null +++ b/crewai_trading/src/crewai_trading/tools/custom_tool.py @@ -0,0 +1,19 @@ +from crewai.tools import BaseTool +from typing import Type +from pydantic import BaseModel, Field + + +class MyCustomToolInput(BaseModel): + """Input schema for MyCustomTool.""" + argument: str = Field(..., description="Description of the argument.") + +class MyCustomTool(BaseTool): + name: str = "Name of my tool" + description: str = ( + "Clear description for what this tool is useful for, your agent will need this information to use it." + ) + args_schema: Type[BaseModel] = MyCustomToolInput + + def _run(self, argument: str) -> str: + # Implementation goes here + return "this is an example of a tool output, ignore it and move along."