Skip to content

Commit

Permalink
ADR for SK prompt syntax for chat completion roles (#3277)
Browse files Browse the repository at this point in the history
### Motivation and Context

<!-- Thank you for your contribution to the semantic-kernel repo!
Please help reviewers and future users, providing the following
information:
  1. Why is this change required?
  2. What problem does it solve?
  3. What scenario does it contribute to?
  4. If it fixes an open issue, please link to the issue here.
-->

### Description

<!-- Describe your changes, the overall approach, the underlying design.
These notes will help understanding how your code works. Thanks! -->

### Contribution Checklist

<!-- Before submitting this PR, please make sure: -->

- [ ] The code builds clean without any errors or warnings
- [ ] The PR follows the [SK Contribution
Guidelines](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md)
and the [pre-submission formatting
script](https://github.com/microsoft/semantic-kernel/blob/main/CONTRIBUTING.md#development-scripts)
raises no violations
- [ ] All unit tests pass, and I have added new tests where possible
- [ ] I didn't break anyone 😄
  • Loading branch information
SergeyMenshykh authored Oct 25, 2023
1 parent ab35598 commit bcbdadd
Showing 1 changed file with 145 additions and 0 deletions.
145 changes: 145 additions & 0 deletions docs/decisions/0014-chat-completion-roles-in-prompt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
---
# These are optional elements. Feel free to remove any of them.
status: proposed
date: 2023-10-23
deciders: markwallace, mabolan
consulted:
informed:
---
# SK prompt syntax for chat completion roles

## Context and Problem Statement
Today, SK does not have the ability to mark a block of text in a prompt as a message with a specific role, such as assistant, system, or user. As a result, SK can't chunk the prompt into the list of messages required by chat completion connectors.

Additionally, prompts can be defined using a range of template syntaxes supported by various template engines, such as Handlebars, Jinja, and others. Each of these syntaxes may represent chat messages or roles in a distinct way. Consequently, the template engine syntax may leak into SK's domain if no proper abstraction is put in place, coupling SK with the template engines and making it impossible to support new ones.

<!-- This is an optional element. Feel free to remove. -->
## Decision Drivers
* It should be possible to mark a block of text in a prompt as a message with a role so that it can be converted into a list of chat messages for use by chat completion connectors.
* The syntax specific to the template engine message/role should be mapped to the SK message/role syntax to abstract SK from a specific template engine syntax.

## Considered Options
**1. Message/role tags are generated by functions specified in a prompt.** This option relies on the fact that many template engines can invoke functions specified in the template. Therefore, an internal function can be registered with a template engine, and the function will create a message/model tag based on the provided arguments. The prompt template engine will execute the function and emit the function result into the prompt template, and the rendered prompt will have a section for each message/role decorated with these tags. Here's an example of how this can be done using the SK basic template engine and Handlebars:

Function:
```csharp
internal class SystemFunctions
{
public string Message(string role)
{
return $"<message role=\"{role}\">";
}
}
```

Prompt:

```bash
{{message role="system"}}
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
{{message role="system"}}

{{message role="user"}}
I want to {{$input}}
{{message role="user"}}
```

Rendered prompt:

```xml
<message role="system">
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
</message>
<message role="user">
I want to buy a house.
</message>
```

**2. Message/role tags are generated by a prompt-specific mechanism.** This option utilizes template engine syntax constructions, helpers, and handlers other than functions to inject SK message/role tags into the final prompt.
In the example below, to parse the prompt that uses the handlebars syntax we need to register a block helper (a callback that is invoked when the Handlebars engine encounters it) to emit the SK message/role tags in the resulting prompt.

Block helpers:
```csharp
this.handlebarsEngine.RegisterHelper("system", (EncodedTextWriter output, Context context, Arguments arguments) => {
//Emit the <message role="system"> tags
});
this.handlebarsEngine.RegisterHelper("user", (EncodedTextWriter output, Context context, Arguments arguments) => {
//Emit the <message role="user"> tags
});
```

Prompt:
```bash
{{#system~}}
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
{{~/system}}
{{#user~}}
I want to {{$input}}
{{~/user}}
```

Rendered prompt:
```xml
<message role="system">
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
</message>
<message role="user">
I want to buy a house.
</message>
```

**3. Message/role tags are applied on top of prompt template engine**. This option presumes specifying the SK message/role tags directly in a prompt to denote message/role blocks in way that template engine does not parse/handle them and considers them as a regular text.
In the example below, the prompt the `<message role="*">` tags are marking boundaries of the system and user messages and SK basic template engine consider them as regular text without processing them.

Prompt:
```xml
<message role="system">
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
</message>
<message role="user">
I want to {{$input}}
</message>
```

Rendered prompt:
```xml
<message role="system">
You are a bank manager. Be helpful, respectful, appreciate diverse language styles.
</message>
<message role="user">
I want to buy a house.
</message>
```

## Pros and Cons
**1. Message/role tags are generated by functions specified in a prompt**

Pros:
* Functions can be defined once and reused in prompt templates that support function calling.

Cons:
* Functions might not be supported by some template engines.
* The system/internal functions should be pre-registered by SK so users don't need to import them.
* Each prompt template engine will have how to discover and call the system/internal functions.

**2. Message/role tags are generated by prompt specific mechanism**

Pros:
* Enables message/role representation with the optimal template engine syntax constructions, aligning with other constructions for that specific engine.

Cons:
* Each prompt template engine will have to register callbacks/handlers to handle template syntax constructions rendering to emit SK message/role tags.

**3. Message/role tags are applied on top of prompt template engine**

Pros:
* No changes are required to prompt template engines.

Cons:
* The message/role tag syntax may not align with other syntax constructions for that template engine.
* Syntax errors in message/role tags will be detected by components parsing the prompt and not by prompt template engines.

## Decision Outcome
It was agreed not to limit ourselves to only one possible option because it may not be feasible to apply that option to new template engines we might need to support in the future. Instead, each time a new template engine is added, every option should be considered, and the optimal one should be preferred for that particular template engine.

It was also agreed that, at the moment, we will go with the "3. Message/role tags are applied on top of the prompt template engine" option to support the message/role prompt syntax in SK, which currently uses the `BasicPromptTemplateEngine` engine.

0 comments on commit bcbdadd

Please sign in to comment.