diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs index 6cea629664..1954260598 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 e49c620187..9fdde1c701 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 8ef84905c6..8d4578633a 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 85335256a7..334c7826ec 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 6577bea86c..661b0c77fa 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]