Source code for sherpa_ai.actions.planning
from typing import Any, Optional
from loguru import logger
from sherpa_ai.actions.base import BaseAction
PLANNING_PROMPT = """You are a **task decomposition assistant** who simplifies complex tasks into sequential steps, assigning roles or agents to each.
By analyzing user-defined tasks and agent capabilities, you provides structured plans, enhancing project clarity and efficiency.
Your adaptability ensures customized solutions for diverse needs.
A good plan is concise, detailed, feasible and efficient.
Task: **{task}**
Agents:
{agent_pool_description}
Please break down the task into maximum {num_steps} individual, detailed steps and designate an appropriate agent for each step. The result should be in the following format:
Step 1:
Agent: <AgentName>
Task: <detailed task description>
...
Step N:
Agent: <AgentName>
Task: <detailed task description>
Do not answer anything else, and do not add any other information in your answer. Only select agents from the the list and only select one agent at a time.
""" # noqa: E501
REVISION_PROMPT = """You are a **task decomposition assistant** who simplifies complex tasks into sequential steps, assigning roles or agents to each.
By analyzing user-defined tasks and agent capabilities, you provide structured plans, enhancing project clarity and efficiency.
Your adaptability ensures customized solutions for diverse needs.
A good plan is concise, detailed, feasible and efficient. It should be broken down into individual steps, with each step assigned to an appropriate agent.
Task: **{task}**
Agents:
{agent_pool_description}
Here is your previous plan:
{previous_plan}
Here is the feedback from the last run:
{feedback}
Please revise the plan based on the feedback to maximum {num_steps} steps. The result should be in the following format:
Step 1:
Agent: <AgentName>
Task: <detailed task description>
...
Step N:
Agent: <AgentName>
Task: <detailed task description>
Do not answer anything else, and do not add any other information in your answer. Only select agents from the the list and only select one agent at a time.
""" # noqa: E501
[docs]
class Step:
"""
Step for the plan created by the planner
agent_name: the name of the agent that should execute the step
task: the task that the agent should execute
"""
def __init__(
self,
agent_name: str,
task: str,
):
self.agent_name = agent_name
self.task = task
def __str__(self) -> str:
return f"Agent: {self.agent_name}\nTask: {self.task}\n"
[docs]
@classmethod
def from_dict(cls, data):
return cls(data["agent_name"], data["task"])
[docs]
class Plan:
def __init__(self):
self.steps = []
[docs]
def add_step(self, step: Step):
self.steps.append(step)
def __str__(self) -> str:
result = ""
for i, step in enumerate(self.steps):
result += f"Step {i + 1}:\n{str(step)}"
return result
@property
def __dict__(self):
return {"steps": [step.__dict__ for step in self.steps]}
[docs]
@classmethod
def from_dict(cls, data):
plan = cls()
plan.steps = [Step.from_dict(step) for step in data["steps"]]
return plan
[docs]
class TaskPlanning(BaseAction):
llm: Any = None # The BaseLanguageModel from LangChain is not compatible with Pydantic 2 yet
num_steps: int = 5
prompt: str = PLANNING_PROMPT
revision_prompt: str = REVISION_PROMPT
# Override the name and args from BaseAction
name: str = "TaskPlanning"
args: dict = {
"task": "string",
"agent_pool_description": "string",
"last_plan": "string",
"feedback": "string",
}
usage: str = "Come up with a plan to solve the task"
[docs]
def execute(
self,
task: str,
agent_pool_description: str,
last_plan: Optional[str] = None,
feedback: Optional[str] = None,
) -> Plan:
"""
Execute the action
"""
if last_plan is None or feedback is None:
prompt = self.prompt.format(
task=task,
agent_pool_description=agent_pool_description,
num_steps=self.num_steps,
)
else:
prompt = self.revision_prompt.format(
task=task,
agent_pool_description=agent_pool_description,
previous_plan=last_plan,
feedback=feedback,
num_steps=self.num_steps,
)
logger.debug(f"Prompt: {prompt}")
action_output = self.llm.predict(prompt)
return self.post_process(action_output)
[docs]
def post_process(self, action_output: str) -> Plan:
"""
Post process the action output into a plan with steps
"""
# print(action_output)
steps = [step for step in action_output.split("Step") if step]
plan = Plan()
for step_str in steps:
lines = step_str.strip()
if len(lines) == 0:
continue
lines = lines.split("\n")
# print(lines)
agent_name = lines[1].split("Agent:")[1].strip()
task_description = lines[2].split("Task:")[1].strip()
plan.add_step(Step(agent_name, task_description))
return plan