语义内核代理编排高级主题

重要

代理框架中的代理编排功能处于实验阶段。 它们处于积极开发阶段,在升级到预览版或候选发布阶段之前可能会发生重大变化。

运行时

运行时是管理代理和业务流程的生命周期、通信和执行的基础组件。 它充当系统中所有执行组件(代理和业务流程特定执行组件)的消息总线和执行环境。

运行时的角色

  • 消息路由: 运行时负责使用 pub-sub 或直接消息传送模型(具体取决于业务流程模式)在代理和业务流程执行组件之间传递消息。
  • 执行组件生命周期管理: 它创建、注册和管理业务流程中涉及的所有执行组件的生命周期,确保隔离和适当的资源管理。
  • 执行上下文: 运行时为业务流程提供执行上下文,允许多个业务流程(及其调用)独立并发运行。

运行时与编排之间的关系

将业务流程视为定义代理彼此交互方式的图形。 运行时是执行此图、管理消息流和代理生命周期的引擎。 开发人员可以使用同一运行时实例上的不同输入多次执行此关系图,运行时将确保每个执行都独立独立。

超时

调用业务流程时,业务流程会立即返回一个处理程序,该处理程序可用于稍后获取结果。 这种异步模式允许更灵活和响应式设计,尤其是在业务流程可能需要很长时间才能完成的情况下。

重要

如果发生超时,则不会取消编排的调用。 编排将继续在后台运行,直到完成。 开发人员仍然可以稍后检索结果。

开发人员以后可以通过对结果对象调用 GetValueAsync 方法来获取协调操作调用的结果。 当应用程序准备好处理结果时,调用可能已完成或可能尚未完成。 因此,开发人员可以选择为 GetValueAsync 该方法指定超时。 如果编排未在指定的超时范围内完成,将引发超时异常。

string output = await result.GetValueAsync(TimeSpan.FromSeconds(60));

如果编排未在指定的超时范围内完成,将引发超时异常。

开发人员以后可以通过对结果对象调用 get 方法来获取协调操作调用的结果。 当应用程序准备好处理结果时,调用可能已完成或可能尚未完成。 因此,开发人员可以选择为 get 该方法指定超时。 如果编排未在指定的超时范围内完成,将引发超时异常。

value = await orchestration_result.get(timeout=60)

如果编排未在指定的超时范围内完成,将抛出超时异常。

注释

代理编排在 Java SDK 中尚不可用。

人工干预

代理响应回调

若要查看调用中的代理响应,开发人员可以提供 ResponseCallback 编排。 这样,开发人员就可以在业务流程过程中观察每个代理的响应。 开发人员可以将此回调用于 UI 更新、日志记录或其他目的。

public ValueTask ResponseCallback(ChatMessageContent response)
{
    Console.WriteLine($"# {response.AuthorName}\n{response.Content}");
    return ValueTask.CompletedTask;
}

SequentialOrchestration orchestration = new SequentialOrchestration(
    analystAgent, writerAgent, editorAgent)
{
    ResponseCallback = ResponseCallback,
};

若要查看调用中的代理响应,开发人员可以向编排提供 agent_response_callback。 这样,开发人员就可以在业务流程过程中观察每个代理的响应。 开发人员可以将此回调用于 UI 更新、日志记录或其他目的。

def agent_response_callback(message: ChatMessageContent) -> None:
    print(f"# {message.name}\n{message.content}")

sequential_orchestration = SequentialOrchestration(
    members=agents,
    agent_response_callback=agent_response_callback,
)

注释

代理编排在 Java SDK 中尚不可用。

人类响应函数

对于支持用户输入的编排(例如交接和群组聊天),请提供一个InteractiveCallback,以便从用户返回一个ChatMessageContent。 通过使用此回调,开发人员可以实现自定义逻辑来收集用户输入,例如显示 UI 提示或与其他系统集成。

HandoffOrchestration orchestration = new(...)
{
    InteractiveCallback = () =>
    {
        Console.Write("User: ");
        string input = Console.ReadLine();
        return new ChatMessageContent(AuthorRole.User, input);
    }
};

对于支持用户输入的编排(例如交接和群组聊天),请提供一个human_response_function,以便从用户返回一个ChatMessageContent。 通过使用此回调,开发人员可以实现自定义逻辑来收集用户输入,例如显示 UI 提示或与其他系统集成。

def human_response_function() -> ChatMessageContent:
    user_input = input("User: ")
    return ChatMessageContent(role=AuthorRole.USER, content=user_input)

handoff_orchestration = HandoffOrchestration(
    ...,
    agent_response_callback=agent_response_callback,
)

注释

代理编排在 Java SDK 中尚不可用。

结构化数据

我们认为结构化数据是构建代理工作流的关键部分。 通过使用结构化数据,开发人员可以创建更易于重用的业务流程,并改进了开发体验。 语义内核 SDK 提供了一种方法,用于将结构化数据作为输入传递给业务流程,并将结构化数据作为输出返回。

重要

在内部,业务流程仍会将数据处理为 ChatMessageContent

结构化输入

开发人员可以使用强类型输入类将结构化数据作为输入传递给业务流程,并将其指定为业务流程的泛型参数。 这样可以实现类型安全,并使编排更灵活地处理复杂的数据结构。 例如,若要对 GitHub 问题进行会审,请为结构化输入定义类:

public sealed class GithubIssue
{
    public string Id { get; set; } = string.Empty;
    public string Title { get; set; } = string.Empty;
    public string Body { get; set; } = string.Empty;
    public string[] Labels { get; set; } = [];
}

然后,开发人员可以通过将此类型作为泛型参数提供,来在编排中使用它作为输入:

HandoffOrchestration<GithubIssue, string> orchestration =
    new(...);

GithubIssue input = new GithubIssue { ... };
var result = await orchestration.InvokeAsync(input, runtime);

自定义输入转换

默认情况下,业务流程将使用内置输入转换,该转换将对象序列化为 JSON 并将其包装在一个 ChatMessageContent中。 如果要自定义结构化输入转换为基础消息类型的方式,可以通过属性提供自己的输入转换函数 InputTransform

HandoffOrchestration<GithubIssue, string> orchestration =
    new(...)
    {
        InputTransform = (issue, cancellationToken) =>
        {
            // For example, create a chat message with a custom format
            var message = new ChatMessageContent(AuthorRole.User, $"[{issue.Id}] {issue.Title}\n{issue.Body}");
            return ValueTask.FromResult<IEnumerable<ChatMessageContent>>([message]);
        },
    };

这样,就可以准确控制键入的输入呈现给代理的方式,从而启用自定义格式、字段选择或多消息输入等高级方案。

小窍门

请参阅Step04a_HandoffWithStructuredInput.cs的完整示例

开发人员可以通过将 Pydantic 模型(或任何自定义类)指定为业务流程的泛型参数,将结构化数据作为输入传递给业务流程。 这可实现类型安全性,并允许业务流程处理复杂的数据结构。

例如,若要对 GitHub 问题进行会审,请为结构化输入定义 Pydantic 模型:

from pydantic import BaseModel

class GithubIssue(BaseModel):
    id: str
    title: str
    body: str
    labels: list[str] = []

然后,可以通过将其作为泛型参数提供,将此类型用作业务流程的输入:

from semantic_kernel.agents import HandoffOrchestration

def custom_input_transform(input_message: GithubIssue) -> ChatMessageContent:
    return ChatMessageContent(role=AuthorRole.USER, content=f"[{input_message.id}] {input_message.title}\n{input_message.body}")


handoff_orchestration = HandoffOrchestration[GithubIssue, ChatMessageContent](
    ...,
    input_transform=custom_input_transform,
)

GithubIssueSample = GithubIssue(
    id="12345",
    title="Bug: ...",
    body="Describe the bug...",
    labels=[],
)

orchestration_result = await handoff_orchestration.invoke(
    task=GithubIssueSample,
    runtime=runtime,
)

小窍门

请参阅step4a_handoff_structured_inputs.py的完整示例

注释

代理编排在 Java SDK 中尚不可用。

结构化输出

代理和编排可以通过将强类型输出类指定为编排的泛型参数来返回结构化输出。 这使你能够处理应用程序中丰富的结构化结果,而不仅仅是纯文本。

例如,假设你想要分析文章并提取主题、情绪和实体。 定义结构化输出的类:

public sealed class Analysis
{
    public IList<string> Themes { get; set; } = [];
    public IList<string> Sentiments { get; set; } = [];
    public IList<string> Entities { get; set; } = [];
}

然后,你可以通过将类型作为泛型参数提供,将它用作编排的输出。

ConcurrentOrchestration<string, Analysis> orchestration =
    new(agent1, agent2, agent3)
    {
        ResultTransform = outputTransform.TransformAsync, // see below
    };

// ...
OrchestrationResult<Analysis> result = await orchestration.InvokeAsync(input, runtime);
Analysis output = await result.GetValueAsync(TimeSpan.FromSeconds(60));

自定义输出转换

默认情况下,编排将使用内置输出转换函数,该函数尝试将代理的响应内容反序列化到您的输出类型。 对于更高级的方案,可以提供自定义输出转换(例如,某些模型具有结构化输出)。

StructuredOutputTransform<Analysis> outputTransform =
    new(chatCompletionService, new OpenAIPromptExecutionSettings { ResponseFormat = typeof(Analysis) });

ConcurrentOrchestration<string, Analysis> orchestration =
    new(agent1, agent2, agent3)
    {
        ResultTransform = outputTransform.TransformAsync,
    };

此方法允许直接从业务流程接收和处理结构化数据,从而更轻松地构建高级工作流和集成。

小窍门

请参阅Step01a_ConcurrentWithStructuredOutput.cs中的完整示例

代理和业务流程可以通过将 Pydantic 模型(或任何自定义类)指定为业务流程的泛型输出类型来返回结构化输出。 这使你能够处理应用程序中丰富的结构化结果,而不仅仅是纯文本。

例如,假设你想要分析文章并提取主题、情绪和实体。 为结构化输出定义 Pydantic 模型:

from pydantic import BaseModel

class ArticleAnalysis(BaseModel):
    themes: list[str]
    sentiments: list[str]
    entities: list[str]

然后,可以将此类型用作业务流程的输出,方法是将其作为泛型参数提供并指定输出转换:

from semantic_kernel.agents import ConcurrentOrchestration
from semantic_kernel.agents.orchestration.tools import structured_outputs_transform
from semantic_kernel.connectors.ai.open_ai import AzureChatCompletion

# `structured_outputs_transform` is a built-in transform that uses the structured output

concurrent_orchestration = ConcurrentOrchestration[str, ArticleAnalysis](
    members=agents,
    output_transform=structured_outputs_transform(ArticleAnalysis, AzureChatCompletion()),
)

...

orchestration_result = await concurrent_orchestration.invoke(
    task="article text",
    runtime=runtime,
)

value = await orchestration_result.get(timeout=20)

# `value` is now an instance of ArticleAnalysis

此方法允许直接从业务流程接收和处理结构化数据,从而更轻松地构建高级工作流和集成。

小窍门

请参阅step1a_concurrent_structured_outputs.py中的完整示例

注释

代理编排在 Java SDK 中尚不可用。

取消

重要

取消将阻止代理处理任何进一步的消息,但它不会停止已处理消息的代理。

重要

取消不会停止运行时。

可以通过对结果处理器调用 Cancel 方法来取消编排。 这将通过向所有代理传播信号来停止业务流程,它们将停止处理任何进一步的消息。

var resultTask = orchestration.InvokeAsync(input, runtime);
resultTask.Cancel();

开发人员可以通过对结果处理程序调用 cancel 方法来取消编排。 这将通过向所有代理传播信号来停止业务流程,它们将停止处理任何进一步的消息。

orchestration_result = await orchestration.invoke(task=task, runtime=runtime)
orchestration_result.cancel()

注释

代理编排在 Java SDK 中尚不可用。

后续步骤

注释

代理编排在 Java SDK 中尚不可用。