From da8dfeb5f25e7ab0c62cc2a38e551c5266648ba9 Mon Sep 17 00:00:00 2001 From: SergeyMenshykh Date: Wed, 24 Jun 2026 12:54:17 +0100 Subject: [PATCH] Remove {resource_instructions} and {script_instructions} placeholder mechanism Embed resource and script instruction text directly in the default prompt template instead of using placeholder substitution. Custom templates now only need the {skills} placeholder. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Skills/AgentSkillsProvider.cs | 39 ++-------------- .../Skills/AgentSkillsProviderBuilder.cs | 4 +- .../Skills/AgentSkillsProviderOptions.cs | 4 +- .../AgentSkillsProviderBuilderTests.cs | 2 +- .../AgentSkills/AgentSkillsProviderTests.cs | 46 ++++++++----------- 5 files changed, 24 insertions(+), 71 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs index 6cea6296646..19542605984 100644 --- a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs @@ -40,16 +40,6 @@ public sealed partial class AgentSkillsProvider : AIContextProvider /// private const string SkillsPlaceholder = "{skills}"; - /// - /// Placeholder token for the script instructions in the prompt template. - /// - private const string ScriptInstructionsPlaceholder = "{script_instructions}"; - - /// - /// Placeholder token for the resource instructions in the prompt template. - /// - private const string ResourceInstructionsPlaceholder = "{resource_instructions}"; - private const string DefaultSkillsInstructionPrompt = """ You have access to skills containing domain-specific knowledge and capabilities. @@ -62,8 +52,9 @@ You have access to skills containing domain-specific knowledge and capabilities. When a task aligns with a skill's domain, follow these steps in exact order: - Use `load_skill` to retrieve the skill's instructions. - Follow the provided guidance. - {resource_instructions} - {script_instructions} + - Use `read_skill_resource` to read any referenced resources, using the name exactly as listed + (e.g. `"style-guide"` not `"style-guide.md"`, `"references/FAQ.md"` not `"FAQ.md"`). + - Use `run_skill_script` to run referenced scripts, using the name exactly as listed. Only load what is needed, when it is needed. """; @@ -258,18 +249,8 @@ private IList BuildTools(IList skills) sb.AppendLine(" "); } - const string ResourceInstruction = - """ - - Use `read_skill_resource` to read any referenced resources, using the name exactly as listed - (e.g. `"style-guide"` not `"style-guide.md"`, `"references/FAQ.md"` not `"FAQ.md"`). - """; - - const string ScriptInstruction = "- Use `run_skill_script` to run referenced scripts, using the name exactly as listed."; - return new StringBuilder(promptTemplate) .Replace(SkillsPlaceholder, sb.ToString().TrimEnd()) - .Replace(ResourceInstructionsPlaceholder, ResourceInstruction) - .Replace(ScriptInstructionsPlaceholder, ScriptInstruction) .ToString(); } @@ -378,20 +359,6 @@ private static void ValidatePromptTemplate(string template, string paramName) $"The custom prompt template must contain the '{SkillsPlaceholder}' placeholder for the generated skills list.", paramName); } - - if (template.IndexOf(ResourceInstructionsPlaceholder, StringComparison.Ordinal) < 0) - { - throw new ArgumentException( - $"The custom prompt template must contain the '{ResourceInstructionsPlaceholder}' placeholder for resource instructions.", - paramName); - } - - if (template.IndexOf(ScriptInstructionsPlaceholder, StringComparison.Ordinal) < 0) - { - throw new ArgumentException( - $"The custom prompt template must contain the '{ScriptInstructionsPlaceholder}' placeholder for script instructions.", - paramName); - } } [LoggerMessage(LogLevel.Information, "Loading skill: {SkillName}")] diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs index e49c620187a..9fdde1c7017 100644 --- a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs +++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderBuilder.cs @@ -134,9 +134,7 @@ public AgentSkillsProviderBuilder UseSource(AgentSkillsSource source) /// /// Sets a custom system prompt template. /// - /// The prompt template with {skills} placeholder for the skills list, - /// {resource_instructions} for optional resource instructions, - /// and {script_instructions} for optional script instructions. + /// The prompt template with {skills} placeholder for the skills list. /// This builder instance for chaining. public AgentSkillsProviderBuilder UsePromptTemplate(string promptTemplate) { diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderOptions.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderOptions.cs index 8ef84905c60..8d4578633a6 100644 --- a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProviderOptions.cs @@ -13,9 +13,7 @@ public sealed class AgentSkillsProviderOptions { /// /// Gets or sets a custom system prompt template for advertising skills. - /// The template must contain {skills} as the placeholder for the generated skills list, - /// {resource_instructions} for resource instructions, - /// and {script_instructions} for script instructions. + /// The template must contain {skills} as the placeholder for the generated skills list. /// When , a default template is used. /// public string? SkillsInstructionPrompt { get; set; } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderBuilderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderBuilderTests.cs index 85335256a70..334c7826ec6 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderBuilderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderBuilderTests.cs @@ -159,7 +159,7 @@ public void Build_FluentChaining_ReturnsSameBuilder() var result = builder .UseSource(source) .UseScriptApproval(false) - .UsePromptTemplate("Skills:\n{skills}\n{resource_instructions}\n{script_instructions}"); + .UsePromptTemplate("Skills:\n{skills}"); // Assert Assert.Same(builder, result); diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs index 6577bea86c4..661b0c77fad 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs @@ -100,7 +100,7 @@ public async Task InvokingCoreAsync_CustomPromptTemplate_UsesCustomTemplateAsync this.CreateSkill("custom-prompt-skill", "Custom prompt", "Body."); var options = new AgentSkillsProviderOptions { - SkillsInstructionPrompt = "Custom template: {skills}\n{resource_instructions}\n{script_instructions}" + SkillsInstructionPrompt = "Custom template: {skills}" }; var provider = new AgentSkillsProvider(new AgentFileSkillsSource(this._testRoot, s_noOpExecutor), options); var inputContext = new AIContext(); @@ -122,7 +122,7 @@ public void Constructor_PromptWithoutSkillsPlaceholder_ThrowsArgumentException() // Arrange var options = new AgentSkillsProviderOptions { - SkillsInstructionPrompt = "No skills placeholder here {resource_instructions} {script_instructions}" + SkillsInstructionPrompt = "No skills placeholder here" }; // Act & Assert @@ -133,28 +133,12 @@ public void Constructor_PromptWithoutSkillsPlaceholder_ThrowsArgumentException() } [Fact] - public void Constructor_PromptWithoutRunnerInstructionsPlaceholder_ThrowsArgumentException() + public void Constructor_PromptWithOnlySkillsPlaceholder_Succeeds() { // Arrange var options = new AgentSkillsProviderOptions { - SkillsInstructionPrompt = "Has skills {skills} but no runner instructions {resource_instructions}" - }; - - // Act & Assert - var ex = Assert.Throws(() => - new AgentSkillsProvider(new AgentFileSkillsSource(this._testRoot, s_noOpExecutor), options)); - Assert.Contains("{script_instructions}", ex.Message); - Assert.Equal("options", ex.ParamName); - } - - [Fact] - public void Constructor_PromptWithBothPlaceholders_Succeeds() - { - // Arrange - var options = new AgentSkillsProviderOptions - { - SkillsInstructionPrompt = "Skills: {skills}\nResources: {resource_instructions}\nRunner: {script_instructions}" + SkillsInstructionPrompt = "Skills: {skills}" }; // Act — should not throw @@ -165,19 +149,25 @@ public void Constructor_PromptWithBothPlaceholders_Succeeds() } [Fact] - public void Constructor_PromptWithoutResourceInstructionsPlaceholder_ThrowsArgumentException() + public async Task InvokingCoreAsync_CustomTemplateWithLegacyPlaceholders_RendersThemLiterallyAsync() { - // Arrange + // Arrange — template contains legacy placeholder tokens that are no longer substituted + this.CreateSkill("literal-test-skill", "Literal test", "Body."); var options = new AgentSkillsProviderOptions { - SkillsInstructionPrompt = "Has skills {skills} and runner {script_instructions} but no resource instructions" + SkillsInstructionPrompt = "Skills: {skills}\nRes: {resource_instructions}\nScript: {script_instructions}" }; + var provider = new AgentSkillsProvider(new AgentFileSkillsSource(this._testRoot, s_noOpExecutor), options); + var inputContext = new AIContext(); + var invokingContext = new AIContextProvider.InvokingContext(this._agent, session: null, inputContext); - // Act & Assert - var ex = Assert.Throws(() => - new AgentSkillsProvider(new AgentFileSkillsSource(this._testRoot, s_noOpExecutor), options)); - Assert.Contains("{resource_instructions}", ex.Message); - Assert.Equal("options", ex.ParamName); + // Act + var result = await provider.InvokingAsync(invokingContext, CancellationToken.None); + + // Assert — legacy tokens render literally, not substituted + Assert.NotNull(result.Instructions); + Assert.Contains("{resource_instructions}", result.Instructions); + Assert.Contains("{script_instructions}", result.Instructions); } [Fact]