Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.Net: Sample Demo Code Showcasing Usage of Reasoning Models #10558

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions dotnet/SK-dotnet.sln
Original file line number Diff line number Diff line change
Expand Up @@ -429,21 +429,23 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Process.IntegrationTests.Re
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OllamaFunctionCalling", "samples\Demos\OllamaFunctionCalling\OllamaFunctionCalling.csproj", "{481A680F-476A-4627-83DE-2F56C484525E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenAIRealtime", "samples\Demos\OpenAIRealtime\OpenAIRealtime.csproj", "{6154129E-7A35-44A5-998E-B7001B5EDE14}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenAIRealtime", "samples\Demos\OpenAIRealtime\OpenAIRealtime.csproj", "{6154129E-7A35-44A5-998E-B7001B5EDE14}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CreateChatGpt", "CreateChatGpt", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sk-chatgpt-azure-function", "samples\Demos\CreateChatGptPlugin\MathPlugin\azure-function\sk-chatgpt-azure-function.csproj", "{2EB6E4C2-606D-B638-2E08-49EA2061C428}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sk-chatgpt-azure-function", "samples\Demos\CreateChatGptPlugin\MathPlugin\azure-function\sk-chatgpt-azure-function.csproj", "{2EB6E4C2-606D-B638-2E08-49EA2061C428}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "kernel-functions-generator", "samples\Demos\CreateChatGptPlugin\MathPlugin\kernel-functions-generator\kernel-functions-generator.csproj", "{78785CB1-66CF-4895-D7E5-A440DD84BE86}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "kernel-functions-generator", "samples\Demos\CreateChatGptPlugin\MathPlugin\kernel-functions-generator\kernel-functions-generator.csproj", "{78785CB1-66CF-4895-D7E5-A440DD84BE86}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Agents.AzureAI", "src\Agents\AzureAI\Agents.AzureAI.csproj", "{EA35F1B5-9148-4189-BE34-5E00AED56D65}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Agents.AzureAI", "src\Agents\AzureAI\Agents.AzureAI.csproj", "{EA35F1B5-9148-4189-BE34-5E00AED56D65}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugins.AI", "src\Plugins\Plugins.AI\Plugins.AI.csproj", "{0C64EC81-8116-4388-87AD-BA14D4B59974}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins.AI", "src\Plugins\Plugins.AI\Plugins.AI.csproj", "{0C64EC81-8116-4388-87AD-BA14D4B59974}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugins.AI.UnitTests", "src\Plugins\Plugins.AI.UnitTests\Plugins.AI.UnitTests.csproj", "{03ACF9DD-00C9-4F2B-80F1-537E2151AF5F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugins.AI.UnitTests", "src\Plugins\Plugins.AI.UnitTests\Plugins.AI.UnitTests.csproj", "{03ACF9DD-00C9-4F2B-80F1-537E2151AF5F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Connectors.Postgres.UnitTests", "src\Connectors\Connectors.Postgres.UnitTests\Connectors.Postgres.UnitTests.csproj", "{2A1EC0DA-AD01-4421-AADC-1DFF65C71CCC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Connectors.Postgres.UnitTests", "src\Connectors\Connectors.Postgres.UnitTests\Connectors.Postgres.UnitTests.csproj", "{2A1EC0DA-AD01-4421-AADC-1DFF65C71CCC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReasoningEffortModels", "samples\Demos\ReasoningEffortModels\ReasoningEffortModels.csproj", "{6D6FF6FA-9168-403B-9477-996F84E84C83}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -1196,6 +1198,12 @@ Global
{2A1EC0DA-AD01-4421-AADC-1DFF65C71CCC}.Publish|Any CPU.Build.0 = Debug|Any CPU
{2A1EC0DA-AD01-4421-AADC-1DFF65C71CCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2A1EC0DA-AD01-4421-AADC-1DFF65C71CCC}.Release|Any CPU.Build.0 = Release|Any CPU
{6D6FF6FA-9168-403B-9477-996F84E84C83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6D6FF6FA-9168-403B-9477-996F84E84C83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D6FF6FA-9168-403B-9477-996F84E84C83}.Publish|Any CPU.ActiveCfg = Debug|Any CPU
{6D6FF6FA-9168-403B-9477-996F84E84C83}.Publish|Any CPU.Build.0 = Debug|Any CPU
{6D6FF6FA-9168-403B-9477-996F84E84C83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D6FF6FA-9168-403B-9477-996F84E84C83}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1360,6 +1368,7 @@ Global
{0C64EC81-8116-4388-87AD-BA14D4B59974} = {D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}
{03ACF9DD-00C9-4F2B-80F1-537E2151AF5F} = {D6D598DF-C17C-46F4-B2B9-CDE82E2DE132}
{2A1EC0DA-AD01-4421-AADC-1DFF65C71CCC} = {5A7028A7-4DDF-4E4F-84A9-37CE8F8D7E89}
{6D6FF6FA-9168-403B-9477-996F84E84C83} = {5D4C0700-BBB5-418F-A7B2-F392B9A18263}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FBDC56A3-86AD-4323-AA0F-201E59123B83}
Expand Down
100 changes: 100 additions & 0 deletions dotnet/samples/Demos/ReasoningEffortModels/AgentBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.Extensions.Configuration;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using OpenAI.Chat;
using ReasoningEffortModels.Options;

namespace ReasoningEffortModels;

public abstract class AgentBase
{
private protected readonly Kernel _kernel;
// Store the reasoning effort level for use later
private protected readonly ChatReasoningEffortLevel _reasoningEffort;

public abstract string AgentName { get; }
public abstract string AgentPrompt { get; }
public abstract string DefaultDeploymentName { get; }

/// <summary>
/// Initializes a new instance of the AgentBase class.
/// </summary>
/// <param name="customKernel">An optional custom kernel.</param>
/// <param name="deploymentName">An optional deployment name. If not provided, DefaultDeploymentName is used.</param>
/// <param name="reasoningEffort">An optional reasoning effort level. Defaults to Medium if not provided.</param>
protected AgentBase(
Kernel? customKernel = null,
string? deploymentName = null,
ChatReasoningEffortLevel? reasoningEffort = null)
{
this._reasoningEffort = reasoningEffort ?? ChatReasoningEffortLevel.Low;

var config = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.AddEnvironmentVariables()
.Build();

var azureOpenAIOptions = config.GetSection(AzureOpenAIOptions.SectionName).Get<AzureOpenAIOptions>();

string chosenDeployment = string.IsNullOrWhiteSpace(deploymentName)
? this.DefaultDeploymentName
: deploymentName;

if (customKernel is null)
{
var builder = Kernel.CreateBuilder();
this._kernel = builder.AddAzureOpenAIChatCompletion(
deploymentName: chosenDeployment,
endpoint: azureOpenAIOptions!.Endpoint,
apiKey: azureOpenAIOptions.ApiKey)
.Build();
}
else
{
this._kernel = customKernel;
}
}

/// <summary>
/// Determines if the provided model identifier corresponds to a reasoning model.
/// Adjust the logic as needed for your specific model identifiers.
/// </summary>
/// <param name="model">The model identifier to check.</param>
/// <returns>True if it is a reasoning model; otherwise, false.</returns>
private bool IsReasoningModel(string model)
{
// Example logic: treat "o1" and "o3-mini" as reasoning models.
return model.Equals("o1", StringComparison.OrdinalIgnoreCase) ||
model.Equals("o3-mini", StringComparison.OrdinalIgnoreCase);
}

/// <summary>
/// Creates and returns a ChatCompletionAgent with appropriate execution settings.
/// </summary>
/// <returns>A configured ChatCompletionAgent.</returns>
public ChatCompletionAgent CreateAgent()
{
// Initialize the prompt execution settings.
OpenAIPromptExecutionSettings executionSettings = new()
{
ToolCallBehavior = ToolCallBehavior.AutoInvokeKernelFunctions,
};

// If the deployment model supports reasoning, add the reasoning effort level.
if (this.IsReasoningModel(this.DefaultDeploymentName))
{
executionSettings.ReasoningEffort = this._reasoningEffort;
}

return new ChatCompletionAgent
{
Name = this.AgentName,
Instructions = this.AgentPrompt,
Kernel = this._kernel,
Arguments = new KernelArguments(executionSettings)
};
}
}
43 changes: 43 additions & 0 deletions dotnet/samples/Demos/ReasoningEffortModels/Agents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) Microsoft. All rights reserved.

using OpenAI.Chat;

namespace ReasoningEffortModels;
// A smart blog post agent that asks for an analytical blog post about Semantic Kernel.
public class SmartBlogPostAgent : AgentBase
{
public override string AgentName => "SmartBlogPostAgent";
public override string AgentPrompt => "Write a smart, detailed blog post on Semantic Kernel that explains its architecture, benefits, and real-world applications.";
public override string DefaultDeploymentName => "o3-mini";

public SmartBlogPostAgent(ChatReasoningEffortLevel reasoningEffort)
: base(reasoningEffort: reasoningEffort)
{
}
}

// A poem agent that asks for a creative poem about Semantic Kernel.
public class PoemAgent : AgentBase
{
public override string AgentName => "PoemAgent";
public override string AgentPrompt => "Compose a creative and whimsical poem about Semantic Kernel and its innovative approach to artificial intelligence.";
public override string DefaultDeploymentName => "o3-mini";

public PoemAgent(ChatReasoningEffortLevel reasoningEffort)
: base(reasoningEffort: reasoningEffort)
{
}
}

// A code example agent that asks for sample code demonstrating the use of Semantic Kernel.
public class CodeExampleAgent : AgentBase
{
public override string AgentName => "CodeExampleAgent";
public override string AgentPrompt => "Provide a sample C# code snippet that demonstrates how to integrate Semantic Kernel into a creative project, including explanations for each step.";
public override string DefaultDeploymentName => "o3-mini";

public CodeExampleAgent(ChatReasoningEffortLevel reasoningEffort)
: base(reasoningEffort: reasoningEffort)
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) Microsoft. All rights reserved.

namespace ReasoningEffortModels.Options;

/// <summary>
/// Configuration for Azure OpenAI service.
/// </summary>
public class AzureOpenAIOptions
{
public const string SectionName = "AzureOpenAI";

/// <summary>
/// Azure OpenAI deployment name, see https://learn.microsoft.com/azure/cognitive-services/openai/how-to/create-resource
/// </summary>
public string DeploymentName { get; set; }

/// <summary>
/// Azure OpenAI deployment URL, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
/// </summary>
public string Endpoint { get; set; }

/// <summary>
/// Azure OpenAI API key, see https://learn.microsoft.com/azure/cognitive-services/openai/quickstart
/// </summary>
public string ApiKey { get; set; }

public bool IsValid =>
!string.IsNullOrWhiteSpace(this.DeploymentName) &&
!string.IsNullOrWhiteSpace(this.Endpoint) &&
!string.IsNullOrWhiteSpace(this.ApiKey);
}
65 changes: 65 additions & 0 deletions dotnet/samples/Demos/ReasoningEffortModels/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright (c) Microsoft. All rights reserved.

using Microsoft.SemanticKernel.Agents;
using Microsoft.SemanticKernel.ChatCompletion;
using OpenAI.Chat;

#pragma warning disable SKEXP0110, SKEXP0001, SKEXP0050, CS8600, CS8604, SKEXP0010

namespace ReasoningEffortModels;

internal sealed class Program
{
// Entry point: creates and runs three agents with different reasoning efforts.
private static async Task Main(string[] args)
{
// Instantiate each agent with its designated reasoning effort
var smartBlogPostAgent = new SmartBlogPostAgent(ChatReasoningEffortLevel.Low);
var poemAgent = new PoemAgent(ChatReasoningEffortLevel.Medium);
var codeExampleAgent = new CodeExampleAgent(ChatReasoningEffortLevel.High);

// Demonstrate Smart Blog Post Agent (Low reasoning)
Console.WriteLine("=== Smart Blog Post Demo (Low Reasoning) ===");
var blogAgentInstance = smartBlogPostAgent.CreateAgent();
await InvokeAgentAsync(blogAgentInstance, "Please generate a detailed blog post about Semantic Kernel that covers its architecture, benefits, and real-world applications.");

// Demonstrate Poem Agent (Medium reasoning)
Console.WriteLine("\n=== Poem Demo (Medium Reasoning) ===");
var poemAgentInstance = poemAgent.CreateAgent();
await InvokeAgentAsync(poemAgentInstance, "Write a creative, whimsical poem about Semantic Kernel and its innovative approach to artificial intelligence.");

// Demonstrate Code Example Agent (High reasoning)
Console.WriteLine("\n=== Code Example Demo (High Reasoning) ===");
var codeAgentInstance = codeExampleAgent.CreateAgent();
await InvokeAgentAsync(codeAgentInstance, "Show me a sample C# code snippet that demonstrates how to integrate Semantic Kernel into a creative project, including explanations for each step.");

Console.WriteLine("Demo complete. Press any key to exit.");
Console.ReadKey();
}

/// <summary>
/// Helper method to invoke an agent with a given user prompt and print out the conversation.
/// </summary>
/// <param name="agent">The ChatCompletionAgent to invoke.</param>
/// <param name="ask">The user prompt to send to the agent.</param>
/// <returns>A Task representing the asynchronous operation.</returns>
private static async Task InvokeAgentAsync(ChatCompletionAgent agent, string ask)
{
// Create a new chat history instance.
ChatHistory chat = new();

// Add the user message (the ask).
Microsoft.SemanticKernel.ChatMessageContent userMessage = new(AuthorRole.User, ask);

chat.Add(userMessage);
Console.WriteLine($"> User: {ask}");

// Invoke the agent asynchronously. The agent's InvokeAsync returns an async stream of responses.
await foreach (var response in agent.InvokeAsync(chat))
{
// Add each agent response to the chat history and print it.
chat.Add(response);
Console.WriteLine($"> {response.Role}: {response.Content}");
}
}
}
68 changes: 68 additions & 0 deletions dotnet/samples/Demos/ReasoningEffortModels/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Reasoning Effort Models Demo

This demo application illustrates how to leverage different reasoning effort levels when using agentic AI with Semantic Kernel. The sample project showcases three distinct agents:

- **Smart Blog Post Agent (Low Reasoning):** Generates an analytical blog post on Semantic Kernel.
- **Poem Agent (Medium Reasoning):** Composes a creative poem about Semantic Kernel.
- **Code Example Agent (High Reasoning):** Provides a sample C# code snippet demonstrating integration with Semantic Kernel.

Each agent uses the `ChatCompletionAgent` with reasoning parameters configured via an extended `AgentBase` class.

## Prerequisites

- [.NET 8 SDK](https://dotnet.microsoft.com/download)
- Valid Azure OpenAI credentials (Deployment Name, Endpoint, and API Key)

## Configuration

The application loads its configuration from either **User Secrets** or **Environment Variables**.

### Using User Secrets (Secret Manager)

**Initialize User Secrets:**
Open a terminal in the project directory and run:
```bash
dotnet user-secrets init
```

Set Your Azure OpenAI Credentials:
Replace the placeholders with your actual credentials:

```
dotnet user-secrets set "AzureOpenAI:DeploymentName" "your-deployment-name"
dotnet user-secrets set "AzureOpenAI:Endpoint" "https://your-resource-name.openai.azure.com/"
dotnet user-secrets set "AzureOpenAI:ApiKey" "your-api-key"
```
Note: We are assigning the deployment in the Agent creation, so DeploymentName is not needed. If you add it, it will override the Agent Creation one (used in the AgentBase class constructor)

### Using environment variables

Use these names:

```
# OpenAI
OpenAI__ApiKey

# Azure OpenAI
AzureOpenAI__DeploymentName
AzureOpenAI__Endpoint
AzureOpenAI__ApiKey
```

## Running the Application
1. Set as Default Startup Project:
- In Visual Studio, right-click the ReasoningEffortModels project in the Solution Explorer and select "Set as Startup Project."
2. Build and Run:
- Via Visual Studio: Press F5 or click the "Start" button.
- Via Command Line: Navigate to the project folder and run:
```
dotnet build
dotnet run
```
The console will display the output for each of the three agents, demonstrating how different reasoning effort levels affect the responses.

## Project Structure
- AgentBase.cs: Contains the abstract base class that sets up the Semantic Kernel and configures reasoning effort.
- SmartBlogPostAgent.cs, PoemAgent.cs, CodeExampleAgent.cs: Implementations of agents with different reasoning settings.
- Program.cs: The demo entry point that instantiates each agent and invokes them with sample prompts.
- Options/AzureOpenAIOptions.cs: Holds the configuration options for connecting to Azure OpenAI.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);VSTHRD111,CA2007,CS8618,CS1591,CA1052,SKEXP0001,SKEXP0010,SKEXP0110,SKEXP0050,CS8600,CS8604</NoWarn>
<UserSecretsId>5ee045b0-aea3-4f08-8d31-32d1a6f8fed0</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
<ProjectReference Include="..\..\..\src\Agents\Abstractions\Agents.Abstractions.csproj" />
<ProjectReference Include="..\..\..\src\Agents\AzureAI\Agents.AzureAI.csproj" />
<ProjectReference Include="..\..\..\src\Agents\Core\Agents.Core.csproj" />
<ProjectReference Include="..\..\..\src\Connectors\Connectors.AzureOpenAI\Connectors.AzureOpenAI.csproj" />
<ProjectReference Include="..\..\..\src\SemanticKernel.Core\SemanticKernel.Core.csproj" />
</ItemGroup>

</Project>
Loading