Skip to content

Commit

Permalink
WIP tutorial
Browse files Browse the repository at this point in the history
  • Loading branch information
jlowin committed May 16, 2024
1 parent 5a8d65e commit 6264089
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 12 deletions.
26 changes: 16 additions & 10 deletions docs/introduction.mdx
Original file line number Diff line number Diff line change
@@ -1,58 +1,64 @@
---
title: Why ControlFlow?
title: What is ControlFlow?
---

**ControlFlow is a framework for orchestrating agentic LLM workflows.**

<Note>
An **agentic workflow** is a process that delegates at least some of its work to an LLM agent. An agent is an autonomous entity that is invoked repeatedly to make decisions and perform complex tasks. To learn more, see the [AI glossary](/glossary/agentic-workflow).
An **agentic workflow** is a process that delegates at least some of its work
to an LLM agent. An agent is an autonomous entity that is invoked repeatedly
to make decisions and perform complex tasks. To learn more, see the [AI
glossary](/glossary/agentic-workflow).
</Note>

LLMs are powerful AI models that can understand and generate human-like text, enabling them to perform a wide range of tasks. However, building applications with LLMs can be challenging due to their complexity, unpredictability, and potential for hallucinating or generating irrelevant outputs.

ControlFlow provides a structured and intuitive way to create sophisticated agentic workflows while adhereing to traditional software engineering best practices. The resulting applications are observable, controllable, and easy to trust.



## Design principles

ControlFlow's design is informed by a strong opinion: LLMs are powerful tools, but they are most effective when applied to small, well-defined tasks within a structured workflow. This approach mitigates many of the challenges associated with LLMs, such as hallucinations, biases, and unpredictable behavior, while also making it easier to debug, monitor, and control the application.

This belief leads to three core design principles that underpin ControlFlow's architecture:

### 🛠️ Specialized over generalized

ControlFlow advocates for the use of **specialized, single-purpose LLMs** rather than monolithic models that try to do everything. By assigning specific tasks to purpose-built models, ControlFlow ensures that the right tool is used for each job, leading to more efficient, cost-effective, and higher-quality results.

### 🎯 Outcome over process

ControlFlow embraces a **declarative approach to defining AI workflows**, allowing developers to focus on the desired outcomes rather than the intricacies of steering LLM behavior. By specifying tasks and their requirements using intuitive constructs, developers can express what needs to be done without worrying about the details of how it will be accomplished.

### 🎛️ Control over autonomy
ControlFlow recognizes the importance of balancing AI capabilities with traditional software development practices. Instead of relying on end-to-end AI systems that make all workflow decisions autonomously, ControlFlow allows as much or as little AI participation as needed, ensuring that developers **maintain visibility and control** over their applications.

ControlFlow recognizes the importance of balancing AI capabilities with traditional software development practices. Instead of relying on end-to-end AI systems that make all workflow decisions autonomously, ControlFlow allows as much or as little AI participation as needed, ensuring that developers **maintain visibility and control** over their applications.

## Why ControlFlow?

## Key features
The three design principles of ControlFlow lead to a number of key features that make it a powerful tool for building AI-powered applications:

### 🧩 Task-centric architecture

ControlFlow breaks down AI workflows into discrete, self-contained tasks, each with a specific objective and set of requirements. This declarative, modular approach lets developers focus on the high-level logic of their applications while allowing the framework to manage the details of coordinating agents and data flow between tasks.

### 🕵️ Agent orchestration

ControlFlow's runtime engine handles the orchestration of specialized AI agents, assigning tasks to the most appropriate models and managing the flow of data between them. This orchestration layer abstracts away the complexities of coordinating multiple AI components, allowing developers to focus on the high-level logic of their applications.

### 🔍 Native debugging and observability
### 🔍 Native debugging and observability

ControlFlow prioritizes transparency and ease of debugging by providing native tools for monitoring and inspecting the execution of AI tasks. Developers can easily track the progress of their workflows, identify bottlenecks or issues, and gain insights into the behavior of individual agents, ensuring that their AI applications are functioning as intended.

### 🤝 Seamless integration

ControlFlow is designed to integrate seamlessly with existing Python codebases, treating AI tasks as first-class citizens in the application logic. The `Task` class provides a clean interface for defining the inputs, outputs, and requirements of each task, making it easy to incorporate AI capabilities into traditional software workflows. This seamless integration allows for a gradual and controlled adoption of AI, reducing the risk and complexity of introducing AI into existing systems.

Together, these features make ControlFlow a powerful and flexible framework for building AI-powered applications that are transparent, maintainable, and aligned with software engineering best practices.



## Why not "super-agents"?

Many agentic LLM frameworks rely on monolithic "super-agents": powerful, unconstrained models that are expected to achieve their goals by autonomously handling a wide range of tasks, tools, and behaviors. The resulting workflows are opaque, unpredictable, and difficult to debug.

This approach naively assumes that the technology is more advanced than it actually is. LLMs feel like magic because they can perform a wide variety of non-algorithmic tasks, but they are still fundamentally limited when it comes to generalizing beyond their traning data and techniques. Moreover, the failure modes of agentic LLMs are difficult to identify, let alone fix, making them difficult to trust in production environments or with mission-critical tasks.

In contrast to these "super-agent" approaches, ControlFlow promotes a modular, decoupled architecture where specialized agents are orchestrated to perform well-defined tasks, after which traditional software regains control of the application. This approach results in workflows that are more transparent, controllable, and debuggable, setting ControlFlow apart from other frameworks.

2 changes: 1 addition & 1 deletion docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"introduction",
"concepts",
"installation",
"quickstart"
"tutorial"
]
},
{
Expand Down
1 change: 0 additions & 1 deletion docs/quickstart.mdx

This file was deleted.

153 changes: 153 additions & 0 deletions docs/tutorial.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
---
title: "Tutorial: Tasks"
---

Welcome to ControlFlow! In this tutorial, we'll explore the fundamental building block of ControlFlow workflows: `Tasks`.

We'll learn how to create tasks, define dependencies between them, and control their execution within a flow. By the end of this tutorial, you'll have a solid understanding of how to break down complex problems into manageable tasks and orchestrate them effectively.

## Creating a Simple Task

At the core of ControlFlow are `Task` objects, which represent discrete units of work to be performed by AI agents. Let's start by creating a simple task that generates a list of top attractions in Washington D.C.:

```python
from controlflow import flow, Task


@flow
def generate_attractions(destination: str):
attractions = Task(
objective="Generate a list of 10 must-see attractions at the destination",
result_type=list[str],
)
return attractions


attractions = generate_attractions("Washington D.C.")
print(attractions)
# ['Lincoln Memorial', 'National Mall', 'United States Capitol', ...]
```

In this example, we:

- Define a function `generate_attractions()` and decorate it with `@flow`, turning it into a ControlFlow workflow
- Create a `Task` object named `attractions` with an objective and result type of `list[str]`. Tasks have access to their flow's context, which is why the task knew about the `destination`.
- Return the `attractions` task from the flow function

When we call `generate_attractions()`, ControlFlow automatically detects and runs the task. When a task is returned from a flow, it is automatically resolved into its result, which is why we received a list of attractions as the output.

## Defining Task Dependencies

Complex workflows often involve multiple tasks with dependencies between them. ControlFlow makes it easy to define these dependencies and ensure tasks are executed in the correct order. Let's expand our example to generate a personalized travel itinerary for Washington D.C.:

```python
@flow
def generate_itinerary(destination: str):
attractions = Task(
objective="Generate a list of 10 must-see attractions at the destination",
result_type=list[str],
)

itinerary = [
Task(
objective=f"Make an itinerary for day {day} in the destination, focusing on attractions in the provided `area`",
context=dict(attractions=attractions, destination=destination, area=area),
result_type=str,
)
for day, area in enumerate(['National Mall', 'Tidal Basin', 'Downtown'], start=1)
]

return itinerary


itinerary = generate_itinerary('Washington D.C.')
for day, activity in enumerate(itinerary, start=1):
print(f"Day {day}:\n\n{activity}\n\n")

# Day 1:
#
# 8:00 AM - 9:00 AM: Lincoln Memorial
# 9:00 AM - 10:00 AM: Washington Monument
# 10:00 AM - 12:00 PM: National Museum of American History
# ...
```

In this example, we:

- Create an `attractions` task that generates a list of top attractions in Washington D.C.
- Create an `itinerary` task for each day of the trip, focusing on attractions in specific areas of the city and using the `attractions` task as context

By passing the `attractions` task as context to the `itinerary` tasks, we define a dependency between them. ControlFlow ensures that the `attractions` task is executed before the `itinerary` tasks, and the `itinerary` tasks can access the result of the `attractions` task.

## Controlling Task Execution

Sometimes, you might want to control the execution of tasks within a flow based on the results of previous tasks. You can do this by calling `task.run()` to manually run a task and get its result:

```python
@flow
def generate_dc_itinerary_with_recommendations():
trip = generate_dc_itinerary()

budget = Task(
objective="Ask the user for their daily budget for meals and activities in Washington D.C.",
result_type=float,
user_access=True,
)
budget.run()

cuisine = Task(
objective="Ask the user for their preferred cuisine type for dining in Washington D.C.",
result_type=str,
user_access=True,
)
cuisine.run()

recommendations = [
Task(
objective="Generate a list of restaurant recommendations for `cuisine` cuisine in Washington D.C. for a budget of `budget` per day",
context=dict(cuisine=cuisine.result, budget=budget.result),
result_type=list[str],
)
for _ in trip['itinerary']
]

return dict(trip=trip, recommendations=recommendations)

trip_with_recs = generate_dc_itinerary_with_recommendations()
print(f"Top attractions: {', '.join(trip_with_recs['trip']['attractions'])}")
for day, (activity, recs) in enumerate(zip(trip_with_recs['trip']['itinerary'], trip_with_recs['recommendations']), start=1):
print(f"Day {day}: {activity}")
print(f"Restaurant recommendations: {', '.join(recs)}")

# Output:
# Top attractions: Lincoln Memorial, National Mall, United States Capitol, White House, Smithsonian National Air and Space Museum, Washington Monument, Smithsonian National Museum of Natural History, Tidal Basin, Vietnam Veterans Memorial, Library of Congress
# Day 1: Start your day at the Lincoln Memorial, then walk along the National Mall, taking in the sights of the Washington Monument and the Reflecting Pool. Visit the Smithsonian National Museum of American History and the Smithsonian National Museum of Natural History.
# Restaurant recommendations: Ben's Chili Bowl, Founding Farmers, Busboys and Poets
# Day 2: Explore the Tidal Basin area, starting with the Jefferson Memorial. Take a stroll around the Tidal Basin to enjoy the cherry blossoms (if in season). Visit the Martin Luther King Jr. Memorial and the Franklin Delano Roosevelt Memorial.
# Restaurant recommendations: Old Ebbitt Grill, The Hamilton, Jaleo
# Day 3: Spend the day in Downtown Washington D.C. Start at the White House Visitor Center, then take a guided tour of the United States Capitol. Visit the Library of Congress and the Supreme Court Building. End your day with a visit to the Smithsonian National Portrait Gallery.
# Restaurant recommendations: Rasika, Zaytinya, Oyamel
```

In this example, we:

- Call the `generate_dc_itinerary()` flow to get the top attractions and daily itinerary
- Create a `budget` task to ask the user for their daily budget, using `user_access=True` to allow user interaction
- Create a `cuisine` task to ask the user for their preferred cuisine type
- Manually run the `budget` and `cuisine` tasks using `task.run()` to get their results
- Create a `recommendations` list comprehension that generates a task for each day of the trip, providing restaurant recommendations based on the user's budget and preferred cuisine
- Return a dictionary with the original `trip` and the `recommendations`

By calling `task.run()`, we can control the execution flow based on task results, allowing for more dynamic and responsive workflows.

## Next Steps

Congratulations on completing this introduction to tasks in ControlFlow! You've learned how to:

- Create simple tasks
- Define dependencies between tasks using context
- Control task execution within a flow using `task.run()`

In the next tutorial, we'll dive deeper into the world of AI agents and explore how they can be used to bring your workflows to life. Stay tuned!

If you can't wait to learn more, check out the [ControlFlow Concepts](/concepts) guide and [API Reference](/api-reference) for additional information and examples. Happy engineering!

0 comments on commit 6264089

Please sign in to comment.