Skip to content

Customisation of speaker select message and prompt in GroupChat #2182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
38 changes: 33 additions & 5 deletions autogen/agentchat/groupchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ class GroupChat:
When set to True and when a message is a function call suggestion,
the next speaker will be chosen from an agent which contains the corresponding function name
in its `function_map`.
- select_speaker_message_template: customize the select speaker message (used in "auto" speaker selection), which appears first in the message context and generally includes the agent descriptions and list of agents. The string value will be converted to an f-string, use "{roles}" to output the agent's and their role descriptions and "{agentlist}" for a comma-separated list of agent names in square brackets. The default value is:
"You are in a role play game. The following roles are available:
{roles}.

Read the following conversation.
Then select the next role from {agentlist} to play. Only return the role."
- select_speaker_prompt_template: customize the select speaker prompt (used in "auto" speaker selection), which appears last in the message context and generally includes the list of agents and guidance for the LLM to select the next agent. The string value will be converted to an f-string, use "{agentlist}" for a comma-separated list of agent names in square brackets. The default value is:
"Read the above conversation. Then select the next role from {agentlist} to play. Only return the role."
- speaker_selection_method: the method for selecting the next speaker. Default is "auto".
Could be any of the following (case insensitive), will raise ValueError if not recognized:
- "auto": the next speaker is selected automatically by LLM.
Expand Down Expand Up @@ -74,6 +82,14 @@ def custom_speaker_selection_func(
speaker_transitions_type: Literal["allowed", "disallowed", None] = None
enable_clear_history: Optional[bool] = False
send_introductions: bool = False
select_speaker_message_template: str = """You are in a role play game. The following roles are available:
{roles}.

Read the following conversation.
Then select the next role from {agentlist} to play. Only return the role."""
select_speaker_prompt_template: str = (
"Read the above conversation. Then select the next role from {agentlist} to play. Only return the role."
)

_VALID_SPEAKER_SELECTION_METHODS = ["auto", "manual", "random", "round_robin"]
_VALID_SPEAKER_TRANSITIONS_TYPE = ["allowed", "disallowed", None]
Expand Down Expand Up @@ -162,6 +178,13 @@ def __post_init__(self):
agents=self.agents,
)

# Check select_speaker_message_template and select_speaker_prompt_template have values
if self.select_speaker_message_template is None or len(self.select_speaker_message_template) == 0:
raise ValueError("select_speaker_message_template cannot be empty or None.")

if self.select_speaker_prompt_template is None or len(self.select_speaker_prompt_template) == 0:
raise ValueError("select_speaker_prompt_template cannot be empty or None.")

@property
def agent_names(self) -> List[str]:
"""Return the names of the agents in the group chat."""
Expand Down Expand Up @@ -232,17 +255,22 @@ def select_speaker_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"""You are in a role play game. The following roles are available:
{self._participant_roles(agents)}.

Read the following conversation.
Then select the next role from {[agent.name for agent in agents]} to play. Only return the role."""
roles = self._participant_roles(agents)
agentlist = f"{[agent.name for agent in agents]}"

return_msg = self.select_speaker_message_template.format(roles=roles, agentlist=agentlist)
return return_msg

def select_speaker_prompt(self, agents: Optional[List[Agent]] = None) -> str:
"""Return the floating system prompt selecting the next speaker. This is always the *last* message in the context."""
if agents is None:
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."

agentlist = f"{[agent.name for agent in agents]}"

return_prompt = self.select_speaker_prompt_template.format(agentlist=agentlist)
return return_prompt

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."""
Expand Down
95 changes: 94 additions & 1 deletion test/agentchat/test_groupchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,98 @@ def custom_speaker_selection_func(last_speaker: Agent, groupchat: GroupChat) ->
assert "teamA_executor" in speakers


def test_select_speaker_message_and_prompt_templates():
"""
In this test, two agents are part of a group chat which has customized select speaker message and select speaker prompt templates. Both valid and empty string values will be used.
The expected behaviour is that the customized speaker selection message and prompts will override the default values or throw exceptions if empty.
"""

agent1 = autogen.ConversableAgent(
"Alice",
description="A wonderful employee named Alice.",
human_input_mode="NEVER",
llm_config=False,
)
agent2 = autogen.ConversableAgent(
"Bob",
description="An amazing employee named Bob.",
human_input_mode="NEVER",
llm_config=False,
)

# Customised message, this is always the first message in the context
custom_msg = """You are the CEO of a niche organisation creating small software tools for the healthcare sector with a small team of specialists. Call them in sequence.
The job roles and responsibilities are:
{roles}

You must select only from {agentlist}."""

# Customised prompt, this is always the last message in the context
custom_prompt = """Read the above conversation.
Then select the next job role from {agentlist} to take action.
RETURN ONLY THE NAME OF THE NEXT ROLE."""

# Test empty is_termination_msg function
groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[],
speaker_selection_method="auto",
max_round=10,
select_speaker_message_template=custom_msg,
select_speaker_prompt_template=custom_prompt,
)

# Test with valid strings, checking for the correct string and roles / agentlist to be included

assert groupchat.select_speaker_msg() == custom_msg.replace(
"{roles}", "Alice: A wonderful employee named Alice.\nBob: An amazing employee named Bob."
).replace("{agentlist}", "['Alice', 'Bob']")

assert groupchat.select_speaker_prompt() == custom_prompt.replace("{agentlist}", "['Alice', 'Bob']")

# Test with empty strings
with pytest.raises(ValueError, match="select_speaker_message_template cannot be empty or None."):
groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[],
speaker_selection_method="auto",
max_round=10,
select_speaker_message_template="",
select_speaker_prompt_template="Not empty.",
)

with pytest.raises(ValueError, match="select_speaker_prompt_template cannot be empty or None."):
groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[],
speaker_selection_method="auto",
max_round=10,
select_speaker_message_template="Not empty.",
select_speaker_prompt_template=None,
)

# Test with None
with pytest.raises(ValueError, match="select_speaker_message_template cannot be empty or None."):
groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[],
speaker_selection_method="auto",
max_round=10,
select_speaker_message_template=None,
select_speaker_prompt_template="Not empty.",
)

with pytest.raises(ValueError, match="select_speaker_prompt_template cannot be empty or None."):
groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[],
speaker_selection_method="auto",
max_round=10,
select_speaker_message_template="Not empty.",
select_speaker_prompt_template="",
)


if __name__ == "__main__":
# test_func_call_groupchat()
# test_broadcast()
Expand All @@ -1190,5 +1282,6 @@ def custom_speaker_selection_func(last_speaker: Agent, groupchat: GroupChat) ->
# test_invalid_allow_repeat_speaker()
# test_graceful_exit_before_max_round()
# test_clear_agents_history()
test_custom_speaker_selection_overrides_transition_graph()
# test_custom_speaker_selection_overrides_transition_graph()
test_select_speaker_message_and_prompt_templates()
# pass