Skip to content

Commit a4584e9

Browse files
authored
Add example showing how to build chat with SK and KM (#513)
1 parent 7388c0a commit a4584e9

File tree

5 files changed

+175
-0
lines changed

5 files changed

+175
-0
lines changed

KernelMemory.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Anthropic", "extensions\Ant
279279
EndProject
280280
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "110-dotnet-anthropic", "examples\110-dotnet-anthropic\110-dotnet-anthropic.csproj", "{EE0D8645-2770-4E12-8E18-019B30970FE6}"
281281
EndProject
282+
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}"
283+
EndProject
282284
Global
283285
GlobalSection(SolutionConfigurationPlatforms) = preSolution
284286
Debug|Any CPU = Debug|Any CPU
@@ -523,6 +525,9 @@ Global
523525
{EE0D8645-2770-4E12-8E18-019B30970FE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
524526
{EE0D8645-2770-4E12-8E18-019B30970FE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
525527
{EE0D8645-2770-4E12-8E18-019B30970FE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
528+
{37FA99CB-AD22-4BAC-B76F-961F84422DEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
529+
{37FA99CB-AD22-4BAC-B76F-961F84422DEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
530+
{37FA99CB-AD22-4BAC-B76F-961F84422DEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
526531
EndGlobalSection
527532
GlobalSection(SolutionProperties) = preSolution
528533
HideSolutionNode = FALSE
@@ -607,6 +612,7 @@ Global
607612
{432AC1B4-8275-4284-9A44-44988A6F0C24} = {DBEA0A6B-474A-4E8C-BCC8-D5D43C063A54}
608613
{A0C81A29-715F-463E-A243-7E45DB8AE53F} = {155DA079-E267-49AF-973A-D1D44681970F}
609614
{EE0D8645-2770-4E12-8E18-019B30970FE6} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
615+
{37FA99CB-AD22-4BAC-B76F-961F84422DEE} = {0A43C65C-6007-4BB4-B3FE-8D439FC91841}
610616
EndGlobalSection
611617
GlobalSection(ExtensibilityGlobals) = postSolution
612618
SolutionGuid = {CC136C62-115C-41D1-B414-F9473EFF6EA8}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.SemanticKernel" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\..\service\Core\Core.csproj" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
3+
using System.Security.Cryptography;
4+
using System.Text;
5+
using Microsoft.KernelMemory;
6+
using Microsoft.KernelMemory.DocumentStorage.DevTools;
7+
using Microsoft.KernelMemory.MemoryStorage.DevTools;
8+
using Microsoft.SemanticKernel;
9+
using Microsoft.SemanticKernel.ChatCompletion;
10+
11+
internal sealed class Program
12+
{
13+
private static readonly List<string> s_documentation =
14+
[
15+
"https://raw.githubusercontent.com/microsoft/kernel-memory/main/README.md",
16+
"https://microsoft.github.io/kernel-memory/quickstart",
17+
"https://microsoft.github.io/kernel-memory/quickstart/configuration",
18+
"https://microsoft.github.io/kernel-memory/quickstart/start-service",
19+
"https://microsoft.github.io/kernel-memory/quickstart/python",
20+
"https://microsoft.github.io/kernel-memory/quickstart/csharp",
21+
"https://microsoft.github.io/kernel-memory/quickstart/java",
22+
"https://microsoft.github.io/kernel-memory/quickstart/javascript",
23+
"https://microsoft.github.io/kernel-memory/quickstart/bash",
24+
"https://microsoft.github.io/kernel-memory/service",
25+
"https://microsoft.github.io/kernel-memory/service/architecture",
26+
"https://microsoft.github.io/kernel-memory/serverless",
27+
"https://microsoft.github.io/kernel-memory/security/filters",
28+
"https://microsoft.github.io/kernel-memory/how-to/custom-partitioning",
29+
"https://microsoft.github.io/kernel-memory/concepts/indexes",
30+
"https://microsoft.github.io/kernel-memory/concepts/document",
31+
"https://microsoft.github.io/kernel-memory/concepts/memory",
32+
"https://microsoft.github.io/kernel-memory/concepts/tag",
33+
"https://microsoft.github.io/kernel-memory/concepts/llm",
34+
"https://microsoft.github.io/kernel-memory/concepts/embedding",
35+
"https://microsoft.github.io/kernel-memory/concepts/cosine-similarity",
36+
"https://microsoft.github.io/kernel-memory/faq",
37+
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/README.md",
38+
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/dotnet/README.md",
39+
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/python/README.md",
40+
"https://raw.githubusercontent.com/microsoft/semantic-kernel/main/java/README.md",
41+
"https://learn.microsoft.com/en-us/semantic-kernel/overview/",
42+
"https://learn.microsoft.com/en-us/semantic-kernel/get-started/quick-start-guide",
43+
"https://learn.microsoft.com/en-us/semantic-kernel/agents/",
44+
];
45+
46+
internal static async Task Main()
47+
{
48+
var openAIApiKey = Environment.GetEnvironmentVariable("OPENAI_APIKEY") ?? throw new ConfigurationException("OPENAI_APIKEY env var not found");
49+
50+
Kernel kernel = Kernel.CreateBuilder()
51+
.AddOpenAIChatCompletion(modelId: "gpt-4", apiKey: openAIApiKey)
52+
.Build();
53+
54+
// Memory instance with persistent storage on disk
55+
IKernelMemory memory = new KernelMemoryBuilder()
56+
.WithOpenAIDefaults(openAIApiKey)
57+
.WithSimpleVectorDb(SimpleVectorDbConfig.Persistent)
58+
.WithSimpleFileStorage(SimpleFileStorageConfig.Persistent)
59+
.Build<MemoryServerless>();
60+
61+
// Memorize some data
62+
Console.WriteLine("# Saving documentation into kernel memory...");
63+
await MemorizeDocuments(memory, s_documentation);
64+
65+
// Infinite chat loop
66+
Console.WriteLine("# Starting chat...");
67+
var chatService = kernel.GetRequiredService<IChatCompletionService>();
68+
await ChatLoop(chatService, memory);
69+
}
70+
71+
private static async Task ChatLoop(IChatCompletionService chatService, IKernelMemory memory)
72+
{
73+
// Chat setup
74+
var systemPrompt = """
75+
You are a helpful assistant replying to user questions using information from your memory.
76+
The topic of the conversation is Kernel Memory (KM) and Semantic Kernel (SK).
77+
Sometimes you don't have relevant memories so you reply saying you don't know, don't have the information.
78+
""";
79+
80+
var chatHistory = new ChatHistory(systemPrompt);
81+
82+
// Start the chat
83+
var assistantMessage = "Hello, how can I help?";
84+
Console.WriteLine($"Copilot> {assistantMessage}\n");
85+
chatHistory.AddAssistantMessage(assistantMessage);
86+
87+
// Infinite chat loop
88+
var reply = new StringBuilder();
89+
while (true)
90+
{
91+
// Get user message (retry if the user enters an empty string)
92+
Console.Write("You> ");
93+
var userMessage = Console.ReadLine()?.Trim();
94+
if (string.IsNullOrWhiteSpace(userMessage)) { continue; }
95+
else { chatHistory.AddUserMessage(userMessage); }
96+
97+
// Recall relevant information from memory
98+
MemoryAnswer recall = await memory.AskAsync(userMessage);
99+
Console.WriteLine("--- recall from memory ---");
100+
Console.WriteLine(recall.Result.Trim());
101+
Console.WriteLine("--------------------------");
102+
103+
// Inject the memory recall in the initial system message
104+
chatHistory[0].Content = $"{systemPrompt}\n\nLong term memory:\n{recall.Result}";
105+
106+
// Generate the next chat message, stream the response
107+
Console.Write("\nCopilot> ");
108+
reply.Clear();
109+
await foreach (StreamingChatMessageContent stream in chatService.GetStreamingChatMessageContentsAsync(chatHistory))
110+
{
111+
Console.Write(stream.Content);
112+
reply.Append(stream.Content);
113+
}
114+
115+
chatHistory.AddAssistantMessage(reply.ToString());
116+
Console.WriteLine("\n");
117+
}
118+
}
119+
120+
private static async Task MemorizeDocuments(IKernelMemory memory, List<string> pages)
121+
{
122+
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");
123+
foreach (var url in pages)
124+
{
125+
var id = GetUrlId(url);
126+
// Check if the page is already in memory, to avoid importing twice
127+
if (!await memory.IsDocumentReadyAsync(id))
128+
{
129+
await memory.ImportWebPageAsync(url, documentId: GetUrlId(url));
130+
}
131+
}
132+
}
133+
134+
private static string GetUrlId(string url)
135+
{
136+
return Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(url))).ToUpperInvariant();
137+
}
138+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
launchSettings.json
2+
!launchSettings.json.example
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"profiles": {
3+
"example302": {
4+
"commandName": "Project",
5+
"launchBrowser": false,
6+
"environmentVariables": {
7+
"ASPNETCORE_ENVIRONMENT": "Development",
8+
"OPENAI_APIKEY": ""
9+
}
10+
}
11+
}
12+
}

0 commit comments

Comments
 (0)