|
|
|
@@ -26,8 +26,11 @@ Let's create a simple Flow where you will use OpenAI to generate a random city i
|
|
|
|
import asyncio
|
|
|
|
import asyncio
|
|
|
|
|
|
|
|
|
|
|
|
from crewai.flow.flow import Flow, listen, start
|
|
|
|
from crewai.flow.flow import Flow, listen, start
|
|
|
|
|
|
|
|
from dotenv import load_dotenv
|
|
|
|
from litellm import completion
|
|
|
|
from litellm import completion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
load_dotenv()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExampleFlow(Flow):
|
|
|
|
class ExampleFlow(Flow):
|
|
|
|
model = "gpt-4o-mini"
|
|
|
|
model = "gpt-4o-mini"
|
|
|
|
@@ -80,6 +83,8 @@ In the above example, we have created a simple Flow that generates a random city
|
|
|
|
|
|
|
|
|
|
|
|
When you run the Flow, it will generate a random city and then generate a fun fact about that city. The output will be printed to the console.
|
|
|
|
When you run the Flow, it will generate a random city and then generate a fun fact about that city. The output will be printed to the console.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Note:** Ensure you have set up your `.env` file to store your `OPENAI_API_KEY`. This key is necessary for authenticating requests to the OpenAI API.
|
|
|
|
|
|
|
|
|
|
|
|
### @start()
|
|
|
|
### @start()
|
|
|
|
|
|
|
|
|
|
|
|
The `@start()` decorator is used to mark a method as the starting point of a Flow. When a Flow is started, all the methods decorated with `@start()` are executed in parallel. You can have multiple start methods in a Flow, and they will all be executed when the Flow is started.
|
|
|
|
The `@start()` decorator is used to mark a method as the starting point of a Flow. When a Flow is started, all the methods decorated with `@start()` are executed in parallel. You can have multiple start methods in a Flow, and they will all be executed when the Flow is started.
|
|
|
|
@@ -123,34 +128,34 @@ import asyncio
|
|
|
|
from crewai.flow.flow import Flow, listen, start
|
|
|
|
from crewai.flow.flow import Flow, listen, start
|
|
|
|
|
|
|
|
|
|
|
|
class OutputExampleFlow(Flow):
|
|
|
|
class OutputExampleFlow(Flow):
|
|
|
|
@start()
|
|
|
|
@start()
|
|
|
|
def first_method(self):
|
|
|
|
def first_method(self):
|
|
|
|
return "Output from first_method"
|
|
|
|
return "Output from first_method"
|
|
|
|
|
|
|
|
|
|
|
|
@listen(first_method)
|
|
|
|
@listen(first_method)
|
|
|
|
def second_method(self, first_output):
|
|
|
|
def second_method(self, first_output):
|
|
|
|
return f"Second method received: {first_output}"
|
|
|
|
return f"Second method received: {first_output}"
|
|
|
|
|
|
|
|
|
|
|
|
async def main():
|
|
|
|
async def main():
|
|
|
|
flow = OutputExampleFlow()
|
|
|
|
flow = OutputExampleFlow()
|
|
|
|
final_output = await flow.kickoff()
|
|
|
|
final_output = await flow.kickoff()
|
|
|
|
print("---- Final Output ----")
|
|
|
|
print("---- Final Output ----")
|
|
|
|
print(final_output)
|
|
|
|
print(final_output)
|
|
|
|
|
|
|
|
|
|
|
|
asyncio.run(main())
|
|
|
|
asyncio.run(main())
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
````
|
|
|
|
|
|
|
|
|
|
|
|
``` text Output
|
|
|
|
``` text Output
|
|
|
|
---- Final Output ----
|
|
|
|
---- Final Output ----
|
|
|
|
Second method received: Output from first_method
|
|
|
|
Second method received: Output from first_method
|
|
|
|
```
|
|
|
|
````
|
|
|
|
|
|
|
|
|
|
|
|
</CodeGroup>
|
|
|
|
</CodeGroup>
|
|
|
|
|
|
|
|
|
|
|
|
In this example, the `second_method` is the last method to complete, so its output will be the final output of the Flow.
|
|
|
|
In this example, the `second_method` is the last method to complete, so its output will be the final output of the Flow.
|
|
|
|
The `kickoff()` method will return the final output, which is then printed to the console.
|
|
|
|
The `kickoff()` method will return the final output, which is then printed to the console.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#### Accessing and Updating State
|
|
|
|
#### Accessing and Updating State
|
|
|
|
|
|
|
|
|
|
|
|
In addition to retrieving the final output, you can also access and update the state within your Flow. The state can be used to store and share data between different methods in the Flow. After the Flow has run, you can access the state to retrieve any information that was added or updated during the execution.
|
|
|
|
In addition to retrieving the final output, you can also access and update the state within your Flow. The state can be used to store and share data between different methods in the Flow. After the Flow has run, you can access the state to retrieve any information that was added or updated during the execution.
|
|
|
|
@@ -191,11 +196,12 @@ async def main():
|
|
|
|
asyncio.run(main())
|
|
|
|
asyncio.run(main())
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
``` text Output
|
|
|
|
```text Output
|
|
|
|
Final Output: Hello from first_method - updated by second_method
|
|
|
|
Final Output: Hello from first_method - updated by second_method
|
|
|
|
Final State:
|
|
|
|
Final State:
|
|
|
|
counter=2 message='Hello from first_method - updated by second_method'
|
|
|
|
counter=2 message='Hello from first_method - updated by second_method'
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
</CodeGroup>
|
|
|
|
</CodeGroup>
|
|
|
|
|
|
|
|
|
|
|
|
In this example, the state is updated by both `first_method` and `second_method`.
|
|
|
|
In this example, the state is updated by both `first_method` and `second_method`.
|
|
|
|
@@ -352,7 +358,7 @@ async def main():
|
|
|
|
asyncio.run(main())
|
|
|
|
asyncio.run(main())
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
``` text Output
|
|
|
|
```text Output
|
|
|
|
Logger: Hello from the start method
|
|
|
|
Logger: Hello from the start method
|
|
|
|
Logger: Hello from the second method
|
|
|
|
Logger: Hello from the second method
|
|
|
|
```
|
|
|
|
```
|
|
|
|
@@ -396,7 +402,7 @@ async def main():
|
|
|
|
asyncio.run(main())
|
|
|
|
asyncio.run(main())
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
``` text Output
|
|
|
|
```text Output
|
|
|
|
---- Logger ----
|
|
|
|
---- Logger ----
|
|
|
|
{'greeting': 'Hello from the start method', 'joke': 'What do computers eat? Microchips.'}
|
|
|
|
{'greeting': 'Hello from the start method', 'joke': 'What do computers eat? Microchips.'}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
@@ -454,7 +460,7 @@ async def main():
|
|
|
|
asyncio.run(main())
|
|
|
|
asyncio.run(main())
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
``` text Output
|
|
|
|
```text Output
|
|
|
|
Starting the structured flow
|
|
|
|
Starting the structured flow
|
|
|
|
Third method running
|
|
|
|
Third method running
|
|
|
|
Fourth method running
|
|
|
|
Fourth method running
|
|
|
|
@@ -485,22 +491,21 @@ This command will generate a new CrewAI project with the necessary folder struct
|
|
|
|
|
|
|
|
|
|
|
|
After running the `crewai create flow name_of_flow` command, you will see a folder structure similar to the following:
|
|
|
|
After running the `crewai create flow name_of_flow` command, you will see a folder structure similar to the following:
|
|
|
|
|
|
|
|
|
|
|
|
| Directory/File | Description |
|
|
|
|
| Directory/File | Description |
|
|
|
|
|:---------------------------------|:------------------------------------------------------------------|
|
|
|
|
| :--------------------- | :----------------------------------------------------------------- |
|
|
|
|
| `name_of_flow/` | Root directory for the flow. |
|
|
|
|
| `name_of_flow/` | Root directory for the flow. |
|
|
|
|
| ├── `crews/` | Contains directories for specific crews. |
|
|
|
|
| ├── `crews/` | Contains directories for specific crews. |
|
|
|
|
| │ └── `poem_crew/` | Directory for the "poem_crew" with its configurations and scripts.|
|
|
|
|
| │ └── `poem_crew/` | Directory for the "poem_crew" with its configurations and scripts. |
|
|
|
|
| │ ├── `config/` | Configuration files directory for the "poem_crew". |
|
|
|
|
| │ ├── `config/` | Configuration files directory for the "poem_crew". |
|
|
|
|
| │ │ ├── `agents.yaml` | YAML file defining the agents for "poem_crew". |
|
|
|
|
| │ │ ├── `agents.yaml` | YAML file defining the agents for "poem_crew". |
|
|
|
|
| │ │ └── `tasks.yaml` | YAML file defining the tasks for "poem_crew". |
|
|
|
|
| │ │ └── `tasks.yaml` | YAML file defining the tasks for "poem_crew". |
|
|
|
|
| │ ├── `poem_crew.py` | Script for "poem_crew" functionality. |
|
|
|
|
| │ ├── `poem_crew.py` | Script for "poem_crew" functionality. |
|
|
|
|
| ├── `tools/` | Directory for additional tools used in the flow. |
|
|
|
|
| ├── `tools/` | Directory for additional tools used in the flow. |
|
|
|
|
| │ └── `custom_tool.py` | Custom tool implementation. |
|
|
|
|
| │ └── `custom_tool.py` | Custom tool implementation. |
|
|
|
|
| ├── `main.py` | Main script for running the flow. |
|
|
|
|
| ├── `main.py` | Main script for running the flow. |
|
|
|
|
| ├── `README.md` | Project description and instructions. |
|
|
|
|
| ├── `README.md` | Project description and instructions. |
|
|
|
|
| ├── `pyproject.toml` | Configuration file for project dependencies and settings. |
|
|
|
|
| ├── `pyproject.toml` | Configuration file for project dependencies and settings. |
|
|
|
|
| └── `.gitignore` | Specifies files and directories to ignore in version control. |
|
|
|
|
| └── `.gitignore` | Specifies files and directories to ignore in version control. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Building Your Crews
|
|
|
|
### Building Your Crews
|
|
|
|
|
|
|
|
|
|
|
|
@@ -542,8 +547,7 @@ class PoemFlow(Flow[PoemState]):
|
|
|
|
@listen(generate_sentence_count)
|
|
|
|
@listen(generate_sentence_count)
|
|
|
|
def generate_poem(self):
|
|
|
|
def generate_poem(self):
|
|
|
|
print("Generating poem")
|
|
|
|
print("Generating poem")
|
|
|
|
poem_crew = PoemCrew().crew()
|
|
|
|
result = PoemCrew().crew().kickoff(inputs={"sentence_count": self.state.sentence_count})
|
|
|
|
result = poem_crew.kickoff(inputs={"sentence_count": self.state.sentence_count})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print("Poem generated", result.raw)
|
|
|
|
print("Poem generated", result.raw)
|
|
|
|
self.state.poem = result.raw
|
|
|
|
self.state.poem = result.raw
|
|
|
|
@@ -554,18 +558,17 @@ class PoemFlow(Flow[PoemState]):
|
|
|
|
with open("poem.txt", "w") as f:
|
|
|
|
with open("poem.txt", "w") as f:
|
|
|
|
f.write(self.state.poem)
|
|
|
|
f.write(self.state.poem)
|
|
|
|
|
|
|
|
|
|
|
|
async def run():
|
|
|
|
async def kickoff():
|
|
|
|
"""
|
|
|
|
|
|
|
|
Run the flow.
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
poem_flow = PoemFlow()
|
|
|
|
poem_flow = PoemFlow()
|
|
|
|
await poem_flow.kickoff()
|
|
|
|
await poem_flow.kickoff()
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
def plot():
|
|
|
|
asyncio.run(run())
|
|
|
|
poem_flow = PoemFlow()
|
|
|
|
|
|
|
|
poem_flow.plot()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|
|
|
|
asyncio.run(kickoff())
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
In this example, the `PoemFlow` class defines a flow that generates a sentence count, uses the `PoemCrew` to generate a poem, and then saves the poem to a file. The flow is kicked off by calling the `kickoff()` method.
|
|
|
|
In this example, the `PoemFlow` class defines a flow that generates a sentence count, uses the `PoemCrew` to generate a poem, and then saves the poem to a file. The flow is kicked off by calling the `kickoff()` method.
|
|
|
|
@@ -587,13 +590,13 @@ source .venv/bin/activate
|
|
|
|
After activating the virtual environment, you can run the flow by executing one of the following commands:
|
|
|
|
After activating the virtual environment, you can run the flow by executing one of the following commands:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
```bash
|
|
|
|
crewai flow run
|
|
|
|
crewai flow kickoff
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
or
|
|
|
|
or
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
```bash
|
|
|
|
uv run run_flow
|
|
|
|
uv run kickoff
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The flow will execute, and you should see the output in the console.
|
|
|
|
The flow will execute, and you should see the output in the console.
|
|
|
|
@@ -658,12 +661,12 @@ By exploring these examples, you can gain insights into how to leverage CrewAI F
|
|
|
|
Also, check out our YouTube video on how to use flows in CrewAI below!
|
|
|
|
Also, check out our YouTube video on how to use flows in CrewAI below!
|
|
|
|
|
|
|
|
|
|
|
|
<iframe
|
|
|
|
<iframe
|
|
|
|
width="560"
|
|
|
|
width="560"
|
|
|
|
height="315"
|
|
|
|
height="315"
|
|
|
|
src="https://www.youtube.com/embed/MTb5my6VOT8"
|
|
|
|
src="https://www.youtube.com/embed/MTb5my6VOT8"
|
|
|
|
title="YouTube video player"
|
|
|
|
title="YouTube video player"
|
|
|
|
frameborder="0"
|
|
|
|
frameborder="0"
|
|
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
|
|
|
referrerpolicy="strict-origin-when-cross-origin"
|
|
|
|
referrerpolicy="strict-origin-when-cross-origin"
|
|
|
|
allowfullscreen
|
|
|
|
allowfullscreen
|
|
|
|
></iframe>
|
|
|
|
></iframe>{" "}
|
|
|
|
|