Portal Community

How SK Planning Works in Octopus

1
User sends a complex request "Create a leave request for the whole team for next Monday, then notify the HR manager by email."
2
SKPlannerAdapter creates a plan The planner calls the LLM to generate a structured execution plan listing the SK functions to call in order.
3
Plan is validated against MaxSteps Plans exceeding MaxSteps are rejected to prevent runaway execution. The planner notifies the user.
4
Each plan step executes Steps may invoke SK semantic functions, SK native functions (C# methods), or Octopus MCP tools.
5
Step results are chained Output from step N is available as input to step N+1 via SK's variable context.
6
Final response synthesised After all steps complete, the LLM generates a natural language summary of what was accomplished.

SKPlannerAdapter

public class SKPlannerAdapter
{
    private readonly Kernel                    _kernel;
    private readonly SemanticKernelPluginConfig _config;

    public SKPlannerAdapter(ISKKernelRegistry registry,
                             IOptions<SemanticKernelPluginConfig> config)
    {
        _kernel = registry.Get("default");
        _config = config.Value;
    }

    public async Task<PlanExecutionResult> ExecutePlanAsync(
        string userGoal,
        ConversationContext ctx,
        CancellationToken ct)
    {
        if (!_config.Planner.Enabled)
            throw new InvalidOperationException("SK Planner is not enabled.");

        // Use SK's FunctionCallingStepwisePlanner
        var plannerConfig = new FunctionCallingStepwisePlannerOptions
        {
            MaxIterations = _config.Planner.MaxSteps
        };
        var planner = new FunctionCallingStepwisePlanner(plannerConfig);

        // Execute the plan
        var result = await planner.ExecuteAsync(_kernel, userGoal, cancellationToken: ct);

        return new PlanExecutionResult
        {
            FinalAnswer    = result.FinalAnswer,
            StepCount      = result.Iterations,
            ChatHistory    = result.ChatHistory
        };
    }
}

Plan Execution Limits

SettingDefaultEffect of Exceeding
Planner.MaxSteps10Plan is abandoned; user gets "plan too complex" message
Planner.AllowLoopsfalsePlans with loops are rejected at validation time
CommandTimeoutSeconds30Individual LLM calls within the plan time out

Exposing MCP Tools to the Planner

Octopus MCP tools are bridged to SK as native kernel functions, making them available to the planner:

// In OnStartAsync — bridge MCP tools to SK kernel functions
public async Task OnStartAsync(IServiceProvider sp, CancellationToken ct)
{
    var registry = sp.GetRequiredService<MCPToolRegistry>();
    var kernel   = sp.GetRequiredService<ISKKernelRegistry>().Get("default");

    // Wrap each registered MCP tool as a KernelFunction
    foreach (var tool in registry.GetAll())
    {
        var skFunction = KernelFunctionFactory.CreateFromMethod(
            async (KernelArguments args, CancellationToken token) =>
            {
                var input = JsonSerializer.Deserialize<JsonElement>(
                    args["input"]?.ToString() ?? "{}");
                return await tool.Handler(input, ConversationContext.Empty, token);
            },
            functionName:  tool.Schema.Name,
            description:   tool.Schema.Description);

        kernel.Plugins.AddFromFunctions("OctopusMCPTools", new[] { skFunction });
    }
}
Planner cost. Each plan step involves one or more LLM calls in addition to the initial planning call. A 5-step plan may incur 6–10 LLM calls. Monitor token usage carefully when the planner is enabled.