Skip to content

Commit

Permalink
Added ability to specify 'role' field for select speaker messages for…
Browse files Browse the repository at this point in the history
… Group Chats (Replaces PR #2167) (#2199)

* Re-commit of code from PR (#2167) addressing #1861, due to wrong basing

* Update website/docs/topics/non-openai-models/best-tips-for-nonopenai-models.md

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>

* Removed unnecessary notebook images

* Update conversation-patterns.ipynb

Updated to include note about being applicable when auto.

* Updated to include checks that the role is not blank/None. Added tests.

* Changed try-except to use pytest

---------

Co-authored-by: Eric Zhu <ekzhu@users.noreply.github.com>
  • Loading branch information
marklysze and ekzhu authored Mar 31, 2024
1 parent fd96d3d commit 3f63db3
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 9 deletions.
12 changes: 10 additions & 2 deletions autogen/agentchat/groupchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def custom_speaker_selection_func(
"clear history" phrase in user prompt. This is experimental feature.
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)
- role_for_select_speaker_messages: sets the role name for speaker selection when in 'auto' mode, typically 'user' or 'system'. (default: 'system')
"""

agents: List[Agent]
Expand All @@ -74,6 +75,7 @@ def custom_speaker_selection_func(
speaker_transitions_type: Literal["allowed", "disallowed", None] = None
enable_clear_history: Optional[bool] = False
send_introductions: bool = False
role_for_select_speaker_messages: Optional[str] = "system"

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

if self.role_for_select_speaker_messages is None or len(self.role_for_select_speaker_messages) == 0:
raise ValueError("role_for_select_speaker_messages 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 @@ -411,7 +416,7 @@ def _prepare_and_select_agents(
selected_agent = self.next_agent(last_speaker, graph_eligible_agents)
elif speaker_selection_method.lower() == "random":
selected_agent = self.random_select_speaker(graph_eligible_agents)
else:
else: # auto
selected_agent = None
select_speaker_messages = self.messages.copy()
# If last message is a tool call or function call, blank the call so the api doesn't throw
Expand All @@ -420,7 +425,10 @@ def _prepare_and_select_agents(
if select_speaker_messages[-1].get("tool_calls", False):
select_speaker_messages[-1] = dict(select_speaker_messages[-1], tool_calls=None)
select_speaker_messages = select_speaker_messages + [
{"role": "system", "content": self.select_speaker_prompt(graph_eligible_agents)}
{
"role": self.role_for_select_speaker_messages,
"content": self.select_speaker_prompt(graph_eligible_agents),
}
]
return selected_agent, graph_eligible_agents, select_speaker_messages

Expand Down
68 changes: 67 additions & 1 deletion test/agentchat/test_groupchat.py
Original file line number Diff line number Diff line change
Expand Up @@ -1176,6 +1176,71 @@ def custom_speaker_selection_func(last_speaker: Agent, groupchat: GroupChat) ->
assert "teamA_executor" in speakers


def test_role_for_select_speaker_messages():
agent1 = autogen.ConversableAgent(
"alice",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is alice speaking.",
)
agent2 = autogen.ConversableAgent(
"bob",
max_consecutive_auto_reply=10,
human_input_mode="NEVER",
llm_config=False,
default_auto_reply="This is bob speaking.",
)

groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[{"role": "user", "content": "Let's have a chat!"}],
max_round=3,
)

# Run the select agents function to get the select speaker messages
selected_agent, agents, messages = groupchat._prepare_and_select_agents(agent1)

# Test default is "system"
assert len(messages) == 2
assert messages[-1]["role"] == "system"

# Test as "user"
groupchat.role_for_select_speaker_messages = "user"
selected_agent, agents, messages = groupchat._prepare_and_select_agents(agent1)

assert len(messages) == 2
assert messages[-1]["role"] == "user"

# Test as something unusual
groupchat.role_for_select_speaker_messages = "SockS"
selected_agent, agents, messages = groupchat._prepare_and_select_agents(agent1)

assert len(messages) == 2
assert messages[-1]["role"] == "SockS"

# Test empty string and None isn't accepted

# Test with empty strings
with pytest.raises(ValueError) as e:
groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[{"role": "user", "content": "Let's have a chat!"}],
max_round=3,
role_for_select_speaker_messages="",
)
assert "role_for_select_speaker_messages cannot be empty or None." in str(e.value)

with pytest.raises(ValueError) as e:
groupchat = autogen.GroupChat(
agents=[agent1, agent2],
messages=[{"role": "user", "content": "Let's have a chat!"}],
max_round=3,
role_for_select_speaker_messages=None,
)
assert "role_for_select_speaker_messages cannot be empty or None." in str(e.value)


if __name__ == "__main__":
# test_func_call_groupchat()
# test_broadcast()
Expand All @@ -1190,5 +1255,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_role_for_select_speaker_messages()
# pass
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Tips for Non-OpenAI Models

Here are some tips for using non-OpenAI Models with AutoGen.

## Finding the right model
Every model will perform differently across the operations within your AutoGen
setup, such as speaker selection, coding, function calling, content creation,
etc. On the whole, larger models (13B+) perform better with following directions
and providing more cohesive responses.

Content creation can be performed by most models.

Fine-tuned models can be great for very specific tasks, such as function calling
and coding.

Specific tasks, such as speaker selection in a Group Chat scenario, that require
very accurate outputs can be a challenge with most open source/weight models. The
use of chain-of-thought and/or few-shot prompting can help guide the LLM to provide
the output in the format you want.

## Validating your program
Testing your AutoGen setup against a very large LLM, such as OpenAI's ChatGPT or
Anthropic's Claude 3, can help validate your agent setup and configuration.

Once a setup is performing as you want, you can replace the models for your agents
with non-OpenAI models and iteratively tweak system messages, prompts, and model
selection.

## Chat template
AutoGen utilises a set of chat messages for the conversation between AutoGen/user
and LLMs. Each chat message has a role attribute that is typically `user`,
`assistant`, or `system`.

A chat template is applied during inference and some chat templates implement rules about
what roles can be used in specific sequences of messages.

For example, when using Mistral AI's API the last chat message must have a role of `user`.
In a Group Chat scenario the message used to select the next speaker will have a role of
`system` by default and the API will throw an exception for this step. To overcome this the
GroupChat's constructor has a parameter called `role_for_select_speaker_messages` that can
be used to change the role name to `user`.

```python
groupchat = autogen.GroupChat(
agents=[user_proxy, coder, pm],
messages=[],
max_round=12,
# Role for select speaker message will be set to 'user' instead of 'system'
role_for_select_speaker_messages='user',
)
```

If the chat template associated with a model you want to use doesn't support the role
sequence and names used in AutoGen you can modify the chat template. See an example of
this on our [vLLM page](/docs/topics/non-openai-models/local-vllm#chat-template).

## Discord
Join AutoGen's [#alt-models](https://discord.com/channels/1153072414184452236/1201369716057440287)
channel on their Discord and discuss non-OpenAI models and configurations.
12 changes: 6 additions & 6 deletions website/docs/topics/non-openai-models/cloud-mistralai.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Now you can set up the Mistral model you want to use."
"Now you can set up the Mistral model you want to use. See the list of [models here](https://docs.mistral.ai/platform/endpoints/)."
]
},
{
Expand Down Expand Up @@ -59,7 +59,7 @@
"source": [
"## Two-Agent Coding Example\n",
"\n",
"In this example, we run a two-agent chat to count how many prime numbers between 1 and 10000 using coding."
"In this example, we run a two-agent chat to count the number of prime numbers between 1 and 10,000 using coding."
]
},
{
Expand Down Expand Up @@ -182,7 +182,7 @@
"source": [
"## Tool Call Example\n",
"\n",
"In this example, instead of writing code, we will have two agent playing chess against each other using tool to make moves.\n",
"In this example, instead of writing code, we will have two agents playing chess against each other using tool calling to make moves.\n",
"\n",
"First install the `chess` package by running the following command:"
]
Expand All @@ -200,7 +200,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Write function for making a move."
"Write the function for making a move."
]
},
{
Expand Down Expand Up @@ -269,7 +269,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Register tools for the agents. See [tutorial chapter on tool use](/docs/tutorial/tool-use) \n",
"Register tools for the agents. See the [tutorial chapter on tool use](/docs/tutorial/tool-use) \n",
"for more information."
]
},
Expand Down Expand Up @@ -303,7 +303,7 @@
"Register nested chats for the player agents.\n",
"Nested chats allows each player agent to chat with the board proxy agent\n",
"to make a move, before communicating with the other player agent.\n",
"See [nested chats tutorial chapter](/docs/tutorial/conversation-patterns#nested-chats)\n",
"See the [nested chats tutorial chapter](/docs/tutorial/conversation-patterns#nested-chats)\n",
"for more information."
]
},
Expand Down
23 changes: 23 additions & 0 deletions website/docs/tutorial/conversation-patterns.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1183,6 +1183,29 @@
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Changing the select speaker role name\n",
"\n",
"As part of the Group chat process, when the select_speaker_method is set to 'auto' (the default value),\n",
"a select speaker message is sent to the LLM to determine the next speaker.\n",
"\n",
"Each message in the chat sequence has a `role` attribute that is typically `user`,\n",
"`assistant`, or `system`. The select speaker message is the last in the chat\n",
"sequence when used and, by default, has a role of `system`.\n",
"\n",
"When using some models, such as Mistral through Mistral.AI's API, the role on\n",
"the last message in the chat sequence has to be `user`.\n",
"\n",
"To change the default behaviour, Autogen provides a way to set the value of the\n",
"select speaker message's role to any string value by setting the\n",
"`role_for_select_speaker_messages` parameter in the GroupChat's constructor. The\n",
"default value is `system` and by setting it to `user` you can accommodate the\n",
"last message role requirement of Mistral.AI's API."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
Expand Down

0 comments on commit 3f63db3

Please sign in to comment.