The AI industry is rapidly advancing towards creating solutions using large language models (LLMs) and maximizing the potential of AI models. Companies are seeking tools that seamlessly integrate AI into existing codebases without the hefty costs associated with hiring professionals and acquiring resources. This is where Controlflow comes into play. With ControlFlow, you can develop complex AI applications using just a few lines of code.
In this tutorial, we will explore ControlFlow and use it to build three exciting AI applications. The projects range from a simple text classifier to complex AI with multiple agents, tasks, and flow.
What is ControlFlow?
ControlFlow is a Python framework that provides a structured approach for defining LLM workflows. It consists of three main components for creating AI applications:
- Tasks: These are the fundamental building blocks of AI workflows. They define discrete, well-defined objectives that need to be accomplished by one or more AI agents.
- Agents: These are the intelligent, autonomous entities powering your AI workflows. You can define a model, provide it with custom instructions, and add various tools to create the agents.
- Flows: Flows are responsible for running multiple AI workflows in a specified order. They offer a structured way to manage tasks, agents, tools, and shared contexts.
By using ControlFlow, you can seamlessly integrate AI capabilities into your Python applications, gain more control over AI workflows, and generate structured outputs rather than just text. It allows you to build complex workflows with ease and is incredibly user-friendly. The best part of using ControlFlow is that it enables you to observe the AI model’s decision-making process at every task.
In simple terms, ControlFlow has two significant uses: it orchestrates your LLM workflows and helps you generate structured outputs, giving you more control over AI.
Setting up ControlFlow
We can simply install ControlFlow by typing the following command in the terminal. It will automatically install all the dependencies.
1 |
$ pip install controlflow |
Generate the OpenAI API key and set it as an environment variable.
1 |
$ export OPENAI_API_KEY="your-api-key" |
Before using ControlFlow, ensure it is properly installed. Type the following command in the terminal to view all Python package versions associated with ControlFlow.
1 |
$ controlflow version |
Output:
1 2 3 4 5 6 |
ControlFlow version: 0.9.4 Prefect version: 3.0.2 LangChain Core version: 0.2.40 Python version: 3.10.12 Platform: Linux-6.1.85+-x86_64-<b>with</b>-glibc2.35 Path: /usr/local/lib/python3.10 |
Creating an agent and running the task is quite simple in ControlFlow. In this example, we have created the Horror storytelling agent by providing it with custom instructions. Then, we will use it to run a simple task by providing it with a prompt. Ultimately, we are generating a short story.
1 2 3 4 5 6 7 |
import controlflow as cf teller = cf.Agent(name="Horror Storyteller", model="openai/gpt-4o", instructions="You are an older man telling horror stories to kids.") story = cf.run("Write a short story.", agents=[teller]) |
Here is the result.
If you encounter a RuntimeError
or runtime issue while running the following code in Colab, please rerun the cell once again.
1. Tweet Classification
Tweet Classifier is a popular small project for students, and it usually takes them months to build a proper text classifier. By using ControlFlow, we can create a proper tweet classifier using a few lines of code.
- Create a list of 4 short tweets.
- Setup an agent with custom instructions using the GPT-4-mini model.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import controlflow as cf tweets = [ "Negativity spreads too easily here. #sigh", "Sometimes venting is necessary. #HateTherapy", "Love fills the air today! 💖 #Blessed", "Thankful for all my Twitter friends! 🌟" ] # Create a specialized agent classifier = cf.Agent( name="Tweet Classifier", model="openai/gpt-4o-mini", instructions="You are an expert at quickly classifying tweets.", ) |
- Create a task to classify tweets as “hate” or “love”, with a prompt, result type, agents, and context. We will provide a list of tweets as context.
- Run the task and display the results.
1 2 3 4 5 6 7 8 9 10 11 |
from typing import Literal # Set up a ControlFlow task to classify tweets classifications = cf.run( 'Classify the tweets', result_type=list[Literal['hate', 'love']], agents=[classifier], context=dict(tweets=tweets), ) print(classifications) |
ControlFlow tasks have classified tweets and generated a list of labels.
Let’s display them in a proper way using the color code (red for hate and green for love).
1 2 3 4 5 6 7 8 9 10 11 |
# ANSI escape code for green text GREEN = '\033[92m' RED = '\033[91m' RESET = '\033[0m' # Print tweets alongside their classifications with classification in green for twt, cls in zip(tweets, classifications): if cls == 'hate': print(f"Tweet: {twt} | Classification: {RED}{cls.upper()}{RESET}\n") else: print(f"Tweet: {twt} | Classification: {GREEN}{cls.upper()}{RESET}\n") |
Two tweets are labeled “hate,” and two tweets are labeled “love.” This is quite accurate and gives output in a structured format that we can integrate into our existing applications.
2. Book Recommender
Two years ago, I built a Goodreads application on Deepnote, which sparked my interest in creating a book recommendation application.
In this project, we will define a data model for book recommendations using the Pydantic model. This model will ensure that each recommendation includes a title, author, publication year, and genre with the correct field types.
The recommend_books
function will use this Pydantic model to define the result type and generate a list of book recommendations based on a specified genre and number of books.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import controlflow as cf from pydantic import BaseModel, Field class BookRecommendation(BaseModel): title: str = Field(description='The title of the recommended book') author: str = Field(description='The author of the book') year_published: int = Field(description='The year the book was published') genre: str = Field(description='The genre of the book') def recommend_books(genre: str, count: int) -> list[BookRecommendation]: return cf.run( f"Recommend {count} books in the {genre} genre with their details", result_type=list[BookRecommendation], context={"genre": genre, "count": count} ) |
We will now generate 5 Science fiction books.
1 2 |
recommended_books = recommend_books(genre="Science Fiction", count=5) print(recommended_books) |
The agent first generated the recommended books in text form, converted them into JSON format, and finally transformed them into the Pydentic model type, which contains all the data with the correct data type.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
[ BookRecommendation(title='Dune', author='Frank Herbert', year_published=1965, genre='Science Fiction'), BookRecommendation( title='Neuromancer', author='William Gibson', year_published=1984, genre='Science Fiction' ), BookRecommendation( title='The Left Hand of Darkness', author='Ursula K. Le Guin', year_published=1969, genre='Science Fiction' ), BookRecommendation(title='Foundation', author='Isaac Asimov', year_published=1951, genre='Science Fiction'), BookRecommendation( title='Snow Crash', author='Neal Stephenson', year_published=1992, genre='Science Fiction' ) ] |
To convert the output into the JSON format, we only have to use the ‘.model_dump_json(indent=2)` function.
1 2 |
for book in recommended_books: print(book.model_dump_json(indent=2)) |
We have an output in JSON format that can be easily integrated into a web application or any code base.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
{ "title": "Dune", "author": "Frank Herbert", "year_published": 1965, "genre": "Science Fiction" } { "title": "Neuromancer", "author": "William Gibson", "year_published": 1984, "genre": "Science Fiction" } { "title": "The Left Hand of Darkness", "author": "Ursula K. Le Guin", "year_published": 1969, "genre": "Science Fiction" } { "title": "Foundation", "author": "Isaac Asimov", "year_published": 1951, "genre": "Science Fiction" } { "title": "Snow Crash", "author": "Neal Stephenson", "year_published": 1992, "genre": "Science Fiction" } |
3. Travel Agent
In this project, we are going to connect two tasks. One task will generate the destination based on user preferences, and then use this destination to create a detailed itinerary. To create this project, we will use multiple agents, multiple tasks, and combine them using a flow.
We will define two model classes for travel preferences and travel itinerary. One will be used for user input, and the other will be used as a structure for output.
Also, we will create a flow function that contains two tasks: one for generating the destination and another for planning the trip based on the user’s preferences.
The process will involve generating the destinations and then using the destination to plan a trip for “n” days.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
import controlflow as cf from pydantic import BaseModel from typing import List # Create specialized agents destination_recommender = cf.Agent(name="DestinationRecommender", model="openai/gpt-4o-mini") itinerary_planner = cf.Agent(name="ItineraryPlanner", model="openai/gpt-4o") # Define our data models class TravelPreferences(BaseModel): preferred_activities: List[str] budget: str # e.g., "low", "medium", "high" travel_duration: int # in days preferred_region: str # e.g., "Asia", "Europe", "South America" class TravelItinerary(BaseModel): destination: str daily_schedule: List[str] @cf.flow def create_travel_itinerary(preferences: TravelPreferences) -> TravelItinerary: # Recommend a single destination within the preferred region destination = cf.run( "Suggest a travel destination based on user preference.", agents=[destination_recommender], result_type=str, context={ "preferred_activities": preferences.preferred_activities, "budget": preferences.budget, "travel_duration": preferences.travel_duration, "preferred_region": preferences.preferred_region } ) # Plan daily schedule using the destination daily_schedule = cf.run( "Create a daily schedule for the trip at the chosen destination", agents=[itinerary_planner], result_type=List[str], context={ "destination": destination, "travel_duration": preferences.travel_duration, "preferred_activities": preferences.preferred_activities } ) return TravelItinerary(destination=destination, daily_schedule=daily_schedule) |
When you are used to coding ControFlow workflows, you will know that it gets quite easy to create a new application and project with just a few lines of code.
We will create user preferences by providing the preferred activity, budget, travel duration, and preferred region. Then, we will use these user preferences to run the flow.
1 2 3 4 5 6 7 8 9 |
preferences = TravelPreferences( preferred_activities=["beach", "local cuisine"], budget="medium", travel_duration=7, preferred_region="Europe" ) # Create a personalized travel itinerary itinerary = create_travel_itinerary(preferences) |
We will now print out the destination and the daily itinerary.
1 2 3 4 5 6 |
print("Recommended Destination:") print(f"- {itinerary.destination}") print("\n\nDaily Schedule:") for schedule in itinerary.daily_schedule: print(f"{schedule}") |
Our destination is the Amalfi Coast in Italy, and we have a really good travel plan.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
Recommended Destination: - Based on your preferences for beach activities and local cuisine, along with a medium budget and a travel duration of 7 days in Europe, I recommend visiting the Amalfi Coast in Italy. This stunning destination offers beautiful beaches, delicious local dishes, and picturesque towns such as Positano and Ravello, perfect for a memorable vacation. Daily Schedule: Day 1: Arrival and exploration of Positano - Arrive in Positano and check into your accommodation - Spend the afternoon relaxing on the Spiaggia Grande beach - Evening: Enjoy dinner at a local restaurant, trying dishes like fresh seafood and pasta Day 2: Beach day and local cuisine in Positano - Morning: Visit Fornillo Beach, a quieter alternative to Spiaggia Grande - Afternoon: Take a cooking class to learn how to make traditional Italian dishes - Evening: Stroll through the town and enjoy gelato from a local gelateria Day 3: Day trip to Amalfi and Ravello - Morning: Take a boat or bus to Amalfi - Visit the Amalfi Cathedral and explore the town - Afternoon: Continue to Ravello and visit Villa Rufolo and its gardens - Evening: Return to Positano and dine at a local trattoria Day 4: Capri Island excursion - Morning: Take a ferry to Capri - Explore the Blue Grotto and take a chairlift to Monte Solaro - Afternoon: Wander around the town of Capri and have lunch - Evening: Return to Positano and relax at your accommodation Day 5: Beach and leisure day in Positano - Morning: Relax at Arienzo Beach, known for its clear waters - Afternoon: Enjoy a leisurely lunch at a beachside café - Evening: Take a sunset boat tour along the coast Day 6: Explore Sorrento - Morning: Travel to Sorrento by bus or ferry - Visit the historic center, including Piazza Tasso and the Marina Grande - Afternoon: Enjoy a meal at a local restaurant and sample limoncello - Evening: Return to Positano Day 7: Departure - Morning: Spend your last hours in Positano shopping for souvenirs - Afternoon: Depart for the airport or onward destination |
We can easily convert this project into a product and build a website that provides users with travel recommendations.
If you are facing issues running the coder, please check out the ControlFlow Tutorial Colab notebook.
Final Thoughts
The ControlFlow is still new, and you might encounter issues when running some of the examples available in the documentation. The good news is that there’s a dedicated team working to resolve these issues in real time, ensuring a smooth onboarding process. All you have to do is create an issue on the GitHub repository at Issues · PrefectHQ/ControlFlow. I faced some issues while running it on Colab, but they were resolved within two hours, thanks to Jeremiah Lowin.
I believe we should be working towards building AI solutions that provide business value, instead of solely focusing on improving model performance by a few percentage points. To realize the full potential of AI, we need tools like ControlFlow, LangChain, and other AI frameworks that can help us build complex AI applications with just a few lines of code.
No comments yet.