Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support hierarchical reasoning and manager agents #167

Merged
merged 3 commits into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion agentstack/cli/agentstack_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,16 @@ def to_json(self):


class ProjectStructure:
def __init__(self):
def __init__(
self,
method: str = "sequential",
manager_agent: Optional[str] = None,
):
self.agents = []
self.tasks = []
self.inputs = {}
self.method = method
self.manager_agent = manager_agent

def add_agent(self, agent):
self.agents.append(agent)
Expand All @@ -67,6 +73,8 @@ def set_inputs(self, inputs):

def to_dict(self):
return {
'method': self.method,
'manager_agent': self.manager_agent,
'agents': self.agents,
'tasks': self.tasks,
'inputs': self.inputs,
Expand Down
9 changes: 7 additions & 2 deletions agentstack/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,10 @@ def insert_template(
template_version=template_data.template_version if template_data else 0,
)

project_structure = ProjectStructure()
project_structure = ProjectStructure(
method=template_data.method if template_data else "sequential",
manager_agent=template_data.manager_agent if template_data else None,
)
project_structure.agents = design["agents"]
project_structure.tasks = design["tasks"]
project_structure.inputs = design["inputs"]
Expand Down Expand Up @@ -471,6 +474,7 @@ def export_template(output_filename: str):
role=agent.role,
goal=agent.goal,
backstory=agent.backstory,
allow_delegation=False, # TODO
model=agent.llm, # TODO consistent naming (llm -> model)
)
)
Expand Down Expand Up @@ -507,11 +511,12 @@ def export_template(output_filename: str):
)

template = TemplateConfig(
template_version=2,
template_version=3,
name=metadata.project_name,
description=metadata.project_description,
framework=get_framework(),
method="sequential", # TODO this needs to be stored in the project somewhere
manager_agent=None, # TODO
agents=agents,
tasks=tasks,
tools=tools,
Expand Down
2 changes: 1 addition & 1 deletion agentstack/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def _import_project_module(path: Path):
assert spec.loader is not None # appease type checker

project_module = importlib.util.module_from_spec(spec)
sys.path.append(str((path / MAIN_FILENAME).parent))
sys.path.insert(0, str((path / MAIN_FILENAME).parent))
spec.loader.exec_module(project_module)
return project_module

Expand Down
3 changes: 1 addition & 2 deletions agentstack/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,7 @@ def main():
except Exception as e:
update_telemetry(telemetry_id, result=1, message=str(e))
print(term_color("An error occurred while running your AgentStack command:", "red"))
print(e)
sys.exit(1)
raise e

update_telemetry(telemetry_id, result=0)

Expand Down
65 changes: 57 additions & 8 deletions agentstack/proj_templates.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Literal
from typing import Optional, Literal
import os, sys
from pathlib import Path
import pydantic
Expand All @@ -19,20 +19,63 @@ class TemplateConfig_v1(pydantic.BaseModel):
tools: list[dict]
inputs: list[str]

def to_v2(self) -> 'TemplateConfig':
return TemplateConfig(
def to_v2(self) -> 'TemplateConfig_v2':
return TemplateConfig_v2(
name=self.name,
description=self.description,
template_version=2,
framework=self.framework,
method=self.method,
agents=[TemplateConfig.Agent(**agent) for agent in self.agents],
tasks=[TemplateConfig.Task(**task) for task in self.tasks],
tools=[TemplateConfig.Tool(**tool) for tool in self.tools],
agents=[TemplateConfig_v2.Agent(**agent) for agent in self.agents],
tasks=[TemplateConfig_v2.Task(**task) for task in self.tasks],
tools=[TemplateConfig_v2.Tool(**tool) for tool in self.tools],
inputs={key: "" for key in self.inputs},
)


class TemplateConfig_v2(pydantic.BaseModel):
class Agent(pydantic.BaseModel):
name: str
role: str
goal: str
backstory: str
model: str

class Task(pydantic.BaseModel):
name: str
description: str
expected_output: str
agent: str

class Tool(pydantic.BaseModel):
name: str
agents: list[str]

name: str
description: str
template_version: Literal[2]
framework: str
method: str
agents: list[Agent]
tasks: list[Task]
tools: list[Tool]
inputs: dict[str, str]

def to_v3(self) -> 'TemplateConfig':
return TemplateConfig(
name=self.name,
description=self.description,
template_version=3,
framework=self.framework,
method=self.method,
manager_agent=None,
agents=[TemplateConfig.Agent(**agent.dict()) for agent in self.agents],
tasks=[TemplateConfig.Task(**task.dict()) for task in self.tasks],
tools=[TemplateConfig.Tool(**tool.dict()) for tool in self.tools],
inputs=self.inputs,
)


class TemplateConfig(pydantic.BaseModel):
"""
Interface for interacting with template configuration files.
Expand All @@ -51,6 +94,8 @@ class TemplateConfig(pydantic.BaseModel):
The framework the template is for.
method: str
The method used by the project. ie. "sequential"
manager_agent: Optional[str]
The name of the agent that manages the project.
agents: list[TemplateConfig.Agent]
A list of agents used by the project.
tasks: list[TemplateConfig.Task]
Expand All @@ -66,6 +111,7 @@ class Agent(pydantic.BaseModel):
role: str
goal: str
backstory: str
allow_delegation: bool = False
model: str

class Task(pydantic.BaseModel):
Expand All @@ -80,9 +126,10 @@ class Tool(pydantic.BaseModel):

name: str
description: str
template_version: Literal[2]
template_version: Literal[3]
framework: str
method: str
manager_agent: Optional[str]
agents: list[Agent]
tasks: list[Task]
tools: list[Tool]
Expand Down Expand Up @@ -134,8 +181,10 @@ def from_json(cls, data: dict) -> 'TemplateConfig':
try:
match data.get('template_version'):
case 1:
return TemplateConfig_v1(**data).to_v2()
return TemplateConfig_v1(**data).to_v2().to_v3()
case 2:
bboynton97 marked this conversation as resolved.
Show resolved Hide resolved
return TemplateConfig_v2(**data).to_v3()
case 3:
return cls(**data) # current version
case _:
raise ValidationError(f"Unsupported template version: {data.get('template_version')}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ class {{cookiecutter.project_metadata.project_name|replace('-', '')|replace('_',

# Agent definitions
{%- for agent in cookiecutter.structure.agents %}
@agent
{% if not agent.name == cookiecutter.structure.manager_agent %}@agent{% endif %}
def {{agent.name}}(self) -> Agent:
return Agent(
config=self.agents_config['{{ agent.name }}'],
tools=[], # Pass in what tools this agent should have
verbose=True
verbose=True,
{% if agent.allow_delegation %}allow_delegation=True{% endif %}
)
{%- endfor %}

Expand All @@ -32,7 +33,7 @@ def crew(self) -> Crew:
return Crew(
agents=self.agents, # Automatically created by the @agent decorator
tasks=self.tasks, # Automatically created by the @task decorator
process=Process.sequential,
process=Process.{{cookiecutter.structure.method}},
{% if cookiecutter.structure.manager_agent %}manager_agent=self.{{cookiecutter.structure.manager_agent}}(),{% endif %}
verbose=True,
# process=Process.hierarchical, # In case you wanna use that instead https://docs.crewai.com/how-to/Hierarchical/
)
54 changes: 54 additions & 0 deletions agentstack/templates/proj_templates/reasoning.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "reasoning",
"description": "Implement test time compute using an agentic framework to emulate o1 with Claude.",
"template_version": 3,
"framework": "crewai",
"agents": [{
"name": "manager",
"role": "Manager",
"goal": "Delegate requests to multiple sub-agents to find the best solution to the user's request using the best resources available. Break up the user's request into very small tasks and delegate them to the right agents. <user_input>{query}</user_input>",
"backstory": "You are responsible for delegating tasks to the right AI agents and ensuring that the team works together to achieve the best results.",
"allow_delegation": true,
"model": "anthropic/claude-3-5-sonnet-20240620"
}, {
"name": "triage",
"role": "Triage",
"goal": "You are responsible for interpreting the request and exploring possible ways of solving the problem. Delegate the actual problem solving to one of your workers. <original_user_input>{query}</original_user_input>",
"backstory": "You are responsible for interpreting the user's request and deciding which agent is best suited to solve the problem. You are the first point of contact for the system.",
"model": "anthropic/claude-3-5-sonnet-20240620"
}, {
"name": "worker",
"role": "Worker",
"goal": "You are responsible for solving the problem that the triage agent has delegated to you. You should use your knowledge and skills to find the best solution to the user's request.",
"backstory": "You are responsible for solving the problem that the triage agent has delegated to you. You are an expert in your field and you should use your knowledge and skills to find the best solution to the user's request.",
"model": "anthropic/claude-3-5-sonnet-20240620"
}, {
"name": "fact_checker",
"role": "Fact Checker",
"goal": "You are responsible for checking the solution that the worker agent has come up with. You should make sure that the solution is correct and that it meets the user's requirements. Evaluate the response in regards to the user's original question, and provide a concise answer that is factually correct. Now is a great time to omit any questionable statements and inconclusive data. <user_original_input>{query}</user_original_input>",
"backstory": "You are responsible for checking the solution that the worker agent has come up with. You should make sure that the solution is correct and that it meets the user's requirements. You are the last line of defense before the solution is presented to the user.",
"model": "anthropic/claude-3-5-sonnet-20240620"
}],
"tasks": [{
"name": "identify_plan_of_action",
"description": "Identify the problem being presented and come up with steps to solve it. Restate the problem in your own words, and identify 3 to 12 steps you can take to explore possible solutions. Do not actually present solutions to the problem yourself, but pass it to a new agent to do so.",
"expected_output": "A detailed description of the problem being presented and a list of possible steps that can be taken to explore possible solutions.",
"agent": "triage"
}, {
"name": "find_solution",
"description": "Identify the problem being presented to you and come up with the best solution you can think of. After you have come up with a solution, pass it to a new agent to check it.",
"expected_output": "A concise, complete solution to the problem being presented.",
"agent": "worker"
}, {
"name": "check_solution",
"description": "Review the problem and solution being presented and determine wether you think it is correct or not.",
"expected_output": "Reiterate the solution to be factually correct.",
"agent": "fact_checker"
}],
"tools": [],
"method": "hierarchical",
"manager_agent": "manager",
"inputs": {
"query": "What is the meaning of life, the universe, and everything?"
}
}
Loading