diff --git a/docs/concepts/flows.mdx b/docs/concepts/flows.mdx index dee6fd4fac..bf9ce8e673 100644 --- a/docs/concepts/flows.mdx +++ b/docs/concepts/flows.mdx @@ -599,13 +599,114 @@ The generated plot will display nodes representing the tasks in your flow, with By visualizing your flows, you can gain a clearer understanding of the workflow's structure, making it easier to debug, optimize, and communicate your AI processes to others. -### Conclusion -Plotting your flows is a powerful feature of CrewAI that enhances your ability to design and manage complex AI workflows. Whether you choose to use the `plot()` method or the command line, generating plots will provide you with a visual representation of your workflows, aiding in both development and presentation. +## Advanced + +In this section, we explore more complex use cases of CrewAI Flows, starting with a self-evaluation loop. This pattern is crucial for developing AI systems that can iteratively improve their outputs through feedback. + +### 1) Self-Evaluation Loop + +The self-evaluation loop is a powerful pattern that allows AI workflows to automatically assess and refine their outputs. This example demonstrates how to set up a flow that generates content, evaluates it, and iterates based on feedback until the desired quality is achieved. + +#### Overview + +The self-evaluation loop involves two main Crews: + +1. **ShakespeareanXPostCrew**: Generates a Shakespearean-style post on a given topic. +2. **XPostReviewCrew**: Evaluates the generated post, providing feedback on its validity and quality. + +The process iterates until the post meets the criteria or a maximum retry limit is reached. This approach ensures high-quality outputs through iterative refinement. + +#### Importance + +This pattern is essential for building robust AI systems that can adapt and improve over time. By automating the evaluation and feedback loop, developers can ensure that their AI workflows produce reliable and high-quality results. + +#### Main Code Highlights + +Below is the `main.py` file for the self-evaluation loop flow: + +```python +from typing import Optional +from crewai.flow.flow import Flow, listen, router, start +from pydantic import BaseModel +from self_evaluation_loop_flow.crews.shakespeare_crew.shakespeare_crew import ( + ShakespeareanXPostCrew, +) +from self_evaluation_loop_flow.crews.x_post_review_crew.x_post_review_crew import ( + XPostReviewCrew, +) + +class ShakespeareXPostFlowState(BaseModel): + x_post: str = "" + feedback: Optional[str] = None + valid: bool = False + retry_count: int = 0 + +class ShakespeareXPostFlow(Flow[ShakespeareXPostFlowState]): + + @start("retry") + def generate_shakespeare_x_post(self): + print("Generating Shakespearean X post") + topic = "Flying cars" + result = ( + ShakespeareanXPostCrew() + .crew() + .kickoff(inputs={"topic": topic, "feedback": self.state.feedback}) + ) + print("X post generated", result.raw) + self.state.x_post = result.raw + + @router(generate_shakespeare_x_post) + def evaluate_x_post(self): + if self.state.retry_count > 3: + return "max_retry_exceeded" + result = XPostReviewCrew().crew().kickoff(inputs={"x_post": self.state.x_post}) + self.state.valid = result["valid"] + self.state.feedback = result["feedback"] + print("valid", self.state.valid) + print("feedback", self.state.feedback) + self.state.retry_count += 1 + if self.state.valid: + return "complete" + return "retry" + + @listen("complete") + def save_result(self): + print("X post is valid") + print("X post:", self.state.x_post) + with open("x_post.txt", "w") as file: + file.write(self.state.x_post) + + @listen("max_retry_exceeded") + def max_retry_exceeded_exit(self): + print("Max retry count exceeded") + print("X post:", self.state.x_post) + print("Feedback:", self.state.feedback) + +def kickoff(): + shakespeare_flow = ShakespeareXPostFlow() + shakespeare_flow.kickoff() + +def plot(): + shakespeare_flow = ShakespeareXPostFlow() + shakespeare_flow.plot() + +if __name__ == "__main__": + kickoff() +``` + +#### Code Highlights + +- **Retry Mechanism**: The flow uses a retry mechanism to regenerate the post if it doesn't meet the criteria, up to a maximum of three retries. +- **Feedback Loop**: Feedback from the `XPostReviewCrew` is used to refine the post iteratively. +- **State Management**: The flow maintains state using a Pydantic model, ensuring type safety and clarity. + +For a complete example and further details, please refer to the [Self Evaluation Loop Flow repository](https://github.com/crewAIInc/crewAI-examples/tree/main/self_evaluation_loop_flow). + ## Next Steps -If you're interested in exploring additional examples of flows, we have a variety of recommendations in our examples repository. Here are four specific flow examples, each showcasing unique use cases to help you match your current problem type to a specific example: +If you're interested in exploring additional examples of flows, we have a variety of recommendations in our examples repository. Here are five specific flow examples, each showcasing unique use cases to help you match your current problem type to a specific example: 1. **Email Auto Responder Flow**: This example demonstrates an infinite loop where a background job continually runs to automate email responses. It's a great use case for tasks that need to be performed repeatedly without manual intervention. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/email_auto_responder_flow) @@ -615,6 +716,8 @@ If you're interested in exploring additional examples of flows, we have a variet 4. **Meeting Assistant Flow**: This flow demonstrates how to broadcast one event to trigger multiple follow-up actions. For instance, after a meeting is completed, the flow can update a Trello board, send a Slack message, and save the results. It's a great example of handling multiple outcomes from a single event, making it ideal for comprehensive task management and notification systems. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/meeting_assistant_flow) +5. **Self Evaluation Loop Flow**: This flow demonstrates a self-evaluation loop where AI workflows automatically assess and refine their outputs through feedback. It involves generating content, evaluating it, and iterating until the desired quality is achieved. This pattern is crucial for developing robust AI systems that can adapt and improve over time. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/self_evaluation_loop_flow) + By exploring these examples, you can gain insights into how to leverage CrewAI Flows for various use cases, from automating repetitive tasks to managing complex, multi-step processes with dynamic decision-making and human feedback. Also, check out our YouTube video on how to use flows in CrewAI below! diff --git a/docs/how-to/create-custom-tools.mdx b/docs/how-to/create-custom-tools.mdx index 31fd09e3f9..2caab716bc 100644 --- a/docs/how-to/create-custom-tools.mdx +++ b/docs/how-to/create-custom-tools.mdx @@ -20,14 +20,21 @@ pip install 'crewai[tools]' ### Subclassing `BaseTool` -To create a personalized tool, inherit from `BaseTool` and define the necessary attributes and the `_run` method. +To create a personalized tool, inherit from `BaseTool` and define the necessary attributes, including the `args_schema` for input validation, and the `_run` method. ```python Code +from typing import Type from crewai_tools import BaseTool +from pydantic import BaseModel, Field + +class MyToolInput(BaseModel): + """Input schema for MyCustomTool.""" + argument: str = Field(..., description="Description of the argument.") class MyCustomTool(BaseTool): name: str = "Name of my tool" description: str = "What this tool does. It's vital for effective utilization." + args_schema: Type[BaseModel] = MyToolInput def _run(self, argument: str) -> str: # Your tool's logic here diff --git a/src/crewai/cli/templates/crew/tools/custom_tool.py b/src/crewai/cli/templates/crew/tools/custom_tool.py index b125293033..d383180519 100644 --- a/src/crewai/cli/templates/crew/tools/custom_tool.py +++ b/src/crewai/cli/templates/crew/tools/custom_tool.py @@ -1,11 +1,17 @@ +from typing import Type from crewai_tools import BaseTool +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, you agent will need this information to use it." ) + args_schema: Type[BaseModel] = MyCustomToolInput def _run(self, argument: str) -> str: # Implementation goes here diff --git a/src/crewai/cli/templates/flow/tools/custom_tool.py b/src/crewai/cli/templates/flow/tools/custom_tool.py index b125293033..030e575ecd 100644 --- a/src/crewai/cli/templates/flow/tools/custom_tool.py +++ b/src/crewai/cli/templates/flow/tools/custom_tool.py @@ -1,4 +1,13 @@ +from typing import Type + from crewai_tools import BaseTool +from pydantic import BaseModel, Field + + +class MyCustomToolInput(BaseModel): + """Input schema for MyCustomTool.""" + + argument: str = Field(..., description="Description of the argument.") class MyCustomTool(BaseTool): @@ -6,6 +15,7 @@ class MyCustomTool(BaseTool): description: str = ( "Clear description for what this tool is useful for, you agent will need this information to use it." ) + args_schema: Type[BaseModel] = MyCustomToolInput def _run(self, argument: str) -> str: # Implementation goes here diff --git a/src/crewai/cli/templates/pipeline/tools/custom_tool.py b/src/crewai/cli/templates/pipeline/tools/custom_tool.py index b125293033..d383180519 100644 --- a/src/crewai/cli/templates/pipeline/tools/custom_tool.py +++ b/src/crewai/cli/templates/pipeline/tools/custom_tool.py @@ -1,11 +1,17 @@ +from typing import Type from crewai_tools import BaseTool +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, you agent will need this information to use it." ) + args_schema: Type[BaseModel] = MyCustomToolInput def _run(self, argument: str) -> str: # Implementation goes here diff --git a/src/crewai/cli/templates/pipeline_router/tools/custom_tool.py b/src/crewai/cli/templates/pipeline_router/tools/custom_tool.py index b125293033..d383180519 100644 --- a/src/crewai/cli/templates/pipeline_router/tools/custom_tool.py +++ b/src/crewai/cli/templates/pipeline_router/tools/custom_tool.py @@ -1,11 +1,17 @@ +from typing import Type from crewai_tools import BaseTool +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, you agent will need this information to use it." ) + args_schema: Type[BaseModel] = MyCustomToolInput def _run(self, argument: str) -> str: # Implementation goes here