다음을 통해 공유


방법: 휴먼 인 더 루프

경고

의미 체계 커널 프로세스 프레임워크 아직 개발 중이며 변경될 수 있습니다.

개요

이전 섹션에서는 새 제품에 대한 설명서 생성을 자동화하는 데 도움이 되는 프로세스를 빌드했습니다. 이제 프로세스에서 제품과 관련된 설명서를 생성할 수 있으며, 교정 및 편집 주기를 통해 실행하여 품질 표시줄을 충족할 수 있습니다. 이 섹션에서는 문서를 게시하기 전에 사용자가 승인하거나 거부하도록 요구하여 해당 프로세스를 다시 개선할 것입니다. 프로세스 프레임워크의 유연성은 여러 가지 방법으로 이 작업을 수행할 수 있다는 것을 의미하지만, 이 예제에서는 승인을 요청하기 위해 외부 pubsub 시스템과의 통합을 보여 줍니다.

흐름 다이어그램은 휴먼 인 더 루프 패턴을 사용하는 프로세스에 대한 다이어그램입니다.

게시가 승인을 기다리도록 설정

프로세스에 대한 첫 번째 변경 내용은 게시 단계가 문서를 게시하기 전에 승인을 기다리도록 하는 것입니다. 한 가지 옵션은 PublishDocumentationPublishDocumentationStep 함수에 승인을 위한 두 번째 매개 변수를 추가하는 것입니다. 이는 모든 필수 매개 변수가 제공된 경우에만 단계의 KernelFunction이 호출되기 때문에 작동합니다.

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

Python 휴먼 인 더 루프 프로세스 동작에 대한 지원은 곧 제공될 예정입니다.

위의 코드를 사용하면 생성된 설명서가 PublishDocumentation 매개 변수로 전송되고 승인 결과가 PublishDocumentationStep 매개 변수로 전송된 경우에만 documentuserApproval 함수가 호출됩니다.

이제 기존 단계 논리 ProofreadStep 를 다시 사용하여 외부 pubsub 시스템에 이벤트를 추가로 내보낼 수 있습니다. 그러면 사용자 승인자에게 새 요청이 있음을 알릴 수 있습니다.

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

새로 생성된 설명서는 교정 에이전트의 승인을 받을 때 게시하려고 하므로 승인된 문서는 게시 단계에서 큐에 대기됩니다. 또한 최신 문서에 대한 업데이트가 포함된 외부 pubsub 시스템을 통해 사람이 알림을 받게 됩니다. 이 새로운 디자인과 일치하도록 프로세스 흐름을 업데이트해 보겠습니다.

Python 휴먼 인 더 루프 프로세스 동작에 대한 지원은 곧 제공될 예정입니다.

// 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;

마지막으로, 인터페이스 IExternalKernelProcessMessageChannel의 구현을 제공해야 합니다. 이는 새 ProxyStep이 내부적으로 사용하기 때문입니다. 이 인터페이스는 외부에서 메시지를 내보내는 데 사용됩니다. 이 인터페이스의 구현은 사용 중인 외부 시스템에 따라 달라집니다. 이 예제에서는 만든 사용자 지정 클라이언트를 사용하여 외부 pubsub 시스템에 메시지를 보냅니다.

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

마지막으로 프로세스 ProxyStepIExternalKernelProcessMessageChannel 구현을 이 경우 MyCloudEventClient 사용할 수 있도록 하려면, 제대로 파이프해야 합니다.

로컬 런타임을 사용하는 경우 StartAsync을(를) KernelProcess 클래스에서 호출할 때 구현된 클래스를 전달할 수 있습니다.

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)

Dapr 런타임을 사용하는 경우 프로젝트의 프로그램 설정에서 종속성 주입을 통해 배관을 수행해야 합니다.

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

Python 휴먼 인 더 루프 프로세스 동작에 대한 지원은 곧 제공될 예정입니다.

프로세스 흐름에 두 가지 변경 내용이 적용되었습니다.

  • HumanApprovalResponse 단계의 userApproval 매개 변수로 라우팅될 docsPublishStep 입력 이벤트가 추가되었습니다.
  • docsPublishStep KernelFunction에는 이제 두 개의 매개 변수가 있으므로 기존 경로를 업데이트하여 document매개 변수 이름을 지정해야 합니다.

이전과 마찬가지로 프로세스를 실행하고, 이번에는 proofreader가 생성된 설명서를 승인하고 document 단계의 docPublishStep 매개 변수로 보낼 때 userApproval 매개 변수를 기다리고 있으므로 단계가 더 이상 호출되지 않습니다. 이 시점에서 호출할 준비가 된 단계가 없고 프로세스를 시작하기 위해 수행한 호출이 반환되므로 프로세스가 유휴 상태가 됩니다. 이 프로세스는 "휴먼 인더 루프"가 게시 요청을 승인하거나 거부하는 작업을 수행할 때까지 이 유휴 상태로 유지됩니다. 이 문제가 발생하고 결과가 프로그램에 다시 전달되면 결과를 사용하여 프로세스를 다시 시작할 수 있습니다.

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

Python 휴먼 인 더 루프 프로세스 동작에 대한 지원은 곧 제공될 예정입니다.

프로세스가 UserApprovedDocument에 의해 다시 시작되면, 중단된 지점에서 이어서 진행하고, docsPublishStep로 설정된 userApproval값을 사용하여 true을 호출하며, 문서가 게시됩니다. 이벤트 UserRejectedDocument로 다시 시작되면 프로세스는 ApplySuggestions 단계에서 docsGenerationStep 함수를 시작하고, 프로세스는 이전과 같이 계속됩니다.

이제 프로세스가 완료되었으며 프로세스에 휴먼 인 더 루프 단계를 성공적으로 추가했습니다. 이제 이 프로세스를 사용하여 제품에 대한 설명서를 생성하고, 교정하고, 사람이 승인한 후에 게시할 수 있습니다.