diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx index 630afbd6a5..60890b5afa 100644 --- a/dotnet/agent-framework-dotnet.slnx +++ b/dotnet/agent-framework-dotnet.slnx @@ -131,6 +131,7 @@ + diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj new file mode 100644 index 0000000000..09359c5e78 --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Agent_Anthropic_Step04_UsingSkills.csproj @@ -0,0 +1,15 @@ + + + + Exe + net10.0 + + enable + enable + + + + + + + diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs new file mode 100644 index 0000000000..f67c25214f --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/Program.cs @@ -0,0 +1,127 @@ +// Copyright (c) Microsoft. All rights reserved. + +// This sample demonstrates how to use Anthropic-managed Skills with an AI agent. +// Skills are pre-built capabilities provided by Anthropic that can be used with the Claude API. +// This sample shows how to: +// 1. List available Anthropic-managed skills +// 2. Use the pptx skill to create PowerPoint presentations +// 3. Download and save generated files + +using Anthropic; +using Anthropic.Core; +using Anthropic.Models.Beta; +using Anthropic.Models.Beta.Files; +using Anthropic.Models.Beta.Messages; +using Anthropic.Models.Beta.Skills; +using Anthropic.Services; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; + +string apiKey = Environment.GetEnvironmentVariable("ANTHROPIC_API_KEY") ?? throw new InvalidOperationException("ANTHROPIC_API_KEY is not set."); +// Skills require Claude 4.5 models (Sonnet 4.5, Haiku 4.5, or Opus 4.5) +string model = Environment.GetEnvironmentVariable("ANTHROPIC_MODEL") ?? "claude-sonnet-4-5-20250929"; + +// Create the Anthropic client +AnthropicClient anthropicClient = new() { ApiKey = apiKey }; + +// List available Anthropic-managed skills (optional - API may not be available in all regions) +Console.WriteLine("Available Anthropic-managed skills:"); +try +{ + SkillListPage skills = await anthropicClient.Beta.Skills.List( + new SkillListParams { Source = "anthropic", Betas = [AnthropicBeta.Skills2025_10_02] }); + + foreach (var skill in skills.Items) + { + Console.WriteLine($" {skill.Source}: {skill.ID} (version: {skill.LatestVersion})"); + } +} +catch (Exception ex) +{ + Console.WriteLine($" (Skills listing not available: {ex.Message})"); +} + +Console.WriteLine(); + +// Define the pptx skill - the SDK handles all beta flags and container configuration automatically +// when using AsAITool(), so no manual RawRepresentationFactory configuration is needed. +BetaSkillParams pptxSkill = new() +{ + Type = BetaSkillParamsType.Anthropic, + SkillID = "pptx", + Version = "latest" +}; + +// Create an agent with the pptx skill enabled. +// Skills require extended thinking and higher max tokens for complex file generation. +// The SDK's AsAITool() handles beta flags and container config automatically. +ChatClientAgent agent = anthropicClient.Beta.AsAIAgent( + model: model, + instructions: "You are a helpful agent for creating PowerPoint presentations.", + tools: [pptxSkill.AsAITool()], + clientFactory: (chatClient) => chatClient + .AsBuilder() + .ConfigureOptions(options => + { + options.RawRepresentationFactory = (_) => new MessageCreateParams() + { + Model = model, + MaxTokens = 20000, + Messages = [], + Thinking = new BetaThinkingConfigParam( + new BetaThinkingConfigEnabled(budgetTokens: 10000)) + }; + }) + .Build()); + +Console.WriteLine("Creating a presentation about renewable energy...\n"); + +// Run the agent with a request to create a presentation +AgentResponse response = await agent.RunAsync("Create a simple 3-slide presentation about renewable energy sources. Include a title slide, a slide about solar energy, and a slide about wind energy."); + +Console.WriteLine("#### Agent Response ####"); +Console.WriteLine(response.Text); + +// Display any reasoning/thinking content +List reasoningContents = response.Messages.SelectMany(m => m.Contents.OfType()).ToList(); +if (reasoningContents.Count > 0) +{ + Console.WriteLine("\n#### Agent Reasoning ####"); + Console.WriteLine($"\e[92m{string.Join("\n", reasoningContents.Select(c => c.Text))}\e[0m"); +} + +// Collect generated files from CodeInterpreterToolResultContent outputs +List hostedFiles = response.Messages + .SelectMany(m => m.Contents.OfType()) + .Where(c => c.Outputs is not null) + .SelectMany(c => c.Outputs!.OfType()) + .ToList(); + +if (hostedFiles.Count > 0) +{ + Console.WriteLine("\n#### Generated Files ####"); + foreach (HostedFileContent file in hostedFiles) + { + Console.WriteLine($" FileId: {file.FileId}"); + + // Download the file using the Anthropic Files API + using HttpResponse fileResponse = await anthropicClient.Beta.Files.Download( + file.FileId, + new FileDownloadParams { Betas = ["files-api-2025-04-14"] }); + + // Save the file to disk + string fileName = $"presentation_{file.FileId.Substring(0, 8)}.pptx"; + using FileStream fileStream = File.Create(fileName); + Stream contentStream = await fileResponse.ReadAsStream(); + await contentStream.CopyToAsync(fileStream); + + Console.WriteLine($" Saved to: {fileName}"); + } +} + +Console.WriteLine("\nToken usage:"); +Console.WriteLine($"Input: {response.Usage?.InputTokenCount}, Output: {response.Usage?.OutputTokenCount}"); +if (response.Usage?.AdditionalCounts is not null) +{ + Console.WriteLine($"Additional: {string.Join(", ", response.Usage.AdditionalCounts)}"); +} diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md new file mode 100644 index 0000000000..f94b16cac9 --- /dev/null +++ b/dotnet/samples/GettingStarted/AgentWithAnthropic/Agent_Anthropic_Step04_UsingSkills/README.md @@ -0,0 +1,119 @@ +# Using Anthropic Skills with agents + +This sample demonstrates how to use Anthropic-managed Skills with AI agents. Skills are pre-built capabilities provided by Anthropic that can be used with the Claude API. + +## What this sample demonstrates + +- Listing available Anthropic-managed skills +- Creating an AI agent with Anthropic Claude Skills support using the simplified `AsAITool()` approach +- Using the pptx skill to create PowerPoint presentations +- Downloading and saving generated files to disk +- Handling agent responses with generated content + +## Prerequisites + +Before you begin, ensure you have the following prerequisites: + +- .NET 10.0 SDK or later +- Anthropic API key configured +- Access to Anthropic Claude models with Skills support + +**Note**: This sample uses Anthropic Claude models with Skills. Skills are a beta feature. For more information, see [Anthropic documentation](https://docs.anthropic.com/). + +Set the following environment variables: + +```powershell +$env:ANTHROPIC_API_KEY="your-anthropic-api-key" # Replace with your Anthropic API key +$env:ANTHROPIC_MODEL="your-anthropic-model" # Replace with your Anthropic model (e.g., claude-sonnet-4-5-20250929) +``` + +## Run the sample + +Navigate to the AgentWithAnthropic sample directory and run: + +```powershell +cd dotnet\samples\GettingStarted\AgentWithAnthropic +dotnet run --project .\Agent_Anthropic_Step04_UsingSkills +``` + +## Available Anthropic Skills + +Anthropic provides several managed skills that can be used with the Claude API: + +- `pptx` - Create PowerPoint presentations +- `xlsx` - Create Excel spreadsheets +- `docx` - Create Word documents +- `pdf` - Create and analyze PDF documents + +You can list available skills using the Anthropic SDK: + +```csharp +SkillListPage skills = await anthropicClient.Beta.Skills.List( + new SkillListParams { Source = "anthropic", Betas = [AnthropicBeta.Skills2025_10_02] }); + +foreach (var skill in skills.Items) +{ + Console.WriteLine($"{skill.Source}: {skill.ID} (version: {skill.LatestVersion})"); +} +``` + +## Expected behavior + +The sample will: + +1. List all available Anthropic-managed skills +2. Create an agent with the pptx skill enabled +3. Run the agent with a request to create a presentation +4. Display the agent's response text +5. Download any generated files and save them to disk +6. Display token usage statistics + +## Code highlights + +### Simplified skill configuration + +The Anthropic SDK handles all beta flags and container configuration automatically when using `AsAITool()`: + +```csharp +// Define the pptx skill +BetaSkillParams pptxSkill = new() +{ + Type = BetaSkillParamsType.Anthropic, + SkillID = "pptx", + Version = "latest" +}; + +// Create an agent - the SDK handles beta flags automatically! +ChatClientAgent agent = anthropicClient.Beta.AsAIAgent( + model: model, + instructions: "You are a helpful agent for creating PowerPoint presentations.", + tools: [pptxSkill.AsAITool()]); +``` + +**Note**: No manual `RawRepresentationFactory`, `Betas`, or `Container` configuration is needed. The SDK automatically adds the required beta headers (`skills-2025-10-02`, `code-execution-2025-08-25`) and configures the container with the skill. + +### Handling generated files + +Generated files are returned as `HostedFileContent` within `CodeInterpreterToolResultContent`: + +```csharp +// Collect generated files from response +List hostedFiles = response.Messages + .SelectMany(m => m.Contents.OfType()) + .Where(c => c.Outputs is not null) + .SelectMany(c => c.Outputs!.OfType()) + .ToList(); + +// Download and save each file +foreach (HostedFileContent file in hostedFiles) +{ + using HttpResponse fileResponse = await anthropicClient.Beta.Files.Download( + file.FileId, + new FileDownloadParams { Betas = ["files-api-2025-04-14"] }); + + string fileName = $"presentation_{file.FileId.Substring(0, 8)}.pptx"; + await using FileStream fileStream = File.Create(fileName); + Stream contentStream = await fileResponse.ReadAsStream(); + await contentStream.CopyToAsync(fileStream); +} +``` diff --git a/dotnet/samples/GettingStarted/AgentWithAnthropic/README.md b/dotnet/samples/GettingStarted/AgentWithAnthropic/README.md index 44c15b384b..345c25142f 100644 --- a/dotnet/samples/GettingStarted/AgentWithAnthropic/README.md +++ b/dotnet/samples/GettingStarted/AgentWithAnthropic/README.md @@ -29,6 +29,7 @@ To use Anthropic with Azure Foundry, you can check the sample [AgentProviders/Ag |[Running a simple agent](./Agent_Anthropic_Step01_Running/)|This sample demonstrates how to create and run a basic agent with Anthropic Claude| |[Using reasoning with an agent](./Agent_Anthropic_Step02_Reasoning/)|This sample demonstrates how to use extended thinking/reasoning capabilities with Anthropic Claude agents| |[Using function tools with an agent](./Agent_Anthropic_Step03_UsingFunctionTools/)|This sample demonstrates how to use function tools with an Anthropic Claude agent| +|[Using Skills with an agent](./Agent_Anthropic_Step04_UsingSkills/)|This sample demonstrates how to use Anthropic-managed Skills (e.g., pptx) with an Anthropic Claude agent| ## Running the samples from the console diff --git a/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs new file mode 100644 index 0000000000..a6a96bd234 --- /dev/null +++ b/dotnet/tests/AnthropicChatCompletion.IntegrationTests/AnthropicSkillsIntegrationTests.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft. All rights reserved. + +using System.Threading.Tasks; +using AgentConformance.IntegrationTests.Support; +using Anthropic; +using Anthropic.Models.Beta; +using Anthropic.Models.Beta.Messages; +using Anthropic.Models.Beta.Skills; +using Anthropic.Services; +using Microsoft.Agents.AI; +using Microsoft.Extensions.AI; +using Shared.IntegrationTests; + +namespace AnthropicChatCompletion.IntegrationTests; + +/// +/// Integration tests for Anthropic Skills functionality. +/// These tests are designed to be run locally with a valid Anthropic API key. +/// +public sealed class AnthropicSkillsIntegrationTests +{ + // All tests for Anthropic are intended to be ran locally as the CI pipeline for Anthropic is not setup. + private const string SkipReason = "Integrations tests for local execution only"; + + private static readonly AnthropicConfiguration s_config = TestConfiguration.LoadSection(); + + [Fact(Skip = SkipReason)] + public async Task CreateAgentWithPptxSkillAsync() + { + // Arrange + AnthropicClient anthropicClient = new() { ApiKey = s_config.ApiKey }; + string model = s_config.ChatModelId; + + BetaSkillParams pptxSkill = new() + { + Type = BetaSkillParamsType.Anthropic, + SkillID = "pptx", + Version = "latest" + }; + + ChatClientAgent agent = anthropicClient.Beta.AsAIAgent( + model: model, + instructions: "You are a helpful agent for creating PowerPoint presentations.", + tools: [pptxSkill.AsAITool()]); + + // Act + AgentResponse response = await agent.RunAsync( + "Create a simple 2-slide presentation: a title slide and one content slide about AI."); + + // Assert + Assert.NotNull(response); + Assert.NotNull(response.Text); + Assert.NotEmpty(response.Text); + } + + [Fact(Skip = SkipReason)] + public async Task ListAnthropicManagedSkillsAsync() + { + // Arrange + AnthropicClient anthropicClient = new() { ApiKey = s_config.ApiKey }; + + // Act + SkillListPage skills = await anthropicClient.Beta.Skills.List( + new SkillListParams { Source = "anthropic", Betas = [AnthropicBeta.Skills2025_10_02] }); + + // Assert + Assert.NotNull(skills); + Assert.NotNull(skills.Items); + Assert.Contains(skills.Items, skill => skill.ID == "pptx"); + } +}