From 014cb45fdcd8a758eceb00f1fc9918dc4b6c9835 Mon Sep 17 00:00:00 2001 From: samrusani <14844597+samrusani@users.noreply.github.com> Date: Fri, 14 Aug 2026 13:06:23 +0200 Subject: [PATCH] fix: remove fictional skill resource examples --- .../Skills/AgentSkillsProvider.cs | 5 ++- .../AgentSkills/AgentSkillsProviderTests.cs | 39 +++++++++++++++++++ .../packages/core/agent_framework/_skills.py | 5 ++- .../packages/core/tests/core/test_skills.py | 26 +++++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs index 38e8015c1c..784f329297 100644 --- a/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI/Skills/AgentSkillsProvider.cs @@ -158,8 +158,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. - - 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 `read_skill_resource` only for a resource explicitly referenced by the loaded skill. + - Pass its resource path exactly as written in that skill. + - Never infer or guess resource paths. - Use `run_skill_script` to run referenced scripts, using the name exactly as listed. Only load what is needed, when it is needed. """; diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs index df64c81ada..d90c1b6209 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentSkills/AgentSkillsProviderTests.cs @@ -79,6 +79,26 @@ public async Task InvokingCoreAsync_WithSkills_AppendsInstructionsAndToolsAsync( Assert.Contains("run_skill_script", toolNames); } + [Fact] + public async Task InvokingCoreAsync_DefaultPromptUsesRuleOnlyResourceGuidanceAsync() + { + // Arrange + var skill = new AgentInlineSkill("resource-guidance", "Resource guidance test", "Body."); + var provider = new AgentSkillsProvider(skill); + var invokingContext = new AIContextProvider.InvokingContext(this._agent, session: null, new AIContext()); + + // Act + var result = await provider.InvokingAsync(invokingContext, CancellationToken.None); + + // Assert + Assert.NotNull(result.Instructions); + Assert.Contains("only for a resource explicitly referenced by the loaded skill", result.Instructions); + Assert.Contains("Pass its resource path exactly as written in that skill", result.Instructions); + Assert.Contains("Never infer or guess resource paths", result.Instructions); + Assert.DoesNotContain("style-guide", result.Instructions); + Assert.DoesNotContain("references/FAQ.md", result.Instructions); + } + [Fact] public async Task InvokingCoreAsync_NullInputInstructions_SetsInstructionsAsync() { @@ -439,6 +459,25 @@ public async Task LoadSkill_DefaultOptions_ReturnsFullContentAsync() Assert.Contains("Skill body.", text); } + [Fact] + public async Task LoadSkill_ListsExplicitResourcePathExactlyAsync() + { + // Arrange + var skill = new AgentInlineSkill("resource-skill", "Resource path test", "Consult the explicitly referenced guide."); + skill.AddResource("references/actual-guide.md", "Guide content"); + var provider = new AgentSkillsProvider(skill); + var invokingContext = new AIContextProvider.InvokingContext(this._agent, session: null, new AIContext()); + var result = await provider.InvokingAsync(invokingContext, CancellationToken.None); + var loadSkillTool = result.Tools!.First(t => t.Name == "load_skill") as AIFunction; + + // Act + var content = await loadSkillTool!.InvokeAsync( + new AIFunctionArguments(new Dictionary { ["skillName"] = "resource-skill" })); + + // Assert + Assert.Contains("", content!.ToString()); + } + [Fact] public async Task LoadSkill_EmptySkillName_ReturnsErrorAsync() { diff --git a/python/packages/core/agent_framework/_skills.py b/python/packages/core/agent_framework/_skills.py index dda42b9a19..d3fb181b26 100644 --- a/python/packages/core/agent_framework/_skills.py +++ b/python/packages/core/agent_framework/_skills.py @@ -1809,8 +1809,9 @@ def _indent_width(s: str) -> int: Only load what is needed, when it is needed.""" RESOURCE_INSTRUCTIONS: Final[str] = ( - "- Use `read_skill_resource` to read any referenced resources, using the name exactly as listed\n" - ' (e.g. `"style-guide"` not `"style-guide.md"`, `"references/FAQ.md"` not `"FAQ.md"`).\n' + "- Use `read_skill_resource` only for a resource explicitly referenced by the loaded skill.\n" + "- Pass its resource path exactly as written in that skill.\n" + "- Never infer or guess resource paths.\n" ) SCRIPT_RUNNER_INSTRUCTIONS: Final[str] = ( diff --git a/python/packages/core/tests/core/test_skills.py b/python/packages/core/tests/core/test_skills.py index fab0a95daf..6af6b68b51 100644 --- a/python/packages/core/tests/core/test_skills.py +++ b/python/packages/core/tests/core/test_skills.py @@ -676,6 +676,20 @@ def test_default_prompt_contains_skills(self) -> None: assert "Does stuff." in prompt assert "load_skill" in prompt + def test_default_prompt_uses_rule_only_resource_guidance(self) -> None: + skills = [ + InlineSkill(frontmatter=SkillFrontmatter(name="my-skill", description="Does stuff."), instructions="Body"), + ] + + prompt = SkillsProvider._create_instructions(None, skills) + + assert prompt is not None + assert "only for a resource explicitly referenced by the loaded skill" in prompt + assert "Pass its resource path exactly as written in that skill" in prompt + assert "Never infer or guess resource paths" in prompt + assert "style-guide" not in prompt + assert "references/FAQ.md" not in prompt + def test_skills_sorted_alphabetically(self) -> None: skills = [ InlineSkill(frontmatter=SkillFrontmatter(name="zebra", description="Z skill."), instructions="Body"), @@ -806,6 +820,18 @@ async def test_load_skill_preserves_file_skill_content(self, tmp_path: Path) -> result = await provider._load_skill(_raw_skills(provider), "my-skill") assert "See [doc](references/FAQ.md)." in result + async def test_load_skill_lists_explicit_resource_path_exactly(self) -> None: + skill = InlineSkill( + frontmatter=SkillFrontmatter(name="my-skill", description="Skill."), + instructions="Consult the explicitly referenced guide.", + resources=[InlineSkillResource(name="references/actual-guide.md", content="Guide content")], + ) + provider = SkillsProvider([skill]) + + result = await provider._load_skill([skill], "my-skill") + + assert '' in result + async def test_load_skill_unknown_returns_error(self, tmp_path: Path) -> None: provider = SkillsProvider.from_paths(str(tmp_path)) await _init_provider(provider)