Skip to content

Commit f431685

Browse files
authored
.Net Agents - Update Templating Pattern (microsoft#10633)
### 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. --> Support processing of templatized instructions for an existing agent. ### Description <!-- Describe your changes, the overall approach, the underlying design. These notes will help understanding how your code works. Thanks! --> An assistant or azure agent may be created with templatized instructions. This change allows the caller to specify the appropriate template factory and format specifier. ### Contribution Checklist <!-- Before submitting this PR, please make sure: --> - [X] The code builds clean without any errors or warnings - [X] 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 - [X] All unit tests pass, and I have added new tests where possible - [X] I didn't break anyone 😄
1 parent ef56875 commit f431685

File tree

7 files changed

+63
-37
lines changed

7 files changed

+63
-37
lines changed

dotnet/samples/Concepts/Agents/OpenAIAssistant_Templating.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ Always state the requested style of the poem.
8686

8787
private async Task InvokeAssistantAgentWithTemplateAsync(
8888
string instructionTemplate,
89-
string? templateFormat = null,
90-
IPromptTemplateFactory? templateFactory = null)
89+
string templateFormat,
90+
IPromptTemplateFactory templateFactory)
9191
{
9292
PromptTemplateConfig config = new()
9393
{
@@ -103,7 +103,7 @@ await this.AssistantClient.CreateAssistantFromTemplateAsync(
103103
metadata: SampleMetadata);
104104

105105
// Create the agent
106-
OpenAIAssistantAgent agent = new(assistant, this.AssistantClient, plugins: null, config, templateFactory)
106+
OpenAIAssistantAgent agent = new(assistant, this.AssistantClient, plugins: null, templateFactory, templateFormat)
107107
{
108108
Arguments =
109109
{

dotnet/samples/GettingStartedWithAgents/AzureAIAgent/Step01_AzureAIAgent.cs

+8-5
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@ public async Task UseTemplateForAzureAgentAsync()
1919
// Define the agent
2020
string generateStoryYaml = EmbeddedResource.Read("GenerateStory.yaml");
2121
PromptTemplateConfig templateConfig = KernelFunctionYaml.ToPromptTemplateConfig(generateStoryYaml);
22-
22+
// Instructions, Name and Description properties defined via the PromptTemplateConfig.
2323
Agent definition = await this.AgentsClient.CreateAgentAsync("gpt-4o", templateConfig.Name, templateConfig.Description, templateConfig.Template);
24-
// Instructions, Name and Description properties defined via the config.
25-
AzureAIAgent agent = new(definition, this.AgentsClient)
24+
AzureAIAgent agent = new(
25+
definition,
26+
this.AgentsClient,
27+
templateFactory: new KernelPromptTemplateFactory(),
28+
templateFormat: PromptTemplateConfig.SemanticKernelTemplateFormat)
2629
{
2730
Arguments =
2831
{
2932
{ "topic", "Dog" },
30-
{ "length", "3" },
31-
},
33+
{ "length", "3" }
34+
}
3235
};
3336

3437
// Create a thread for the agent conversation.

dotnet/samples/GettingStartedWithAgents/OpenAIAssistant/Step01_Assistant.cs

+8-5
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,19 @@ public async Task UseTemplateForAssistantAgentAsync()
1717
// Define the agent
1818
string generateStoryYaml = EmbeddedResource.Read("GenerateStory.yaml");
1919
PromptTemplateConfig templateConfig = KernelFunctionYaml.ToPromptTemplateConfig(generateStoryYaml);
20-
21-
// Instructions, Name and Description properties defined via the config.
20+
// Instructions, Name and Description properties defined via the PromptTemplateConfig.
2221
Assistant definition = await this.AssistantClient.CreateAssistantFromTemplateAsync(this.Model, templateConfig, metadata: SampleMetadata);
23-
OpenAIAssistantAgent agent = new(definition, this.AssistantClient)
22+
OpenAIAssistantAgent agent = new(
23+
definition,
24+
this.AssistantClient,
25+
templateFactory: new KernelPromptTemplateFactory(),
26+
templateFormat: PromptTemplateConfig.SemanticKernelTemplateFormat)
2427
{
2528
Arguments =
2629
{
2730
{ "topic", "Dog" },
28-
{ "length", "3" },
29-
},
31+
{ "length", "3" }
32+
}
3033
};
3134

3235
// Create a thread for the agent conversation.

dotnet/src/Agents/Abstractions/KernelAgent.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,14 @@ public abstract class KernelAgent : Agent
5050
/// <returns>The formatted system instructions for the agent.</returns>
5151
protected async Task<string?> FormatInstructionsAsync(Kernel kernel, KernelArguments? arguments, CancellationToken cancellationToken)
5252
{
53-
// Use the provided template as the instructions
54-
if (this.Template is not null)
53+
if (this.Template is null)
5554
{
56-
return await this.Template.RenderAsync(kernel, arguments, cancellationToken).ConfigureAwait(false);
55+
// Use the instructions as-is
56+
return this.Instructions;
5757
}
5858

59-
// Use the instructions as-is
60-
return this.Instructions;
59+
// Use the provided template as the instructions
60+
return await this.Template.RenderAsync(kernel, arguments, cancellationToken).ConfigureAwait(false);
6161
}
6262

6363
/// <summary>

dotnet/src/Agents/AzureAI/AzureAIAgent.cs

+21-8
Original file line numberDiff line numberDiff line change
@@ -54,25 +54,38 @@ public static class Tools
5454
/// </summary>
5555
/// <param name="model">The agent model definition.</param>
5656
/// <param name="client">An <see cref="AgentsClient"/> instance.</param>
57-
/// <param name="templateConfig">The prompt template configuration.</param>
58-
/// <param name="templateFactory">An optional template factory.</param>
57+
/// <param name="plugins">Optional collection of plugins to add to the kernel.</param>
58+
/// <param name="templateFactory">An optional factory to produce the <see cref="IPromptTemplate"/> for the agent.</param>
59+
/// <param name="templateFormat">The format of the prompt template used when "templateFactory" parameter is supplied.</param>
5960
public AzureAIAgent(
6061
Azure.AI.Projects.Agent model,
6162
AgentsClient client,
62-
PromptTemplateConfig? templateConfig = null,
63-
IPromptTemplateFactory? templateFactory = null)
63+
IEnumerable<KernelPlugin>? plugins = null,
64+
IPromptTemplateFactory? templateFactory = null,
65+
string? templateFormat = null)
6466
{
6567
this.Client = client;
6668
this.Definition = model;
6769
this.Description = this.Definition.Description;
6870
this.Id = this.Definition.Id;
6971
this.Name = this.Definition.Name;
70-
this.Instructions = templateConfig?.Template ?? this.Definition.Instructions;
72+
this.Instructions = this.Definition.Instructions;
7173

72-
if (templateConfig is not null)
74+
if (templateFactory != null)
7375
{
74-
this.Template = templateFactory?.Create(templateConfig)
75-
?? throw new KernelException($"Invalid prompt template factory {templateFactory} for format {templateConfig.TemplateFormat}");
76+
Verify.NotNullOrWhiteSpace(templateFormat);
77+
78+
PromptTemplateConfig templateConfig = new(this.Instructions)
79+
{
80+
TemplateFormat = templateFormat
81+
};
82+
83+
this.Template = templateFactory.Create(templateConfig);
84+
}
85+
86+
if (plugins != null)
87+
{
88+
this.Kernel.Plugins.AddRange(plugins);
7689
}
7790
}
7891

dotnet/src/Agents/Core/ChatCompletionAgent.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Copyright (c) Microsoft. All rights reserved.
22

3+
using System;
34
using System.Collections.Generic;
45
using System.Runtime.CompilerServices;
56
using System.Text;
@@ -62,7 +63,7 @@ public override IAsyncEnumerable<ChatMessageContent> InvokeAsync(
6263
Kernel? kernel = null,
6364
CancellationToken cancellationToken = default)
6465
{
65-
var agentName = this.GetDisplayName();
66+
string agentName = this.GetDisplayName();
6667

6768
return ActivityExtensions.RunWithActivityAsync(
6869
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, agentName, this.Description),
@@ -77,7 +78,7 @@ public override IAsyncEnumerable<StreamingChatMessageContent> InvokeStreamingAsy
7778
Kernel? kernel = null,
7879
CancellationToken cancellationToken = default)
7980
{
80-
var agentName = this.GetDisplayName();
81+
string agentName = this.GetDisplayName();
8182

8283
return ActivityExtensions.RunWithActivityAsync(
8384
() => ModelDiagnostics.StartAgentInvocationActivity(this.Id, agentName, this.Description),
@@ -143,7 +144,7 @@ private async IAsyncEnumerable<ChatMessageContent> InternalInvokeAsync(
143144

144145
int messageCount = chat.Count;
145146

146-
var serviceType = chatCompletionService.GetType();
147+
Type serviceType = chatCompletionService.GetType();
147148

148149
this.Logger.LogAgentChatServiceInvokingAgent(nameof(InvokeAsync), this.Id, agentName, serviceType);
149150

@@ -190,7 +191,7 @@ private async IAsyncEnumerable<StreamingChatMessageContent> InternalInvokeStream
190191

191192
int messageCount = chat.Count;
192193

193-
var serviceType = chatCompletionService.GetType();
194+
Type serviceType = chatCompletionService.GetType();
194195

195196
this.Logger.LogAgentChatServiceInvokingAgent(nameof(InvokeAsync), this.Id, agentName, serviceType);
196197

dotnet/src/Agents/OpenAI/OpenAIAssistantAgent.cs

+13-7
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ public sealed partial class OpenAIAssistantAgent : KernelAgent
3535
/// <param name="definition">The assistant definition.</param>
3636
/// <param name="client">The OpenAI provider for accessing the Assistant API service.</param>
3737
/// <param name="plugins">Optional collection of plugins to add to the kernel.</param>
38-
/// <param name="templateConfig">The prompt template configuration.</param>
3938
/// <param name="templateFactory">An optional factory to produce the <see cref="IPromptTemplate"/> for the agent.</param>
39+
/// <param name="templateFormat">The format of the prompt template used when "templateFactory" parameter is supplied.</param>
4040
public OpenAIAssistantAgent(
4141
Assistant definition,
4242
AssistantClient client,
4343
IEnumerable<KernelPlugin>? plugins = null,
44-
PromptTemplateConfig? templateConfig = null,
45-
IPromptTemplateFactory? templateFactory = null)
44+
IPromptTemplateFactory? templateFactory = null,
45+
string? templateFormat = null)
4646
{
4747
this.Client = client;
4848

@@ -51,12 +51,18 @@ public OpenAIAssistantAgent(
5151
this.Description = this.Definition.Description;
5252
this.Id = this.Definition.Id;
5353
this.Name = this.Definition.Name;
54-
this.Instructions = templateConfig?.Template ?? this.Definition.Instructions;
54+
this.Instructions = this.Definition.Instructions;
5555

56-
if (templateConfig is not null)
56+
if (templateFactory != null)
5757
{
58-
this.Template = templateFactory?.Create(templateConfig)
59-
?? throw new KernelException($"Invalid prompt template factory {templateFactory} for format {templateConfig.TemplateFormat}");
58+
Verify.NotNullOrWhiteSpace(templateFormat);
59+
60+
PromptTemplateConfig templateConfig = new(this.Instructions)
61+
{
62+
TemplateFormat = templateFormat
63+
};
64+
65+
this.Template = templateFactory.Create(templateConfig);
6066
}
6167

6268
if (plugins != null)

0 commit comments

Comments
 (0)