SelectorGroupChat 实现了一个团队,参与者轮流向所有其他成员广播消息。一个生成模型(例如,LLM)基于共享的上下文选择下一个发言者,从而实现动态的、上下感知的协作。
主要特点包括:
基于模型的话者选择
可配置的参与者角色和描述
防止同一发言者连续发言(可选)
可定制的选择提示
可自定义的选择功能,以覆盖默认的基于模型的选择
注意
SelectorGroupChat 是一个高级API。如需更多控制和自定义,请参考核心API文档中的群聊模式以实现您自己的群聊逻辑。
它是如何工作的?
SelectorGroupChat 是一个类似于 RoundRobinGroupChat 的群聊, 但具有基于模型的下一个发言者选择机制。 当团队通过 run() 或 run_stream() 接收到任务时, 将执行以下步骤:
团队分析当前的对话上下文,包括对话历史记录和参与者的
name和description属性,使用模型来确定下一个发言者。默认情况下,团队不会连续选择同一个发言人,除非这是唯一可用的代理。可以通过设置allow_repeated_speaker=True来更改此行为。您还可以通过提供自定义选择函数来覆盖模型。团队提示选定的发言者代理提供响应,然后广播给所有其他参与者。
检查终止条件以确定是否应结束对话,如果没有,则从第1步重复该过程。
当对话结束时,团队会返回包含此任务对话历史的
TaskResult。
一旦团队完成任务,对话上下文将保存在团队和所有参与者中,以便下一个任务可以从之前的对话上下文继续。 你可以通过调用reset()来重置对话上下文。
在本节中,我们将通过一个简单的网络搜索和数据分析任务来演示如何使用SelectorGroupChat。
示例:网络搜索/分析
```python # 这里是一个示例代码,展示如何使用autogen进行网络搜索和分析 import autogen # 初始化autogen agent = autogen.create_agent() # 设置搜索查询 query = "最新科技新闻" # 执行搜索并分析结果 results = agent.search_and_analyze(query) # 打印分析结果 print(results) ```
from typing import Sequence
from autogen_agentchat.agents import AssistantAgent, UserProxyAgent
from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
from autogen_agentchat.messages import AgentEvent, ChatMessage
from autogen_agentchat.teams import SelectorGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient代理
该系统使用了三个专门的代理:
规划代理: 战略协调者,将复杂任务分解为可管理的子任务。
网页搜索代理: 一个信息检索专家,与
search_web_tool接口。数据分析师代理: 一个擅长执行计算的代理专家,配备了
percentage_change_tool。
工具 search_web_tool 和 percentage_change_tool 是外部工具,代理可以使用它们来执行任务。
def search_web_tool(query: str) -> str:
if "2006-2007" in query:
return """以下是2006-2007赛季迈阿密热火队球员的总得分情况:
乌多尼斯·哈斯勒姆: 844 分
德韦恩·韦德: 1397 分
詹姆斯·波西: 550 分
...
"""
elif "2007-2008" in query:
return "迈阿密热火队2007-2008赛季德韦恩·韦德的总篮板数为214个。"
elif "2008-2009" in query:
return "迈阿密热火队2008-2009赛季德韦恩·韦德的总篮板数为398个。"
return "未找到数据。"
def percentage_change_tool(start: float, end: float) -> float:
return ((end - start) / start) * 100让我们使用AssistantAgent类来创建专门的代理。需要注意的是,代理的name和description属性被模型用来决定下一个发言者,因此建议提供有意义的名称和描述。
model_client = OpenAIChatCompletionClient(
model=os.getenv("OPENAI_MODEL"),
api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL"),
model_info={
"family": "unknown",
"function_calling": True,
"json_output": True,
"structured_output": True,
"vision": False
}
)
planning_agent = AssistantAgent(
"PlanningAgent",
description="规划任务的智能体,在给定新任务时应该首先参与。",
model_client=model_client,
system_message="""
你是一个规划智能体。
你的工作是将复杂任务分解为较小的、可管理的子任务。
你的团队成员包括:
WebSearchAgent: 搜索信息
DataAnalystAgent: 执行计算
你只负责规划和分配任务 - 不自己执行它们。
分配任务时,请使用以下格式:
1. <智能体> : <任务>
不要自己回答问题,一定要等所有智能体完成各自任务后,才能总结最终结果。
所有任务都必须被执行后,再总结发现并以"结束"结尾。
""",
)
web_search_agent = AssistantAgent(
"WebSearchAgent",
description="用于在网络上搜索信息的智能体。",
tools=[search_web_tool],
model_client=model_client,
system_message="""
你是一个网络搜索智能体。
你唯一的工具是search_web_tool - 使用它来查找信息。
每次只能进行一次搜索调用。
一旦获得结果,你不要基于这些结果进行计算。
完成搜索后,简要报告你找到的结果,不要做进一步分析。
永远不要尝试结束对话,这是PlanningAgent的工作。
""",
)
data_analyst_agent = AssistantAgent(
"DataAnalystAgent",
description="用于执行计算的智能体。",
model_client=model_client,
tools=[percentage_change_tool],
system_message="""
你是一个数据分析师。
根据分配给你的任务,你应该分析数据并使用提供的工具提供结果。
如果你还没有看到数据,请询问。
使用percentage_change_tool来计算百分比变化。
完成计算后,简要报告你的分析结果,不要试图总结整个任务。
永远不要尝试结束对话,这是PlanningAgent的工作。
""",
)
默认情况下,AssistantAgent返回工具输出作为响应。如果你的工具没有返回一个自然语言格式的格式良好的字符串,你可能希望在创建代理时通过设置reflect_on_tool_use=True来添加一个反思步骤。这将允许代理对工具输出进行反思,并提供自然语言响应。
工作流程
任务由
SelectorGroupChat接收,它根据代理的描述选择最合适的代理来处理初始任务(通常是规划代理)。规划代理会分析任务并将其分解为子任务,使用以下格式将每个子任务分配给最合适的代理:
:根据对话上下文和代理描述,
SelectorGroupChat管理器动态选择下一个代理来处理其分配的次级任务。Web Search Agent 会一次执行一次搜索,并将结果存储在共享的会话历史中。
当选择时,数据分析师会使用可用的计算工具处理收集到的信息。
工作流程继续,动态选择代理,直到发生以下任一情况:
规划代理确定所有子任务已完成并发送“终止”
满足另一个终止条件(例如,最大消息数)
在定义您的代理时,请确保包含一个有帮助的description,因为这用于决定接下来选择哪个代理。
终止条件#
让我们使用两终止条件: TextMentionTermination 用于在 Planning Agent 发送“TERMINATE”时结束对话, 和 MaxMessageTermination 用于将对话限制在 25 条消息以内,以避免无限循环。
text_mention_termination = TextMentionTermination("结束")
max_messages_termination = MaxMessageTermination(max_messages=25)
termination = text_mention_termination | max_messages_termination选择器提示
SelectorGroupChat 使用模型根据对话上下文选择下一个发言者。 我们将使用自定义的selector提示符来与工作流正确对齐。
selector_prompt = """选择一个智能体来执行任务。
{roles}
当前会话上下文:
{history}
阅读上述对话,然后从{participants}中选择一个智能体来执行下一个任务。
确保PlanningAgent在其他智能体开始工作前已分配了任务。
遵循以下原则选择智能体:
1. 如果是新任务,选择PlanningAgent进行规划
2. 如果PlanningAgent已分配搜索任务,选择WebSearchAgent
3. 如果PlanningAgent已分配计算任务,选择DataAnalystAgent
4. 如果所有任务都已完成,选择PlanningAgent做总结
记住,PlanningAgent不应该自己执行任务,只有当所有其他智能体都完成任务后,才让PlanningAgent总结结果。
只选择一个智能体。
"""提示
尝试不要在选择器提示中给模型过多的指令。
什么是太多了?这取决于你使用的模型的能力。 对于GPT-4o和等效模型,你可以使用一个带有条件的选择提示,用于决定何时选择每个发言者。 对于较小的模型如Phi-4,你应该尽可能简化选择提示, 如本示例中使用的提示。
通常,如果你发现自己为每个代理编写多个条件,这是一个信号,表明你应该考虑使用自定义选择函数,或将任务分解为由单独的代理或团队处理的更小的顺序任务。
运行团队
让我们创建一个包含代理、终止条件和自定义选择器提示的团队。
team = SelectorGroupChat(
[planning_agent, web_search_agent, data_analyst_agent],
model_client=model_client,
termination_condition=termination,
selector_prompt=selector_prompt,
allow_repeated_speaker=True, # 允许一个智能体连续多次发言
)
task = "2006-2007赛季得分最高的迈阿密热火队球员是谁,以及这位球员在2007-2008和2008-2009赛季之间的总篮板数变化百分比是多少?"
# Use asyncio.run(...) if you are running this in a script.
async def main():
await Console(team.run_stream(task=task))
if __name__ == "__main__":
asyncio.run(main())完整代码
from autogen_agentchat.agents import AssistantAgent
from autogen_agentchat.conditions import MaxMessageTermination, TextMentionTermination
from autogen_agentchat.messages import AgentEvent, ChatMessage
from autogen_agentchat.teams import SelectorGroupChat
from autogen_agentchat.ui import Console
from autogen_ext.models.openai import OpenAIChatCompletionClient
from dotenv import load_dotenv
import os
import asyncio
load_dotenv()
# Note: This example uses mock tools instead of real APIs for demonstration purposes
def search_web_tool(query: str) -> str:
if "2006-2007" in query:
return """以下是2006-2007赛季迈阿密热火队球员的总得分情况:
乌多尼斯·哈斯勒姆: 844 分
德韦恩·韦德: 1397 分
詹姆斯·波西: 550 分
...
"""
elif "2007-2008" in query:
return "迈阿密热火队2007-2008赛季德韦恩·韦德的总篮板数为214个。"
elif "2008-2009" in query:
return "迈阿密热火队2008-2009赛季德韦恩·韦德的总篮板数为398个。"
return "未找到数据。"
def percentage_change_tool(start: float, end: float) -> float:
return ((end - start) / start) * 100
model_client = OpenAIChatCompletionClient(
model=os.getenv("OPENAI_MODEL"),
api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL"),
model_info={
"family": "unknown",
"function_calling": True,
"json_output": True,
"structured_output": True,
"vision": False
}
)
planning_agent = AssistantAgent(
"PlanningAgent",
description="规划任务的智能体,在给定新任务时应该首先参与。",
model_client=model_client,
system_message="""
你是一个规划智能体。
你的工作是将复杂任务分解为较小的、可管理的子任务。
你的团队成员包括:
WebSearchAgent: 搜索信息
DataAnalystAgent: 执行计算
你只负责规划和分配任务 - 不自己执行它们。
分配任务时,请使用以下格式:
1. <智能体> : <任务>
不要自己回答问题,一定要等所有智能体完成各自任务后,才能总结最终结果。
所有任务都必须被执行后,再总结发现并以"结束"结尾。
""",
)
web_search_agent = AssistantAgent(
"WebSearchAgent",
description="用于在网络上搜索信息的智能体。",
tools=[search_web_tool],
model_client=model_client,
system_message="""
你是一个网络搜索智能体。
你唯一的工具是search_web_tool - 使用它来查找信息。
每次只能进行一次搜索调用。
一旦获得结果,你不要基于这些结果进行计算。
完成搜索后,简要报告你找到的结果,不要做进一步分析。
永远不要尝试结束对话,这是PlanningAgent的工作。
""",
)
data_analyst_agent = AssistantAgent(
"DataAnalystAgent",
description="用于执行计算的智能体。",
model_client=model_client,
tools=[percentage_change_tool],
system_message="""
你是一个数据分析师。
根据分配给你的任务,你应该分析数据并使用提供的工具提供结果。
如果你还没有看到数据,请询问。
使用percentage_change_tool来计算百分比变化。
完成计算后,简要报告你的分析结果,不要试图总结整个任务。
永远不要尝试结束对话,这是PlanningAgent的工作。
""",
)
text_mention_termination = TextMentionTermination("结束")
max_messages_termination = MaxMessageTermination(max_messages=50) # 增加最大消息数
termination = text_mention_termination | max_messages_termination
selector_prompt = """选择一个智能体来执行任务。
{roles}
当前会话上下文:
{history}
阅读上述对话,然后从{participants}中选择一个智能体来执行下一个任务。
确保PlanningAgent在其他智能体开始工作前已分配了任务。
遵循以下原则选择智能体:
1. 如果是新任务,选择PlanningAgent进行规划
2. 如果PlanningAgent已分配搜索任务,选择WebSearchAgent
3. 如果PlanningAgent已分配计算任务,选择DataAnalystAgent
4. 如果所有任务都已完成,选择PlanningAgent做总结
记住,PlanningAgent不应该自己执行任务,只有当所有其他智能体都完成任务后,才让PlanningAgent总结结果。
只选择一个智能体。
"""
team = SelectorGroupChat(
[planning_agent, web_search_agent, data_analyst_agent],
model_client=model_client,
termination_condition=termination,
selector_prompt=selector_prompt,
allow_repeated_speaker=False, # 不允许一个智能体连续多次发言,确保轮流发言
)
task = "2006-2007赛季得分最高的迈阿密热火队球员是谁,以及这位球员在2007-2008和2008-2009赛季之间的总篮板数变化百分比是多少?"
# Use asyncio.run(...) if you are running this in a script.
async def main():
await Console(team.run_stream(task=task))
if __name__ == "__main__":
asyncio.run(main())
运行结果
---------- user ----------
2006-2007赛季得分最高的迈阿密热火队球员是谁,以及这位球员在2007-2008和2008-2009赛季之间的总篮板数变化百分比是多少?
---------- PlanningAgent ----------
1. WebSearchAgent : 查找2006-2007赛季迈阿密热火队得分最高的球员
2. WebSearchAgent : 查找该球员在2007-2008赛季的总篮板数
3. WebSearchAgent : 查找该球员在2008-2009赛季的总篮板数
4. DataAnalystAgent : 计算2007-2008和2008-2009赛季之间的总篮板数变化百分比
---------- WebSearchAgent ----------
[FunctionCall(id='call_0_e6b3ef80-78ed-43af-bafa-0d9773be4616', arguments='{"query": "2006-2007赛季迈阿密热火队得分最高的球员"}', name='search_web_tool')]
---------- WebSearchAgent ----------
[FunctionExecutionResult(content='以下是2006-2007赛季迈阿密热火队球员的总得分情况:\n 乌多尼斯·哈斯勒姆: 844 分\n 德韦恩·韦德: 1397 分\n 詹姆斯·波西: 550 分\n ...\n ', name='search_web_tool', call_id='call_0_e6b3ef80-78ed-43af-bafa-0d9773be4616', is_error=False)]
---------- WebSearchAgent ----------
以下是2006-2007赛季迈阿密热火队球员的总得分情况:
乌多尼斯·哈斯勒姆: 844 分
德韦恩·韦德: 1397 分
詹姆斯·波西: 550 分
...
---------- WebSearchAgent ----------
2006-2007赛季迈阿密热火队得分最高的球员是德韦恩·韦德(Dwyane Wade),总得分为1397分。
接下来,我将查找德韦恩·韦德在2007-2008赛季的总篮板数。
---------- WebSearchAgent ----------
[FunctionCall(id='call_0_05e4a002-5c73-4b5a-b3ab-1b89db93c89c', arguments='{"query":"德韦恩·韦德 2007-2008赛季 总篮板数"}', name='search_web_tool')]
---------- WebSearchAgent ----------
[FunctionExecutionResult(content='迈阿密热火队2007-2008赛季德韦恩·韦德的总篮板数为214个。', name='search_web_tool', call_id='call_0_05e4a002-5c73-4b5a-b3ab-1b89db93c89c', is_error=False)]
---------- WebSearchAgent ----------
迈阿密热火队2007-2008赛季德韦恩·韦德的总篮板数为214个。
---------- WebSearchAgent ----------
德韦恩·韦德在2007-2008赛季的总篮板数为214个。接下来,我将查找他在2008-2009赛季的总篮板数。
---------- WebSearchAgent ----------
[FunctionCall(id='call_0_d91e4353-65b4-4036-8a49-ab02e4e7d52b', arguments='{"query":"德韦恩·韦德 2008-2009赛季 总篮板数"}', name='search_web_tool')]
---------- WebSearchAgent ----------
[FunctionExecutionResult(content='迈阿密热火队2008-2009赛季德韦恩·韦德的总篮板数为398个。', name='search_web_tool', call_id='call_0_d91e4353-65b4-4036-8a49-ab02e4e7d52b', is_error=False)]
---------- WebSearchAgent ----------
迈阿密热火队2008-2009赛季德韦恩·韦德的总篮板数为398个。
---------- DataAnalystAgent ----------
根据提供的数据:
1. 2006-2007赛季得分最高的迈阿密热火队球员是德韦恩·韦德(Dwyane Wade),总得分为1397分。
2. 德韦恩·韦德在2007-2008和2008-2009赛季之间的总篮板数变化百分比:
- 2007-2008赛季总篮板数:214个
- 2008-2009赛季总篮板数:398个
接下来,我将计算这两个赛季之间的篮板数变化百分比。
---------- DataAnalystAgent ----------
[FunctionCall(id='call_0_3a7d9170-6b01-4b3a-9fef-74c460929db3', arguments='{"start":214,"end":398}', name='percentage_change_tool')]
---------- DataAnalystAgent ----------
[FunctionExecutionResult(content='85.98130841121495', name='percentage_change_tool', call_id='call_0_3a7d9170-6b01-4b3a-9fef-74c460929db3', is_error=False)]
---------- DataAnalystAgent ----------
85.98130841121495
---------- DataAnalystAgent ----------
德韦恩·韦德(Dwyane Wade)在2007-2008和2008-2009赛季之间的总篮板数变化百分比为 85.98%。
---------- PlanningAgent ----------
总结发现:
1. 2006-2007赛季迈阿密热火队得分最高的球员是德韦恩·韦德(Dwyane Wade),总得分为1397分。
2. 德韦恩·韦德在2007-2008赛季的总篮板数为214个,2008-2009赛季为398个。
3. 2007-2008和2008-2009赛季之间的总篮板数变化百分比为85.98%。
结束