Skip to content

Commit

Permalink
Copilot Chat: Use standard semantic skills to define chat prompts (mi…
Browse files Browse the repository at this point in the history
…crosoft#1853)

### Motivation and Context
Currently the prompts are made available inside of appsettings.json
which differs from the standard convention of shipping prompts in
skprompt.txt files. This PR updates the copilot chat to call Semantic
Skills that have been configured with config.json and skprompt.txt files
instead of inline prompts in code.

In response to:
[microsoft#1726](microsoft#1763)

### Contribution Checklist
<!-- Before submitting this PR, please make sure: -->
- [x] The code builds clean without any errors or warnings
- [x] The PR follows SK Contribution Guidelines
(https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
- [x] The code follows the .NET coding conventions
(https://learn.microsoft.com/dotnet/csharp/fundamentals/coding-style/coding-conventions)
verified with `dotnet format`
- [x] All unit tests pass, and I have added new tests where possible
- [x] I didn't break anyone 😄

---------

Co-authored-by: Gina Triolo <[email protected]>
Co-authored-by: Harleen Thind <[email protected]>
Co-authored-by: Ben Thomas <[email protected]>
  • Loading branch information
4 people authored Jul 17, 2023
1 parent fc86af0 commit 7d972b7
Show file tree
Hide file tree
Showing 23 changed files with 582 additions and 383 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ public static IKernel RegisterCopilotChatSkills(this IKernel kernel, IServicePro
chatSessionRepository: sp.GetRequiredService<ChatSessionRepository>(),
promptOptions: sp.GetRequiredService<IOptions<PromptsOptions>>(),
documentImportOptions: sp.GetRequiredService<IOptions<DocumentMemoryOptions>>(),
planner: sp.GetRequiredService<CopilotChatPlanner>(),
logger: sp.GetRequiredService<ILogger<ChatSkill>>()),
planner: sp.GetRequiredService<CopilotChatPlanner>()),
nameof(ChatSkill));

return kernel;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) Microsoft. All rights reserved.

using System;
using System.IO;
using Microsoft.Extensions.Logging;
using Microsoft.SemanticKernel.AI.TextCompletion;
using Microsoft.SemanticKernel.SemanticFunctions;
using SemanticKernel.Service.CopilotChat.Skills;

namespace SemanticKernel.Service.CopilotChat.Options;

/// <summary>
/// Options for prompts of semantic functions
/// </summary>
public class PluginPromptOptions
{
/// <summary>
/// Number of tokens used by the prompt.txt template
/// </summary>
public int PromptTokenCount { get; set; }

/// <summary>
/// Settings for the text completion request.
/// </summary>
public CompleteRequestSettings CompletionSettings { get; set; }

private readonly ILogger _logger;

public PluginPromptOptions(int promptTokenCount, CompleteRequestSettings completionSettings, ILogger logger)
{
this.PromptTokenCount = promptTokenCount;
this.CompletionSettings = completionSettings;
this._logger = logger;
}

public PluginPromptOptions(string promptTextPath, string configJsonPath, ILogger logger)
{
this._logger = logger;

if (!File.Exists(promptTextPath))
{
var exceptionMsg = $"{Constants.PromptFileName} file does not exist at " + nameof(promptTextPath);
throw new ArgumentException(exceptionMsg);
}

var promptText = File.ReadAllText(promptTextPath);
this.PromptTokenCount = Utilities.TokenCount(promptText);

if (File.Exists(configJsonPath))
{
try
{
var config = PromptTemplateConfig.FromJson(File.ReadAllText(configJsonPath));
this.CompletionSettings = CompleteRequestSettings.FromCompletionConfig(config.Completion);
}
catch (ArgumentException ex)
{
const string exceptionAdditionalInfoMsg = "Unable to parse the config file located at " + nameof(ex.ParamName);
this._logger.LogWarning(exceptionAdditionalInfoMsg);
throw ex;
}
}
else
{
var exceptionMsg = $"{Constants.ConfigFileName} file does not exist at " + nameof(configJsonPath);
throw new ArgumentException(exceptionMsg);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,14 @@ namespace SemanticKernel.Service.CopilotChat.Options;
/// </summary>
public class PromptsOptions
{
public const string PropertyName = "Prompts";
public const string PropertyName = "ReusablePromptVariables";

/// <summary>
/// Token limit of the chat model.
/// </summary>
/// <remarks>https://platform.openai.com/docs/models/overview for token limits.</remarks>
[Required, Range(0, int.MaxValue)] public int CompletionTokenLimit { get; set; }

/// <summary>
/// The token count left for the model to generate text after the prompt.
/// </summary>
[Required, Range(0, int.MaxValue)] public int ResponseTokenLimit { get; set; }

/// <summary>
/// Weight of memories in the contextual part of the final prompt.
/// Contextual prompt excludes all the system commands and user intent.
Expand Down Expand Up @@ -57,103 +52,14 @@ public class PromptsOptions
// System
[Required, NotEmptyOrWhitespace] public string KnowledgeCutoffDate { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string InitialBotMessage { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string SystemDescription { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string SystemResponse { get; set; } = string.Empty;

internal string[] SystemAudiencePromptComponents => new string[]
{
this.SystemAudience,
"{{ChatSkill.ExtractChatHistory}}",
this.SystemAudienceContinuation
};

internal string SystemAudienceExtraction => string.Join("\n", this.SystemAudiencePromptComponents);

internal string[] SystemIntentPromptComponents => new string[]
{
this.SystemDescription,
this.SystemIntent,
"{{ChatSkill.ExtractChatHistory}}",
this.SystemIntentContinuation
};

internal string SystemIntentExtraction => string.Join("\n", this.SystemIntentPromptComponents);

// Intent extraction
[Required, NotEmptyOrWhitespace] public string SystemIntent { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string SystemIntentContinuation { get; set; } = string.Empty;

// Audience extraction
[Required, NotEmptyOrWhitespace] public string SystemAudience { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string SystemAudienceContinuation { get; set; } = string.Empty;

// Memory extraction
[Required, NotEmptyOrWhitespace] public string SystemCognitive { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string MemoryFormat { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string MemoryAntiHallucination { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string MemoryContinuation { get; set; } = string.Empty;

// Long-term memory
[Required, NotEmptyOrWhitespace] public string LongTermMemoryName { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string LongTermMemoryExtraction { get; set; } = string.Empty;

internal string[] LongTermMemoryPromptComponents => new string[]
{
this.SystemCognitive,
$"{this.LongTermMemoryName} Description:\n{this.LongTermMemoryExtraction}",
this.MemoryAntiHallucination,
$"Chat Description:\n{this.SystemDescription}",
"{{ChatSkill.ExtractChatHistory}}",
this.MemoryContinuation
};

internal string LongTermMemory => string.Join("\n", this.LongTermMemoryPromptComponents);

// Working memory
[Required, NotEmptyOrWhitespace] public string WorkingMemoryName { get; set; } = string.Empty;
[Required, NotEmptyOrWhitespace] public string WorkingMemoryExtraction { get; set; } = string.Empty;

internal string[] WorkingMemoryPromptComponents => new string[]
{
this.SystemCognitive,
$"{this.WorkingMemoryName} Description:\n{this.WorkingMemoryExtraction}",
this.MemoryAntiHallucination,
$"Chat Description:\n{this.SystemDescription}",
"{{ChatSkill.ExtractChatHistory}}",
this.MemoryContinuation
};

internal string WorkingMemory => string.Join("\n", this.WorkingMemoryPromptComponents);

// Memory map
internal IDictionary<string, string> MemoryMap => new Dictionary<string, string>()
internal List<string> MemoryTypes => new()
{
{ this.LongTermMemoryName, this.LongTermMemory },
{ this.WorkingMemoryName, this.WorkingMemory }
"LongTermMemory",
"WorkingMemory"
};

// Chat commands
internal string SystemChatContinuation = "SINGLE RESPONSE FROM BOT TO USER:\n[{{TimeSkill.Now}} {{timeSkill.Second}}] bot:";

internal string[] SystemChatPromptComponents => new string[]
{
this.SystemDescription,
this.SystemResponse,
"{{$audience}}",
"{{$userIntent}}",
"{{$chatContext}}",
this.SystemChatContinuation
};

internal string SystemChatPrompt => string.Join("\n\n", this.SystemChatPromptComponents);

internal double ResponseTemperature { get; } = 0.7;
internal double ResponseTopP { get; } = 1;
internal double ResponsePresencePenalty { get; } = 0.5;
internal double ResponseFrequencyPenalty { get; } = 0.5;

internal double IntentTemperature { get; } = 0.7;
internal double IntentTopP { get; } = 1;
internal double IntentPresencePenalty { get; } = 0.5;
internal double IntentFrequencyPenalty { get; } = 0.5;
}
Loading

0 comments on commit 7d972b7

Please sign in to comment.