Browse Source

LLamaSharp.Examples: Document Q&A with local storage (#532)

* LLama.Examples: disable console logging

* LLama.Examples: rename titles to signal grouped topics

* LLama.Examples: add additional PDF for Q&A

* LLama.Examples: improve kernel memory demo

multi-document ingestion

* LLama.Examples: improve message before resetting to main menu

* LLama.Examples: document Q&A with local memory
tags/0.11.0
Scott W Harden GitHub 2 years ago
parent
commit
91ca9d2732
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
5 changed files with 273 additions and 54 deletions
  1. BIN
      LLama.Examples/Assets/sample-KM-Readme.pdf
  2. +25
    -22
      LLama.Examples/ExampleRunner.cs
  3. +84
    -32
      LLama.Examples/Examples/KernelMemory.cs
  4. +161
    -0
      LLama.Examples/Examples/KernelMemorySaveAndLoad.cs
  5. +3
    -0
      LLama.Examples/LLama.Examples.csproj

BIN
LLama.Examples/Assets/sample-KM-Readme.pdf View File


+ 25
- 22
LLama.Examples/ExampleRunner.cs View File

@@ -5,26 +5,27 @@ public class ExampleRunner
{
private static readonly Dictionary<string, Func<Task>> Examples = new()
{
{ "Run a chat session with history.", ChatSessionWithHistory.Run },
{ "Run a chat session without stripping the role names.", ChatSessionWithRoleName.Run },
{ "Run a chat session with the role names stripped.", ChatSessionStripRoleName.Run },
{ "Run a chat session in Chinese GB2312 encoding", ChatChineseGB2312.Run },
{ "Interactive mode chat by using executor.", InteractiveModeExecute.Run },
{ "Instruct mode chat by using executor.", InstructModeExecute.Run },
{ "Stateless mode chat by using executor.", StatelessModeExecute.Run },
{ "Load and save chat session.", SaveAndLoadSession.Run },
{ "Load and save state of model and executor.", LoadAndSaveState.Run },
{ "Get embeddings from LLama model.", () => Task.Run(GetEmbeddings.Run) },
{ "Quantize the model.", () => Task.Run(QuantizeModel.Run) },
{ "Automatic conversation.", TalkToYourself.Run },
{ "Constrain response to json format using grammar.", GrammarJsonResponse.Run },
{ "Semantic Kernel Prompt.", SemanticKernelPrompt.Run },
{ "Semantic Kernel Chat.", SemanticKernelChat.Run },
{ "Semantic Kernel Memory.", SemanticKernelMemory.Run },
{ "Coding Assistant.", CodingAssistant.Run },
{ "Batched Executor (Fork)", BatchedExecutorFork.Run },
{ "Batched Executor (Rewind)", BatchedExecutorRewind.Run },
{ "SK Kernel Memory.", KernelMemory.Run },
{ "Chat Session: History", ChatSessionWithHistory.Run },
{ "Chat Session: Role names", ChatSessionWithRoleName.Run },
{ "Chat Session: Role names stripped", ChatSessionStripRoleName.Run },
{ "Chat Session: Coding Assistant", CodingAssistant.Run },
{ "Chat Session: Automatic conversation", TalkToYourself.Run },
{ "Chat Session: Chinese characters", ChatChineseGB2312.Run },
{ "Executor: Interactive mode chat", InteractiveModeExecute.Run },
{ "Executor: Instruct mode chat", InstructModeExecute.Run },
{ "Executor: Stateless mode chat", StatelessModeExecute.Run },
{ "Save and Load: chat session", SaveAndLoadSession.Run },
{ "Save and Load: state of model and executor", LoadAndSaveState.Run },
{ "LLama Model: Get embeddings", () => Task.Run(GetEmbeddings.Run) },
{ "LLama Model: Quantize", () => Task.Run(QuantizeModel.Run) },
{ "Grammar: Constrain response to json format", GrammarJsonResponse.Run },
{ "Kernel Memory: Document Q&A", KernelMemory.Run },
{ "Kernel Memory: Save and Load", KernelMemorySaveAndLoad.Run },
{ "Semantic Kernel: Prompt", SemanticKernelPrompt.Run },
{ "Semantic Kernel: Chat", SemanticKernelChat.Run },
{ "Semantic Kernel: Store", SemanticKernelMemory.Run },
{ "Batched Executor: Fork", BatchedExecutorFork.Run },
{ "Batched Executor: Rewind", BatchedExecutorRewind.Run },
{ "Exit", () => { Environment.Exit(0); return Task.CompletedTask; } }
};

@@ -44,8 +45,10 @@ public class ExampleRunner
AnsiConsole.Write(new Rule(choice));
await example();
}
Console.WriteLine("Press any key to continue...");
Console.ReadKey();

Console.WriteLine("Press ENTER to go to the main menu...");
Console.ReadLine();

AnsiConsole.Clear();
}
}


+ 84
- 32
LLama.Examples/Examples/KernelMemory.cs View File

@@ -1,58 +1,110 @@
using LLamaSharp.KernelMemory;
using Microsoft.KernelMemory;
using Microsoft.KernelMemory.Configuration;
using System.Diagnostics;

namespace LLama.Examples.Examples
{
// This example is from Microsoft's official kernel memory "custom prompts" example:
// https://github.com/microsoft/kernel-memory/blob/6d516d70a23d50c6cb982e822e6a3a9b2e899cfa/examples/101-dotnet-custom-Prompts/Program.cs#L1-L86

// Microsoft.KernelMemory has more features than Microsoft.SemanticKernel.
// See https://microsoft.github.io/kernel-memory/ for details.

public class KernelMemory
{
public static async Task Run()
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(
"""

This program uses the Microsoft.KernelMemory package to ingest documents
and answer questions about them in an interactive chat prompt.

""");

// Setup the kernel memory with the LLM model
string modelPath = UserSettings.GetModelPath();
IKernelMemory memory = CreateMemory(modelPath);

Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("This example is from : \n" +
"https://github.com/microsoft/kernel-memory/blob/main/examples/101-using-core-nuget/Program.cs");
// Ingest documents (format is automatically detected from the filename)
string[] filesToIngest = [
Path.GetFullPath(@"./Assets/sample-SK-Readme.pdf"),
Path.GetFullPath(@"./Assets/sample-KM-Readme.pdf"),
];

var searchClientConfig = new SearchClientConfig
for (int i = 0; i < filesToIngest.Length; i++)
{
MaxMatchesCount = 1,
AnswerTokens = 100,
};
string path = filesToIngest[i];
Stopwatch sw = Stopwatch.StartNew();
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"Importing {i + 1} of {filesToIngest.Length}: {path}");
await memory.ImportDocumentAsync(path, steps: Constants.PipelineWithoutSummary);
Console.WriteLine($"Completed in {sw.Elapsed}\n");
}

var memory = new KernelMemoryBuilder()
.WithLLamaSharpDefaults(new LLamaSharpConfig(modelPath)
{
DefaultInferenceParams = new Common.InferenceParams
{
AntiPrompts = new List<string> { "\n\n" }
}
})
.WithSearchClientConfig(searchClientConfig)
.With(new TextPartitioningOptions
{
MaxTokensPerParagraph = 300,
MaxTokensPerLine = 100,
OverlappingTokens = 30
})
.Build();
// Ask a predefined question
Console.ForegroundColor = ConsoleColor.Green;
string question1 = "What formats does KM support";
Console.WriteLine($"Question: {question1}");
await AnswerQuestion(memory, question1);

// Let the user ask additional questions
while (true)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("Question: ");
string question = Console.ReadLine()!;
if (string.IsNullOrEmpty(question))
return;

await memory.ImportDocumentAsync(@"./Assets/sample-SK-Readme.pdf", steps: Constants.PipelineWithoutSummary);
await AnswerQuestion(memory, question);
}
}

private static IKernelMemory CreateMemory(string modelPath)
{
Common.InferenceParams infParams = new() { AntiPrompts = ["\n\n"] };

var question = "What's Semantic Kernel?";
LLamaSharpConfig lsConfig = new(modelPath) { DefaultInferenceParams = infParams };

SearchClientConfig searchClientConfig = new()
{
MaxMatchesCount = 1,
AnswerTokens = 100,
};

Console.WriteLine($"\n\nQuestion: {question}");
TextPartitioningOptions parseOptions = new()
{
MaxTokensPerParagraph = 300,
MaxTokensPerLine = 100,
OverlappingTokens = 30
};

var answer = await memory.AskAsync(question);
return new KernelMemoryBuilder()
.WithLLamaSharpDefaults(lsConfig)
.WithSearchClientConfig(searchClientConfig)
.With(parseOptions)
.Build();
}

Console.WriteLine($"\nAnswer: {answer.Result}");
private static async Task AnswerQuestion(IKernelMemory memory, string question)
{
Stopwatch sw = Stopwatch.StartNew();
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine($"Generating answer...");

Console.WriteLine("\n\n Sources:\n");
MemoryAnswer answer = await memory.AskAsync(question);
Console.WriteLine($"Answer generated in {sw.Elapsed}");

foreach (var x in answer.RelevantSources)
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine($"Answer: {answer.Result}");
foreach (var source in answer.RelevantSources)
{
Console.WriteLine($" - {x.SourceName} - {x.Link} [{x.Partitions.First().LastUpdate:D}]");
Console.WriteLine($"Source: {source.SourceName}");
}
Console.WriteLine();
}
}
}
}

+ 161
- 0
LLama.Examples/Examples/KernelMemorySaveAndLoad.cs View File

@@ -0,0 +1,161 @@
using LLamaSharp.KernelMemory;
using Microsoft.KernelMemory;
using Microsoft.KernelMemory.Configuration;
using Microsoft.KernelMemory.ContentStorage.DevTools;
using Microsoft.KernelMemory.FileSystem.DevTools;
using Microsoft.KernelMemory.MemoryStorage.DevTools;
using System.Diagnostics;

namespace LLama.Examples.Examples;

public class KernelMemorySaveAndLoad
{
static string StorageFolder => Path.GetFullPath($"./storage-{nameof(KernelMemorySaveAndLoad)}");
static bool StorageExists => Directory.Exists(StorageFolder) && Directory.GetDirectories(StorageFolder).Length > 0;

public static async Task Run()
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(
"""

This program uses the Microsoft.KernelMemory package to ingest documents
and store the embeddings as local files so they can be quickly recalled
when this application is launched again.

""");

string modelPath = UserSettings.GetModelPath();
IKernelMemory memory = CreateMemoryWithLocalStorage(modelPath);

Console.ForegroundColor = ConsoleColor.Yellow;
if (StorageExists)
{
Console.WriteLine(
"""
Kernel memory files have been located!
Information about previously analyzed documents has been loaded.

""");
}
else
{
Console.WriteLine(
$"""

Existing kernel memory was not found.
Documents will be analyzed (slow) and information saved to disk.
Analysis will not be required the next time this program is run.
Press ENTER to proceed...
""");
Console.ReadLine();
await IngestDocuments(memory);
}

await AskSingleQuestion(memory, "What formats does KM support?");
await StartUserChatSession(memory);
}

private static IKernelMemory CreateMemoryWithLocalStorage(string modelPath)
{
Common.InferenceParams infParams = new() { AntiPrompts = ["\n\n"] };

LLamaSharpConfig lsConfig = new(modelPath) { DefaultInferenceParams = infParams };

SearchClientConfig searchClientConfig = new()
{
MaxMatchesCount = 1,
AnswerTokens = 100,
};

TextPartitioningOptions parseOptions = new()
{
MaxTokensPerParagraph = 300,
MaxTokensPerLine = 100,
OverlappingTokens = 30
};

SimpleFileStorageConfig storageConfig = new()
{
Directory = StorageFolder,
StorageType = FileSystemTypes.Disk,
};

SimpleVectorDbConfig vectorDbConfig = new()
{
Directory = StorageFolder,
StorageType = FileSystemTypes.Disk,
};

Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"Kernel memory folder: {StorageFolder}");

Console.ForegroundColor = ConsoleColor.DarkGray;
return new KernelMemoryBuilder()
.WithSimpleFileStorage(storageConfig)
.WithSimpleVectorDb(vectorDbConfig)
.WithLLamaSharpDefaults(lsConfig)
.WithSearchClientConfig(searchClientConfig)
.With(parseOptions)
.Build();
}

private static async Task AskSingleQuestion(IKernelMemory memory, string question)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"Question: {question}");
await ShowAnswer(memory, question);
}

private static async Task StartUserChatSession(IKernelMemory memory)
{
while (true)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("Question: ");
string question = Console.ReadLine()!;
if (string.IsNullOrEmpty(question))
return;

await ShowAnswer(memory, question);
}
}

private static async Task IngestDocuments(IKernelMemory memory)
{
string[] filesToIngest = [
Path.GetFullPath(@"./Assets/sample-SK-Readme.pdf"),
Path.GetFullPath(@"./Assets/sample-KM-Readme.pdf"),
];

for (int i = 0; i < filesToIngest.Length; i++)
{
string path = filesToIngest[i];
Stopwatch sw = Stopwatch.StartNew();
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"Importing {i + 1} of {filesToIngest.Length}: {path}");
await memory.ImportDocumentAsync(path, steps: Constants.PipelineWithoutSummary);
Console.WriteLine($"Completed in {sw.Elapsed}\n");
}
}

private static async Task ShowAnswer(IKernelMemory memory, string question)
{
Stopwatch sw = Stopwatch.StartNew();
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine($"Generating answer...");

MemoryAnswer answer = await memory.AskAsync(question);
Console.WriteLine($"Answer generated in {sw.Elapsed}");

Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine($"Answer: {answer.Result}");
foreach (var source in answer.RelevantSources)
{
Console.WriteLine($"Source: {source.SourceName}");
}
Console.WriteLine();
}
}

+ 3
- 0
LLama.Examples/LLama.Examples.csproj View File

@@ -58,6 +58,9 @@
<None Update="Assets\reason-act.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Assets\sample-KM-Readme.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Assets\sample-SK-Readme.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>


Loading…
Cancel
Save