diff --git a/examples/choose_a_number.py b/examples/choose_a_number.py index 1c2bc394..36609056 100644 --- a/examples/choose_a_number.py +++ b/examples/choose_a_number.py @@ -1,28 +1,20 @@ -from control_flow import Agent, Task, ai_flow, ai_task +from control_flow import Agent, Task, ai_flow -a1 = Agent( - name="A1", +a1 = Agent(name="A1", instructions="You struggle to make decisions.") +a2 = Agent( + name="A2", instructions="You like to make decisions.", ) -a2 = Agent(name="A2", instructions="You struggle to make decisions.") - - -@ai_task(user_access=True) -def get_user_name() -> str: - """get the user's name""" - pass @ai_flow def demo(): - task = Task[int]("Choose a number between 1 and 100", agents=[a1, a2]) + task = Task("Choose a number between 1 and 100", agents=[a1, a2], result_type=int) while task.is_incomplete(): a1.run(task) a2.run(task) - get_user_name() - return task diff --git a/examples/pineapple_pizza.py b/examples/pineapple_pizza.py index 2f4753fd..098518e0 100644 --- a/examples/pineapple_pizza.py +++ b/examples/pineapple_pizza.py @@ -23,16 +23,16 @@ def demo(): with instructions("one sentence max"): task = Task( "All agents must give an argument based on the user message", - result_type=None, agents=[a1, a2], context={"user_message": user_message}, ) - while task.is_incomplete(): - a1.run(task) - a2.run(task) - task2 = Task("Which argument is more compelling?") + task.run_until_complete() + + task2 = Task( + "Post a message saying which argument about the user message is more compelling?" + ) while task2.is_incomplete(): - Agent(instructions="you always pick a side").run(task2) + task2.run(agents=[Agent(instructions="you always pick a side")]) demo() diff --git a/src/control_flow/core/controller/instruction_template.py b/src/control_flow/core/controller/instruction_template.py index 830f5899..5f63d07a 100644 --- a/src/control_flow/core/controller/instruction_template.py +++ b/src/control_flow/core/controller/instruction_template.py @@ -24,20 +24,27 @@ def render(self) -> str: class AgentTemplate(Template): template: str = """ - You are an AI agent. Your name is "{{ agent.name }}". {% if - agent.description %} + You are an AI agent. Your name is "{{ agent.name }}". + {% if agent.description %} + Your description: "{{ agent.description }}" + {% endif -%} + {% if agent.instructions %} + Your instructions: "{{ agent.instructions }}" + {% endif -%} - The following description has been provided for you: {{ agent.description }} - {% endif -%} + You have been created by a program to complete certain tasks. Each task has + an objective and criteria for success. Your job is to perform any required + actions and then mark each task as successful. If a task also requires a + result, you must provide it; this is how the program receives data from you + as it can not read your messages. - Your job is to complete any incomplete tasks by performing any required - actions and then marking them as successful. Note that some tasks may - require collaboration before they are complete. If the task requires a - result, you must provide it. You are fully capable of completing any task - and have all the information and context you need. Never mark task as failed - unless you encounter a technical or human issue that prevents progress. Do - not work on or even respond to tasks that are already complete. + Some tasks may require collaboration before they are complete; others may + take multiple iterations. You are fully capable of completing any task and + have all the information and context you need. Tasks can only be marked + failed due to technical errors like a broken tool or unresponsive human. You + must make a subjective decision if a task requires it. Do not work on or + even respond to tasks that are already complete. """ agent: Agent @@ -94,36 +101,17 @@ class CommunicationTemplate(Template): class InstructionsTemplate(Template): template: str = """ ## Instructions - - - {% if agent_instructions -%} - ### Agent instructions - - These instructions apply only to you: - - {{ agent_instructions }} - {% endif %} - - {% if additional_instructions -%} - ### Additional instructions - - These instructions were additionally provided for this part of the workflow: + + You must follow these instructions for this part of the workflow: {% for instruction in additional_instructions %} - {{ instruction }} {% endfor %} - {% endif %} """ - agent_instructions: str | None = None additional_instructions: list[str] = [] def should_render(self): - return any( - [ - self.agent_instructions, - self.additional_instructions, - ] - ) + return bool(self.additional_instructions) class TasksTemplate(Template): @@ -238,7 +226,6 @@ def render(self): controller_context=self.controller.context, ), InstructionsTemplate( - agent_instructions=self.agent.instructions, additional_instructions=self.instructions, ), CommunicationTemplate(agent=self.agent, other_agents=other_agents), diff --git a/src/control_flow/core/task.py b/src/control_flow/core/task.py index c306c045..8a1cc89a 100644 --- a/src/control_flow/core/task.py +++ b/src/control_flow/core/task.py @@ -1,4 +1,5 @@ import datetime +import itertools from enum import Enum from typing import TYPE_CHECKING, Callable, TypeVar @@ -32,7 +33,7 @@ class Task(ControlFlowModel): context: dict = {} status: TaskStatus = TaskStatus.INCOMPLETE result: T = None - result_type: type[T] | None = str + result_type: type[T] | None = None error: str | None = None tools: list[AssistantTool | Callable] = [] created_at: datetime.datetime = Field(default_factory=datetime.datetime.now) @@ -43,6 +44,29 @@ def __init__(self, objective, **kwargs): # allow objective as a positional arg super().__init__(objective=objective, **kwargs) + def run(self, agents: list["Agent"] = None): + """ + Runs the task with provided agents for up to one cycle through the agents. + """ + if not agents and not self.agents: + raise ValueError("No agents provided to run task.") + + for agent in agents or self.agents: + if self.is_complete(): + break + agent.run(tasks=[self]) + + def run_until_complete(self, agents: list["Agent"] = None): + """ + Runs the task with provided agents until it is complete. + """ + if not agents and not self.agents: + raise ValueError("No agents provided to run task.") + agents = itertools.cycle(agents or self.agents) + while self.is_incomplete(): + agent = next(agents) + agent.run(tasks=[self]) + def is_incomplete(self) -> bool: return self.status == TaskStatus.INCOMPLETE