Task
. Each task represents an objective that we want an AI agent to complete. Let’s create a simple task to say hello:
Task
object, you’ll notice a few important things: it’s in an INCOMPLETE
state and while it has no result
value, its result_type
is a string. This means that the task has not been completed yet, but when it does, the result will be a string.
.run()
method. This will set up an agentic loop, assigning the task to an agent and waiting for it to complete. The agent’s job is to provide a result that satisfies the task’s requirements as quickly as possible.
Let’s run our task and examine it to see what happened:
SUCCESSFUL
state, and its result has been updated to "Hello"
. The agent successfully completed the task!
result_type
that specifies the datatype of the result we expect.task.run()
assigns the task to an agent, which is responsible for providing a result that satisfies the task’s requirements.user_access
parameter to True
when creating a task.
Let’s create a task to ask the user for their name. We’ll also create a Pydantic model to represent the user’s name, which will allow us to apply complex typing or validation, if needed.
ValueError
when a task fails that contains the reason for the failure.
user_access=True
allows agents to interact with a usercontext
parameter for the poem
task. This parameter allows us to specify additional information that the agent can use to complete the task, which could include constant values or other tasks. If the context value includes a task, ControlFlow will automatically infer that the second task depends on the first.
One benefit of this approach is that you can run any task without having to run its dependencies explicitly. ControlFlow will automatically run any dependencies before executing the task you requested. In the above example, we only ran the poem
task, but ControlFlow automatically ran the name
task first, then passed its result to the poem
task’s context. We can see that both tasks were successfully completed and have result
values.
tools
parameter of the task. These functions will be available to the agent when it runs the task, allowing it to use them to complete the task more effectively. The only requirement is that the functions are type-annotated and have a docstring, so that the agent can understand how to use them.
In this example, we create a task to roll various dice, and provide a roll_die
function as a tool to the task, which the agent can use to complete the task:
context
parameter, including constant values or other taskstools
parameterTasks
are the building blocks of an agentic workflow, then Flows
are the glue that holds them together.
Each flow represents a shared history and context for all tasks and agents in a workflow. This allows you to maintain a consistent state across multiple tasks, even if they are not directly dependent on each other.
controlflow.settings.strict_flow_context=True
.@flow
decorator@flow
decorator. This will automatically create a shared flow context for all tasks inside the flow. Here’s how we would rewrite the last example with a flow function:
hello_flow
is now a portable agentic workflow that can be run anywhere. On every call, it will automatically create a flow context for all tasks inside the flow, ensuring that they share the same state and history.
name
task, nor did we access its result
attribute at the end. That’s because @flow
-decorated functions are executed eagerly by default. This means that when you call a flow function, all tasks inside the flow are run automatically and any tasks returned from the flow are replaced with their result values.
Most of the time, you’ll use eagerly-executed @flows
and lazily-executed Tasks
in your workflows. Eager flows are more intuitive and easier to work with, since they behave like normal functions, while lazy tasks allow the orchestrator to take advantage of observed dependencies to optimize task execution and agent selection, though it’s possible to customize both behaviors.
However, you’ll frequently need a task’s result inside your flow function. In this case, you can eagerly run the task by calling its .run()
method, then use the task’s result
attribute as needed.
In this example, we collect the user’s height, then use it to determine if they are tall enough to receive a poem:
instructions
parameter for the height
task. This parameter allows you to provide additional instructions to the agent about how to complete the task.@flow
decorator creates a flow function that can be run anywhere@flow
-decorated functions are executed eagerly, meaning all tasks inside the flow are run automatically.run()
methodinstructions
parameter allows you to provide additional instructions to the agent about how to complete the taskagents
parameter. Here’s an example of assigning the docs_agent
to a task that requires writing a technical document:
technical_document
task, ControlFlow will automatically assign the docs_agent
to complete the task. The agent will use the instructions provided to generate a technical document that meets the task’s requirements.
agents
parameter. This allows you to leverage the unique capabilities of different agents to complete a task more effectively. Here’s an example of assigning an editor agent to review the technical document created by the docs_agent
:
technical_document
task, ControlFlow will assign both the docs_agent
and the editor_agent
to complete the task. The docs_agent
will generate the technical document, and the editor_agent
will review and edit the document to ensure its accuracy and readability. By default, they will be run in round-robin fashion, but you can customize the agent selection strategy by passing a function as the task’s agent_strategy
.
instructions
context manager. This allows you to provide additional instructions to the agents about how to complete any task. As long as the context manager is active, any tasks/agents run within its scope will follow the provided instructions. Here, we use it to limit the length of the technical document to 5 sentences in order to keep the example manageable.
agents
parameter