Skip to content

Commit

Permalink
Improve auto reply registration (#1170)
Browse files Browse the repository at this point in the history
* Improve auto reply registration

* object key

* fix test error

* bug fix in math user proxy agent

* allow send/receive without reply

* reset -> stop
  • Loading branch information
sonichi authored Aug 4, 2023
1 parent 45b7d90 commit 2208dfb
Show file tree
Hide file tree
Showing 10 changed files with 890 additions and 315 deletions.
3 changes: 1 addition & 2 deletions flaml/autogen/agentchat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@
from .responsive_agent import ResponsiveAgent
from .assistant_agent import AssistantAgent
from .user_proxy_agent import UserProxyAgent
from .groupchat import GroupChatManager, GroupChatParticipant
from .groupchat import GroupChatManager

__all__ = [
"Agent",
"ResponsiveAgent",
"AssistantAgent",
"UserProxyAgent",
"GroupChatManager",
"GroupChatParticipant",
]
4 changes: 2 additions & 2 deletions flaml/autogen/agentchat/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ def name(self):
"""Get the name of the agent."""
return self._name

def send(self, message: Union[Dict, str], recipient: "Agent"):
def send(self, message: Union[Dict, str], recipient: "Agent", request_reply: Optional[bool] = None):
"""(Aabstract method) Send a message to another agent."""

def receive(self, message: Union[Dict, str], sender: "Agent"):
def receive(self, message: Union[Dict, str], sender: "Agent", request_reply: Optional[bool] = None):
"""(Abstract method) Receive a message from another agent."""

def reset(self):
Expand Down
6 changes: 3 additions & 3 deletions flaml/autogen/agentchat/contrib/math_user_proxy_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def __init__(
default_auto_reply=default_auto_reply,
**kwargs,
)
self.register_auto_reply(Agent, self._generate_math_reply)
self.register_auto_reply(Agent, self._generate_math_reply, 1)
# fixed var
self._max_invalid_q_per_step = max_invalid_q_per_step

Expand Down Expand Up @@ -283,7 +283,7 @@ def _generate_math_reply(
):
"""Generate an auto reply."""
if messages is None:
messages = self._oai_messages[sender.name]
messages = self._oai_messages[sender]
message = messages[-1]
message = message.get("content", "")
code_blocks = extract_code(message)
Expand Down Expand Up @@ -313,7 +313,7 @@ def _generate_math_reply(
reply = reply.strip()

if self.last_reply == reply:
return reply + "\nYour query or result is same from the last, please try a new approach."
return True, reply + "\nYour query or result is same from the last, please try a new approach."
self.last_reply = reply

if not all_success:
Expand Down
119 changes: 32 additions & 87 deletions flaml/autogen/agentchat/groupchat.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import sys
from typing import Dict, List, Optional, Tuple, Union
from typing import Dict, List, Optional, Union
from .agent import Agent
from .responsive_agent import ResponsiveAgent


class GroupChatManager(ResponsiveAgent):
"""(WIP) A chat manager agent that can manage a group chat of multiple agents."""

agents: List["GroupChatParticipant"]
agents: List[Agent]
max_round: int

def _participant_roles(self):
return "\n".join([f"{agent.name}: {agent.system_message}" for agent in self.agents])

def _select_speaker_msg(self):
return {
"role": "system",
"content": f"""You are in a role play game. The following roles are available:
return f"""You are in a role play game. The following roles are available:
{self._participant_roles()}. Read the following conversation.
Then select the next role from {self._agent_names} to play. Only return the role.""",
}
Then select the next role from {self._agent_names} to play. Only return the role."""

def __init__(
self,
Expand All @@ -28,6 +25,7 @@ def __init__(
# unlimited consecutive auto reply by default
max_consecutive_auto_reply: Optional[int] = sys.maxsize,
human_input_mode: Optional[str] = "NEVER",
system_message: Optional[str] = "Group chat manager.",
# seed: Optional[int] = 4,
**kwargs,
):
Expand All @@ -37,11 +35,9 @@ def __init__(
human_input_mode=human_input_mode,
**kwargs,
)
self.register_auto_reply(GroupChatParticipant, self._generate_reply_for_participant)
self.register_auto_reply(Agent, self._generate_reply_for_participant)
self.max_round = max_round
self._agent_names = []
self._next_speaker = None
self._round = 0
self._messages = []
# self._random = random.Random(seed)

Expand All @@ -50,94 +46,43 @@ def _generate_reply_for_participant(
messages: Optional[List[Dict]] = None,
sender: Optional[Agent] = None,
) -> Union[str, Dict, None]:
self._agent_names = [agent.name for agent in self.agents]
if messages is None:
messages = self._oai_messages[sender.name]
messages = self._oai_messages[sender]
message = messages[-1]
# set the name to sender's name if the role is not function
if message["role"] != "function":
message["name"] = sender.name
self._messages.append(message)
self._next_speaker = None
# broadcast the message to all agents except the sender
for agent in self.agents:
if agent != sender:
self.send(message, agent)
if self._round == 0:
self._agent_names = [agent.name for agent in self.agents]
self._round += 1
if self._round >= self.max_round:
return True, None
# speaker selection msg from an agent
self._next_speaker = self._select_speaker(sender)
self._next_speaker.send(self._next_speaker.generate_reply(sender=self), self)
speaker = sender
for i in range(self.max_round):
# set the name to speaker's name if the role is not function
if message["role"] != "function":
message["name"] = speaker.name
self._messages.append(message)
# broadcast the message to all agents except the speaker
for agent in self.agents:
if agent != speaker:
self.send(message, agent, request_reply=False)
if i != self.max_round - 1:
# speaker selection msg from an agent
speaker = self._select_speaker(speaker)
speaker.send(speaker.generate_reply(sender=self), self, request_reply=False)
message = self.last_message(speaker)
return True, None

@property
def next_speaker(self):
"""Return the next speaker."""
return self._next_speaker

def _select_speaker(self, last_speaker: "GroupChatParticipant"):
def _select_speaker(self, last_speaker: Agent):
"""Select the next speaker."""
final, name = self._generate_oai_reply([self._select_speaker_msg()] + self._messages)
self.update_system_message(self._select_speaker_msg())
final, name = self._generate_oai_reply(self._messages)
if not final:
# i = self._random.randint(0, len(self._agent_names) - 1) # randomly pick an id
name = self._agent_names[(self._agent_names.index(last_speaker.name) + 1) % len(self._agent_names)]
return self.agent_by_name(name)
return self.agents[(self._agent_names.index(last_speaker.name) + 1) % len(self._agent_names)]
try:
return self.agent_by_name(name)
except ValueError:
return self.agents[(self._agent_names.index(last_speaker.name) + 1) % len(self._agent_names)]

def agent_by_name(self, name: str) -> "GroupChatParticipant":
def agent_by_name(self, name: str) -> Agent:
"""Find the next speaker based on the message."""
return self.agents[self._agent_names.index(name)]

def reset(self):
super().reset()
self._round = 0
self._messages.clear()
self._next_speaker = None


class GroupChatParticipant(ResponsiveAgent):
"""(WIP) A group chat participant agent that can participate in a group chat."""

group_chat_manager: GroupChatManager

def __init__(
self,
name,
group_chat_manager=None,
**kwargs,
):
super().__init__(
name=name,
**kwargs,
)
self.register_auto_reply(GroupChatManager, self._generate_reply_for_chat_manager)
self.group_chat_manager = group_chat_manager

def _generate_reply_for_chat_manager(
self,
messages: Optional[List[Dict]] = None,
sender: Optional[Agent] = None,
) -> Tuple[bool, Union[str, Dict, None]]:
"""Generate reply for the chat manager."""
return self.group_chat_manager.next_speaker != self, None


# def _speaker_selection(self, instruction):
# """Select the next speaker."""
# if self.llm_config is False:
# if self.human_input_mode == "NEVER":
# return self.name
# else:
# return self.get_human_input(instruction["content"])
# sender = self.chat_manager.room
# roles_msg = {
# "content": f"""The following roles are available:
# {self._participant_roles()}""",
# "role": "system",
# }
# old_system_msg = self.system_message
# self.update_system_message(instruction["content"])
# reply = self._generate_oai_reply([roles_msg] + self.chat_messages[sender.name])
# self.update_system_message(old_system_msg)
# return reply
Loading

0 comments on commit 2208dfb

Please sign in to comment.