Octopus
SK Planner Integration
The SK stepwise planner decomposes a user goal into a sequence of function calls, executes them in order, and synthesises a final response. The SKPlannerAdapter bridges this with the Octopus conversation loop.
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
| Setting | Default | Effect of Exceeding |
|---|---|---|
Planner.MaxSteps | 10 | Plan is abandoned; user gets "plan too complex" message |
Planner.AllowLoops | false | Plans with loops are rejected at validation time |
CommandTimeoutSeconds | 30 | Individual 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.