Skip to content

Commit

Permalink
Added Manager module in decision-making. [To-Do]: 1. optimizer parser…
Browse files Browse the repository at this point in the history
… 2. Manager's prompt
  • Loading branch information
yushengsu-thu committed Sep 1, 2023
1 parent f06aab2 commit 5c010f5
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 10 deletions.
1 change: 1 addition & 0 deletions agentverse/agents/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
from agentverse.agents.pipeline.critic import CriticAgent
from agentverse.agents.pipeline.evaluator import EvaluatorAgent
from agentverse.agents.pipeline.solver import SolverAgent
from agentverse.agents.pipeline.manager import ManagerAgent
from agentverse.agents.pipeline.executor import ExecutorAgent
106 changes: 106 additions & 0 deletions agentverse/agents/pipeline/manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from __future__ import annotations

import asyncio
from colorama import Fore

from agentverse.logging import get_logger
import bdb
from string import Template
from typing import TYPE_CHECKING, List, Tuple

# from agentverse.environments import PipelineEnvironment
from agentverse.message import Message

from agentverse.agents import agent_registry
from agentverse.agents.base import BaseAgent
from agentverse.utils import AgentCriticism

import random


logger = get_logger()


@agent_registry.register("manager")
class ManagerAgent(BaseAgent):
prompt_template: str

def step(
self,
former_solution: str,
critic_opinions: List[AgentCriticism],
advice: str,
task_description: str = "",
previous_sentence: str = "",
) -> Message:
prompt = self._fill_prompt_template(
former_solution, critic_opinions, advice, task_description, previous_sentence
)

logger.debug(f"Prompt:\n{prompt}", "Manager", Fore.CYAN)
parsed_response = None
for i in range(self.max_retry):
try:
# LLM Manager
'''
response = self.llm.generate_response(prompt)
parsed_response = self.output_parser.parse(response)
'''
# Random Manager
parsed_response = random.choice(critic_opinions)
break
except (KeyboardInterrupt, bdb.BdbQuit):
raise
except Exception as e:
logger.error(e)
logger.warn("Retrying...")
continue
return parsed_response


async def astep(self, env_description: str = "") -> Message:
"""Asynchronous version of step"""
pass

def _fill_prompt_template(
self,
former_solution: str,
critic_opinions: List[AgentCriticism],
advice: str,
task_description: str,
previous_sentence: str,
) -> str:
"""Fill the placeholders in the prompt template
In the role_assigner agent, three placeholders are supported:
- ${task_description}
- ${former_solution}
- ${critic_messages}
- ${advice}
- ${previous_sentence}
"""
input_arguments = {
"task_description": task_description,
"former_solution": former_solution,
"previous_sentence": previous_sentence,
"critic_opinions": "\n".join(
[
f"{critic.sender_agent.role_description} said: {critic.criticism}"
for critic in critic_opinions
]
),
"advice": advice,
}


# manger select the proper sentence
template = Template(self.prompt_template)
return template.safe_substitute(input_arguments)

def add_message_to_memory(self, messages: List[Message]) -> None:
self.memory.add_message(messages)

def reset(self) -> None:
"""Reset the agent"""
self.memory.reset()
# TODO: reset receiver
12 changes: 11 additions & 1 deletion agentverse/agentversepipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ def from_task(cls, task: str):
# Build the environment
env_config = task_config["environment"]

# Build agents
#if task_config["environment"]["decision_maker"]["type"] == "dynamic":
# task_config["cnt_agents"] += 1
# env_config["cnt_agents"] = task_config["cnt_agents"]


# Build agents for all pipeline (task)
agents = {}
for i, agent_config in enumerate(task_config["agents"]):
agent_type = AGENT_TYPES(i)
Expand All @@ -53,8 +58,12 @@ def from_task(cls, task: str):
]
else:
agents[agent_type] = load_agent(agent_config)

env_config["agents"] = agents




# env_config["role_assignment"] = load_agent(task_config["agents"][0])
# env_config["execution"] = load_agent(task_config["agents"][3])
# env_config["evaluation"] = load_agent(task_config["agents"][4])
Expand All @@ -76,6 +85,7 @@ def run(
single_agent: bool = False,
discussion_mode: bool = False,
):

"""Run the environment from scratch until it is done.
**Rewrite in pipeline to let the pipeline architecture more clear**
Expand Down
33 changes: 32 additions & 1 deletion agentverse/environments/decision_maker/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,44 @@ class DynamicDecisionMaker(BaseDecisionMaker):
async def astep(
self,
agents: List[BaseAgent],
manager: List[BaseAgent],
task_description: str,
previous_plan: str = "No solution yet.",
advice: str = "No advice yet.",
previous_sentence: str = "No any sentence yet.",
*args,
**kwargs,
) -> List[str]:
pass


# Speak simultaneously
# Manger select the optimial one as the current spoken sentence
reviews = list()
for i in range(len(agents)):

review = await asyncio.gather(
*[
agent.astep(previous_plan, advice, task_description)
for agent in agents[1:]
]
)

#typewriter_log("Reviews:", Fore.YELLOW)
#typewriter_log(
# "\n".join(
# [
# f"[{review.sender_agent.role_description}]: {review.criticism}"
# for review in reviews
# ]
# ),
# Fore.YELLOW,
#)

previous_sentence = manager.step(previous_plan, review, advice, task_description, previous_sentence)
reviews.append(previous_sentence)

result = agents[0].step(previous_plan, reviews, advice, task_description)
return [result]


def reset(self):
Expand Down
1 change: 1 addition & 0 deletions agentverse/environments/decision_maker/horizontal.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ async def astep(
*args,
**kwargs,
) -> List[str]:

# pass

# Here we assume that the first agent is the [summarizer].
Expand Down
36 changes: 28 additions & 8 deletions agentverse/environments/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from agentverse.utils import AGENT_TYPES
from agentverse.environments.decision_maker import (
BaseDecisionMaker,
DynamicDecisionMaker,
decision_maker_registry,
)
from agentverse.environments.evaluator import BaseEvaluator, evaluator_registry
Expand Down Expand Up @@ -42,6 +43,7 @@ class PipelineEnvironment(BaseModel):

agents: Dict[Enum, Union[BaseAgent, List[BaseAgent]]] = None


role_assigner: BaseRoleAssigner
decision_maker: BaseDecisionMaker
executor: BaseExecutor
Expand All @@ -58,7 +60,6 @@ def build_components(config: Dict, registry):
component_type = config.pop("type")
component = registry.build(component_type, **config)
return component

role_assigner = build_components(
kwargs.pop("role_assigner", {"type": "role_description"}),
role_assigner_registry,
Expand All @@ -74,6 +75,7 @@ def build_components(config: Dict, registry):
kwargs.pop("evaluator", {"type": "basic"}), evaluator_registry
)


super().__init__(
role_assigner=role_assigner,
decision_maker=decision_maker,
Expand Down Expand Up @@ -124,7 +126,12 @@ async def step(self) -> List[Message]:
# ================== EXPERT RECRUITMENT ==================

# ================== DECISION MAKING ==================
plan = await self.decision_making(agents, previous_plan, advice)
# if dynamic
if str(type(self.decision_maker)) == str("<class 'agentverse.environments.decision_maker.dynamic.DynamicDecisionMaker'>"):
plan = await self.decision_making(agents, self.agents[AGENT_TYPES.MANAGER], previous_plan, advice)
#plan = await self.decision_making(agents, previous_plan, advice)
else:
plan = await self.decision_making(agents, previous_plan, advice)
# Although plan may be a list in some cases, all the cases we currently consider
# only have one plan, so we just take the first element.
# TODO: make it more general
Expand Down Expand Up @@ -170,6 +177,7 @@ async def step(self) -> List[Message]:

def role_assign(self, advice: str = "") -> List[BaseAgent]:
"""Assign roles to agents"""

agents = self.role_assigner.step(
role_assigner=self.agents[AGENT_TYPES.ROLE_ASSIGNMENT],
group_members=[self.agents[AGENT_TYPES.SOLVER]]
Expand All @@ -182,16 +190,28 @@ def role_assign(self, advice: str = "") -> List[BaseAgent]:
async def decision_making(
self,
agents: List[BaseAgent],
manager: List[BaseAgent],
previous_plan: str,
advice: str = "No advice yet.",
) -> List[str]:
# TODO: plan should be string or a special type of object?
plan = await self.decision_maker.astep(
agents=agents,
task_description=self.task_description,
previous_plan=previous_plan,
advice=advice,
)

#dynamic
if str(type(manager)) == str("<class 'agentverse.agents.pipeline.manager.ManagerAgent'>"):
plan = await self.decision_maker.astep(
agents=agents,
manager=manager,
task_description=self.task_description,
previous_plan=previous_plan,
advice=advice,
)
else:
plan = await self.decision_maker.astep(
agents=agents,
task_description=self.task_description,
previous_plan=previous_plan,
advice=advice,
)
return plan

# def solve(
Expand Down
2 changes: 2 additions & 0 deletions agentverse/environments/role_assigner/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class BaseRoleAssigner(BaseModel):
The base class of role assignment class.
"""


@abstractmethod
def step(
self,
Expand All @@ -24,6 +25,7 @@ def step(
*args,
**kwargs,
) -> List[str]:

pass

def reset(self):
Expand Down
38 changes: 38 additions & 0 deletions agentverse/tasks/humaneval/gpt-3.5-new/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@ prompts:

Now give your response.


manager_prompt: &manager_prompt |-
According to the current solution and the previous sentences, select the most appropriate critic to optimize the current solution.
```python
${task_description}
```
# Previous Solution
The solution you gave in the last step is:
${former_solution}

# Critics
There are some critics on the above solution:
```
${critic_opinions}
```

# Previous Sentences
The previous sentences in the previous rounds is:
${previous_sentence}


evaluator_prompt: &evaluator_prompt |-
You are an experienced code reviewer. As a good reviewer, you carefully check the correctness of the given code completion. When the completion is incorrect, you should patiently teach the writer how to correct the completion.

Expand Down Expand Up @@ -173,3 +194,20 @@ agents:
type: mgsm-evaluator
dimensions:
- Score


- #manager_agent:
agent_type: manager
name: Manager
prompt_template: *manager_prompt
memory:
memory_type: chat_history
llm:
llm_type: gpt-3.5-turbo
model: "gpt-3.5-turbo"
temperature: 0
max_tokens: 1024
output_parser:
type: humaneval-manager


23 changes: 23 additions & 0 deletions agentverse/tasks/humaneval/output_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,29 @@ def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
return AgentFinish({"output": cleaned_output}, text)



@output_parser_registry.register("humaneval-manager")
class HumanevalManagerParser(OutputParser):
def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
return AgentFinish({"output": output.content}, output.content)


@output_parser_registry.register("humaneval-solver")
class HumanevalSolverParser(OutputParser):
def parse(self, output: LLMResult) -> Union[AgentAction, AgentFinish]:
text = output.content
end_pos = text.rfind("```")
if end_pos == -1:
raise OutputParserError(text)
text = text[:end_pos]
cleaned_output = text.strip().strip("```").strip()
if cleaned_output.startswith("python"):
cleaned_output = cleaned_output[6:].strip()
elif cleaned_output.startswith("python3"):
cleaned_output = cleaned_output[7:].strip()
return AgentFinish({"output": cleaned_output}, text)


@output_parser_registry.register("humaneval-evaluator")
class HumanevalEvaluatorParser(OutputParser):
dimensions: List[str] = None
Expand Down
1 change: 1 addition & 0 deletions agentverse/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class AGENT_TYPES(Enum):
CRITIC = 2
EXECUTION = 3
EVALUATION = 4
MANAGER = 5


class Singleton(abc.ABCMeta, type):
Expand Down

0 comments on commit 5c010f5

Please sign in to comment.