Skip to content

Commit 7296433

Browse files
authored
Merge pull request #167 from tcdent/crew-hierarchical
Support hierarchical reasoning and manager agents
2 parents b3294e4 + b46796d commit 7296433

7 files changed

Lines changed: 134 additions & 18 deletions

File tree

agentstack/cli/agentstack_data.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,16 @@ def to_json(self):
5151

5252

5353
class ProjectStructure:
54-
def __init__(self):
54+
def __init__(
55+
self,
56+
method: str = "sequential",
57+
manager_agent: Optional[str] = None,
58+
):
5559
self.agents = []
5660
self.tasks = []
5761
self.inputs = {}
62+
self.method = method
63+
self.manager_agent = manager_agent
5864

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

6874
def to_dict(self):
6975
return {
76+
'method': self.method,
77+
'manager_agent': self.manager_agent,
7078
'agents': self.agents,
7179
'tasks': self.tasks,
7280
'inputs': self.inputs,

agentstack/cli/cli.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -388,7 +388,10 @@ def insert_template(
388388
template_version=template_data.template_version if template_data else 0,
389389
)
390390

391-
project_structure = ProjectStructure()
391+
project_structure = ProjectStructure(
392+
method=template_data.method if template_data else "sequential",
393+
manager_agent=template_data.manager_agent if template_data else None,
394+
)
392395
project_structure.agents = design["agents"]
393396
project_structure.tasks = design["tasks"]
394397
project_structure.inputs = design["inputs"]
@@ -471,6 +474,7 @@ def export_template(output_filename: str):
471474
role=agent.role,
472475
goal=agent.goal,
473476
backstory=agent.backstory,
477+
allow_delegation=False, # TODO
474478
model=agent.llm, # TODO consistent naming (llm -> model)
475479
)
476480
)
@@ -507,11 +511,12 @@ def export_template(output_filename: str):
507511
)
508512

509513
template = TemplateConfig(
510-
template_version=2,
514+
template_version=3,
511515
name=metadata.project_name,
512516
description=metadata.project_description,
513517
framework=get_framework(),
514518
method="sequential", # TODO this needs to be stored in the project somewhere
519+
manager_agent=None, # TODO
515520
agents=agents,
516521
tasks=tasks,
517522
tools=tools,

agentstack/cli/run.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def _import_project_module(path: Path):
8484
assert spec.loader is not None # appease type checker
8585

8686
project_module = importlib.util.module_from_spec(spec)
87-
sys.path.append(str((path / MAIN_FILENAME).parent))
87+
sys.path.insert(0, str((path / MAIN_FILENAME).parent))
8888
spec.loader.exec_module(project_module)
8989
return project_module
9090

agentstack/main.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,7 @@ def main():
200200
except Exception as e:
201201
update_telemetry(telemetry_id, result=1, message=str(e))
202202
print(term_color("An error occurred while running your AgentStack command:", "red"))
203-
print(e)
204-
sys.exit(1)
203+
raise e
205204

206205
update_telemetry(telemetry_id, result=0)
207206

agentstack/proj_templates.py

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Literal
1+
from typing import Optional, Literal
22
import os, sys
33
from pathlib import Path
44
import pydantic
@@ -19,20 +19,63 @@ class TemplateConfig_v1(pydantic.BaseModel):
1919
tools: list[dict]
2020
inputs: list[str]
2121

22-
def to_v2(self) -> 'TemplateConfig':
23-
return TemplateConfig(
22+
def to_v2(self) -> 'TemplateConfig_v2':
23+
return TemplateConfig_v2(
2424
name=self.name,
2525
description=self.description,
2626
template_version=2,
2727
framework=self.framework,
2828
method=self.method,
29-
agents=[TemplateConfig.Agent(**agent) for agent in self.agents],
30-
tasks=[TemplateConfig.Task(**task) for task in self.tasks],
31-
tools=[TemplateConfig.Tool(**tool) for tool in self.tools],
29+
agents=[TemplateConfig_v2.Agent(**agent) for agent in self.agents],
30+
tasks=[TemplateConfig_v2.Task(**task) for task in self.tasks],
31+
tools=[TemplateConfig_v2.Tool(**tool) for tool in self.tools],
3232
inputs={key: "" for key in self.inputs},
3333
)
3434

3535

36+
class TemplateConfig_v2(pydantic.BaseModel):
37+
class Agent(pydantic.BaseModel):
38+
name: str
39+
role: str
40+
goal: str
41+
backstory: str
42+
model: str
43+
44+
class Task(pydantic.BaseModel):
45+
name: str
46+
description: str
47+
expected_output: str
48+
agent: str
49+
50+
class Tool(pydantic.BaseModel):
51+
name: str
52+
agents: list[str]
53+
54+
name: str
55+
description: str
56+
template_version: Literal[2]
57+
framework: str
58+
method: str
59+
agents: list[Agent]
60+
tasks: list[Task]
61+
tools: list[Tool]
62+
inputs: dict[str, str]
63+
64+
def to_v3(self) -> 'TemplateConfig':
65+
return TemplateConfig(
66+
name=self.name,
67+
description=self.description,
68+
template_version=3,
69+
framework=self.framework,
70+
method=self.method,
71+
manager_agent=None,
72+
agents=[TemplateConfig.Agent(**agent.dict()) for agent in self.agents],
73+
tasks=[TemplateConfig.Task(**task.dict()) for task in self.tasks],
74+
tools=[TemplateConfig.Tool(**tool.dict()) for tool in self.tools],
75+
inputs=self.inputs,
76+
)
77+
78+
3679
class TemplateConfig(pydantic.BaseModel):
3780
"""
3881
Interface for interacting with template configuration files.
@@ -51,6 +94,8 @@ class TemplateConfig(pydantic.BaseModel):
5194
The framework the template is for.
5295
method: str
5396
The method used by the project. ie. "sequential"
97+
manager_agent: Optional[str]
98+
The name of the agent that manages the project.
5499
agents: list[TemplateConfig.Agent]
55100
A list of agents used by the project.
56101
tasks: list[TemplateConfig.Task]
@@ -66,6 +111,7 @@ class Agent(pydantic.BaseModel):
66111
role: str
67112
goal: str
68113
backstory: str
114+
allow_delegation: bool = False
69115
model: str
70116

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

81127
name: str
82128
description: str
83-
template_version: Literal[2]
129+
template_version: Literal[3]
84130
framework: str
85131
method: str
132+
manager_agent: Optional[str]
86133
agents: list[Agent]
87134
tasks: list[Task]
88135
tools: list[Tool]
@@ -134,8 +181,10 @@ def from_json(cls, data: dict) -> 'TemplateConfig':
134181
try:
135182
match data.get('template_version'):
136183
case 1:
137-
return TemplateConfig_v1(**data).to_v2()
184+
return TemplateConfig_v1(**data).to_v2().to_v3()
138185
case 2:
186+
return TemplateConfig_v2(**data).to_v3()
187+
case 3:
139188
return cls(**data) # current version
140189
case _:
141190
raise ValidationError(f"Unsupported template version: {data.get('template_version')}")

agentstack/templates/crewai/{{cookiecutter.project_metadata.project_slug}}/src/crew.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ class {{cookiecutter.project_metadata.project_name|replace('-', '')|replace('_',
88

99
# Agent definitions
1010
{%- for agent in cookiecutter.structure.agents %}
11-
@agent
11+
{% if not agent.name == cookiecutter.structure.manager_agent %}@agent{% endif %}
1212
def {{agent.name}}(self) -> Agent:
1313
return Agent(
1414
config=self.agents_config['{{ agent.name }}'],
1515
tools=[], # Pass in what tools this agent should have
16-
verbose=True
16+
verbose=True,
17+
{% if agent.allow_delegation %}allow_delegation=True{% endif %}
1718
)
1819
{%- endfor %}
1920

@@ -32,7 +33,7 @@ def crew(self) -> Crew:
3233
return Crew(
3334
agents=self.agents, # Automatically created by the @agent decorator
3435
tasks=self.tasks, # Automatically created by the @task decorator
35-
process=Process.sequential,
36+
process=Process.{{cookiecutter.structure.method}},
37+
{% if cookiecutter.structure.manager_agent %}manager_agent=self.{{cookiecutter.structure.manager_agent}}(),{% endif %}
3638
verbose=True,
37-
# process=Process.hierarchical, # In case you wanna use that instead https://docs.crewai.com/how-to/Hierarchical/
3839
)
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"name": "reasoning",
3+
"description": "Implement test time compute using an agentic framework to emulate o1 with Claude.",
4+
"template_version": 3,
5+
"framework": "crewai",
6+
"agents": [{
7+
"name": "manager",
8+
"role": "Manager",
9+
"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>",
10+
"backstory": "You are responsible for delegating tasks to the right AI agents and ensuring that the team works together to achieve the best results.",
11+
"allow_delegation": true,
12+
"model": "anthropic/claude-3-5-sonnet-20240620"
13+
}, {
14+
"name": "triage",
15+
"role": "Triage",
16+
"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>",
17+
"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.",
18+
"model": "anthropic/claude-3-5-sonnet-20240620"
19+
}, {
20+
"name": "worker",
21+
"role": "Worker",
22+
"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.",
23+
"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.",
24+
"model": "anthropic/claude-3-5-sonnet-20240620"
25+
}, {
26+
"name": "fact_checker",
27+
"role": "Fact Checker",
28+
"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>",
29+
"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.",
30+
"model": "anthropic/claude-3-5-sonnet-20240620"
31+
}],
32+
"tasks": [{
33+
"name": "identify_plan_of_action",
34+
"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.",
35+
"expected_output": "A detailed description of the problem being presented and a list of possible steps that can be taken to explore possible solutions.",
36+
"agent": "triage"
37+
}, {
38+
"name": "find_solution",
39+
"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.",
40+
"expected_output": "A concise, complete solution to the problem being presented.",
41+
"agent": "worker"
42+
}, {
43+
"name": "check_solution",
44+
"description": "Review the problem and solution being presented and determine wether you think it is correct or not.",
45+
"expected_output": "Reiterate the solution to be factually correct.",
46+
"agent": "fact_checker"
47+
}],
48+
"tools": [],
49+
"method": "hierarchical",
50+
"manager_agent": "manager",
51+
"inputs": {
52+
"query": "What is the meaning of life, the universe, and everything?"
53+
}
54+
}

0 commit comments

Comments
 (0)