Condividi tramite


Esecutori

Gli executor sono i blocchi predefiniti fondamentali che elaborano i messaggi in un flusso di lavoro. Sono unità di elaborazione autonome che ricevono messaggi tipizzato, eseguono operazioni e possono produrre messaggi o eventi di output.

Informazioni generali

Ogni executor ha un identificatore univoco e può gestire tipi di messaggio specifici. Gli executor possono essere:

  • Componenti della logica personalizzati : elaborare i dati, chiamare LE API o trasformare i messaggi
  • Agenti di intelligenza artificiale : usare IMS per generare risposte (vedere Agenti nei flussi di lavoro)

Importante

Il modo consigliato per definire i gestori di messaggi executor in C# consiste nell'usare l'attributo [MessageHandler] nei metodi all'interno di una partial classe che deriva da Executor. In questo modo viene usata la generazione della sorgente in fase di compilazione per la registrazione del gestore, offrendo prestazioni migliori, validazione in fase di compilazione e compatibilità nativa AOT.

Struttura di Base dell'Executor

Gli executor derivano dalla Executor classe base e usano l'attributo per dichiarare i [MessageHandler] metodi del gestore. La classe deve essere contrassegnata partial per abilitare la generazione di origine.

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

È anche possibile inviare messaggi manualmente senza restituire un valore:

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

Tipi di input multipli

Gestire più tipi di input definendo più [MessageHandler] metodi:

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

Esecutori Basati su Funzione

Creare un executor da una funzione usando il BindExecutor metodo di estensione:

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

Struttura di Base dell'Executor

Gli executor ereditano dalla Executor classe base. Ogni executor usa metodi decorati con il decoratore @handler. I gestori devono avere annotazioni di tipo appropriate per specificare i tipi di messaggio elaborati.

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 basati su funzioni

Creare un executor a partire da una funzione usando il decorator @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())

Tipi di input multipli

Gestire più tipi di input definendo più gestori:

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)

Parametri di tipo espliciti

In alternativa alle annotazioni di tipo, è possibile specificare i tipi in modo esplicito tramite parametri decorator:

Importante

Quando si usano parametri di tipo espliciti, è necessario specificare tutti i tipi tramite l'elemento Decorator. Non è possibile combinare parametri espliciti con annotazioni di tipo. Il input parametro è obbligatorio output e workflow_output sono facoltativi.

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)

Oggetto WorkflowContext

WorkflowContext fornisce metodi per interagire con il flusso di lavoro durante l'esecuzione:

  • send_message — inviare messaggi agli executor connessi
  • yield_output — produrre output del flusso di lavoro restituiti/trasmessi al chiamante
class OutputExecutor(Executor):

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

Se un gestore non invia né messaggi né restituisce output, non è necessario alcun parametro di tipo:

class LogExecutor(Executor):

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

Passaggi successivi