Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Build AI-enabled .NET applications with Semantic Kernel using services, plugins, prompts, and function-calling patterns that remain testable and maintainable. USE FOR: adding AI-driven prompts, plugins, or orchestration to a .NET app; reviewing kernel construction, service registration, or plugin usage; building function-calling. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant
.claude/skills/managedcode-semantic-kernel/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-09 | ✗→✓ | ▲ Improved | 44% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 81% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 35% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 29% | 0% |
| case-15 | ✓→✓ | = Same ✓ | 94% | 0% |
| Concept | Description | |---------|-------------| | Kernel | Central orchestrator for AI services and plugins | | Plugin | Collection of functions exposed to the LLM | | Function | Native C# method or prompt template | | Chat Completion | LLM service for generating responses | | Memory | Vector storage for semantic search |
dotnet-1.79.0 and later, keep OpenAPI plugin server URL validation enabled, do not re-enable automatic redirects on the default HttpPlugin or WebFileDownloadPlugin clients without an explicit trusted-host policy, and use the current Microsoft Agent Framework-compatible migration samples when moving SK agent code to Agent Framework.1.79.0. The release fixes the Cosmos vector-store path, rejects mixed-separator UNC paths, URL-encodes OpenAPI server variables, adds Ollama Think, and allows deterministic TimePlugin tests through TimeProvider injection.2.0.0-beta.3 update in 1.79.0 as a breaking dependency change. Re-run prompt-template tests and remove security workarounds that are no longer needed after the vulnerable transitive version is gone.csharpvar builder = Kernel.CreateBuilder(); builder.AddAzureOpenAIChatCompletion( deploymentName: "gpt-4", endpoint: config["AzureOpenAI:Endpoint"]!, apiKey: config["AzureOpenAI:ApiKey"]!); // Or OpenAI builder.AddOpenAIChatCompletion( modelId: "gpt-4", apiKey: config["OpenAI:ApiKey"]!); var kernel = builder.Build();
csharpbuilder.Services.AddKernel() .AddAzureOpenAIChatCompletion( deploymentName: "gpt-4", endpoint: config["AzureOpenAI:Endpoint"]!, apiKey: config["AzureOpenAI:ApiKey"]!); // Register plugins builder.Services.AddSingleton<WeatherPlugin>(); builder.Services.AddSingleton<OrderPlugin>(); // In your service public class AiService(Kernel kernel) { public async Task<string> ChatAsync(string message) { var response = await kernel.InvokePromptAsync(message); return response.ToString(); } }
csharppublic class WeatherPlugin { [KernelFunction] [Description("Gets the current weather for a specified city")] public async Task<string> GetWeather( [Description("The city name, e.g., 'Seattle'")] string city, [Description("Temperature unit: 'celsius' or 'fahrenheit'")] string unit = "celsius") { // Call actual weather API var weather = await _weatherService.GetCurrentAsync(city); return $"Weather in {city}: {weather.Temperature}° {unit}, {weather.Condition}"; } [KernelFunction] [Description("Gets the weather forecast for the next N days")] public async Task<string> GetForecast( [Description("The city name")] string city, [Description("Number of days (1-7)")] int days = 3) { var forecast = await _weatherService.GetForecastAsync(city, days); return FormatForecast(forecast); } }
| Practice | Why It Matters | |----------|----------------| | Clear [Description] | LLM uses this to decide when to call | | Specific parameter names | Helps LLM map user intent | | Idempotent functions | Safe to retry on failures | | Return meaningful strings | LLM needs to understand results | | Validate inputs | LLM may hallucinate parameters |
csharpvar settings = new OpenAIPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }; kernel.Plugins.AddFromObject(new WeatherPlugin(), "Weather"); kernel.Plugins.AddFromObject(new OrderPlugin(), "Orders"); var result = await kernel.InvokePromptAsync( "What's the weather in Seattle and do I have any pending orders?", new KernelArguments(settings));
csharpvar settings = new OpenAIPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Required( [kernel.Plugins["Weather"]["GetWeather"]]) };
csharpvar chatService = kernel.GetRequiredService<IChatCompletionService>(); var history = new ChatHistory(); history.AddSystemMessage("You are a helpful assistant."); history.AddUserMessage(userMessage); var response = await chatService.GetChatMessageContentAsync( history, executionSettings: new OpenAIPromptExecutionSettings { FunctionChoiceBehavior = FunctionChoiceBehavior.Auto() }, kernel: kernel); history.AddAssistantMessage(response.Content!);
csharpawait foreach (var chunk in chatService.GetStreamingChatMessageContentsAsync( history, executionSettings, kernel)) { Console.Write(chunk.Content); }
csharp// WRONG - agents share plugins var sharedKernel = Kernel.CreateBuilder().Build(); sharedKernel.Plugins.AddFromObject(new AllPlugins()); var agent1 = new ChatCompletionAgent { Kernel = sharedKernel }; var agent2 = new ChatCompletionAgent { Kernel = sharedKernel }; // Both agents have same plugins! // CORRECT - isolated kernels var kernel1 = CreateKernelForAgent1(); kernel1.Plugins.AddFromObject(new WeatherPlugin()); var kernel2 = CreateKernelForAgent2(); kernel2.Plugins.AddFromObject(new OrderPlugin()); var agent1 = new ChatCompletionAgent { Kernel = kernel1 }; var agent2 = new ChatCompletionAgent { Kernel = kernel2 };
| Anti-Pattern | Why It's Bad | Better Approach | |--------------|--------------|-----------------| | Vague [Description] | LLM won't call at right time | Be specific and actionable | | Sharing kernel across agents | Plugin leakage | Clone or create new kernels | | No input validation | Hallucinated parameters | Validate and return errors | | Using deprecated Planners | Removed in favor of function calling | Use FunctionChoiceBehavior | | Ignoring logging | Can't debug AI decisions | Enable Semantic Kernel logging |
csharp[KernelFunction] [Description("Places an order for a product")] public async Task<string> PlaceOrder( [Description("Product ID")] string productId, [Description("Quantity (1-100)")] int quantity) { // Validate inputs if (string.IsNullOrEmpty(productId)) return "Error: Product ID is required"; if (quantity < 1 || quantity > 100) return "Error: Quantity must be between 1 and 100"; try { var order = await _orderService.CreateAsync(productId, quantity); return $"Order {order.Id} placed successfully for {quantity} units"; } catch (ProductNotFoundException) { return $"Error: Product '{productId}' not found"; } }
csharp[Fact] public async Task GetWeather_ReturnsFormattedWeather() { var mockWeatherService = new Mock<IWeatherService>(); mockWeatherService.Setup(w => w.GetCurrentAsync("Seattle")) .ReturnsAsync(new Weather { Temperature = 20, Condition = "Sunny" }); var plugin = new WeatherPlugin(mockWeatherService.Object); var result = await plugin.GetWeather("Seattle", "celsius"); Assert.Contains("20°", result); Assert.Contains("Sunny", result); }
For complex multi-agent scenarios, consider microsoft-agent-framework:
Other measured skills in the registry, with their headline benchmark lift.