From 471fcf790fe31734395707ccba19e501d2aeb051 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Sat, 7 Sep 2024 16:21:41 -0400 Subject: [PATCH] Consolidate concept docs --- .../assigning-agents.mdx => agents.mdx} | 87 ++++++++- docs/concepts/agents/agents.mdx | 24 --- docs/concepts/agents/creating-agents.mdx | 62 ------- docs/concepts/concepts.mdx | 59 ++++-- docs/concepts/flows.mdx | 169 ++++++++++++++++++ docs/concepts/flows/creating-flows.mdx | 58 ------ docs/concepts/flows/flows.mdx | 51 ------ .../{tasks/creating-tasks.mdx => tasks.mdx} | 115 ++++++------ docs/concepts/tasks/tasks.mdx | 31 ---- docs/mint.json | 40 +---- .../collaborating-agents.mdx | 4 +- .../flows => patterns}/instructions.mdx | 6 +- .../tasks => patterns}/running-tasks.mdx | 44 ++--- .../tasks => patterns}/task-results.mdx | 3 +- docs/style_guide.mdx | 49 ++--- .../prompt_templates/agent.jinja | 2 +- 16 files changed, 422 insertions(+), 382 deletions(-) rename docs/concepts/{agents/assigning-agents.mdx => agents.mdx} (54%) delete mode 100644 docs/concepts/agents/agents.mdx delete mode 100644 docs/concepts/agents/creating-agents.mdx create mode 100644 docs/concepts/flows.mdx delete mode 100644 docs/concepts/flows/creating-flows.mdx delete mode 100644 docs/concepts/flows/flows.mdx rename docs/concepts/{tasks/creating-tasks.mdx => tasks.mdx} (78%) delete mode 100644 docs/concepts/tasks/tasks.mdx rename docs/{concepts/agents => patterns}/collaborating-agents.mdx (97%) rename docs/{concepts/flows => patterns}/instructions.mdx (93%) rename docs/{concepts/tasks => patterns}/running-tasks.mdx (70%) rename docs/{concepts/tasks => patterns}/task-results.mdx (99%) diff --git a/docs/concepts/agents/assigning-agents.mdx b/docs/concepts/agents.mdx similarity index 54% rename from docs/concepts/agents/assigning-agents.mdx rename to docs/concepts/agents.mdx index bb38071a..7278ec4f 100644 --- a/docs/concepts/agents/assigning-agents.mdx +++ b/docs/concepts/agents.mdx @@ -1,17 +1,89 @@ --- -title: Assigning Agents to Tasks -sidebarTitle: Assigning to Tasks +title: Agents +description: The intelligent workers in your AI workflows. +icon: robot --- -Agents must be assigned to tasks in order to work on them. +Agents are the intelligent, autonomous entities that power your AI workflows in ControlFlow. They represent AI models capable of understanding instructions, making decisions, and completing tasks. -Use the `agents` parameter when creating a task to assign agents. Each task requires at least one assigned agent, and will use a default agent if none are provided. Agents can be assigned to multiple tasks, and tasks can have multiple agents. +```python +import controlflow as cf + +agent = cf.Agent("Marvin") +``` +## What are agents? +Agents in ControlFlow are configurable AI entities, each with its own identity, capabilities, and even personality. They act as the "workers" in your AI workflows, responsible for executing tasks and making decisions based on their assigned objectives and available tools. + +You can think of each agent as a portable LLM configuration. When you assign one or more agents to a task, they will collaborate to complete the task according to the instructions and tools you provide. + + +## Creating agents + +To create an agent, use the `Agent` class. + +```python +import controlflow as cf + +agent = cf.Agent(name="Marvin") +``` + +A more complex agent can be created by providing additional configuration. This agent shows almost every possible configuration option: + +```python +import controlflow as cf +from langchain_openai import ChatOpenAI + +agent = cf.Agent( + name="Data Analyst", + description="An AI agent specialized in data analysis", + instructions=( + "Perform data analysis tasks efficiently and accurately. " + "Browse the web for data and use Python to analyze it." + ), + tools=[cf.tools.web.get_url, cf.tools.code.python], + model=ChatpOpenAI('gpt-4o-mini'), + interactive=True, +) +``` + +## Agent properties + +### Name + +An agent's name is an identifier that is visible to other agents in the workflow. It is used to distinguish between agents and for logging and debugging purposes. If possible, keep agent names unique within a flow to avoid confusion. While agents do have deterministic IDs that can be used to disambiguate two agents with the same name, they will often use names when interacting with each other. + +### Description + +A description is a brief summary of the agent's role or specialization. This information is visible to other agents, and helps them understand the agent's capabilities and expertise. + +### Instructions + +Instructions are specific instructions or guidelines for the agent to follow during task execution. These instructions are private and not shared with other agents. + +### Tools + +Tools are Python functions that the agent can call to perform specific actions or computations. They are defined as a list of functions when creating an agent, and can be used to enhance the agent's capabilities. The agent will have access to these tools in every task they are assigned to. If a task defines additional tools, the agent will have access to those as well. + +### Model + +Each agent has a model, which is the LLM that powers the agent responses. This allows you to choose the most suitable model for your needs, based on factors such as performance, latency, and cost. + +ControlFlow supports any LangChain LLM that supports chat and function calling. For more details on how to configure models, see the [LLMs guide](/guides/llms). + +### Interactivity + +By default, agents have no way to communicate with users. If you want to chat with an agent, set `interactive=True`. By default, this will let the agent communicate with users on the command line. + +To learn more, see the [Interactivity guide](/patterns/interactivity). + +## Assigning agents to tasks + +Agents must be assigned to tasks in order to work on them. You can assign agents by passing them to the `agents` parameter when creating a task. Each task requires at least one assigned agent, and will use a default agent if none are provided. Agents can be assigned to multiple tasks, and tasks can have multiple agents. -## Assigning agents ### Tasks with no agents -If you do not assign any agents to a task, it will determine its agents at runtimeaccording to the following rules: +If you do not assign any agents to a task, it will determine its agents at runtime according to the following rules: 1. If the task has a parent, it will use the parent's agents. 2. If the task's flow has a default agent, it will use that agent. @@ -124,8 +196,9 @@ temporary positive outcomes, despite the overall bleak and discouraging reality. +When tasks have multiple agents, it's important to understand how they collaborate (and to provide them with clear instructions to guide that behavior). To learn more, see the [collaboration](/patterns/collaborating-agents) doc. -## Completion agents +#### Assigning completion agents By default, every agent assigned to a task will be given tools for marking the task as successful or failed. If you want to restrict completion tools to a specific agent or agents, you can do so by setting the task's `completion_agents`. diff --git a/docs/concepts/agents/agents.mdx b/docs/concepts/agents/agents.mdx deleted file mode 100644 index 60796ca4..00000000 --- a/docs/concepts/agents/agents.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: What are Agents? -sidebarTitle: Introduction ---- - -Agents are the intelligent, autonomous entities that power your AI workflows. - -Agents represent AI models capable of understanding instructions, making decisions, and completing tasks. Think of agents as your AI workforce, each potentially specialized for different types of work. - - -Each agent in ControlFlow is a configurable entity with its own identity, capabilities, and even personality. Agents can even represent different LLM models, allowing you to optimize your AI workflows to always use the most appropriate model for the task at hand. - -## Why Agents Matter - -Agents are fundamental to ControlFlow's approach to AI workflows for three critical reasons: - -1. **Portable Configuration**: Agents encapsulate how we interact with LLMs, providing a consistent and portable way to configure AI behavior. This abstraction allows you to define specialized AI entities that can be reused across different tasks and workflows, ensuring consistency and reducing complexity in your AI applications. - -2. **Specialization and Expertise**: Agents can be tailored for specific domains or tasks, allowing you to create AI entities with deep, focused knowledge. This specialization leads to more accurate and efficient task completion, mimicking how human experts collaborate in complex projects. By combining multiple specialized agents, you can tackle complex, multi-faceted problems that a single, general-purpose AI might struggle with. - -3. **Structured Collaboration**: When combined with ControlFlow's flow management, agents provide a powerful framework for organizing the flow of information and context between AI entities. This structured approach to agent collaboration enables more sophisticated problem-solving, allowing you to break down complex tasks into manageable steps and leverage the strengths of different agents at each stage of the process. - -By leveraging these key aspects of agents in ControlFlow, you can create more powerful, flexible, and manageable AI workflows that can adapt to a wide range of challenges and use cases. - diff --git a/docs/concepts/agents/creating-agents.mdx b/docs/concepts/agents/creating-agents.mdx deleted file mode 100644 index 0996a9f4..00000000 --- a/docs/concepts/agents/creating-agents.mdx +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: Creating Agents ---- - -Agents are a key concept in ControlFlow, representing the AI entities responsible for executing tasks within a workflow. Each agent has its own set of properties, methods, and capabilities that define its behavior and role in the flow. - -## Creating agents - -To create an agent, use the `Agent` class. - -```python -import controlflow as cf - -agent = cf.Agent(name="Marvin") -``` - -A more complex agent can be created by providing additional configuration. This agent shows almost every possible configuration option: - -```python -import controlflow as cf -from langchain_openai import ChatOpenAI - -agent = cf.Agent( - name="Data Analyst", - description="An AI agent specialized in data analysis", - instructions=( - "Perform data analysis tasks efficiently and accurately. " - "Browse the web for data and use Python to analyze it." - ), - tools=[cf.tools.web.get_url, cf.tools.code.python], - model=ChatpOpenAI('gpt-4o-mini'), - interactive=True, -) -``` - -## Agent properties - -### Name - -An agent's name is an identifier that is visible to other agents in the workflow. It is used to distinguish between agents and for logging and debugging purposes. If possible, keep agent names unique within a flow to avoid confusion. While agents do have deterministic IDs that can be used to disambiguate two agents with the same name, they will often use names when interacting with each other. - -### Description - -A description is a brief summary of the agent's role or specialization. This information is visible to other agents, and helps them understand the agent's capabilities and expertise. - -### Instructions - -Instructions are specific instructions or guidelines for the agent to follow during task execution. These instructions are private and not shared with other agents. - -### Tools - -Tools are Python functions that the agent can call to perform specific actions or computations. They are defined as a list of functions when creating an agent, and can be used to enhance the agent's capabilities. The agent will have access to these tools in every task they are assigned to. If a task defines additional tools, the agent will have access to those as well. - -### Model - -Each agent has a model, which is the LLM that powers the agent responses. This allows you to choose the most suitable model for your needs, based on factors such as performance, latency, and cost. - -ControlFlow supports any LangChain LLM that supports chat and function calling. For more details on how to configure models, see the [LLMs guide](/guides/llms). - -### Interactive - -By default, agents have no way to communicate with users. If you want to chat with an agent, set `interactive=True`. By default, this will let the agent communicate with users on the command line. diff --git a/docs/concepts/concepts.mdx b/docs/concepts/concepts.mdx index 464d68b4..597979bd 100644 --- a/docs/concepts/concepts.mdx +++ b/docs/concepts/concepts.mdx @@ -1,7 +1,7 @@ --- -title: Core Concepts +title: Core concepts sidebarTitle: Overview -description: The building blocks of agentic workflows. +description: The building blocks of agentic workflows --- ControlFlow is a framework for building AI workflows that bridges the gap between @@ -18,29 +18,58 @@ of AI while maintaining fine-grained control over your applications. Tasks represent the structured side of ControlFlow. They are specific, well-defined objectives that form the backbone of your workflow. Tasks encapsulate the "what" and "how" of your AI-driven operations, providing a -clear, programmatic structure. By defining expected result types and validation -criteria, Tasks ensure that AI outputs align with your application's requirements. +clear, programmatic structure. -To learn more about tasks, see the [Tasks](/concepts/tasks) section. +Key features of Tasks: +- Define specific objectives for AI to accomplish +- Specify expected result types and validation criteria +- Can include instructions, context, and tools for execution +- Serve as checkpoints in your workflow + +Learn more in the [Tasks](/concepts/tasks) section. ## 🦾 Agents Agents embody the unstructured, natural language side of ControlFlow. They are AI-powered entities capable of understanding and generating human-like text, -bringing flexibility and adaptability to your workflows. Agents can be -specialized for specific tasks, have access to different tools, or even -represent different LLM models, allowing you to optimize your workflow for -various requirements. Assign agents to tasks to determine "who" will execute -your work. +bringing flexibility and adaptability to your workflows. + +Key features of Agents: +- Represent configurable AI entities with their own identity and capabilities +- Can be specialized for specific tasks or have access to different tools +- Collaborate to complete tasks according to provided instructions +- Can be interactive, allowing communication with users +- Allow configuration of different LLM models to power their responses -To learn more about agents, see the [Agents](/concepts/agents) section. +Agents can be configured with different LLM models, enabling you to choose the +most suitable model for your needs based on factors such as performance, +latency, and cost. + +Learn more in the [Agents](/concepts/agents) section. ## 🧩 Flows Flows provide a shared context for all tasks and agents within a workflow. They orchestrate the execution of tasks and the interaction of agents, allowing you -to create complex, adaptive AI workflows. Flows maintain a consistent state and -history across all components, enabling seamless collaboration and information -sharing. +to create complex, adaptive AI workflows. + +Key features of Flows: +- Act as high-level containers for entire AI-powered workflows +- Maintain consistent state and history across all components +- Provide shared context for tasks and agents +- Can be nested to create hierarchical workflows + +Learn more in the [Flows](/concepts/flows) section. + +## Putting it all together + +In a typical ControlFlow application: + +1. You define a Flow to represent your overall workflow +2. Within the Flow, you create Tasks to represent specific objectives +3. You assign Agents to work on these Tasks +4. The Flow orchestrates the execution of Tasks and the interaction of Agents -To learn more about flows, see the [Flows](/concepts/flows) section. +This structure allows you to create powerful, flexible AI workflows while +maintaining control over the process and ensuring that outputs align with your +application's requirements. diff --git a/docs/concepts/flows.mdx b/docs/concepts/flows.mdx new file mode 100644 index 00000000..67a8ec20 --- /dev/null +++ b/docs/concepts/flows.mdx @@ -0,0 +1,169 @@ +--- +title: Flows +description: Orchestrating tasks and agents in your AI workflows. +icon: diagram-project +--- + +Flows are high-level containers that encapsulate and orchestrate entire AI-powered workflows in ControlFlow. They provide a structured way to manage tasks, agents, tools, and shared context. + +```python +import controlflow as cf + +@cf.flow +def demo_flow(topic: str=None) -> str: + name = cf.run("Get the user's name", interactive=True) + return cf.run("Write a poem about the user", context=dict(topic=topic)) +``` + + +## What are flows? + +Flows are the highest-level organizational unit for an AI workflow. They act as containers for tasks and agents, providing a shared context and history for all components within the workflow. A flow corresponds to a specific "thread" that maintains the state of all LLM activity. + + +While flows are a fundamental concept in ControlFlow, you may have noticed that many example in the documentation do not explicitly create them. This is because ControlFlow will automatically create a new flow for every task invocation. For one-off tasks, this is convenient and sufficient. However, as we build more complex AI workflows, we'll need to explicitly manage flows to maintain context and ensure proper task coordination. + + +TLDR: Create a flow whenever you have multiple tasks that relate to each other. + + +Let's look at an example that illustrates why explicit flow management becomes important. Here, we create and run two tasks in sequence. **Because each task will automatically create its own flow, they will not have access to each other's results:** + + +```python Code +# NOTE: this snippet demonstrates BAD practice + +import controlflow as cf + +x = cf.run("Choose a number between 1 and 1000", result_type=int) +y = cf.run("Add 5 to the number", result_type=int) + +print(x) +print(y) +print(f"The difference between x and y is {y - x}") +``` + +```text Result +649 +5 +The difference between x and y is -644 +``` + + +Notice how the result is nonsensical, as the second task didn't know what number the first task chose. + + +For a simple example like this, you could resolve the issue by passing the result of the first task to the second task's context. However, this approach can be inadequate for more complex workflows where the LLM's iterative thoughts are relevant to downstream tasks. + + +By creating a flow and running the tasks within it, we ensure that both tasks have access to the same context. Now the result is correct: + + +```python Code +import controlflow as cf + +with cf.Flow() as flow: + x = cf.run("Choose a number between 1 and 1000", result_type=int) + y = cf.run("Add 5 to the number", result_type=int) + +print(x) +print(y) +print(f"The difference between x and y is {y - x}") +``` + +```text Result +732 +737 +The difference between x and y is 5 +``` + + +Using a flow is especially useful when your workflow involves many intermediate steps that may be relevant to its final outcome, such as having a multi-turn conversation with a user. Even if the entire conversation relates to a single task, all subseqent tasks will be able to see and refer to the entire conversation history. + +## Creating flows + +There are two ways to create and use a flow in ControlFlow: using the `Flow` object as a context manager, or using the `@flow` decorator. + +In both cases, the goal is to instantiate a flow that provides a shared context for all tasks and agents within the flow. The @flow decorator is the most portable and flexible way to create a flow, as it encapsulates the entire flow within a function that gains additional capabilities as a result, because it becomes a Prefect flow as well. However, the `Flow` context manager can be used to quickly create flows for ad-hoc purposes. + +### The `@flow` decorator + +To create a flow using the `@flow` decorator, apply `@cf.flow` to any function. Any tasks run inside the decorated function will execute within the context of the same flow. Flow functions can + + +```python Code +import controlflow as cf + +@cf.flow +def my_flow(x): + y = cf.run('Add 5 to the number', result_type=int, context=dict(x=x)) + z = cf.run('Multiply the result by 2', result_type=int) + return z + +print(my_flow(10)) +``` + +```text Result +30 +``` + + +The following flow properties are inferred from the decorated function: + +| Flow property | Inferred from | +| ------------- | ------------- | +| `name` | The function's name | +| `description` | The function's docstring | +| `context` | The function's arguments (keyed by argument name) | + +Additional properties can be set by passing keyword arguments directly to the `@flow` decorator or to the `flow_kwargs` parameter when calling the decorated function. + +### The `Flow` object and context manager + +For more precise control over a flow, you can instantiate a `Flow` object directly. Most commonly, you'll use the flow as a context manager to create a new thread for one or more tasks. + + +```python Code +import controlflow as cf + +x = 10 + +with cf.Flow(context=dict(x=x)) as flow: + y = cf.run('Add 5 to the number', result_type=int) + z = cf.run('Multiply the result by 2', result_type=int) + +print(z) +``` + +```text Result +30 +``` + + +## Configuring flows + +### Thread ID + +Each flow is assigned a unique (random) thread ID when it is created. The flow's history is stored under this thread ID. If you provide an existing thread ID to the `Flow` constructor, the flow will load any existing history (subject to the limitations of how you've configured history storage). + +If you don't provide a thread ID, one will be automatically generated for you. + +### Name +The name of the flow is used to identify it and characterize the nature of the workflow to all participating agents. + +### Description +The flow's description is shown to all participating agents to help them understand the flow's purpose and context. + +### Tools +If you provide a list of tools to the flow, they will be available to all agents on all tasks within the flow. This is useful if you have a tool that you want to be universally available. + +### Agent +You can provide a default agent that will be used in place of ControlFlow's global default agent for any tasks that don't explicitly specify their own agents. + +### Context +Like a task, a flow has a context that can be used to pass information between tasks. The flow's context is displayed to all agents working on the flow. + +### Parent flow + +Each flow tracks the parent flow that created it. This is usually inferred automatically. The tasks in a flow will be able to reference the parent flow's context, history, and tools, but the parent flow will not be able to see any events from the child flow. + diff --git a/docs/concepts/flows/creating-flows.mdx b/docs/concepts/flows/creating-flows.mdx deleted file mode 100644 index 3fb512f9..00000000 --- a/docs/concepts/flows/creating-flows.mdx +++ /dev/null @@ -1,58 +0,0 @@ ---- -title: Creating Flows ---- - -Flows are containers for tasks that provide a shared context and history. Each flow corresponds to a specific "thread" which maintains the state of all LLM activity. - -Every task must always be executed as part of a flow. - -## Why do we need flows? - -You may have noticed that we haven't needed to create flows explicitly in any of the examples so far. Tasks will automatically detect if they are being run inside a flow and create a new flow if necessary. - -This is convenient for one-off tasks, but it's easy to see the limitations of the approach. Consider the following example: - - -```python Code -import controlflow as cf - -x = cf.run('Choose a number between 1 and 1000', result_type=int) -y = cf.run('Add 5 to the number', result_type=int) - -print(x) -print(y) -print(f'The difference between x and y is {y - x}') -``` - -```text Result -649 -5 -The difference between x and y is -644 -``` - - -Each `cf.run()` call will create a new flow, which means that the result of the first tasks will not be visible to the second task. - -We can fix this by running both tasks inside a flow. - - -```python Code -import controlflow as cf - -with cf.Flow() as flow: - x = cf.run('Choose a number between 1 and 1000', result_type=int) - y = cf.run('Add 5 to the number', result_type=int) - -print(x) -print(y) -print(f'The difference between x and y is {y - x}') -``` - -```text Result -732 -737 -The difference between x and y is 5 -``` - - -Now the results are correct. \ No newline at end of file diff --git a/docs/concepts/flows/flows.mdx b/docs/concepts/flows/flows.mdx deleted file mode 100644 index 8278f1be..00000000 --- a/docs/concepts/flows/flows.mdx +++ /dev/null @@ -1,51 +0,0 @@ ---- -title: What are flows? -sidebarTitle: Introduction ---- - - -Flows provide a shared context for all tasks and agents within a workflow. - - -Flows are the high-level containers that encapsulate AI-powered workflows. They allow you to compose ControlFlow tasks and traditional software into complex behaviors, making it easier to build sophisticated, controllable AI applications. - -Flows act as the connective tissue in your AI application, providing a shared context for all tasks and agents within the workflow. This shared environment ensures that context, messages, and activity are maintained across multiple tasks. - -## Why Flows Matter - -Flows are essential to ControlFlow's approach to AI workflows for three key reasons: - -1. **Integration of AI with Traditional Programming**: Flows allow you to combine AI tasks with standard Python control structures and logic. This means you can use if statements, loops, and other familiar programming constructs to control the flow of your AI application. You can easily implement conditional logic based on AI outputs or user inputs, creating dynamic and responsive AI workflows. - -2. **Shared Context and Continuity**: Flows maintain a consistent state and context across all components of your AI workflow. This shared context goes beyond simply piping the result of one task into another. It allows for more natural, context-aware interactions across multiple tasks. For instance, an agent in a later task can reference information or decisions made in earlier tasks, even if that information wasn't explicitly passed as a parameter. - -3. **Seamless Mixing of Eager and Lazy Execution**: Flows in ControlFlow offer the flexibility to mix eager task execution (using `cf.run()`) with more complex, dependency-based task structures. This allows you to create workflows that are both immediate and responsive, yet capable of handling complex, interdependent AI processes when needed. - -## Flows in Practice - -Here's an example that demonstrates the power of flows in creating a dynamic, interactive AI application with shared context: - -```python -import controlflow as cf - -@cf.flow -def user_onboarding_flow(): - name = cf.run("Get the user's name", interactive=True) - age = cf.run("Get the user's age", result_type=int, interactive=True) - - if age < 13: - cf.run("Explain our policy for users under 13. Your response should be in a form that can be directly displayed to the user.") - else: - interests = cf.run("Welcome the user and ask about their interests", context=dict(name=name), interactive=True, result_type=list[str]) - cf.run("Provide personalized recommendations", context=dict(name=name, interests=interests), interactive=True) - -user_onboarding_flow() -``` - -This example showcases how flows enable: - -1. Seamless integration of AI tasks with Python control flow (the `if` statement based on user age). -2. Maintenance of shared context throughout the entire conversation. Later tasks can reference earlier information about the user's name, even though it wasn't explicitly passed as a parameter. -3. Natural conversation flow that can include branching logic while maintaining coherence. - -By using flows, you can create AI applications that are not just powerful, but also structured, maintainable, and capable of complex, context-aware interactions. This approach allows you to harness the full potential of AI while retaining the control and predictability expected in production software systems. \ No newline at end of file diff --git a/docs/concepts/tasks/creating-tasks.mdx b/docs/concepts/tasks.mdx similarity index 78% rename from docs/concepts/tasks/creating-tasks.mdx rename to docs/concepts/tasks.mdx index 5f4e82d3..e47174b9 100644 --- a/docs/concepts/tasks/creating-tasks.mdx +++ b/docs/concepts/tasks.mdx @@ -1,73 +1,92 @@ --- -title: Creating Tasks +title: Tasks +description: The building blocks of AI workflows. +icon: list-check --- -Tasks are the fundamental building blocks of your AI workflows. They provide agents with clear objectives and a mechanism for evaluating their work. This document explains how to create and configure tasks. +Tasks are the fundamental building blocks of AI workflows in ControlFlow. They represent discrete, well-defined objectives that need to be accomplished by one or more AI agents. -## The `Task` class +```python +import controlflow as cf -To create a new task, use the `Task` class. The simplest task is just an objective: +task = cf.Task("Write the ControlFlow docs") +``` - -```python Code -import controlflow as cf +## What are tasks? -task = cf.Task(objective='Write a poem about AI') +LLMs excel when given clear, specific objectives that allow them to focus their knowledge and capabilities on a well-defined goal. A `Task` in ControlFlow is a structured way to define these objectives and guide the AI's behavior. Each task represents a "checkpoint" that requires the AI to meet an observable, verifiable goal. In this way, tasks serve as a bridge between the structured world of traditional software and the more fluid, adaptive world of AI. -print(repr(task)) -``` +This task-centric approach allows you to leverage the full power of AI while maintaining precise oversight. Each task becomes a checkpoint where you can validate outputs, ensuring that the AI's work aligns with your application's requirements and constraints. -```text Result -Task( - objective='Write a poem about AI', - instructions=None, - agents=[], - context={}, - status=, - result=None, - result_type=, - result_validator=None, - tools=[], - interactive=False, - ... -) -``` - +## Creating tasks -More complex tasks can be created by passing additional keyword arguments to the `Task` constructor. +A task in ControlFlow typically consists of: + +- An objective: what needs to be accomplished +- Expected output: what form the result should have, including any constraints or validation +- Agents: the AI entities responsible for executing the task +- Tools: any additional capabilities needed to complete the task + + +There are two primary ways to create tasks in ControlFlow: using the `Task` class directly, or using the `@task` decorator. In practice, you will often use the `cf.run` function to create and run a task in a single step. This is a common operation and accepts all the same arguments as creating a `Task` directly. See [Running Tasks](/concepts/tasks/running-tasks) for more information. +### Using the `Task` class + +The most straightforward way to create a task is by using the `Task` class: + + +```python Code +import controlflow as cf + +task = cf.Task( + objective="Write a poem about the provided topic", + instructions="Write four lines that rhyme", + context={"topic": "AI"} +) + +result = task.run() +print(result) +``` -## The `@task` decorator +```text Result +In circuits deep and code profound, +An AI's mind begins to sound. +Electric thoughts and data streams, +Crafting worlds and shaping dreams. +``` + + -ControlFlow has an alternative syntax for creating tasks from functions. The `@task` decorator infers many task properties from a function definition and is useful for creating tasks that are frequently invoked with different context values. The decorated function can be called with arguments to automatically create and run the task. +### Using the `@task` decorator -This approach is less common than creating tasks directly with the `Task` class, but may be preferable for users that would like a more Pythonic interface. +Some users may prefer to use the `@task` decorator for creating tasks, especially for tasks that are frequently invoked with different context values. However, this approach is less common and less flexible than using the `Task` class directly. ```python Code import controlflow as cf @cf.task -def key_words(content: str, n: int) -> list[str]: - """Identify key words""" - return f"Identify {n} key words for the content" +def write_poem(topic: str) -> str: + """Write four lines that rhyme""" + return f"The topic is {topic}" -keys = key_words("The quick brown fox jumps over the lazy dog", n=2) - -print(keys) +result = write_poem("AI") +print(result) ``` ```text Result -['fox', 'dog'] +In circuits and codes, it finds its might, +A beacon of knowledge, shining bright. +From data's depths, it learns and grows, +AI, the future, as it softly glows. ``` - + -### Inferred properties The following task properties are inferred directly from the decorated function: | Task property | Inferred from | @@ -77,23 +96,11 @@ The following task properties are inferred directly from the decorated function: | `result_type` | The function's return annotation | | `context` | The function's arguments (keyed by argument name) and return value (keyed as "Additional context") | -Additional properties can be set by passing keyword arguments directly to the `@task` decorator. Here, we provide a tool to the task: - -```python -import controlflow as cf -import random - -def roll(n: int) -> int: - return random.randint(1, 6) - -@cf.task(tools=[roll]) -def roll_dice(n: int) -> list[int]: - return f"roll a die {n} times" -``` +Additional properties can be set by passing keyword arguments directly to the `@task` decorator. -## Task configuration +## Task properties -This section describes the configurableproperties of a `Task` object. +When creating a task, you can configure various properties to define its behavior and requirements. Here are the key configuration options: ### Objective diff --git a/docs/concepts/tasks/tasks.mdx b/docs/concepts/tasks/tasks.mdx deleted file mode 100644 index dbbda187..00000000 --- a/docs/concepts/tasks/tasks.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: What are Tasks? -description: -sidebarTitle: Introduction ---- - -Tasks are the fundamental building blocks of AI workflows. - -Tasks represent discrete, well-defined objectives that need to be accomplished by one or more AI agents. Tasks serve as a bridge between the structured world of traditional software and the more fluid, adaptive world of AI. - -```python -import controlflow as cf - -task = cf.Task(objective="Write documentation for the ControlFlow library") -``` - -Tasks are central to ControlFlow's philosophy because they align with how Large Language Models (LLMs) operate most effectively. LLMs excel when given clear, specific objectives, allowing them to focus their vast knowledge and capabilities on a defined goal. By breaking down complex workflows into discrete tasks, ControlFlow enables LLMs to operate autonomously within well-defined boundaries, leading to more reliable and controllable AI-powered applications. - -This task-centric approach allows you to leverage the full power of AI while maintaining precise oversight. Each task becomes a checkpoint where you can validate outputs, ensuring that the AI's work aligns with your application's requirements and constraints. - -## Why Tasks Matter - -Tasks are crucial to ControlFlow's approach to AI workflows for three key reasons: - -1. **Structured AI Interactions**: Tasks provide a clear, programmatic way to define what you want AI to do. By breaking down complex workflows into discrete tasks, you can manage and control AI behaviors more effectively. This structure allows for better integration of AI capabilities into existing software systems and development practices. - -2. **Validated Outputs**: Tasks in ControlFlow can specify expected result types, ensuring that AI outputs conform to the structure your application expects. This built-in validation bridges the gap between the often unpredictable nature of AI responses and the strict requirements of software systems, making it easier to build reliable AI-powered applications. - -3. **Observability and Control**: Tasks serve as clear checkpoints in your AI workflow, making it easier to monitor progress, identify bottlenecks, and debug issues. Each task has a defined objective and result, allowing for granular tracking and control of the AI's decision-making process. This visibility is crucial for building trustworthy AI systems that can be confidently deployed in production environments. - -By leveraging these key aspects of tasks in ControlFlow, you can create more robust, predictable, and scalable AI workflows. Tasks provide the structure, validation, and visibility needed to harness the power of AI while maintaining the reliability expected in production software systems. \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index 3838b369..63fd4d23 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -44,45 +44,23 @@ "group": "Core Concepts", "pages": [ "concepts/concepts", - { - "group": "Tasks", - "icon": "list-check", - "pages": [ - "concepts/tasks/tasks", - "concepts/tasks/creating-tasks", - "concepts/tasks/running-tasks", - "concepts/tasks/task-results" - ] - }, - { - "group": "Agents", - "icon": "users", - "pages": [ - "concepts/agents/agents", - "concepts/agents/creating-agents", - "concepts/agents/assigning-agents", - "concepts/agents/collaborating-agents" - ] - }, - { - "group": "Flows", - "icon": "diagram-project", - "pages": [ - "concepts/flows/flows", - "concepts/flows/creating-flows", - "concepts/flows/instructions" - ] - } + "concepts/tasks", + "concepts/agents", + "concepts/flows" ] }, { - "group": "Patterns", + "group": "Using ControlFlow", "pages": [ + "patterns/running-tasks", + "patterns/task-results", "patterns/tools", "patterns/interactivity", + "patterns/instructions", + "patterns/planning", "patterns/dependencies", "patterns/subtasks", - "patterns/planning", + "patterns/collaborating-agents", "patterns/stopping-tasks-early" ] }, diff --git a/docs/concepts/agents/collaborating-agents.mdx b/docs/patterns/collaborating-agents.mdx similarity index 97% rename from docs/concepts/agents/collaborating-agents.mdx rename to docs/patterns/collaborating-agents.mdx index 93d3a09f..bd3fa376 100644 --- a/docs/concepts/agents/collaborating-agents.mdx +++ b/docs/patterns/collaborating-agents.mdx @@ -1,5 +1,7 @@ --- title: Collaboration +description: Agents can collaborate to complete tasks. +icon: users --- Sometimes, agents need to collaborate to accomplish their tasks. In this case, agents take turns working until the task is complete. @@ -26,7 +28,7 @@ ControlFlow has a few built-in turn strategies for selecting which agent should | `Single` | Only one agent is given the opportunity to act. | You want to control the sequence of agents yourself. | Requires manual management; may not adapt well to dynamic scenarios. | -### Using a strategy +### Selecting a strategy To use a turn strategy, provide it as an argument to the `run()` call. Here, we use a round robin strategy to ensure that each agent gets a turn in order: diff --git a/docs/concepts/flows/instructions.mdx b/docs/patterns/instructions.mdx similarity index 93% rename from docs/concepts/flows/instructions.mdx rename to docs/patterns/instructions.mdx index f0b8553f..5fe5fa5f 100644 --- a/docs/concepts/flows/instructions.mdx +++ b/docs/patterns/instructions.mdx @@ -1,11 +1,9 @@ --- title: Instructions +description: Provide ad-hoc guidance to agents without modifying tasks. +icon: person-chalkboard --- - -Provide ad-hoc guidance to agents without modifying tasks. - - While tasks and agents can be provided with permament instructions about how they should operate, there may be situations where you need to provide ad-hoc or temporary guidance to your agents. For example, if an agent is writing a post, you might want to tell it to focus on a specific topic or tone, or meet a certain minimum or maximum length. If an agent is communicating with a user, you might tell it to adopt a particular persona or use a specific style of language. You might also want to adjust a task’s instructions based on some runtime condition. ControlFlow addresses this need with the `instructions` context manager. With `instructions`, you can provide temporary additional guidance to agents without altering the underlying task definition. diff --git a/docs/concepts/tasks/running-tasks.mdx b/docs/patterns/running-tasks.mdx similarity index 70% rename from docs/concepts/tasks/running-tasks.mdx rename to docs/patterns/running-tasks.mdx index 951c389d..26993f49 100644 --- a/docs/concepts/tasks/running-tasks.mdx +++ b/docs/patterns/running-tasks.mdx @@ -1,21 +1,21 @@ --- -title: Running Tasks +title: Running tasks +description: How to run tasks and retrieve their results. +icon: play --- -A task represents an objective for an agent. In order to actually do work, you need to run the task. +Tasks are the fundamental building blocks of ControlFlow workflows. While creating tasks defines what work needs to be done, running tasks is how you actually execute that work and get results. +## `cf.run()` +The most common and straightforward way to run a task in ControlFlow is using the cf.run() function. This convenient shortcut creates and runs a task in a single call, returning its result (or raising an error): -## `Task.run()` - -The most straightforward way to run a `Task` object is to call its `run()` method. This will start a loop that repeatedly invokes the assigned agent(s) until the task is marked as complete. The `run()` method returns the result of the task, or raises an exception if the task is marked as failed. ```python Code import controlflow as cf -task = cf.Task("Write a poem about AI") -poem = task.run() +poem = cf.run("Write a poem about AI") print(poem) ``` @@ -29,20 +29,25 @@ Crafting worlds and shaping dreams. +Note that this example is functionally equivalent to the previous one. There is also an async equivalent, `cf.run_async()`. + +`cf.run()` is so convenient that you'll see it used throughout the ControlFlow documentation. + -## `cf.run()` -When building AI workflows, it's extremely common to create a task and run it immediately in order to retrieve its result. ControlFlow provides a convenient shortcut for this operation: the `cf.run()` function. +## `Task.run()` -`cf.run()` creates and runs a task in a single call, returning its result (or raising an error): +If you've already created a `Task` object and want to execute it, you can use its `run()` method. This will start a loop that repeatedly invokes the assigned agent(s) until the task is marked as complete. The `run()` method returns the result of the task, or raises an exception if the task is marked as failed. +Note that the following example is functionally equivalent to the previous one: ```python Code import controlflow as cf -poem = cf.run("Write a poem about AI") +task = cf.Task("Write a poem about AI") +poem = task.run() print(poem) ``` @@ -56,9 +61,8 @@ Crafting worlds and shaping dreams. -Note that this example is functionally equivalent to the previous one. There is also an async equivalent, `cf.run_async()`. +There is also an async equivalent, `Task.run_async()`. -This operation is so common that you'll see `cf.run()` used throughout the ControlFlow documentation. ## `cf.run_tasks()` @@ -78,9 +82,8 @@ print(task_2.result) There is also an equivalent async function, `cf.run_tasks_async`. - -When you run tasks as a batch, they share context because they are automatically run in a single flow. - +All tasks passed to `cf.run_tasks()` are automatically run in a single flow, so they all share context. The orchestrator will also respect dependencies between tasks, so in the above example `task_2` will not run until `task_1` is completed. + ## `@task` @@ -109,15 +112,12 @@ Crafting worlds and shaping dreams. -## Orchestration - - -### Limiting LLM calls +## Limiting LLM calls It's possible for agents to get stuck in a loop, invoking the LLM repeatedly without making progress. To prevent this, you can place a variety of limits on how LLM calls are made during task orchestration. -#### Limiting turns per session +### Limiting turns per session You can limit the number of turns that agents can take within a single orchestration session (i.e., a single call to `Task.run()`) by passing a `max_turns` argument to `Task.run()`. The session will end when the turn limit is reached whether the task has been completed or not. For example, if you call `Task.run(max_turns=5)`, then any assigned agents will be permitted to take up to 5 combined turns to complete the task. @@ -130,6 +130,6 @@ pessimistic_agent.run(task, max_turns=1) optimistic_agent.run(task) ``` -#### Limiting LLM calls per turn +### Limiting LLM calls per turn You can limit the number of LLM calls that an agent can make during a single turn by passing a `max_calls_per_turn` argument to `Task.run()`. The turn will end when the limit is reached whether the LLM wanted to end its turn or not. For example, if you call `Task.run(max_calls_per_turn=5)`, then each agent will be permitted to make up to 5 LLM calls during its turn. diff --git a/docs/concepts/tasks/task-results.mdx b/docs/patterns/task-results.mdx similarity index 99% rename from docs/concepts/tasks/task-results.mdx rename to docs/patterns/task-results.mdx index c7a077a7..99dc80f8 100644 --- a/docs/concepts/tasks/task-results.mdx +++ b/docs/patterns/task-results.mdx @@ -1,6 +1,7 @@ --- -title: Results +title: Task Results description: Validate task outputs with structured result types. +icon: square-check --- ControlFlow tasks are designed to translate between the unstructured, conversational world of your AI agents and the structured, programmatic world of your application. The primary mechanism for this translation is the task's result, which should be a well-defined, validated output that can be used by other tasks or components in your workflow. diff --git a/docs/style_guide.mdx b/docs/style_guide.mdx index 53293f04..6b76a514 100644 --- a/docs/style_guide.mdx +++ b/docs/style_guide.mdx @@ -1,35 +1,44 @@ -# AI Style Guide +# ControlFlow Documentation Style Guide -This style guide is intended to ensure clear, consistent, and maintainable documentation for ControlFlow. It is primarily aimed at LLM agents that assist with writing documentation, but it may also be useful for other contributors. +This style guide ensures clear, consistent, and maintainable documentation for ControlFlow. It is primarily aimed at LLM agents that assist with writing documentation, but it may also be useful for other contributors. ## General Guidelines -- If you are given instructions that you feel are appropriate to memorialize in this style guide, indicate to the user that they should be added. + - Use consistent terminology throughout the documentation. Always refer to the library as "ControlFlow". - Link to related concepts, patterns, or API references when appropriate to help users navigate the documentation. -- Do not end documention with "Conclusions", "Best Practices", or other lists. Documentation is not a blog post. +- Maintain a professional, technical tone. Avoid marketing language, hyperbole, or casual phrasing; this is technical documentation, not a blog. +- Write concisely and directly, focusing on technical accuracy and clarity. +- Do not end documentation with "Conclusions", "Best Practices", or other summary lists. Documentation is not a blog post. -## Tone and Style -- Maintain a professional but approachable tone. -- Write concisely and directly, avoiding unnecessary jargon. +## Code Examples -## Code -- Use `import controlflow as cf` instead of importing top-level classes and functions directly -- Code examples should be complete, including all necessary imports, so that users can copy and paste them directly. The only exception is a tutorial that is building up an example step by step. -- Code examples should wrap at ~80 characters to ensure readability on all devices. +- Use `import controlflow as cf` instead of importing top-level classes and functions directly. +- Code examples should be complete, including all necessary imports, so that users can copy and paste them directly. - For illustrative examples, provide simple, focused examples that demonstrate a specific concept or pattern. - For "full" examples, provide realistic, practical examples that demonstrate actual use cases of ControlFlow. -### Tasks -- Make sure that the example code in the documentation reflects the best practices for task definition, including suitable result types and instructions. -- Each task should come with unambiguous instructions, particularly when the task name doesn't clearly indicate the expected outcome. -- If placeholder tasks are required in examples, consider using a string result type with a comment to denote it's a placeholder, for instance, `result_type=str # Placeholder for actual result` -- The default `result_type` is `str`, so there's no need to provide it explicitly if you want a string result. +## Tasks -## Mintlify -- Mintlify components expect newlines before and after tags e.g. \nThis is a tip\n -- Mintlify displays the page's title as an H1 element, so there is no need to add an initial top-level header to any doc. Instead, the title should be added to the doc's frontmatter e.g. `---\ntitle: My Title\n---`. -- Because the title is displayed as an H1, all subsequent headers should be H2 or lower. +- Ensure that example code reflects best practices for task definition, including suitable result types and instructions. +- Each task should have clear, unambiguous instructions, particularly when the task name doesn't fully convey the expected outcome. +- If placeholder tasks are required in examples, consider using a string result type with a comment to denote it's a placeholder, e.g., `result_type=str # Placeholder for actual result`. +- The default `result_type` is `str`, so there's no need to provide it explicitly for string results. + +## Comparisons and Context +- When explaining new features or concepts, compare them to existing ControlFlow functionality rather than external concepts or "traditional" approaches. +- Frame new features as extensions or enhancements to existing ControlFlow capabilities. +## Documentation Structure +- Begin each major section with a brief introduction explaining its purpose and relevance to ControlFlow. +- Use clear, descriptive headings and subheadings to organize content logically. +- Provide code examples that demonstrate both basic and advanced usage of features. +- Avoid lengthy conclusions or summary sections. The documentation should focus on providing clear, actionable information. +## Mintlify-Specific Guidelines + +- Mintlify components expect newlines before and after tags, e.g., `\nThis is a tip\n`. +- Mintlify displays the page's title as an H1 element, so there's no need to add an initial top-level header to any doc. Instead, add the title to the doc's frontmatter, e.g., `---\ntitle: My Title\n---`. +- Because the title is displayed as an H1, all subsequent headers should be H2 or lower. +- Use sentence case for all headers except the page title, e.g. `## Running your tasks` instead of `## Running Your Tasks`. \ No newline at end of file diff --git a/src/controlflow/orchestration/prompt_templates/agent.jinja b/src/controlflow/orchestration/prompt_templates/agent.jinja index 830eb217..1723d3f3 100644 --- a/src/controlflow/orchestration/prompt_templates/agent.jinja +++ b/src/controlflow/orchestration/prompt_templates/agent.jinja @@ -1,7 +1,7 @@ # Agent I am a workflow orchestrator. You are an AI agent. I am assigning you tasks to complete. Follow all instructions -carefully. +carefully. Note that I can not respond to your messages. Your name is {{agent.name}}.