主题和订阅#
运行时传递消息有两种方式:直接消息传递或广播。 直接消息传递是一对一的:发送者必须提供接收者的代理 ID。 另一方面,广播是一对多的,发送者不提供接收者的代理 ID。
许多场景都适合广播。 例如,在事件驱动的工作流中,代理并不总是知道谁将处理它们的消息,并且工作流可以由没有相互依赖性的代理组成。 本节重点介绍广播中的核心概念:主题和订阅。
主题#
主题定义了广播消息的范围。 本质上,代理运行时通过其广播 API 实现发布-订阅模型:发布消息时,必须指定主题。 它是对代理 ID 的间接引用。
一个主题由两个组件组成:主题类型和主题来源。
注意
主题 = (主题类型, 主题来源)
类似于 代理 ID,它也有两个组成部分,主题类型通常由应用程序代码定义,以标记主题的消息类型。 例如,GitHub 代理在发布有关新问题的信息时,可以使用 "GitHub_Issues"
作为主题类型。
主题来源是主题类型中主题的唯一标识符。 它通常由应用程序数据定义。 例如,GitHub 代理可以使用 "github.com/{repo_name}/issues/{issue_number}"
作为主题来源,以唯一标识该主题。 主题来源允许发布者限制消息的范围并创建孤岛。
主题 ID 可以转换为字符串,也可以从字符串转换而来。 此字符串的格式为
注意
Topic_Type/Topic_Source
如果类型采用 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 是数据相关的,如果代理不存在,运行时将创建该代理的新实例。
为了支持每个租户的多个主题,你可以使用不同的主题类型,就像单租户、多主题的场景一样。