Поделиться через


Исполнители

Исполнителями являются основные стандартные блоки, обрабатывающие сообщения в рабочем процессе. Они являются автономными единицами обработки, которые получают типизированные сообщения, выполняют операции и могут создавать выходные сообщения или события.

Обзор

Каждый исполнитель имеет уникальный идентификатор и может обрабатывать определенные типы сообщений. Исполнителями могут быть:

  • Пользовательские компоненты логики — обработка данных, вызов API или преобразование сообщений
  • Агенты ИИ — используйте LLM для создания ответов (см. раздел "Агенты в рабочих процессах")

Это важно

Рекомендуемый способ определения обработчиков сообщений исполнителя в C# — использовать [MessageHandler] атрибут для методов в классе, наследуемом partial от Executor. В этом случае используется генерация исходного кода во время компиляции для регистрации обработчиков, что обеспечивает лучшую производительность, проверку на этапе компиляции и совместимость с Native AOT.

Базовая структура исполнителя

Исполнители наследуют от Executor базового класса и используют [MessageHandler] атрибут для объявления методов обработчика. Класс должен быть помечен partial для включения генерации кода.

using Microsoft.Agents.AI.Workflows;

internal sealed partial class UppercaseExecutor() : Executor("UppercaseExecutor")
{
    [MessageHandler]
    private ValueTask<string> HandleAsync(string message, IWorkflowContext context)
    {
        string result = message.ToUpperInvariant();
        return ValueTask.FromResult(result); // Return value is automatically sent to connected executors
    }
}

Вы также можете отправлять сообщения вручную, не возвращая значение:

internal sealed partial class UppercaseExecutor() : Executor("UppercaseExecutor")
{
    [MessageHandler]
    private async ValueTask HandleAsync(string message, IWorkflowContext context)
    {
        string result = message.ToUpperInvariant();
        await context.SendMessageAsync(result); // Manually send messages to connected executors
    }
}

Несколько типов входных данных

Обработка нескольких типов входных данных путем определения нескольких [MessageHandler] методов:

internal sealed partial class SampleExecutor() : Executor("SampleExecutor")
{
    [MessageHandler]
    private ValueTask<string> HandleStringAsync(string message, IWorkflowContext context)
    {
        return ValueTask.FromResult(message.ToUpperInvariant());
    }

    [MessageHandler]
    private ValueTask<int> HandleIntAsync(int message, IWorkflowContext context)
    {
        return ValueTask.FromResult(message * 2);
    }
}

Функционально-ориентированные исполнители

Создайте исполнителя из функции с помощью BindExecutor метода расширения:

Func<string, string> uppercaseFunc = s => s.ToUpperInvariant();
var uppercase = uppercaseFunc.BindExecutor("UppercaseExecutor");

Базовая структура исполнителя

Исполнители наследуются от Executor базового класса. Каждый обработчик использует методы с декоратором @handler. Обработчики должны иметь правильные заметки типа, чтобы указать типы сообщений, которые они обрабатывают.

from agent_framework import (
    Executor,
    WorkflowContext,
    handler,
)

class UpperCase(Executor):

    @handler
    async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -> None:
        """Convert the input to uppercase and forward it to the next node."""
        await ctx.send_message(text.upper())

Функционально-ориентированные исполнители

Создайте исполнителя из функции с помощью @executor декоратора:

from agent_framework import (
    WorkflowContext,
    executor,
)

@executor(id="upper_case_executor")
async def upper_case(text: str, ctx: WorkflowContext[str]) -> None:
    """Convert the input to uppercase and forward it to the next node."""
    await ctx.send_message(text.upper())

Несколько типов входных данных

Обработка нескольких типов входных данных путем определения нескольких обработчиков:

class SampleExecutor(Executor):

    @handler
    async def to_upper_case(self, text: str, ctx: WorkflowContext[str]) -> None:
        await ctx.send_message(text.upper())

    @handler
    async def double_integer(self, number: int, ctx: WorkflowContext[int]) -> None:
        await ctx.send_message(number * 2)

Явные параметры типа

В качестве альтернативы примечаниям типа можно явно указать типы с помощью параметров декоратора:

Это важно

При использовании явных параметров типа необходимо указать все типы с помощью декоратора— нельзя смешивать явные параметры с заметками типа. Параметр input является обязательным; output и workflow_output необязателен.

class ExplicitTypesExecutor(Executor):

    @handler(input=str, output=str)
    async def to_upper_case(self, text, ctx) -> None:
        await ctx.send_message(text.upper())

    @handler(input=str | int, output=str)
    async def handle_mixed(self, message, ctx) -> None:
        await ctx.send_message(str(message).upper())

    @handler(input=str, output=int, workflow_output=bool)
    async def process_with_workflow_output(self, message, ctx) -> None:
        await ctx.send_message(len(message))
        await ctx.yield_output(True)

Объект WorkflowContext

Эта WorkflowContext предоставляет методы для взаимодействия с рабочим процессом в ходе выполнения.

  • send_message — отправка сообщений подключенным исполнителям
  • yield_output — создание результатов рабочего процесса, возвращаемых или передаваемых потоково вызывающему
class OutputExecutor(Executor):

    @handler
    async def handle(self, message: str, ctx: WorkflowContext[Never, str]) -> None:
        await ctx.yield_output("Hello, World!")

Если обработчик не отправляет сообщения и не выдает выходные данные, параметр типа не требуется:

class LogExecutor(Executor):

    @handler
    async def handle(self, message: str, ctx: WorkflowContext) -> None:
        print("Doing some work...")

Дальнейшие шаги