SelectorGroupChat 实现了一个团队,参与者轮流向所有其他成员广播消息。一个生成模型(例如,LLM)基于共享的上下文选择下一个发言者,从而实现动态的、上下感知的协作。

主要特点包括:

  • 基于模型的话者选择

  • 可配置的参与者角色和描述

  • 防止同一发言者连续发言(可选)

  • 可定制的选择提示

  • 可自定义的选择功能,以覆盖默认的基于模型的选择

注意

SelectorGroupChat 是一个高级API。如需更多控制和自定义,请参考核心API文档中的群聊模式以实现您自己的群聊逻辑。

它是如何工作的?

SelectorGroupChat 是一个类似于 RoundRobinGroupChat 的群聊, 但具有基于模型的下一个发言者选择机制。 当团队通过 run() 或 run_stream() 接收到任务时, 将执行以下步骤:

  1. 团队分析当前的对话上下文,包括对话历史记录和参与者的name和description属性,使用模型来确定下一个发言者。默认情况下,团队不会连续选择同一个发言人,除非这是唯一可用的代理。可以通过设置allow_repeated_speaker=True来更改此行为。您还可以通过提供自定义选择函数来覆盖模型。

  2. 团队提示选定的发言者代理提供响应,然后广播给所有其他参与者。

  3. 检查终止条件以确定是否应结束对话,如果没有,则从第1步重复该过程。

  4. 当对话结束时,团队会返回包含此任务对话历史的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

代理

Selector Group Chat

该系统使用了三个专门的代理:

  • 规划代理: 战略协调者,将复杂任务分解为可管理的子任务。

  • 网页搜索代理: 一个信息检索专家,与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来添加一个反思步骤。这将允许代理对工具输出进行反思,并提供自然语言响应。

工作流程

  1. 任务由 SelectorGroupChat 接收,它根据代理的描述选择最合适的代理来处理初始任务(通常是规划代理)。

  2. 规划代理会分析任务并将其分解为子任务,使用以下格式将每个子任务分配给最合适的代理: :

  3. 根据对话上下文和代理描述,SelectorGroupChat 管理器动态选择下一个代理来处理其分配的次级任务。

  4. Web Search Agent 会一次执行一次搜索,并将结果存储在共享的会话历史中。

  5. 当选择时,数据分析师会使用可用的计算工具处理收集到的信息。

  6. 工作流程继续,动态选择代理,直到发生以下任一情况:

    • 规划代理确定所有子任务已完成并发送“终止”

    • 满足另一个终止条件(例如,最大消息数)

在定义您的代理时,请确保包含一个有帮助的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%。

结束