Groupchat send introductions (#961)

* Allow the GroupChatManager to send introductions.

* Fixed function name.

* Added test cases for sending introductions.

* Trying to sort out why remote pytest is failing.

* Fixed broken plugin behavior.

* Update autogen/agentchat/groupchat.py

Co-authored-by: Chi Wang <wang.chi@microsoft.com>

* Updated as per Chi's suggestions.

---------

Co-authored-by: Chi Wang <wang.chi@microsoft.com>
Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
This commit is contained in:
afourney 2024-02-24 08:50:46 -08:00 committed by GitHub
parent fb2b412c4a
commit 477598afff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 139 additions and 2 deletions

View File

@ -58,7 +58,8 @@ class GroupChat:
Must be supplied if `allowed_or_disallowed_speaker_transitions` is not None. Must be supplied if `allowed_or_disallowed_speaker_transitions` is not None.
- enable_clear_history: enable possibility to clear history of messages for agents manually by providing - enable_clear_history: enable possibility to clear history of messages for agents manually by providing
"clear history" phrase in user prompt. This is experimental feature. "clear history" phrase in user prompt. This is experimental feature.
See description of `GroupChatManager.clear_agents_history` function for more info. See description of GroupChatManager.clear_agents_history function for more info.
- send_introductions: send a round of introductions at the start of the group chat, so agents know who they can speak to (default: False)
""" """
agents: List[Agent] agents: List[Agent]
@ -71,6 +72,7 @@ class GroupChat:
allowed_or_disallowed_speaker_transitions: Optional[Dict] = None allowed_or_disallowed_speaker_transitions: Optional[Dict] = None
speaker_transitions_type: Optional[str] = None speaker_transitions_type: Optional[str] = None
enable_clear_history: Optional[bool] = False enable_clear_history: Optional[bool] = False
send_introductions: Optional[bool] = False
_VALID_SPEAKER_SELECTION_METHODS = ["auto", "manual", "random", "round_robin"] _VALID_SPEAKER_SELECTION_METHODS = ["auto", "manual", "random", "round_robin"]
_VALID_SPEAKER_TRANSITIONS_TYPE = ["allowed", "disallowed", None] _VALID_SPEAKER_TRANSITIONS_TYPE = ["allowed", "disallowed", None]
@ -229,6 +231,16 @@ Then select the next role from {[agent.name for agent in agents]} to play. Only
agents = self.agents agents = self.agents
return f"Read the above conversation. Then select the next role from {[agent.name for agent in agents]} to play. Only return the role." return f"Read the above conversation. Then select the next role from {[agent.name for agent in agents]} to play. Only return the role."
def introductions_msg(self, agents: Optional[List[Agent]] = None) -> str:
"""Return the system message for selecting the next speaker. This is always the *first* message in the context."""
if agents is None:
agents = self.agents
return f"""Hello everyone. We have assembled a great team today to answer questions and solve tasks. In attendance are:
{self._participant_roles(agents)}
"""
def manual_select_speaker(self, agents: Optional[List[Agent]] = None) -> Union[Agent, None]: def manual_select_speaker(self, agents: Optional[List[Agent]] = None) -> Union[Agent, None]:
"""Manually select the next speaker.""" """Manually select the next speaker."""
if agents is None: if agents is None:
@ -535,6 +547,16 @@ class GroupChatManager(ConversableAgent):
message = messages[-1] message = messages[-1]
speaker = sender speaker = sender
groupchat = config groupchat = config
send_introductions = getattr(groupchat, "send_introductions", False)
if send_introductions:
# Broadcast the intro
intro = groupchat.introductions_msg()
for agent in groupchat.agents:
self.send(intro, agent, request_reply=False, silent=True)
# NOTE: We do not also append to groupchat.messages,
# since groupchat handles its own introductions
if self.client_cache is not None: if self.client_cache is not None:
for a in groupchat.agents: for a in groupchat.agents:
a.previous_cache = a.client_cache a.previous_cache = a.client_cache
@ -598,6 +620,16 @@ class GroupChatManager(ConversableAgent):
message = messages[-1] message = messages[-1]
speaker = sender speaker = sender
groupchat = config groupchat = config
send_introductions = getattr(groupchat, "send_introductions", False)
if send_introductions:
# Broadcast the intro
intro = groupchat.introductions_msg()
for agent in groupchat.agents:
self.a_send(intro, agent, request_reply=False, silent=True)
# NOTE: We do not also append to groupchat.messages,
# since groupchat handles its own introductions
if self.client_cache is not None: if self.client_cache is not None:
for a in groupchat.agents: for a in groupchat.agents:
a.previous_cache = a.client_cache a.previous_cache = a.client_cache

View File

@ -448,6 +448,110 @@ def test_next_agent():
assert groupchat.next_agent(agent4, [agent1, agent2, agent3]) == agent1 assert groupchat.next_agent(agent4, [agent1, agent2, agent3]) == agent1
def test_send_intros():
agent1 = autogen.ConversableAgent(
"alice",
description="The first agent.",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is alice speaking. TERMINATE",
)
agent2 = autogen.ConversableAgent(
"bob",
description="The second agent.",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is bob speaking. TERMINATE",
)
agent3 = autogen.ConversableAgent(
"sam",
description="The third agent.",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is sam speaking. TERMINATE",
)
agent4 = autogen.ConversableAgent(
"sally",
description="The fourth agent.",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is sally speaking. TERMINATE",
)
# Test empty is_termination_msg function
groupchat = autogen.GroupChat(
agents=[agent1, agent2, agent3],
messages=[],
speaker_selection_method="round_robin",
max_round=10,
send_introductions=True,
)
intro = groupchat.introductions_msg()
assert "The first agent." in intro
assert "The second agent." in intro
assert "The third agent." in intro
assert "The fourth agent." not in intro
intro = groupchat.introductions_msg([agent1, agent2, agent4])
assert "The first agent." in intro
assert "The second agent." in intro
assert "The third agent." not in intro
assert "The fourth agent." in intro
groupchat = autogen.GroupChat(
agents=[agent1, agent2, agent3],
messages=[],
speaker_selection_method="round_robin",
max_round=10,
send_introductions=True,
)
group_chat_manager = autogen.GroupChatManager(
groupchat=groupchat,
llm_config=False,
is_termination_msg=lambda x: x.get("content", "").rstrip().find("TERMINATE") >= 0,
)
group_chat_manager.initiate_chat(group_chat_manager, message="The initiating message.")
for a in [agent1, agent2, agent3]:
messages = agent1.chat_messages[group_chat_manager]
assert len(messages) == 3
assert "The first agent." in messages[0]["content"]
assert "The second agent." in messages[0]["content"]
assert "The third agent." in messages[0]["content"]
assert "The initiating message." == messages[1]["content"]
assert messages[2]["content"] == agent1._default_auto_reply
# Reset and start again
agent1.reset()
agent2.reset()
agent3.reset()
agent4.reset()
# Check the default (no introductions)
groupchat2 = autogen.GroupChat(
agents=[agent1, agent2, agent3], messages=[], speaker_selection_method="round_robin", max_round=10
)
group_chat_manager2 = autogen.GroupChatManager(
groupchat=groupchat2,
llm_config=False,
is_termination_msg=lambda x: x.get("content", "").rstrip().find("TERMINATE") >= 0,
)
group_chat_manager2.initiate_chat(group_chat_manager2, message="The initiating message.")
for a in [agent1, agent2, agent3]:
messages = agent1.chat_messages[group_chat_manager2]
assert len(messages) == 2
assert "The initiating message." == messages[0]["content"]
assert messages[1]["content"] == agent1._default_auto_reply
def test_selection_helpers(): def test_selection_helpers():
agent1 = autogen.ConversableAgent( agent1 = autogen.ConversableAgent(
"alice", "alice",
@ -814,6 +918,7 @@ if __name__ == "__main__":
# test_agent_mentions() # test_agent_mentions()
# test_termination() # test_termination()
# test_next_agent() # test_next_agent()
test_send_intros()
# test_invalid_allow_repeat_speaker() # test_invalid_allow_repeat_speaker()
# test_graceful_exit_before_max_round() # test_graceful_exit_before_max_round()
test_clear_agents_history() # test_clear_agents_history()