mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-01-08 07:38:29 +00:00
Update docs and add more examples
This commit is contained in:
@@ -543,9 +543,14 @@ In this example, the `PoemFlow` class defines a flow that generates a sentence c
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Recommend checking out our flow examples in the CrewAI Examples repository to see more use cases.
|
||||
- Currently, there are 4 flow examples:
|
||||
- email auto responder flow
|
||||
- lead score flow
|
||||
- Write a book flow
|
||||
- Meeting assistant flow
|
||||
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:
|
||||
|
||||
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)
|
||||
|
||||
2. **Lead Score Flow**: This flow showcases adding human-in-the-loop feedback and handling different conditional branches using the router. It's an excellent example of how to incorporate dynamic decision-making and human oversight into your workflows. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/lead-score-flow)
|
||||
|
||||
3. **Write a Book Flow**: This example excels at chaining multiple crews together, where the output of one crew is used by another. Specifically, one crew outlines an entire book, and another crew generates chapters based on the outline. Eventually, everything is connected to produce a complete book. This flow is perfect for complex, multi-step processes that require coordination between different tasks. [View Example](https://github.com/crewAIInc/crewAI-examples/tree/main/write_a_book_with_flows)
|
||||
|
||||
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)
|
||||
|
||||
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.
|
||||
|
||||
@@ -53,6 +53,11 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
|
||||
Crews
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./core-concepts/Flows">
|
||||
Flows
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="./core-concepts/Pipeline">
|
||||
Pipeline
|
||||
@@ -193,6 +198,26 @@ Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By
|
||||
Landing Page Generator
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/email_auto_responder_flow">
|
||||
Email Auto Responder Flow
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/lead-score-flow">
|
||||
Lead Score Flow
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/write_a_book_with_flows">
|
||||
Write a Book Flow
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a target='_blank' href="https://github.com/crewAIInc/crewAI-examples/tree/main/meeting_assistant_flow">
|
||||
Meeting Assistant Flow
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
39
poetry.lock
generated
39
poetry.lock
generated
@@ -1539,12 +1539,12 @@ files = [
|
||||
google-auth = ">=2.14.1,<3.0.dev0"
|
||||
googleapis-common-protos = ">=1.56.2,<2.0.dev0"
|
||||
grpcio = [
|
||||
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.33.2,<2.0dev", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
grpcio-status = [
|
||||
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""},
|
||||
{version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""},
|
||||
]
|
||||
proto-plus = ">=1.22.3,<2.0.0dev"
|
||||
protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<6.0.0.dev0"
|
||||
@@ -1806,6 +1806,22 @@ cachetools = "*"
|
||||
numpy = "*"
|
||||
requests = "*"
|
||||
|
||||
[[package]]
|
||||
name = "graphviz"
|
||||
version = "0.20.3"
|
||||
description = "Simple Python interface for Graphviz"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "graphviz-0.20.3-py3-none-any.whl", hash = "sha256:81f848f2904515d8cd359cc611faba817598d2feaac4027b266aa3eda7b3dde5"},
|
||||
{file = "graphviz-0.20.3.zip", hash = "sha256:09d6bc81e6a9fa392e7ba52135a9d49f1ed62526f96499325930e87ca1b5925d"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["flake8", "pep8-naming", "tox (>=3)", "twine", "wheel"]
|
||||
docs = ["sphinx (>=5,<7)", "sphinx-autodoc-typehints", "sphinx-rtd-theme"]
|
||||
test = ["coverage", "pytest (>=7,<8.1)", "pytest-cov", "pytest-mock (>=3)"]
|
||||
|
||||
[[package]]
|
||||
name = "greenlet"
|
||||
version = "3.1.0"
|
||||
@@ -4046,8 +4062,8 @@ files = [
|
||||
|
||||
[package.dependencies]
|
||||
numpy = [
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.22.4", markers = "python_version < \"3.11\""},
|
||||
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
|
||||
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
|
||||
]
|
||||
python-dateutil = ">=2.8.2"
|
||||
@@ -4546,6 +4562,19 @@ files = [
|
||||
{file = "pyarrow-17.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7"},
|
||||
{file = "pyarrow-17.0.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204"},
|
||||
{file = "pyarrow-17.0.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8"},
|
||||
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155"},
|
||||
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145"},
|
||||
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c"},
|
||||
{file = "pyarrow-17.0.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c"},
|
||||
{file = "pyarrow-17.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca"},
|
||||
{file = "pyarrow-17.0.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb"},
|
||||
{file = "pyarrow-17.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df"},
|
||||
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687"},
|
||||
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b"},
|
||||
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5"},
|
||||
{file = "pyarrow-17.0.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda"},
|
||||
{file = "pyarrow-17.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204"},
|
||||
{file = "pyarrow-17.0.0.tar.gz", hash = "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -4561,6 +4590,7 @@ description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"},
|
||||
{file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"},
|
||||
]
|
||||
|
||||
@@ -4571,6 +4601,7 @@ description = "A collection of ASN.1-based protocols modules"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"},
|
||||
{file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"},
|
||||
]
|
||||
|
||||
@@ -6992,4 +7023,4 @@ tools = ["crewai-tools"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = ">=3.10,<=3.13"
|
||||
content-hash = "0b03c4204e1f31a5bf692ba4585b88d5f6ad4028693fdbc4d9b1546450c9af5a"
|
||||
content-hash = "80b7909ba10f19ffeeac6d6ce3c802158c75ff4328da0379549c28c054bd16eb"
|
||||
|
||||
@@ -32,6 +32,7 @@ json-repair = "^0.25.2"
|
||||
auth0-python = "^4.7.1"
|
||||
poetry = "^1.8.3"
|
||||
litellm = "^1.44.22"
|
||||
graphviz = "^0.20.3"
|
||||
|
||||
[tool.poetry.extras]
|
||||
tools = ["crewai-tools"]
|
||||
|
||||
@@ -2,6 +2,7 @@ import asyncio
|
||||
import inspect
|
||||
from typing import Any, Callable, Dict, Generic, List, Set, Type, TypeVar, Union
|
||||
|
||||
import graphviz
|
||||
from pydantic import BaseModel
|
||||
|
||||
T = TypeVar("T", bound=Union[BaseModel, Dict[str, Any]])
|
||||
@@ -250,3 +251,103 @@ class Flow(Generic[T], metaclass=FlowMeta):
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
|
||||
def visualize(self, filename="flow_graph"):
|
||||
dot = graphviz.Digraph(comment="Flow Graph", engine="dot")
|
||||
dot.attr(
|
||||
rankdir="TB", size="20,20", splines="curved"
|
||||
) # Changed to curved splines
|
||||
|
||||
# Color scheme (using company colors)
|
||||
colors = {
|
||||
"bg": "#FFFFFF",
|
||||
"start": "#FF5A50",
|
||||
"method": "#333333",
|
||||
"router_outline": "#FF5A50",
|
||||
"edge": "#333333",
|
||||
"text": "#FFFFFF",
|
||||
}
|
||||
|
||||
dot.attr(bgcolor=colors["bg"])
|
||||
|
||||
# Add nodes for each relevant method
|
||||
for method_name, method in self._methods.items():
|
||||
if (
|
||||
hasattr(method, "__is_start_method__")
|
||||
or method_name in self._listeners
|
||||
or method_name in self._routers.values()
|
||||
):
|
||||
shape = "rectangle"
|
||||
style = "filled,rounded"
|
||||
fillcolor = colors["method"]
|
||||
|
||||
if hasattr(method, "__is_start_method__"):
|
||||
fillcolor = colors["start"]
|
||||
|
||||
dot.node(
|
||||
method_name,
|
||||
method_name,
|
||||
shape=shape,
|
||||
style=style,
|
||||
fillcolor=fillcolor,
|
||||
fontcolor=colors["text"],
|
||||
penwidth="2",
|
||||
)
|
||||
|
||||
# Add edges and enhanced router representation
|
||||
for method_name, method in self._methods.items():
|
||||
if method_name in self._listeners:
|
||||
condition_type, trigger_methods = self._listeners[method_name]
|
||||
for trigger in trigger_methods:
|
||||
if condition_type == "AND":
|
||||
dot.edge(
|
||||
trigger,
|
||||
method_name,
|
||||
color=colors["edge"],
|
||||
style="dashed",
|
||||
penwidth="2",
|
||||
)
|
||||
else: # OR condition
|
||||
dot.edge(
|
||||
trigger, method_name, color=colors["edge"], penwidth="2"
|
||||
)
|
||||
|
||||
if method_name in self._routers.values():
|
||||
for trigger, router in self._routers.items():
|
||||
if router == method_name:
|
||||
# Create a subgraph for the router and its outputs
|
||||
subgraph_name = f"cluster_{method_name}"
|
||||
subgraph = graphviz.Digraph(name=subgraph_name)
|
||||
subgraph.attr(
|
||||
label="",
|
||||
style="filled,rounded",
|
||||
color=colors["router_outline"],
|
||||
fillcolor=colors["method"],
|
||||
penwidth="3",
|
||||
) # Changed to solid line and increased penwidth
|
||||
|
||||
# Router label (method name) and outputs
|
||||
label = f"{method_name}\\n\\nPossible outcomes:\\n• Success\\n• Failure"
|
||||
subgraph.node(
|
||||
method_name,
|
||||
label,
|
||||
shape="plaintext",
|
||||
fontcolor=colors["text"],
|
||||
)
|
||||
|
||||
# Add the subgraph to the main graph
|
||||
dot.subgraph(subgraph)
|
||||
|
||||
# Connect trigger to router (to the border of the subgraph)
|
||||
dot.edge(
|
||||
trigger,
|
||||
method_name,
|
||||
color=colors["edge"],
|
||||
style="solid",
|
||||
penwidth="2",
|
||||
lhead=subgraph_name,
|
||||
)
|
||||
|
||||
# Render and save the graph
|
||||
dot.render(filename, format="png", cleanup=True, view=True)
|
||||
print(f"Graph saved as {filename}.png")
|
||||
|
||||
BIN
src/crewai/flow/flow_graph.png
Normal file
BIN
src/crewai/flow/flow_graph.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
119
src/crewai/flow/large_flow.py
Normal file
119
src/crewai/flow/large_flow.py
Normal file
@@ -0,0 +1,119 @@
|
||||
import asyncio
|
||||
|
||||
from crewai.flow.flow import Flow, listen, start
|
||||
|
||||
|
||||
class BinaryBranchingFlow(Flow):
|
||||
@start()
|
||||
def start_method(self):
|
||||
print("Starting the binary branching flow")
|
||||
self.state["counter"] = 1
|
||||
return "Start result"
|
||||
|
||||
# Level 1
|
||||
@listen(start_method)
|
||||
def first_branch_left(self):
|
||||
print("First branch (left)")
|
||||
self.state["counter"] += 1
|
||||
return "First branch left result"
|
||||
|
||||
@listen(start_method)
|
||||
def first_branch_right(self):
|
||||
print("First branch (right)")
|
||||
self.state["counter"] += 1
|
||||
return "First branch right result"
|
||||
|
||||
# Level 2 - Left Branch
|
||||
@listen(first_branch_left)
|
||||
def second_branch_left_left(self):
|
||||
print("Second branch from first left (left)")
|
||||
self.state["counter"] += 1
|
||||
return "Second branch left left result"
|
||||
|
||||
@listen(first_branch_left)
|
||||
def second_branch_left_right(self):
|
||||
print("Second branch from first left (right)")
|
||||
self.state["counter"] += 1
|
||||
return "Second branch left right result"
|
||||
|
||||
# Level 2 - Right Branch
|
||||
@listen(first_branch_right)
|
||||
def second_branch_right_left(self):
|
||||
print("Second branch from first right (left)")
|
||||
self.state["counter"] += 1
|
||||
return "Second branch right left result"
|
||||
|
||||
@listen(first_branch_right)
|
||||
def second_branch_right_right(self):
|
||||
print("Second branch from first right (right)")
|
||||
self.state["counter"] += 1
|
||||
return "Second branch right right result"
|
||||
|
||||
# Level 3 - Left Left Branch
|
||||
@listen(second_branch_left_left)
|
||||
def third_branch_left_left_left(self):
|
||||
print("Third branch from second left left (left)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch left left left result"
|
||||
|
||||
@listen(second_branch_left_left)
|
||||
def third_branch_left_left_right(self):
|
||||
print("Third branch from second left left (right)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch left left right result"
|
||||
|
||||
# Level 3 - Left Right Branch
|
||||
@listen(second_branch_left_right)
|
||||
def third_branch_left_right_left(self):
|
||||
print("Third branch from second left right (left)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch left right left result"
|
||||
|
||||
@listen(second_branch_left_right)
|
||||
def third_branch_left_right_right(self):
|
||||
print("Third branch from second left right (right)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch left right right result"
|
||||
|
||||
# Level 3 - Right Left Branch
|
||||
@listen(second_branch_right_left)
|
||||
def third_branch_right_left_left(self):
|
||||
print("Third branch from second right left (left)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch right left left result"
|
||||
|
||||
@listen(second_branch_right_left)
|
||||
def third_branch_right_left_right(self):
|
||||
print("Third branch from second right left (right)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch right left right result"
|
||||
|
||||
# Level 3 - Right Right Branch
|
||||
@listen(second_branch_right_right)
|
||||
def third_branch_right_right_left(self):
|
||||
print("Third branch from second right right (left)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch right right left result"
|
||||
|
||||
@listen(second_branch_right_right)
|
||||
def third_branch_right_right_right(self):
|
||||
print("Third branch from second right right (right)")
|
||||
self.state["counter"] += 1
|
||||
return "Third branch right right right result"
|
||||
|
||||
# Final method for visualization
|
||||
@listen(third_branch_left_left_left) # This is the deepest branch in the tree
|
||||
def final_method(self):
|
||||
print("Final method reached!")
|
||||
print(f"Final counter value: {self.state['counter']}")
|
||||
return "Final result"
|
||||
|
||||
|
||||
async def main():
|
||||
flow = BinaryBranchingFlow()
|
||||
# Uncomment this if you want to run the flow with kickoff
|
||||
# await flow.kickoff()
|
||||
flow.visualize()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
@@ -21,7 +21,7 @@ class AndExampleFlow(Flow):
|
||||
|
||||
async def main():
|
||||
flow = AndExampleFlow()
|
||||
await flow.kickoff()
|
||||
flow.visualize()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -20,7 +20,8 @@ class OrExampleFlow(Flow):
|
||||
|
||||
async def main():
|
||||
flow = OrExampleFlow()
|
||||
await flow.kickoff()
|
||||
flow.visualize()
|
||||
# await flow.kickoff()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -35,7 +35,7 @@ class RouterFlow(Flow[ExampleState]):
|
||||
|
||||
async def main():
|
||||
flow = RouterFlow()
|
||||
await flow.kickoff()
|
||||
flow.visualize()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
@@ -27,7 +27,8 @@ class FlexibleExampleFlow(Flow):
|
||||
|
||||
async def main():
|
||||
flow = FlexibleExampleFlow()
|
||||
await flow.kickoff()
|
||||
# await flow.kickoff()
|
||||
flow.visualize()
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
|
||||
Reference in New Issue
Block a user