Skip to content

Commit

Permalink
.Net: Processes Sample - Account Creation (#8990)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

### Description

Adding New Processes Feature Sample -  Create Account

This sample helps showcase the capabilities of process showing the use
of:
- cycles
- conditional outputs
- use of multiple outputs/inputs -> fan-in/fan-out behavior
- complex example that showcases a real life scenario

Adding Processes Samples README with diagrams to make it easier to
understand how the sample processes flow works

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [x] The code builds clean without any errors or warnings
- [x] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄

---------

Co-authored-by: Ben Thomas <bentho@microsoft.com>
  • Loading branch information
esttenorio and Ben Thomas authored Sep 30, 2024
1 parent 5b0a1af commit bd4d12b
Show file tree
Hide file tree
Showing 21 changed files with 1,138 additions and 77 deletions.
11 changes: 11 additions & 0 deletions dotnet/samples/GettingStartedWithProcesses/Events/CommonEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Events;

/// <summary>
/// Processes Events emitted by shared steps.<br/>
/// </summary>
public static class CommonEvents
{
public static readonly string UserInputReceived = nameof(UserInputReceived);
public static readonly string AssistantResponseGenerated = nameof(AssistantResponseGenerated);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,11 @@
<Import Project="$(RepoRoot)/dotnet/src/InternalUtilities/samples/SamplesInternalUtilities.props" />

<ItemGroup>
<ProjectReference
Include="..\..\src\Connectors\Connectors.AzureOpenAI\Connectors.AzureOpenAI.csproj" />
<ProjectReference
Include="..\..\src\Experimental\Process.Abstractions\Process.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Connectors\Connectors.AzureOpenAI\Connectors.AzureOpenAI.csproj" />
<ProjectReference Include="..\..\src\Experimental\Process.Abstractions\Process.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Experimental\Process.Core\Process.Core.csproj" />
<ProjectReference
Include="..\..\src\Experimental\Process.LocalRuntime\Process.LocalRuntime.csproj" />
<ProjectReference
Include="..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\src\Experimental\Process.LocalRuntime\Process.LocalRuntime.csproj" />
<ProjectReference Include="..\..\src\SemanticKernel.Abstractions\SemanticKernel.Abstractions.csproj" />
<ProjectReference Include="..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
</ItemGroup>

Expand Down
127 changes: 127 additions & 0 deletions dotnet/samples/GettingStartedWithProcesses/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Semantic Kernel Processes - Getting Started

This project contains a step by step guide to get started with _Semantic Kernel Processes_.


#### NuGet:
- [Microsoft.SemanticKernel.Process.Abstractions](https://www.nuget.org/packages/Microsoft.SemanticKernel.Process.Abstractions)
- [Microsoft.SemanticKernel.Process.Core](https://www.nuget.org/packages/Microsoft.SemanticKernel.Process.Core)
- [Microsoft.SemanticKernel.Process.LocalRuntime](https://www.nuget.org/packages/Microsoft.SemanticKernel.Process.LocalRuntime)

#### Sources
- [Semantic Kernel Processes - Abstractions](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/Experimental/Process.Abstractions)
- [Semantic Kernel Processes - Core](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/Experimental/Process.Core)
- [Semantic Kernel Processes - LocalRuntime](https://github.com/microsoft/semantic-kernel/tree/main/dotnet/src/Experimental/Process.LocalRuntime)

The examples can be run as integration tests but their code can also be copied to stand-alone programs.

## Examples

The getting started with agents examples include:

Example|Description
---|---
[Step01_Processes](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/GettingStartedWithProcesses/Step01_Processes.cs)|How to create a simple process with a loop and a conditional exit
[Step02_AccountOpening](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/GettingStartedWithProcesses/Step02_AccountOpening.cs)|Showcasing processes cycles, fan in, fan out for opening an account.

### Step01_Processes

```mermaid
flowchart LR
Intro(Intro)--> UserInput(User Input)
UserInput-->|User message == 'exit'| Exit(Exit)
UserInput-->|User message| AssistantResponse(Assistant Response)
AssistantResponse--> UserInput
```

### Step02_AccountOpening

```mermaid
flowchart LR
User(User) -->|Provides user details| FillForm(Fill New <br/> Customer <br/> Form)
FillForm -->|Need more info| AssistantMessage(Assistant <br/> Message)
FillForm -->|Welcome Message| AssistantMessage
FillForm --> CompletedForm((Completed Form))
AssistantMessage --> User
CompletedForm --> CreditCheck(Customer <br/> Credit Score <br/> Check)
CompletedForm --> Fraud(Fraud Detection)
CompletedForm -->|New Customer Form + Conversation Transcript| CoreSystem
CreditCheck -->|Failed - Notify user about insufficient credit score| Mailer(Mail <br/> Service)
CreditCheck -->|Approved| Fraud
Fraud --> |Failed - Notify user about failure to confirm user identity| Mailer
Fraud --> |Passed| CoreSystem(Core System <br/> Record <br/> Creation)
CoreSystem --> Marketing(New Marketing <br/> Record Creation)
CoreSystem --> CRM(CRM Record <br/> Creation)
CoreSystem -->|Account Details| Welcome(Welcome <br/> Packet)
Marketing -->|Success| Welcome
CRM -->|Success| Welcome
Welcome -->|Success: Notify User about Account Creation| Mailer
Mailer -->|End of Interaction| User
```


## Running Examples with Filters
Examples may be explored and ran within _Visual Studio_ using _Test Explorer_.

You can also run specific examples via the command-line by using test filters (`dotnet test --filter`). Type `dotnet test --help` at the command line for more details.

Example:

```
dotnet test --filter Step01_Processes
```

## Configuring Secrets

Each example requires secrets / credentials to access OpenAI or Azure OpenAI.

We suggest using .NET [Secret Manager](https://learn.microsoft.com/en-us/aspnet/core/security/app-secrets) to avoid the risk of leaking secrets into the repository, branches and pull requests. You can also use environment variables if you prefer.

To set your secrets with .NET Secret Manager:

1. Navigate the console to the project folder:

```
cd dotnet/samples/GettingStartedWithProcesses
```

2. Examine existing secret definitions:

```
dotnet user-secrets list
```

3. If needed, perform first time initialization:

```
dotnet user-secrets init
```

4. Define secrets for either Open AI:

```
dotnet user-secrets set "OpenAI:ChatModelId" "..."
dotnet user-secrets set "OpenAI:ApiKey" "..."
```

5. Or Azure Open AI:

```
dotnet user-secrets set "AzureOpenAI:DeploymentName" "..."
dotnet user-secrets set "AzureOpenAI:ChatDeploymentName" "..."
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://... .openai.azure.com/"
dotnet user-secrets set "AzureOpenAI:ApiKey" "..."
```

> NOTE: Azure secrets will take precedence, if both Open AI and Azure Open AI secrets are defined, unless `ForceOpenAI` is set:
```
protected override bool ForceOpenAI => true;
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft. All rights reserved.

using Events;
using Microsoft.SemanticKernel;

namespace SharedSteps;

/// <summary>
/// Step used in the Processes Samples:
/// - Step_02_AccountOpening.cs
/// </summary>
public class DisplayAssistantMessageStep : KernelProcessStep
{
public static class Functions
{
public const string DisplayAssistantMessage = nameof(DisplayAssistantMessage);
}

[KernelFunction(Functions.DisplayAssistantMessage)]
public async ValueTask DisplayAssistantMessageAsync(KernelProcessStepContext context, string assistantMessage)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"ASSISTANT: {assistantMessage}\n");
Console.ResetColor();

// Emit the assistantMessageGenerated
await context.EmitEventAsync(new() { Id = CommonEvents.AssistantResponseGenerated, Data = assistantMessage });
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Copyright (c) Microsoft. All rights reserved.

using Events;
using Microsoft.SemanticKernel;

namespace SharedSteps;

/// <summary>
/// A step that elicits user input.
///
/// Step used in the Processes Samples:
/// - Step_01_Processes.cs
/// - Step_02_AccountOpening.cs
/// </summary>
public class ScriptedUserInputStep : KernelProcessStep<UserInputState>
{
public static class Functions
{
public const string GetUserInput = nameof(GetUserInput);
}

/// <summary>
/// The state object for the user input step. This object holds the user inputs and the current input index.
/// </summary>
protected UserInputState? _state;

/// <summary>
/// Method to be overridden by the user to populate with custom user messages
/// </summary>
public virtual void PopulateUserInputs()
{
return;
}

/// <summary>
/// Activates the user input step by initializing the state object. This method is called when the process is started
/// and before any of the KernelFunctions are invoked.
/// </summary>
/// <param name="state">The state object for the step.</param>
/// <returns>A <see cref="ValueTask"/></returns>
public override ValueTask ActivateAsync(KernelProcessStepState<UserInputState> state)
{
state.State ??= new();
_state = state.State;

PopulateUserInputs();

return ValueTask.CompletedTask;
}

/// <summary>
/// Gets the user input.
/// </summary>
/// <param name="context">An instance of <see cref="KernelProcessStepContext"/> which can be
/// used to emit events from within a KernelFunction.</param>
/// <returns>A <see cref="ValueTask"/></returns>
[KernelFunction(Functions.GetUserInput)]
public async ValueTask GetUserInputAsync(KernelProcessStepContext context)
{
var userMessage = _state!.UserInputs[_state.CurrentInputIndex];
_state.CurrentInputIndex++;

Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"USER: {userMessage}");
Console.ResetColor();

// Emit the user input
await context.EmitEventAsync(new() { Id = CommonEvents.UserInputReceived, Data = userMessage });
}
}

/// <summary>
/// The state object for the <see cref="ScriptedUserInputStep"/>
/// </summary>
public record UserInputState
{
public List<string> UserInputs { get; init; } = [];

public int CurrentInputIndex { get; set; } = 0;
}
Loading

0 comments on commit bd4d12b

Please sign in to comment.