Compartir a través de


Ejecutores

Los ejecutores son los componentes fundamentales que procesan mensajes en un flujo de trabajo. Son unidades de procesamiento autónomas que reciben mensajes tipados, realizan operaciones y pueden generar mensajes o eventos de salida.

Información general

Cada ejecutor tiene un identificador único y puede controlar tipos de mensajes específicos. Los ejecutores pueden ser:

  • Componentes lógicos personalizados : procesar datos, llamar a las API o transformar mensajes
  • Agentes de IA — use LLMs para generar respuestas (consulte Agentes en Flujos de Trabajo)

Importante

La manera recomendada de definir controladores de mensajes del ejecutor en C# es usar el [MessageHandler] atributo en métodos dentro de una partial clase que deriva de Executor. Esto usa la generación de código fuente en tiempo de compilación para el registro del manejador, lo que proporciona un mejor rendimiento, validación en tiempo de compilación y compatibilidad con AOT nativo.

Estructura básica del ejecutor

Los ejecutores derivan de la Executor clase base y usan el [MessageHandler] atributo para declarar métodos de controlador. La clase debe marcarse partial para habilitar la generación de código fuente.

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
    }
}

También puede enviar mensajes manualmente sin devolver un valor:

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
    }
}

Varios tipos de entrada

Controle varios tipos de entrada mediante la definición de varios [MessageHandler] métodos:

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);
    }
}

Ejecutores basados en funciones

Cree un ejecutor a partir de una función mediante el método de extensión BindExecutor.

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

Estructura básica del ejecutor

Los ejecutores heredan de la Executor clase base. Cada ejecutor usa métodos decorados con el decorador @handler. Los controladores deben contar con anotaciones tipográficas correctas para especificar los tipos de mensaje que procesan.

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())

ejecutores de Function-Based

Cree un ejecutor a partir de una función mediante el @executor decorador:

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())

Varios tipos de entrada

Controle varios tipos de entrada mediante la definición de varios controladores:

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)

Parámetros de tipo explícitos

Como alternativa a las anotaciones de tipo, puede especificar tipos explícitamente a través de parámetros de decorador:

Importante

Al usar parámetros de tipo explícitos, debe especificar todos los tipos a través del decorador; no se pueden mezclar parámetros explícitos con anotaciones de tipo. El input parámetro es obligatorio; output y workflow_output son opcionales.

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)

El objeto WorkflowContext

WorkflowContext proporciona métodos para interactuar con el flujo de trabajo durante la ejecución:

  • send_message : enviar mensajes a ejecutores conectados
  • yield_output : genera salidas de flujo de trabajo devueltas o transmitidas al autor de la llamada.
class OutputExecutor(Executor):

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

Si un controlador no envía mensajes ni genera salidas, no se necesita ningún parámetro de tipo:

class LogExecutor(Executor):

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

Pasos siguientes