Skip to content

Commit

Permalink
Add chat example using SK and KM
Browse files Browse the repository at this point in the history
  • Loading branch information
dluc committed May 24, 2024
1 parent 7388c0a commit a461885
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 0 deletions.
6 changes: 6 additions & 0 deletions KernelMemory.sln
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Anthropic", "extensions\Ant
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "110-dotnet-anthropic", "examples\110-dotnet-anthropic\110-dotnet-anthropic.csproj", "{EE0D8645-2770-4E12-8E18-019B30970FE6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "302-dotnet-sk-km-chat", "examples\302-dotnet-sk-km-chat\302-dotnet-sk-km-chat.csproj", "{37FA99CB-AD22-4BAC-B76F-961F84422DEE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -523,6 +525,9 @@ Global
{EE0D8645-2770-4E12-8E18-019B30970FE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EE0D8645-2770-4E12-8E18-019B30970FE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EE0D8645-2770-4E12-8E18-019B30970FE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37FA99CB-AD22-4BAC-B76F-961F84422DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37FA99CB-AD22-4BAC-B76F-961F84422DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37FA99CB-AD22-4BAC-B76F-961F84422DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -607,6 +612,7 @@ Global
{432AC1B4-8275-4284-9A44-44988A6F0C24} = {DBEA0A6B-474A-4E8C-BCC8-D5D43C063A54}
{A0C81A29-715F-463E-A243-7E45DB8AE53F} = {155DA079-E267-49AF-973A-D1D44681970F}
{EE0D8645-2770-4E12-8E18-019B30970FE6} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
{37FA99CB-AD22-4BAC-B76F-961F84422DEE} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CC136C62-115C-41D1-B414-F9473EFF6EA8}
Expand Down
17 changes: 17 additions & 0 deletions examples/302-dotnet-sk-km-chat/302-dotnet-sk-km-chat.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.SemanticKernel" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\service\Core\Core.csproj" />
</ItemGroup>

</Project>
138 changes: 138 additions & 0 deletions examples/302-dotnet-sk-km-chat/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// Copyright (c) Microsoft. All rights reserved.

using System.Security.Cryptography;
using System.Text;
using Microsoft.KernelMemory;
using Microsoft.KernelMemory.DocumentStorage.DevTools;
using Microsoft.KernelMemory.MemoryStorage.DevTools;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.ChatCompletion;

internal sealed class Program
{
private static readonly List<string> s_documentation =
[
"https://raw.githubusercontent.com/microsoft/kernel-memory/main/README.md",
"https://microsoft.github.io/kernel-memory/quickstart",
"https://microsoft.github.io/kernel-memory/quickstart/configuration",
"https://microsoft.github.io/kernel-memory/quickstart/start-service",
"https://microsoft.github.io/kernel-memory/quickstart/python",
"https://microsoft.github.io/kernel-memory/quickstart/csharp",
"https://microsoft.github.io/kernel-memory/quickstart/java",
"https://microsoft.github.io/kernel-memory/quickstart/javascript",
"https://microsoft.github.io/kernel-memory/quickstart/bash",
"https://microsoft.github.io/kernel-memory/service",
"https://microsoft.github.io/kernel-memory/service/architecture",
"https://microsoft.github.io/kernel-memory/serverless",
"https://microsoft.github.io/kernel-memory/security/filters",
"https://microsoft.github.io/kernel-memory/how-to/custom-partitioning",
"https://microsoft.github.io/kernel-memory/concepts/indexes",
"https://microsoft.github.io/kernel-memory/concepts/document",
"https://microsoft.github.io/kernel-memory/concepts/memory",
"https://microsoft.github.io/kernel-memory/concepts/tag",
"https://microsoft.github.io/kernel-memory/concepts/llm",
"https://microsoft.github.io/kernel-memory/concepts/embedding",
"https://microsoft.github.io/kernel-memory/concepts/cosine-similarity",
"https://microsoft.github.io/kernel-memory/faq",
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/README.md",
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/dotnet/README.md",
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/python/README.md",
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/java/README.md",
"https://learn.microsoft.com/en-us/semantic-kernel/overview/",
"https://learn.microsoft.com/en-us/semantic-kernel/get-started/quick-start-guide",
"https://learn.microsoft.com/en-us/semantic-kernel/agents/",
];

internal static async Task Main()
{
var openAIApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new ConfigurationException("OPENAI_APIKEY env var not found");

Kernel kernel = Kernel.CreateBuilder()
.AddOpenAIChatCompletion(modelId: "gpt-4", apiKey: openAIApiKey)
.Build();

// Memory instance with persistent storage on disk
IKernelMemory memory = new KernelMemoryBuilder()
.WithOpenAIDefaults(openAIApiKey)
.WithSimpleVectorDb(SimpleVectorDbConfig.Persistent)
.WithSimpleFileStorage(SimpleFileStorageConfig.Persistent)
.Build<MemoryServerless>();

// Memorize some data
Console.WriteLine("# Saving documentation into kernel memory...");
await MemorizeDocuments(memory, s_documentation);

// Infinite chat loop
Console.WriteLine("# Starting chat...");
var chatService = kernel.GetRequiredService<IChatCompletionService>();
await ChatLoop(chatService, memory);
}

private static async Task ChatLoop(IChatCompletionService chatService, IKernelMemory memory)
{
// Chat setup
var systemPrompt = """
You are a helpful assistant replying to user questions using information from your memory.
The topic of the conversation is Kernel Memory (KM) and Semantic Kernel (SK).
Sometimes you don't have relevant memories so you reply saying you don't know, don't have the information.
""";

var chatHistory = new ChatHistory(systemPrompt);

// Start the chat
var assistantMessage = "Hello, how can I help?";
Console.WriteLine($"Copilot> {assistantMessage}\n");
chatHistory.AddAssistantMessage(assistantMessage);

// Infinite chat loop
var reply = new StringBuilder();
while (true)
{
// Get user message (retry if the user enters an empty string)
Console.Write("You> ");
var userMessage = Console.ReadLine()?.Trim();
if (string.IsNullOrWhiteSpace(userMessage)) { continue; }
else { chatHistory.AddUserMessage(userMessage); }

// Recall relevant information from memory
MemoryAnswer recall = await memory.AskAsync(userMessage);
Console.WriteLine("--- recall from memory ---");
Console.WriteLine(recall.Result.Trim());
Console.WriteLine("--------------------------");

// Inject the memory recall in the initial system message
chatHistory[0].Content = $"{systemPrompt}\n\nLong term memory:\n{recall.Result}";

// Generate the next chat message, stream the response
Console.Write("\nCopilot> ");
reply.Clear();
await foreach (StreamingChatMessageContent stream in chatService.GetStreamingChatMessageContentsAsync(chatHistory))
{
Console.Write(stream.Content);
reply.Append(stream.Content);
}

chatHistory.AddAssistantMessage(reply.ToString());
Console.WriteLine("\n");
}
}

private static async Task MemorizeDocuments(IKernelMemory memory, List<string> pages)
{
await memory.ImportTextAsync("We can talk about Semantic Kernel and Kernel Memory, you can ask any questions, I will try to reply using information from KM public documentation in Github", documentId: "help");
foreach (var url in pages)
{
var id = GetUrlId(url);
// Check if the page is already in memory, to avoid importing twice
if (!await memory.IsDocumentReadyAsync(id))
{
await memory.ImportWebPageAsync(url, documentId: GetUrlId(url));
}
}
}

private static string GetUrlId(string url)
{
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(url))).ToUpperInvariant();
}
}
2 changes: 2 additions & 0 deletions examples/302-dotnet-sk-km-chat/Properties/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
launchSettings.json
!launchSettings.json.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"profiles": {
"example302": {
"commandName": "Project",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development",
"OPENAI_APIKEY": ""
}
}
}
}

0 comments on commit a461885

Please sign in to comment.