Tutorial
Welcome to ControlFlow!
ControlFlow is a declarative framework for building agentic workflows. That means that you define the objectives you want an AI agent to complete, and ControlFlow handles the rest. You can think of ControlFlow as a high-level orchestrator for AI agents, allowing you to focus on the logic of your application while ControlFlow manages the details of agent selection, data flow, and error handling.
In this tutorial, we’ll introduce the basics of ControlFlow, including tasks, flows, agents, and more. By the end, you’ll have a solid understanding of how to create and run complex agentic workflows.
The tutorial is divided into the following sections:
- Hello, world: Your first task
- Hello, user: Interacting with users
- Hello, tasks: Chaining tasks together
- Hello, flow: Building a flow
- Hello, agents: Working with agents
Install ControlFlow
To run the code in this tutorial, you’ll need to install ControlFlow and configure API keys for your LLM provider. Please see the installation instructions for more information.
Hello, world
Creating a task
The starting point of every agentic workflow is a Task
. Each task represents an objective that we want an AI agent to complete. Let’s create a simple task to say hello:
If you examine this 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.
Running a task
To run a task to completion, call its .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:
The task is now in a SUCCESSFUL
state, and its result has been updated to "Hello"
. The agent successfully completed the task!
If you run the task a second time, it will immediately return the previous result. That’s because this specific task has already been completed, so ControlFlow will use the existing result instead of running an agent again.
Recap
What we learned
- Tasks represent objectives that we want an AI agent to complete.
- Each task has a
result_type
that specifies the datatype of the result we expect. - Calling
task.run()
assigns the task to an agent, which is responsible for providing a result that satisfies the task’s requirements.
Hello, user
User input
By default, agents cannot interact with (human) users. ControlFlow is designed primarily to be an agentic workflow orchestrator, not a chatbot. However, there are times when user input is necessary to complete a task. In these cases, you can set the 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.
If you run the above code, the agent will ask for your name in your terminal. You can respond with something like “My name is Marvin” or even refuse to respond. The agent will continue to prompt you until it has enough information to complete the task.
This is the essence of an agentic workflow: you declare what you need, and the agent figures out how to get it.
Failing a task
In the previous example, if you refuse to provide your name a few times, your agent will eventually mark the task as failed. Agents only do this when they are unable to complete the task, and it’s up to you to decide how to handle the failure. ControlFlow will raise a ValueError
when a task fails that contains the reason for the failure.
Recap
What we learned
- Setting
user_access=True
allows agents to interact with a user - Pydantic models can be used to represent and validate complex result types
- Agents will continue to work until the task’s requirements are met
- Agents can fail a task if they are unable to complete it
Hello, tasks
Task dependencies
So far, we’ve created and run tasks in isolation. However, agentic workflows are much more powerful when you use the results of one task to inform another. This allows you to build up complex behaviors by chaining tasks together, while still maintaining the benefits of structured, observable workflows.
To see how this works, let’s build a workflow that asks the user for their name, then uses that information to write them a personalized poem:
In this example, we introduced a context
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.
Custom tools
For certain tasks, you may want your agents to use specialized tools or APIs to complete the task.
To add tools to a task, pass a list of Python functions to the 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:
Recap
What we learned
- You can provide additional information to a task using the
context
parameter, including constant values or other tasks - If a task depends on another task, ControlFlow will automatically run the dependencies first
- You can provide custom tools to a task by passing a list of Python functions to the
tools
parameter
Hello, flow
If Tasks
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.
When you run a task outside a flow, as we did in the previous examples, ControlFlow automatically creates a flow context for that run. This is very convenient for testing and interactive use, but you can disable this behavior by setting controlflow.settings.strict_flow_context=True
.
The @flow
decorator
The simplest way to create a flow is by decorating a function with the @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.
Eager execution
Notice that in the above flow, we never explicitly ran the 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:
In this example, we introduced the instructions
parameter for the height
task. This parameter allows you to provide additional instructions to the agent about how to complete the task.
Recap
What we learned
- Flows provide a shared context for all tasks and agents inside the flow
- The
@flow
decorator creates a flow function that can be run anywhere - By default,
@flow
-decorated functions are executed eagerly, meaning all tasks inside the flow are run automatically - You can eagerly run a task inside a flow by calling its
.run()
method - The
instructions
parameter allows you to provide additional instructions to the agent about how to complete the task
Hello, agents
You’ve made it through an entire tutorial on agentic workflows without ever encountering an actual agent! That’s because ControlFlow abstracts away the complexities of agent selection and orchestration, allowing you to focus on the high-level logic of your application.
But agents are the heart of ControlFlow, and understanding how to create and use them is essential to building sophisticated agentic workflows.
Creating an agent
To create an agent, provide at least a name, as well as optional description, instructions, or tools. Here’s an example of creating an agent that specializes in writing technical documentation:
What’s the difference between a description and instructions? The description is a high-level overview of the agent’s purpose and capabilities, while instructions provide detailed guidance on how the agent should complete a task. Agent descriptions can be seen by other agents, but instructions are private, which can affect how agents collaborate with each other.
Assigning an agent to a task
To use an agent to complete a task, assign the agent to the task’s agents
parameter. Here’s an example of assigning the docs_agent
to a task that requires writing a technical document:
When you run the 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.
Assigning multiple agents to a task
You can assign multiple agents to a task by passing a list of agents to the 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
:
When you run the 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
In the above example, we also introduced the 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.
Recap
What we learned
- Agents are autonomous entities that complete tasks on behalf of the user
- You can create an agent by providing a name, description, instructions, and LangChain model
- Assign an agent to a task by passing it to the task’s
agents
parameter - You can assign multiple agents to a task to have them collaborate
What’s next?
Congratulations, you’ve completed the ControlFlow tutorial! You’ve learned how to:
-
Create tasks and run them to completion
-
Interact with users and handle user input
-
Chain tasks together to build complex workflows
-
Create flows to maintain a shared context across multiple tasks
-
Work with agents to complete tasks autonomously
-
Understand ControlFlow’s workflow APIs and execution modes
-
Learn how to use different LLM models