diff --git a/autogen/agentchat/groupchat.py b/autogen/agentchat/groupchat.py index 1947a392aa1d..f0573989f42e 100644 --- a/autogen/agentchat/groupchat.py +++ b/autogen/agentchat/groupchat.py @@ -58,7 +58,8 @@ class GroupChat: 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 "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] @@ -71,6 +72,7 @@ class GroupChat: allowed_or_disallowed_speaker_transitions: Optional[Dict] = None speaker_transitions_type: Optional[str] = None enable_clear_history: Optional[bool] = False + send_introductions: Optional[bool] = False _VALID_SPEAKER_SELECTION_METHODS = ["auto", "manual", "random", "round_robin"] _VALID_SPEAKER_TRANSITIONS_TYPE = ["allowed", "disallowed", None] @@ -229,6 +231,16 @@ def select_speaker_prompt(self, agents: Optional[List[Agent]] = None) -> str: 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." + 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]: """Manually select the next speaker.""" if agents is None: @@ -529,6 +541,16 @@ def run_chat( message = messages[-1] speaker = sender 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: for a in groupchat.agents: a.previous_cache = a.client_cache @@ -592,6 +614,16 @@ async def a_run_chat( message = messages[-1] speaker = sender 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: for a in groupchat.agents: a.previous_cache = a.client_cache diff --git a/test/agentchat/test_groupchat.py b/test/agentchat/test_groupchat.py index 8bb55fc0f66c..51a4906c09ca 100644 --- a/test/agentchat/test_groupchat.py +++ b/test/agentchat/test_groupchat.py @@ -448,6 +448,110 @@ def test_next_agent(): 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(): agent1 = autogen.ConversableAgent( "alice", @@ -814,6 +918,7 @@ def chat(gc_manager: autogen.GroupChatManager): # test_agent_mentions() # test_termination() # test_next_agent() + test_send_intros() # test_invalid_allow_repeat_speaker() # test_graceful_exit_before_max_round() - test_clear_agents_history() + # test_clear_agents_history()