Útmutató: Ember a folyamatban

Figyelmeztetés

A Szemantic Kernel Process Framework kísérleti, még fejlesztés alatt áll, és változhat.

Áttekintés

Az előző szakaszokban egy folyamatot hoztunk létre, amely segít automatizálni az új termék dokumentációjának létrehozását. A folyamat most már képes olyan dokumentációt létrehozni, amely a termékre jellemző, és egy korrektúra és szerkesztési ciklus futtatásával biztosíthatja, hogy megfeleljen a minőségi sávunknak. Ebben a szakaszban ismét továbbfejlesztjük ezt a folyamatot azáltal, hogy egy embernek jóvá kell hagynia vagy el kell utasítania a dokumentációt a közzététel előtt. A folyamat-keretrendszer rugalmassága azt jelenti, hogy ennek többféle módja is lehet, de ebben a példában egy külső pubsub rendszerrel való integrációt mutatunk be jóváhagyás kéréséhez.

Folyamatábra a folyamatunkhoz emberi beavatkozással rendelkező mintával.

Közzétételi várakozás a jóváhagyásra

A folyamat első módosítása, hogy a közzétételi lépés megvárja a jóváhagyást, mielőtt közzéteszi a dokumentációt. Az egyik lehetőség, hogy egyszerűen hozzáadunk egy második paramétert a PublishDocumentation függvényhez a PublishDocumentationStep-ben történő jóváhagyáshoz. Ez azért működik, mert egy lépésben lévő KernelFunction csak akkor lesz meghívva, ha az összes szükséges paraméter meg lett adva.

// A process step to publish documentation
public class PublishDocumentationStep : KernelProcessStep
{
    [KernelFunction]
    public DocumentInfo PublishDocumentation(DocumentInfo document, bool userApproval) // added the userApproval parameter
    {
        // Only publish the documentation if it has been approved
        if (userApproval)
        {
            // For example purposes we just write the generated docs to the console
            Console.WriteLine($"[{nameof(PublishDocumentationStep)}]:\tPublishing product documentation approved by user: \n{document.Title}\n{document.Content}");
        }
        return document;
    }
}

Hamarosan elérhető lesz a Python emberi közbeavatkozással működő folyamatainak viselkedésének támogatása.

A fenti kóddal a PublishDocumentationPublishDocumentationStep függvénye csak akkor lesz meghívva, ha a létrehozott dokumentációt elküldte a document paraméternek, és a jóváhagyás eredményét a userApproval paraméternek küldi el.

Most már újra felhasználhatjuk a lépés meglévő logikáját ProofreadStep , hogy egy eseményt is kibocsátsunk a külső pubsub rendszerünknek, amely értesíti az emberi jóváhagyót, hogy van egy új kérés.

// A process step to publish documentation
public class ProofReadDocumentationStep : KernelProcessStep
{
    ...

    if (formattedResponse.MeetsExpectations)
    {
        // Events that are getting piped to steps that will be resumed, like PublishDocumentationStep.OnPublishDocumentation
        // require events to be marked as public so they are persisted and restored correctly
        await context.EmitEventAsync("DocumentationApproved", data: document, visibility: KernelProcessEventVisibility.Public);
    }
    ...
}

Mivel az újonnan létrehozott dokumentációt közzé szeretnénk tenni, amikor a korrektúraügynök jóváhagyja, a jóváhagyott dokumentumok a közzétételi lépésben lesznek várólistára. Ezen kívül a külső pubsub rendszeren keresztül egy ember értesítést kap a legújabb dokumentum frissítésével. Frissítsük a folyamat folyamatát úgy, hogy megfeleljen ennek az új tervnek.

Hamarosan elérhető lesz a Python emberi közbeavatkozással működő folyamatainak viselkedésének támogatása.

// Create the process builder
ProcessBuilder processBuilder = new("DocumentationGeneration");

// Add the steps
var infoGatheringStep = processBuilder.AddStepFromType<GatherProductInfoStep>();
var docsGenerationStep = processBuilder.AddStepFromType<GenerateDocumentationStepV2>();
var docsProofreadStep = processBuilder.AddStepFromType<ProofreadStep>();
var docsPublishStep = processBuilder.AddStepFromType<PublishDocumentationStep>();

// internal component that allows emitting SK events externally, a list of topic names
// is needed to link them to existing SK events
var proxyStep = processBuilder.AddProxyStep(["RequestUserReview", "PublishDocumentation"]);

// Orchestrate the events
processBuilder
    .OnInputEvent("StartDocumentGeneration")
    .SendEventTo(new(infoGatheringStep));

processBuilder
    .OnInputEvent("UserRejectedDocument")
    .SendEventTo(new(docsGenerationStep, functionName: "ApplySuggestions"));

// When external human approval event comes in, route it to the 'isApproved' parameter of the docsPublishStep
processBuilder
    .OnInputEvent("UserApprovedDocument")
    .SendEventTo(new(docsPublishStep, parameterName: "userApproval"));

// Hooking up the rest of the process steps
infoGatheringStep
    .OnFunctionResult()
    .SendEventTo(new(docsGenerationStep, functionName: "GenerateDocumentation"));

docsGenerationStep
    .OnEvent("DocumentationGenerated")
    .SendEventTo(new(docsProofreadStep));

docsProofreadStep
    .OnEvent("DocumentationRejected")
    .SendEventTo(new(docsGenerationStep, functionName: "ApplySuggestions"));

// When the proofreader approves the documentation, send it to the 'document' parameter of the docsPublishStep
// Additionally, the generated document is emitted externally for user approval using the pre-configured proxyStep
docsProofreadStep
    .OnEvent("DocumentationApproved")
    // [NEW] addition to emit messages externally
    .EmitExternalEvent(proxyStep, "RequestUserReview") // Hooking up existing "DocumentationApproved" to external topic "RequestUserReview"
    .SendEventTo(new(docsPublishStep, parameterName: "document"));

// When event is approved by user, it gets published externally too
docsPublishStep
    .OnFunctionResult()
    // [NEW] addition to emit messages externally
    .EmitExternalEvent(proxyStep, "PublishDocumentation");

var process = processBuilder.Build();
return process;

Végül meg kell adni az interfész IExternalKernelProcessMessageChannel implementálását, mivel azt az új ProxySteprendszer belsőleg használja. Ez a felület külső üzenetek küldéséhez használható. Ennek a felületnek a megvalósítása a használt külső rendszertől függ. Ebben a példában egy egyéni ügyfelet fogunk használni, amelyet létrehoztunk, hogy üzeneteket küldjünk egy külső pubsub-rendszernek.

// Example of potential custom IExternalKernelProcessMessageChannel implementation 
public class MyCloudEventClient : IExternalKernelProcessMessageChannel
{
    private MyCustomClient? _customClient;

    // Example of an implementation for the process
    public async Task EmitExternalEventAsync(string externalTopicEvent, KernelProcessProxyMessage message)
    {
        // logic used for emitting messages externally.
        // Since all topics are received here potentially 
        // some if else/switch logic is needed to map correctly topics with external APIs/endpoints.
        if (this._customClient != null)
        {
            switch (externalTopicEvent) 
            {
                case "RequestUserReview":
                    var requestDocument = message.EventData.ToObject() as DocumentInfo;
                    // As an example only invoking a sample of a custom client with a different endpoint/api route
                    this._customClient.InvokeAsync("REQUEST_USER_REVIEW", requestDocument);
                    return;

                case "PublishDocumentation":
                    var publishedDocument = message.EventData.ToObject() as DocumentInfo;
                    // As an example only invoking a sample of a custom client with a different endpoint/api route
                    this._customClient.InvokeAsync("PUBLISH_DOC_EXTERNALLY", publishedDocument);
                    return;
            }
        }
    }

    public async ValueTask Initialize()
    {
        // logic needed to initialize proxy step, can be used to initialize custom client
        this._customClient = new MyCustomClient("http://localhost:8080");
        this._customClient.Initialize();
    }

    public async ValueTask Uninitialize()
    {
        // Cleanup to be executed when proxy step is uninitialized
        if (this._customClient != null)
        {
            await this._customClient.ShutdownAsync();
        }
    }
}

Végül ahhoz, hogy a folyamat ProxyStep használhassa a IExternalKernelProcessMessageChannel megvalósítást, ebben az esetben MyCloudEventClient, megfelelően kell összekapcsolnunk.

Helyi futtatókörnyezet használata esetén a implementált osztály átadható az StartAsync osztály meghívásakorKernelProcess.

KernelProcess process;
IExternalKernelProcessMessageChannel myExternalMessageChannel = new MyCloudEventClient();
// Start the process with the external message channel
await process.StartAsync(kernel, new KernelProcessEvent 
    {
        Id = inputEvent,
        Data = input,
    },
    myExternalMessageChannel)

A Dapr Runtime használatakor a vízvezetéket függőséginjektálással kell elvégezni a projekt programbeállításánál.

var builder = WebApplication.CreateBuilder(args);
...
// depending on the application a singleton or scoped service can be used
// Injecting SK Process custom client IExternalKernelProcessMessageChannel implementation
builder.Services.AddSingleton<IExternalKernelProcessMessageChannel, MyCloudEventClient>();

Hamarosan elérhető lesz a Python emberi közbeavatkozással működő folyamatainak viselkedésének támogatása.

Két módosítás történt a folyamaton:

  • Hozzáadott egy HumanApprovalResponse nevű bemeneti eseményt, amely a userApproval lépés docsPublishStep paraméteréhez lesz irányítva.
  • Mivel a KernelFunction docsPublishStep-ben most már két paraméter található, frissíteni kell a meglévő útvonalat a documentparaméternevének megadásához.

Futtassa a folyamatot úgy, ahogy korábban is tette, és vegye észre, hogy ezúttal, amikor a korrektúra jóváhagyja a létrehozott dokumentációt, és elküldi a document paramétert a docPublishStep lépéshez, a lépés már nem lesz végrehajtva, mert a userApproval paraméterre vár. Ezen a ponton a folyamat tétlen áll, mert nincsenek olyan lépések, amelyek készen állnak a meghívásra, és az általunk tett hívás, amely elindította a folyamatot, visszatér. A folyamat ebben az inaktív állapotban marad, amíg a "human-in-the-loop" meg nem teszi a szükséges lépéseket a közzétételi kérelem jóváhagyásához vagy elutasításához. Miután ez megtörtént, és az eredmény vissza lett adva a programunknak, újraindíthatjuk a folyamatot az eredménnyel.

// Restart the process with approval for publishing the documentation.
await process.StartAsync(kernel, new KernelProcessEvent { Id = "UserApprovedDocument", Data = true });

Hamarosan elérhető lesz a Python emberi közbeavatkozással működő folyamatainak viselkedésének támogatása.

Amikor a folyamat újra elindul a UserApprovedDocument-val, onnan folytatódik, ahol abbahagyta, és meghívja a docsPublishStep-et úgy, hogy a userApproval beállítás true-ra van állítva, majd közzétesszük a dokumentációnkat. Ha újraindul a UserRejectedDocument eseménnyel, a folyamat újra elindítja a ApplySuggestions függvényt a docsGenerationStep lépésben és a folyamat ugyanúgy folytatódik, ahogy korábban.

A folyamat befejeződött, és sikeresen hozzáadtunk egy emberi beavatkozást igénylő lépést a folyamathoz. A folyamat most már használható a termék dokumentációjának létrehozására, korrektúra készítésére és közzétételére, miután egy ember jóváhagyta.