Semantic Kernel Plugins Created: 03 Feb 2026 Updated: 03 Feb 2026

Nesting Kernel Functions in Semantic Kernel

One of the most powerful features of Semantic Kernel is the ability to nest kernel functions - calling one function from within another. This technique allows developers to create complex workflows by breaking down tasks into smaller, reusable units.

In this article, we'll build a Smart Shopping Assistant that demonstrates function nesting through a practical e-commerce scenario.

What is Function Nesting?

Function nesting involves calling one kernel function from within another. This creates a hierarchical structure where:

  1. Higher-level functions orchestrate the workflow
  2. Lower-level functions perform specific tasks
  3. The Kernel is passed as a parameter to enable cross-plugin communication
+---------------------------+
| ShoppingAssistantPlugin | <-- Orchestrator (calls other plugins)
+---------------------------+
|
+---> PricingPlugin.CalculateDiscount()
|
+---> InventoryPlugin.CheckStock()
|
+---> PricingPlugin.CalculateFinalPrice()

Benefits of Function Nesting

Benefit Description
ModularityEach plugin handles a specific domain
ReusabilityFunctions can be used independently or composed
TestabilityIndividual plugins can be tested in isolation
MaintainabilityChanges in one plugin don't affect others
ClarityComplex logic is broken into understandable pieces

The Scenario: Smart Shopping Assistant

Our shopping assistant will:

  1. Check product inventory
  2. Determine customer discount based on membership level
  3. Calculate the final price
  4. Process the order

Implementation

Step 1: Define the Inventory Plugin

public class InventoryPlugin
{
private readonly Dictionary<string, int> _stock = new()
{
["laptop"] = 15,
["phone"] = 50,
["tablet"] = 8,
["headphones"] = 100
};

[KernelFunction("check_stock")]
[Description("Checks if a product is available in stock")]
public StockResult CheckStock(string productName, int quantity)
{
Console.WriteLine($"[Inventory] Checking stock for: {productName}");
var normalizedName = productName.ToLower();
if (!_stock.TryGetValue(normalizedName, out int available))
{
return new StockResult(false, 0, "Product not found");
}

bool isAvailable = available >= quantity;
string message = isAvailable
? $"Available: {available} units in stock"
: $"Insufficient stock: only {available} units available";

return new StockResult(isAvailable, available, message);
}
}

public record StockResult(bool IsAvailable, int AvailableQuantity, string Message);

Step 2: Define the Pricing Plugin

public class PricingPlugin
{
private readonly Dictionary<string, decimal> _prices = new()
{
["laptop"] = 999.99m,
["phone"] = 699.99m,
["tablet"] = 449.99m,
["headphones"] = 149.99m
};

[KernelFunction("get_base_price")]
[Description("Gets the base price for a product")]
public decimal GetBasePrice(string productName)
{
Console.WriteLine($"[Pricing] Getting base price for: {productName}");
return _prices.TryGetValue(productName.ToLower(), out decimal price)
? price
: 0m;
}

[KernelFunction("calculate_discount")]
[Description("Calculates discount percentage based on membership level")]
public int CalculateDiscount(string membershipLevel)
{
Console.WriteLine($"[Pricing] Calculating discount for: {membershipLevel}");
return membershipLevel.ToLower() switch
{
"gold" => 20,
"silver" => 10,
"bronze" => 5,
_ => 0
};
}

[KernelFunction("calculate_final_price")]
[Description("Calculates the final price after discount")]
public decimal CalculateFinalPrice(decimal basePrice, int quantity, int discountPercent)
{
var subtotal = basePrice * quantity;
var discount = subtotal * discountPercent / 100;
var finalPrice = subtotal - discount;
Console.WriteLine($"[Pricing] Subtotal: ${subtotal}, Discount: ${discount}, Final: ${finalPrice}");
return finalPrice;
}
}

Step 3: Define the Shopping Assistant Plugin (Orchestrator)

This is where function nesting happens - the orchestrator calls functions from other plugins:

public class ShoppingAssistantPlugin
{
[KernelFunction("process_order")]
[Description("Processes a complete order by checking inventory, applying discounts, and calculating final price")]
public async Task<string> ProcessOrder(
Kernel kernel,
string productName,
int quantity,
string membershipLevel)
{
Console.WriteLine($"\n{'=',-50}");
Console.WriteLine($"Processing order: {quantity}x {productName} for {membershipLevel} member");
Console.WriteLine($"{'=',-50}\n");

// Step 1: Check inventory (NESTED CALL)
var stockResult = await kernel.InvokeAsync<StockResult>(
nameof(InventoryPlugin),
"check_stock",
new KernelArguments
{
["productName"] = productName,
["quantity"] = quantity
});

if (!stockResult.IsAvailable)
{
return $"""
ORDER FAILED
Product: {productName}
Reason: {stockResult.Message}
""";
}

// Step 2: Get base price (NESTED CALL)
var basePrice = await kernel.InvokeAsync<decimal>(
nameof(PricingPlugin),
"get_base_price",
new KernelArguments { ["productName"] = productName });

// Step 3: Calculate discount (NESTED CALL)
var discountPercent = await kernel.InvokeAsync<int>(
nameof(PricingPlugin),
"calculate_discount",
new KernelArguments { ["membershipLevel"] = membershipLevel });

// Step 4: Calculate final price (NESTED CALL)
var finalPrice = await kernel.InvokeAsync<decimal>(
nameof(PricingPlugin),
"calculate_final_price",
new KernelArguments
{
["basePrice"] = basePrice,
["quantity"] = quantity,
["discountPercent"] = discountPercent
});

return $"""
ORDER CONFIRMED
================
Product: {productName}
Quantity: {quantity}
Unit Price: ${basePrice}
Membership: {membershipLevel}
Discount: {discountPercent}%
Final Price: ${finalPrice}
Stock Status: {stockResult.Message}
""";
}
}

Step 4: Register Plugins and Execute

using Microsoft.SemanticKernel;

var kernel = Kernel.CreateBuilder().Build();

// Register all plugins
kernel.ImportPluginFromType<InventoryPlugin>();
kernel.ImportPluginFromType<PricingPlugin>();
kernel.ImportPluginFromType<ShoppingAssistantPlugin>();

// Process an order - this single call triggers multiple nested function calls
var result = await kernel.InvokeAsync<string>(
nameof(ShoppingAssistantPlugin),
"process_order",
new KernelArguments
{
["productName"] = "laptop",
["quantity"] = 2,
["membershipLevel"] = "gold"
});

Console.WriteLine(result);

Execution Flow

When ProcessOrder is called, the following nested calls occur:

ProcessOrder("laptop", 2, "gold")
|
+---> InventoryPlugin.CheckStock("laptop", 2)
| Returns: StockResult(true, 15, "Available...")
|
+---> PricingPlugin.GetBasePrice("laptop")
| Returns: 999.99
|
+---> PricingPlugin.CalculateDiscount("gold")
| Returns: 20
|
+---> PricingPlugin.CalculateFinalPrice(999.99, 2, 20)
Returns: 1599.98

Expected Output

==================================================
Processing order: 2x laptop for gold member
==================================================

[Inventory] Checking stock for: laptop
[Pricing] Getting base price for: laptop
[Pricing] Calculating discount for: gold
[Pricing] Subtotal: $1999.98, Discount: $399.996, Final: $1599.984

ORDER CONFIRMED
================
Product: laptop
Quantity: 2
Unit Price: $999.99
Membership: gold
Discount: 20%
Final Price: $1599.984
Stock Status: Available: 15 units in stock

Key Takeaways

  1. Kernel as Parameter: The orchestrator receives Kernel as a parameter to call other functions
  2. Plugin Isolation: Each plugin handles its own domain (inventory, pricing)
  3. Composition: Complex workflows are built by composing simple functions
  4. Type Safety: Return types are strongly typed using generics (InvokeAsync<T>)

Static vs Dynamic Orchestration

AspectStatic (This Example)Dynamic (AI-Driven)
Flow ControlDeveloper-definedAI decides
PredictabilityDeterministicNon-deterministic
Use CaseBusiness rulesNatural language
Function CallsExplicit in codeAuto-selected by LLM

Function nesting is a form of Static AI Orchestration (manual planning) where the developer explicitly defines which functions to call and when. This provides full control over the execution flow.

When to Use Function Nesting

  1. Business workflows with defined steps
  2. Order processing pipelines
  3. Data validation chains
  4. Report generation
  5. Any scenario requiring predictable, deterministic execution
Share this lesson: