10
10
11
11
@dataclass
12
12
class GroupChat :
13
- """A group chat class that contains a list of agents and the maximum number of rounds."""
13
+ """A group chat class that contains the following data fields:
14
+ - agents: a list of participating agents.
15
+ - messages: a list of messages in the group chat.
16
+ - max_round: the maximum number of rounds.
17
+ - admin_name: the name of the admin agent if there is one. Default is "Admin".
18
+ KeyBoardInterrupt will make the admin agent take over.
19
+ - func_call_filter: whether to enforce function call filter. Default is True.
20
+ When set to True and when a message is a function call suggestion,
21
+ the next speaker will be chosen from an agent which contains the corresponding function name
22
+ in its `function_map`.
23
+ """
14
24
15
25
agents : List [Agent ]
16
26
messages : List [Dict ]
17
27
max_round : int = 10
18
- admin_name : str = "Admin" # the name of the admin agent
28
+ admin_name : str = "Admin"
29
+ func_call_filter : bool = True
19
30
20
31
@property
21
32
def agent_names (self ) -> List [str ]:
@@ -30,45 +41,69 @@ def agent_by_name(self, name: str) -> Agent:
30
41
"""Find the next speaker based on the message."""
31
42
return self .agents [self .agent_names .index (name )]
32
43
33
- def next_agent (self , agent : Agent ) -> Agent :
44
+ def next_agent (self , agent : Agent , agents : List [ Agent ] ) -> Agent :
34
45
"""Return the next agent in the list."""
35
- return self .agents [(self .agent_names .index (agent .name ) + 1 ) % len (self .agents )]
36
-
37
- def select_speaker_msg (self ):
46
+ if agents == self .agents :
47
+ return agents [(self .agent_names .index (agent .name ) + 1 ) % len (agents )]
48
+ else :
49
+ offset = self .agent_names .index (agent .name ) + 1
50
+ for i in range (len (self .agents )):
51
+ if self .agents [(offset + i ) % len (self .agents )] in agents :
52
+ return self .agents [(offset + i ) % len (self .agents )]
53
+
54
+ def select_speaker_msg (self , agents : List [Agent ]):
38
55
"""Return the message for selecting the next speaker."""
39
56
return f"""You are in a role play game. The following roles are available:
40
57
{ self ._participant_roles ()} .
41
58
42
59
Read the following conversation.
43
- Then select the next role from { self . agent_names } to play. Only return the role."""
60
+ Then select the next role from { [ agent . name for agent in agents ] } to play. Only return the role."""
44
61
45
62
def select_speaker (self , last_speaker : Agent , selector : ConversableAgent ):
46
63
"""Select the next speaker."""
47
- selector .update_system_message (self .select_speaker_msg ())
48
-
49
- # Warn if GroupChat is underpopulated, without established changing behavior
50
- n_agents = len (self .agent_names )
51
- if n_agents < 3 :
52
- logger .warning (
53
- f"GroupChat is underpopulated with { n_agents } agents. Direct communication would be more efficient."
54
- )
55
-
64
+ if self .func_call_filter and self .messages and "function_call" in self .messages [- 1 ]:
65
+ # find agents with the right function_map which contains the function name
66
+ agents = [
67
+ agent for agent in self .agents if agent .can_execute_function (self .messages [- 1 ]["function_call" ]["name" ])
68
+ ]
69
+ if len (agents ) == 1 :
70
+ # only one agent can execute the function
71
+ return agents [0 ]
72
+ elif not agents :
73
+ # find all the agents with function_map
74
+ agents = [agent for agent in self .agents if agent .function_map ]
75
+ if len (agents ) == 1 :
76
+ return agents [0 ]
77
+ elif not agents :
78
+ raise ValueError (
79
+ f"No agent can execute the function { self .messages [- 1 ]['name' ]} . "
80
+ "Please check the function_map of the agents."
81
+ )
82
+ else :
83
+ agents = self .agents
84
+ # Warn if GroupChat is underpopulated
85
+ n_agents = len (agents )
86
+ if n_agents < 3 :
87
+ logger .warning (
88
+ f"GroupChat is underpopulated with { n_agents } agents. Direct communication would be more efficient."
89
+ )
90
+ selector .update_system_message (self .select_speaker_msg (agents ))
56
91
final , name = selector .generate_oai_reply (
57
92
self .messages
58
93
+ [
59
94
{
60
95
"role" : "system" ,
61
- "content" : f"Read the above conversation. Then select the next role from { self . agent_names } to play. Only return the role." ,
96
+ "content" : f"Read the above conversation. Then select the next role from { [ agent . name for agent in agents ] } to play. Only return the role." ,
62
97
}
63
98
]
64
99
)
65
100
if not final :
66
101
# i = self._random.randint(0, len(self._agent_names) - 1) # randomly pick an id
67
- return self .next_agent (last_speaker )
102
+ return self .next_agent (last_speaker , agents )
68
103
try :
69
104
return self .agent_by_name (name )
70
105
except ValueError :
71
- return self .next_agent (last_speaker )
106
+ return self .next_agent (last_speaker , agents )
72
107
73
108
def _participant_roles (self ):
74
109
return "\n " .join ([f"{ agent .name } : { agent .system_message } " for agent in self .agents ])
0 commit comments