主题和订阅#

运行时有两种传递消息的方式,直接消息传递或广播。直接消息传递是一对一的:发送者必须提供接收者的代理ID。另一方面,广播是一对多的,发送者不需要提供接收者的代理ID。

许多场景适合使用广播。 例如,在事件驱动的工作流中,代理并不总是知道谁会处理他们的消息,而且一个工作流可以由没有相互依赖的代理组成。 本节重点介绍广播中的核心概念:主题和订阅。

主题#

一个主题定义了广播消息的范围。 本质上,代理运行时通过其广播API实现了发布-订阅模型:发布消息时,必须指定主题。 它是代理ID的间接引用。

一个主题由两个部分组成:主题类型和主题来源。

注意

主题 = (主题类型, 主题来源)

类似于代理 ID, 主题类型通常也由应用程序代码定义,以标记该主题的消息类型。 例如,GitHub 代理在发布关于新问题(issues)的消息时,可能会使用"GitHub_Issues"作为主题类型。

主题源是主题类型中主题的唯一标识符。它通常由应用程序数据定义。例如,GitHub代理可以使用"github.com/{repo_name}/issues/{issue_number}"作为主题源来唯一标识主题。主题源允许发布者限制消息的范围并创建隔离区。

主题ID可以在字符串之间相互转换。该字符串的格式为:

注意

主题类型/主题来源

如果类型以UTF8编码且仅包含字母(a-z)和数字(0-9)或下划线(_),则被视为有效。有效的标识符不能以数字开头,也不能包含任何空格。 如果源以UTF8编码且仅包含在(包括)ascii 32(空格)和126(~)之间的字符,则被视为有效。

订阅#

订阅将主题映射到代理ID。

Subscription

上图展示了主题和订阅之间的关系。代理运行时会跟踪这些订阅,并使用它们将消息传递给代理。

如果某个主题没有订阅,发布到该主题的消息将不会传递给任何代理。如果一个主题有很多订阅,消息将根据所有订阅传递给每个接收代理,且每个代理只会收到一次。应用程序可以使用代理运行时的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")

下图展示了在这个例子中基于类型的订阅是如何工作的。

Type-Based Subscription Single-Tenant, Single Topic Scenario Example

如果具有该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" 的代理。

下图展示了在这个例子中基于类型的订阅是如何工作的。

Type-Based Subscription Single-Tenant, Multiple Topics Scenario Example

多租户场景#

在单租户场景中,主题源始终相同(例如,"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")的代理。

下图展示了在这个例子中基于类型的订阅是如何工作的。

Type-Based Subscription Multi-Tenant Scenario Example

请注意代理ID是数据相关的,如果不存在,运行时会创建一个新的代理实例。

为了支持每个租户的多个主题,你可以使用不同的主题类型,就像单租户、多主题的场景一样。