From d1343928f6967e860a6a94ce0eb88d276eedd098 Mon Sep 17 00:00:00 2001 From: Jeremiah Lowin <153965+jlowin@users.noreply.github.com> Date: Wed, 4 Sep 2024 21:47:45 -0400 Subject: [PATCH] New flow docs; clean up --- docs/concepts/agents/assigning-agents.mdx | 52 +++- docs/concepts/flows/creating-flows.mdx | 58 +++++ docs/concepts/tasks/result-types.mdx | 294 ---------------------- docs/concepts/tasks/task-results.mdx | 5 +- docs/concepts/tasks/tasks.mdx | 1 + docs/guides/default-agent.mdx | 1 + docs/guides/llms.mdx | 1 + docs/installation.mdx | 9 +- docs/mint.json | 1 + docs/patterns/dependencies.mdx | 4 +- docs/patterns/interactivity.mdx | 3 +- docs/patterns/subtasks.mdx | 4 +- docs/patterns/tools.mdx | 5 +- docs/quickstart.mdx | 2 +- 14 files changed, 118 insertions(+), 322 deletions(-) create mode 100644 docs/concepts/flows/creating-flows.mdx delete mode 100644 docs/concepts/tasks/result-types.mdx diff --git a/docs/concepts/agents/assigning-agents.mdx b/docs/concepts/agents/assigning-agents.mdx index 2afb2b6e..bb38071a 100644 --- a/docs/concepts/agents/assigning-agents.mdx +++ b/docs/concepts/agents/assigning-agents.mdx @@ -1,15 +1,29 @@ --- title: Assigning Agents to Tasks -sidebarTitle: Task Assignment +sidebarTitle: Assigning to Tasks --- -To assign an agent to a task, use 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. +Agents must be assigned to tasks in order to work on them. + +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. + +## 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: + +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. +3. It will use the global default agent (`controlflow.defaults.agent`). + +To see the agents assigned to a task, use its `get_agents()` method. This will return a list of all the agents assigned to the task, including any inherited from its environment. ### Tasks with one agent -To assign a single agent to a task, create the task and pass the agent to the `agents` parameter: +To assign a single agent to a task, pass a agent to the `agents` parameter: -```python +```python Providing agents to a task import controlflow as cf poet = cf.Agent(name="Poet") @@ -17,9 +31,9 @@ poet = cf.Agent(name="Poet") poem = cf.run("Write a short poem about AI", agents=[poet]) ``` -Alternatively, you can use the agent's own `run` method: +Alternatively, you can use the agent's own `run` method to create and run a task in one step: -```python +```python Calling Agent.run() import controlflow as cf poet = cf.Agent(name="Poet") @@ -109,3 +123,29 @@ temporary positive outcomes, despite the overall bleak and discouraging reality. ```` + + +## 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`. + + + Setting `completion_agents` will prevent other agents from marking the task as successful or failed. Make sure your completion agents are also assigned to the task! + + +```python Completion agents +import controlflow as cf + +a1 = cf.Agent(name="A1") +a2 = cf.Agent(name="A2") +a3 = cf.Agent(name="A3") + + +task = cf.Task( + ..., + # all three agents can work on the task + agents=[a1, a2, a3], + # only a1 and a2 can mark the task as successful + completion_agents=[a1, a2], +) +``` \ No newline at end of file diff --git a/docs/concepts/flows/creating-flows.mdx b/docs/concepts/flows/creating-flows.mdx new file mode 100644 index 00000000..3fb512f9 --- /dev/null +++ b/docs/concepts/flows/creating-flows.mdx @@ -0,0 +1,58 @@ +--- +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/tasks/result-types.mdx b/docs/concepts/tasks/result-types.mdx deleted file mode 100644 index 8c3662e7..00000000 --- a/docs/concepts/tasks/result-types.mdx +++ /dev/null @@ -1,294 +0,0 @@ ---- -title: Typed Results ---- - - -Validate task outputs with structured result types. - - -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. - - -ControlFlow allows you to specify the expected structure of a task's result using the `result_type` parameter. This ensures that the result conforms to a specific data schema, making it easier to work with and reducing the risk of errors in downstream tasks. - - - -## String results - -By default, the `result_type` of a task is a string, which essentially means the agent can return any value that satisfies the task's objective. - -For example, if you ask an agent to "Say hello in three languages", it might return a simple string like `"Hello; Hola; Bonjour"` or a more complex, conversational response instead: - - -```python Code -import controlflow as cf - -result = cf.run("Say hello in three languages") - -print(result) -``` - -```text Simple result -Hello; Hola; Bonjour -``` - -```text Complex result -Hello there! - -In three languages, "Hello" can be expressed as follows: - -1. English: Hello -2. Spanish: Hola -3. French: Bonjour -``` - - -Sometimes this flexibility is useful, especially if your task's result will only be consumed as the input to another ControlFlow task. However, it can also lead to ambiguity and errors if the agent produces unexpected output, and is difficult to work with in an automated or programmatic way. - -## Builtin types - -You can cast task results to any of Python's built-in types. - -### Basic types - -If your result is a number, you can specify the `result_type` as `int` or `float`: - - -```python Code -import controlflow as cf - -result = cf.run("What is 2 + 2?", result_type=int) - -print(result) -assert isinstance(result, int) -``` -```text Result -4 -``` - - -You can use `bool` for tasks whose result is a simple true/false value: - - -```python Code -import controlflow as cf - -result = cf.run("The Earth is flat", result_type=bool) - -print(result) -assert result is False -``` -```text Result -False -``` - - - -### Compound types -You can also use typed collections like lists and dicts to specify the structure of your task's result. - -Let's revisit the example of asking an agent to say hello in three languages, but this time specifying that the result should be a list of strings, or `list[str]`. This forces the agent to produce the result you probably expected (three separate strings, each representing a greeting in a different language): - - -```python Code -import controlflow as cf - -result = cf.run("Say hello in three languages", result_type=list[str]) - -print(result) -print(result[0]) -``` - -```text Result -['Hello', 'Hola', 'Bonjour'] -'Hello' -``` - - -### Annotated types - -Sometimes, data types are not precise enough to guide the agent to the desired result. In these cases, you can use an annotated type to provide more specific instructions. - -For example, if we want to ensure that the agent returns a string that is only a zip code, we can specify the `result_type` as `Annotated[str, "a 5 digit zip code"]`. - - -```python Code -import controlflow as cf - -result = cf.run( - "What is the zip code of the White House?", - result_type=Annotated[str, "a 5 digit zip code"], -) - -print(result) -``` - -```text Result -20500 -``` - - - -Note that annotated types are not validated; the annotation is provided as part of the agent's natural language instructions. You could additionaly provide a custom [result validator](#result-validators) to enforce the constraint. - - -## Classification - -You can limit the result to one of a specific set of values, in order to label or classify a response. To do this, specify a list or tuple of allowed values for the result type. Here, we classify the media type of "Star Wars: Return of the Jedi": - - -```python Code -import controlflow as cf - -media = cf.run( - "Star Wars: Return of the Jedi", - result_type=["movie", "tv show", "book", "comic", "other"] -) - -print(media) -``` - -```text Result -movie -``` - - - - -For classification tasks, ControlFlow asks agents to choose a value by index rather than writing out the entire response. This optimization significantly improves latency while also conserving output tokens. - - -## Structured results - -For complex, structured results, you can use a Pydantic model as the `result_type`. Pydantic models provide a powerful way to define data schemas and validate input data. - - -```python Code -import controlflow as cf -from pydantic import BaseModel, Field - -class ResearchReport(BaseModel): - title: str - summary: str - key_findings: list[str] = Field(min_items=3, max_items=10) - references: list[str] - -result = cf.run( - "Generate a research report on quantum computing", - result_type=ResearchReport, -) - -print(repr(result)) -``` - -```text Result -ResearchReport( - title='Quantum Computing: Current Landscape and Future Prospects', - summary='Quantum computing represents a significant leap in computational capabilities, leveraging the principles of quantum mechanics to perform complex calculations far beyond the reach of classical computers. This report delves into the current state of quantum computing, exploring its foundational principles, recent advancements, and the potential implications for various industries. Key findings highlight the technological hurdles, notable achievements, and the transformative potential of quantum computing in solving intractable problems.', - key_findings=[ - 'Principles of Quantum Mechanics: Quantum computing utilizes qubits, superposition, and entanglement to process information in fundamentally new ways, enabling parallel computation on a massive scale.', - 'Technological Achievements: Major milestones include the development of stable qubits, error correction algorithms, and quantum supremacy demonstrations by leading tech companies like Google and IBM.', - 'Applications and Impacts: Quantum computing shows promise in fields such as cryptography, materials science, pharmaceuticals, and artificial intelligence, potentially revolutionizing these sectors by providing unprecedented computational power.', - 'Challenges and Limitations: Significant obstacles remain, including qubit stability, error rates, and the need for extremely low temperatures. Overcoming these challenges is essential for the practical deployment of quantum computers.', - 'Future Directions: Ongoing research focuses on improving qubit coherence times, developing scalable quantum architectures, and creating robust quantum algorithms to harness the full potential of quantum computing.' - ], - references=[ - 'Nielsen, M. A., & Chuang, I. L. (2010). Quantum Computation and Quantum Information. Cambridge University Press.', - 'Arute, F., Arya, K., Babbush, R., Bacon, D., Bardin, J. C., Barends, R., ... & Martinis, J. M. (2019). Quantum supremacy using a programmable superconducting processor. Nature, 574(7779), 505-510.', - 'Preskill, J. (2018). Quantum Computing in the NISQ era and beyond. Quantum, 2, 79.', - 'Montanaro, A. (2016). Quantum algorithms: an overview. npj Quantum Information, 2, 15023.', - 'Shor, P. W. (1997). Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer. SIAM Journal on Computing, 26(5), 1484-1509.' - ] -) -``` - - -### Advanced validation - -Because Pydantic models are fully hydrated by ControlFlow, you can use any of Pydantic's built-in or custom validators to further constrain or modify the result after it has been produced. - - -```python -from pydantic import BaseModel, field_validator - -class SentimentAnalysis(BaseModel): - text: str - sentiment: float - - @field_validator('sentiment') - def check_sentiment_range(cls, v): - if not -1 <= v <= 1: - raise ValueError('Sentiment must be between -1 and 1') - return v - -result = cf.run( - "Analyze sentiment of given text", - result_type=SentimentAnalysis, - context=dict(text="I love ControlFlow!"), -) - -print(repr(result)) -``` - -```text Result -SentimentAnalysis(text='I love ControlFlow!', sentiment=0.9) -``` - - -## No result - -Sometimes, you may want to ask an agent to perform an action without expecting or requiring a result. In this case, you can specify `result_type=None`. For example, you might want to ask an agent to use a tool or post a message to the workflow thread, without requiring any task output. - -```python -import controlflow as cf - -def status_tool(status: str) -> None: - """Submit a status update to the workflow thread.""" - print(f"Submitting status update: {status}") - -cf.run( - "Use your tool to submit a status update", - result_type=None, - tools=[status_tool], -) -``` - -Note that it is generally recommended to ask agents to produce a result, even if its just a quick status update. This is because other agents in the workflow can usually see the result of a task, but they may not be able to see any tool calls, messages, or side effects that the agent used to produce the result. Therefore, results can be helpful even if the assigned agent doesn't need them. - - -## Custom result validation - -In addition to using Pydantic validation, you can also supply a custom validation function as the task's `result_validator`. - -After the raw LLM result has been coerced into the `result_type`, it will be passed to your custom validator, which must either return the result or raise an exception. This gives you the opportunity to perform additional validation or modification of the result. - - - - -```python Code -import controlflow as cf - -def constrain_sentiment(value: float) -> float: - if not 0 <= value <= 1: - raise ValueError("Sentiment must be between 0 and 1") - return value - -sentiment = cf.run( - "Analyze sentiment of given text", - result_type=float, - context=dict(text="I love ControlFlow!"), - result_validator=constrain_sentiment, -) - -print(sentiment) -``` - -```text Result -0.9 -``` - -Because the output of the result validator is used as the result, you can use it to modify the result after it has been produced by an agent. For example, you might want to round a floating point number or convert a string to a specific format. Note, however, that result validation takes place *after* the raw LLM result has been coerced to the provided `result_type`. - - -Remember that result validators must either **return** the result or **raise** an exception. They are not true/false checks! - \ No newline at end of file diff --git a/docs/concepts/tasks/task-results.mdx b/docs/concepts/tasks/task-results.mdx index b5e15cf1..c7a077a7 100644 --- a/docs/concepts/tasks/task-results.mdx +++ b/docs/concepts/tasks/task-results.mdx @@ -1,11 +1,8 @@ --- title: Results +description: Validate task outputs with structured result types. --- - -Validate task outputs with structured result types. - - 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/concepts/tasks/tasks.mdx b/docs/concepts/tasks/tasks.mdx index e80ca538..dbbda187 100644 --- a/docs/concepts/tasks/tasks.mdx +++ b/docs/concepts/tasks/tasks.mdx @@ -1,5 +1,6 @@ --- title: What are Tasks? +description: sidebarTitle: Introduction --- diff --git a/docs/guides/default-agent.mdx b/docs/guides/default-agent.mdx index 01d58895..bd1a061d 100644 --- a/docs/guides/default-agent.mdx +++ b/docs/guides/default-agent.mdx @@ -1,5 +1,6 @@ --- title: Configuring the Default Agent +description: Set global and flow-specific defaults. icon: robot --- diff --git a/docs/guides/llms.mdx b/docs/guides/llms.mdx index 647d0552..9d3ac0cb 100644 --- a/docs/guides/llms.mdx +++ b/docs/guides/llms.mdx @@ -1,5 +1,6 @@ --- title: Configuring LLMs +description: ControlFlow supports a variety of LLMs and model providers. icon: gear --- diff --git a/docs/installation.mdx b/docs/installation.mdx index d62a6c23..a380b93b 100644 --- a/docs/installation.mdx +++ b/docs/installation.mdx @@ -1,13 +1,10 @@ --- title: Installation & Setup -description: Learn how to install ControlFlow and configure your API keys. icon: wrench --- - - -Pin to a specific version if you want to avoid breaking changes. -However, we recommend frequent updates to get new features and bug fixes. - + +ControlFlow is under active development. You should pin to a specific version if you want to avoid breaking changes. However, we recommend frequent updates to get new features and bug fixes. + ## Install ControlFlow diff --git a/docs/mint.json b/docs/mint.json index 5353051f..b4076369 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -68,6 +68,7 @@ "icon": "diagram-project", "pages": [ "concepts/flows/flows", + "concepts/flows/creating-flows", "concepts/flows/instructions" ] } diff --git a/docs/patterns/dependencies.mdx b/docs/patterns/dependencies.mdx index 81bd9c58..30be3d08 100644 --- a/docs/patterns/dependencies.mdx +++ b/docs/patterns/dependencies.mdx @@ -1,10 +1,8 @@ --- title: Dependencies +description: Manage task dependencies to create complex, multi-step workflows. icon: link --- - -Manage task dependencies to create complex, multi-step workflows. - In complex workflows, tasks often need to be executed in a specific order. Some tasks may rely on the outputs of others, or there might be a logical sequence that must be followed to achieve the desired outcome. ControlFlow provides several mechanisms to define and manage these task dependencies, ensuring that your workflow executes in the correct order and that data flows properly between tasks. diff --git a/docs/patterns/interactivity.mdx b/docs/patterns/interactivity.mdx index 5466783a..63564592 100644 --- a/docs/patterns/interactivity.mdx +++ b/docs/patterns/interactivity.mdx @@ -1,10 +1,11 @@ --- title: Interactivity +description: Chat with your AI agents. icon: comments --- -Chat with your AI agents by setting `interactive=True`. +TLDR: set `interactive=True` to chat with your agents. ControlFlow agents are primarily designed to solve problems by working autonomously. However, there are situations where user input is necessary to guide the agent's decision-making process. By incorporating user input into your ControlFlow workflows, you can create more dynamic, interactive AI applications that adapt to user needs in real-time. diff --git a/docs/patterns/subtasks.mdx b/docs/patterns/subtasks.mdx index e7b3563b..fdd5e53c 100644 --- a/docs/patterns/subtasks.mdx +++ b/docs/patterns/subtasks.mdx @@ -1,11 +1,9 @@ --- title: Subtasks +description: Break complex or open-ended tasks into smaller, manageable steps. icon: sitemap --- - -Break complex or open-ended tasks into smaller, manageable steps. - In complex AI workflows, breaking down large tasks into smaller, manageable steps can significantly improve the quality and reliability of the results. ControlFlow's subtask feature provides a powerful mechanism for structuring these hierarchical task relationships, allowing you to guide AI agents through a series of logical steps to achieve a larger goal. diff --git a/docs/patterns/tools.mdx b/docs/patterns/tools.mdx index 68e90596..87c141f2 100644 --- a/docs/patterns/tools.mdx +++ b/docs/patterns/tools.mdx @@ -1,12 +1,9 @@ --- title: Tools +description: Give agents new abilities with custom tools. icon: toolbox --- - -Give you agents new abilities with custom tools. - - A tool is a Python function that your agents can use to accomplish tasks. They let you extend the capabilities of your agents beyond their built-in knowledge and abilities, allowing them to interact with external systems, perform calculations, or access specific information. Tools can be simple utility functions, complex data processing operations, or API calls to external services. Here's a basic example of a tool for rolling dice: diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index 346fd75a..247529f1 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -1,6 +1,6 @@ --- title: "Quickstart" -description: Build your first agentic workflow in less than 30 seconds +description: Build your first agentic workflow in less than 30 seconds. icon: rocket ---