主题和订阅#
运行时有两种消息传递方式:直接消息传递或广播。直接消息传递是一对一的:发送者必须提供接收者的代理ID。另一方面,广播是一对多的,发送者不需要提供接收者的代理ID。
许多场景都适合广播。例如,在事件驱动的工作流中,代理并不总是知道谁将处理它们的消息,并且工作流可以由没有相互依赖关系的代理组成。本节重点介绍广播中的核心概念:主题和订阅。
主题#
主题定义了广播消息的范围。实质上,代理运行时通过其广播API实现发布-订阅模型:发布消息时,必须指定主题。它是在代理ID之上的一种间接方式。
主题由两个组件组成:主题类型和主题来源。
注意
主题 = (主题类型, 主题来源)
与代理ID类似,代理ID也有两个组件,主题类型通常由应用程序代码定义,用于标记主题所针对的消息类型。例如,GitHub代理在发布有关新问题的消息时,可能会使用"GitHub_Issues"作为主题类型。
主题来源是主题类型中主题的唯一标识符。它通常由应用程序数据定义。例如,GitHub代理可能会使用"github.com/{repo_name}/issues/{issue_number}"作为主题来源来唯一标识主题。主题来源允许发布者限制消息范围并创建隔离区。
主题ID可以转换为字符串,也可以从字符串转换而来。此字符串的格式是
注意
主题类型/主题来源
如果类型为UTF8,并且只包含字母数字(a-z)和(0-9),或者下划线(_),则被认为是有效的。有效的标识符不能以数字开头,也不能包含任何空格。如果来源为UTF8,并且只包含ASCII 32(空格)到126(~)之间的字符(包括两端),则被认为是有效的。
订阅#
订阅将主题映射到代理ID。
上图显示了主题和订阅之间的关系。代理运行时跟踪订阅并使用它们将消息传递给代理。
如果某个主题没有订阅,发布到此主题的消息将不会传递给任何代理。如果某个主题有多个订阅,消息将按照所有订阅传递给每个接收代理一次。应用程序可以使用代理运行时的API添加或删除订阅。
基于类型的订阅#
基于类型的订阅将主题类型映射到代理类型(参见代理ID)。它声明了从主题到代理ID的无限制映射,而无需知道确切的主题来源和代理密钥。其机制很简单:任何与基于类型的订阅的主题类型匹配的主题都将映射到具有订阅的代理类型和分配给主题来源值的代理密钥的代理ID。对于Python API,请使用TypeSubscription。
注意
基于类型的订阅 = 主题类型 –> 代理类型
一般来说,基于类型的订阅是声明订阅的首选方式。它具有可移植性且与数据无关:开发人员无需编写依赖于特定代理ID的应用程序代码。
基于类型的订阅场景#
当确切的主题或代理ID是数据依赖时,基于类型的订阅可以应用于许多场景。这些场景可以根据两个考量因素进行分类:(1)是单租户还是多租户,以及(2)每个租户是一个主题还是多个主题。租户通常指处理特定用户会话或特定请求的一组代理。
单租户,单主题#
在此场景中,整个应用程序只有一个租户和一个主题。这是最简单的场景,可以在许多情况下使用,例如命令行工具或单用户应用程序。
要在此场景中应用基于类型的订阅,为每个代理类型创建一个基于类型的订阅,并对所有基于类型的订阅使用相同的主题类型。发布时,始终使用相同的主题,即相同的主题类型和主题来源。
例如,假设有三个代理类型:"triage_agent"、"coder_agent"和"reviewer_agent",主题类型为"default",创建以下基于类型的订阅
# Type-based Subscriptions for single-tenant, single topic scenario
TypeSubscription(topic_type="default", agent_type="triage_agent")
TypeSubscription(topic_type="default", agent_type="coder_agent")
TypeSubscription(topic_type="default", agent_type="reviewer_agent")
有了上述基于类型的订阅,所有消息都使用相同的主题来源"default"。因此,主题始终是("default", "default")。发布到此主题的消息将传递给所有上述类型的所有代理。具体来说,消息将发送到以下代理ID
# The agent IDs created based on the topic source
AgentID("triage_agent", "default")
AgentID("coder_agent", "default")
AgentID("reviewer_agent", "default")
下图显示了基于类型的订阅在此示例中如何工作。
如果ID对应的代理不存在,运行时将创建它。
单租户,多主题#
在此场景中,只有一个租户,但您希望控制哪个代理处理哪个主题。当您希望创建隔离区并让不同的代理专门处理不同的主题时,这非常有用。
要在此场景中应用基于类型的订阅,为每个代理类型创建一个基于类型的订阅,但使用不同的主题类型。如果您希望这些代理类型共享相同的主题,可以将相同的主题类型映射到多个代理类型。对于主题来源,发布时仍对所有消息使用相同的值。
继续以上述相同代理类型为例,创建以下基于类型的订阅
# Type-based Subscriptions for single-tenant, multiple topics scenario
TypeSubscription(topic_type="triage", agent_type="triage_agent")
TypeSubscription(topic_type="coding", agent_type="coder_agent")
TypeSubscription(topic_type="coding", agent_type="reviewer_agent")
有了上述基于类型的订阅,任何发布到主题("triage", "default")的消息都将传递给类型为"triage_agent"的代理,任何发布到主题("coding", "default")的消息都将传递给类型为"coder_agent"和"reviewer_agent"的代理。
下图显示了基于类型的订阅在此示例中如何工作。
多租户场景#
在单租户场景中,主题来源始终相同(例如"default")——它在应用程序代码中是硬编码的。当转移到多租户场景时,主题来源变得依赖于数据。
注意
您处于多租户场景的一个很好的迹象是,您需要相同代理类型的多个实例。例如,您可能希望拥有不同的代理实例来处理不同的用户会话以保持私有数据隔离,或者,您可能希望将繁重的工作负载分配给相同代理类型的多个实例,并让它们并发工作。
继续上述示例,如果您希望拥有专门的代理实例来处理特定的GitHub问题,则需要将主题来源设置为问题的唯一标识符。
例如,假设代理类型"triage_agent"有一个基于类型的订阅
TypeSubscription(topic_type="github_issues", agent_type="triage_agent")
当消息发布到主题("github_issues", "github.com/microsoft/autogen/issues/1")时,运行时会将消息传递给ID为("triage_agent", "github.com/microsoft/autogen/issues/1")的代理。当消息发布到主题("github_issues", "github.com/microsoft/autogen/issues/9")时,运行时会将消息传递给ID为("triage_agent", "github.com/microsoft/autogen/issues/9")的代理。
下图显示了基于类型的订阅在此示例中如何工作。
请注意,代理ID是数据依赖的,如果代理不存在,运行时将创建一个新的代理实例。
为了支持每个租户的多个主题,您可以使用不同的主题类型,就像单租户、多主题场景一样。