Skip to main content

Semantic Kernel + AxonFlow Integration

What Semantic Kernel Does Well

Semantic Kernel is Microsoft's SDK for building AI agents and copilots—used in production across Microsoft's own products. Its strengths are substantial:

Plugin Architecture: Modular, composable functions that can be orchestrated together. Native and semantic functions combine naturally.

Enterprise-Ready Design: Built for production from day one. Strongly typed, with proper error handling and observability hooks.

Multi-Language Support: Native SDKs for C#, Python, and Java. First-class support across Microsoft's ecosystem.

Planner Capabilities: Automatic orchestration of plugins to achieve goals. The AI decides which functions to call and in what order.

Memory Systems: Persistent memory with vector store integrations. Context preservation across sessions.

Azure Integration: Deep integration with Azure OpenAI, Azure AI Search, and other Azure services. Enterprise SSO and compliance built in.


What Semantic Kernel Doesn't Try to Solve

Semantic Kernel focuses on AI orchestration and plugin composition. These concerns are explicitly out of scope:

Production RequirementSemantic Kernel's Position
Policy enforcement before plugin executionNot provided—plugins execute based on planner logic, not policies
PII detection in plugin inputs/outputsNot addressed—data flows through plugins without filtering
SQL injection preventionNot provided—must implement at plugin level
Per-user or per-plugin cost attributionNot tracked—requires external monitoring
Audit trails for complianceNot built in—must implement logging externally
Cross-plugin access controlNot addressed—any plugin can be invoked by the planner
Token budget enforcementNot provided—planners can consume unlimited tokens

This isn't a criticism—it's a design choice. Semantic Kernel handles orchestration. Governance is a separate concern.


Where Teams Hit Production Friction

Based on real enterprise deployments, here are the blockers that appear after the prototype works:

1. The Planner Gone Wild

A planner decides to use a database plugin, then a file plugin, then the database again. Each step is valid. 500 iterations later, the planner is still "planning".

Semantic Kernel executed the planner correctly. Nothing was watching how much it was planning.

2. The "What Plugins Ran?" Question

A user complains about an incorrect AI response. Support needs:

  • Which plugins were invoked?
  • In what order?
  • What inputs did each receive?
  • What was the planner's reasoning?

Semantic Kernel orchestrated the plugins. Without custom logging, the execution trace is gone.

3. The PII in Plugin Output

A summarization plugin processes customer feedback. The output includes customer names and email addresses. This output feeds into a reporting plugin.

Now PII is in your reports. Semantic Kernel has no mechanism to filter sensitive data between plugins.

4. The Security Review Block

Security review: BLOCKED
- No audit trail for plugin invocations
- PII can flow through plugin chains
- No policy enforcement before plugin execution
- Cost controls missing
- Access control for plugins not implemented

The copilot worked perfectly. It can't ship.

5. The Unauthorized Database Access

A planner decides a user's query requires a database plugin. The user shouldn't have database access. Semantic Kernel invokes the plugin—there's no permission check at the AI layer.


How AxonFlow Plugs In

AxonFlow doesn't replace Semantic Kernel. It sits underneath it—providing the governance layer that Semantic Kernel intentionally doesn't include:

┌─────────────────┐
│ Your App │
└────────┬────────┘

v
┌─────────────────┐
│ Semantic Kernel │ <-- Plugins, Planners, Memory
└────────┬────────┘

v
┌─────────────────────────────────┐
│ AxonFlow │
│ ┌───────────┐ ┌────────────┐ │
│ │ Policy │ │ Audit │ │
│ │ Enforce │ │ Trail │ │
│ └───────────┘ └────────────┘ │
│ ┌───────────┐ ┌────────────┐ │
│ │ PII │ │ Cost │ │
│ │ Detection│ │ Control │ │
│ └───────────┘ └────────────┘ │
└────────────────┬────────────────┘

v
┌─────────────────┐
│ LLM Provider │
└─────────────────┘

What this gives you:

  • Every plugin invocation logged with input/output and user context
  • PII detected and blocked before flowing between plugins
  • SQL injection attempts blocked in data-processing plugins
  • Cost tracked per plugin, per user, per planner execution
  • Compliance auditors can query the full plugin execution chain

What stays the same:

  • Your Semantic Kernel code doesn't change
  • Plugin definitions work as before
  • No new abstractions to learn

Integration Patterns

Pattern 1: Governed Kernel (Java)

Wrap Semantic Kernel with AxonFlow governance:

package com.example.semantickernel;

import com.getaxonflow.sdk.AxonFlow;
import com.getaxonflow.sdk.AxonFlowConfig;
import com.getaxonflow.sdk.PolicyApprovalResult;
import com.getaxonflow.sdk.TokenUsage;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

public class GovernedKernel {

private final AxonFlow axonflow;
private final Map<String, Function<String, String>> plugins = new HashMap<>();
private final String userToken;

public GovernedKernel(String userToken) {
this.userToken = userToken;
this.axonflow = AxonFlow.create(AxonFlowConfig.builder()
.agentUrl(System.getenv("AXONFLOW_AGENT_URL"))
.clientId("semantic-kernel-java")
.clientSecret(System.getenv("AXONFLOW_CLIENT_SECRET"))
.build());
}

public void registerPlugin(String name, Function<String, String> execute) {
plugins.put(name, execute);
}

public PluginResult invokePlugin(String pluginName, String input) throws Exception {
long startTime = System.currentTimeMillis();

Map<String, Object> context = new HashMap<>();
context.put("plugin", pluginName);
context.put("framework", "semantic-kernel");

// 1. Policy check
PolicyApprovalResult approval = axonflow.getPolicyApprovedContext(
userToken, input, context
);

if (!approval.isApproved()) {
return new PluginResult(false, null, approval.getBlockReason());
}

// 2. Execute plugin
Function<String, String> plugin = plugins.get(pluginName);
if (plugin == null) {
throw new IllegalArgumentException("Plugin not found: " + pluginName);
}

String result = plugin.apply(input);
long latencyMs = System.currentTimeMillis() - startTime;

// 3. Audit
axonflow.auditLLMCall(
approval.getContextId(),
result.substring(0, Math.min(200, result.length())),
"semantic-kernel",
pluginName,
TokenUsage.of(50, 100, 150),
latencyMs
);

return new PluginResult(true, result, null);
}

public record PluginResult(boolean success, String result, String blockReason) {}
}

// Usage
GovernedKernel kernel = new GovernedKernel("user-123");

kernel.registerPlugin("summarize", text -> "Summary: " + text.substring(0, 100) + "...");
kernel.registerPlugin("translate", text -> "Translated: " + text);

PluginResult result = kernel.invokePlugin("summarize", "Long document text...");
if (result.success()) {
System.out.println(result.result());
} else {
System.out.println("Blocked: " + result.blockReason());
}

Pattern 2: Governed Kernel (TypeScript)

import { AxonFlow } from "@axonflow/sdk";

interface PluginResult {
success: boolean;
result?: string;
blockReason?: string;
}

type PluginFunction = (input: string) => Promise<string>;

class GovernedKernel {
private axonflow: AxonFlow;
private plugins: Map<string, PluginFunction> = new Map();
private userToken: string;

constructor(userToken: string) {
this.userToken = userToken;
this.axonflow = new AxonFlow({
endpoint: process.env.AXONFLOW_AGENT_URL!,
tenant: "semantic-kernel-ts",
});
}

registerPlugin(name: string, execute: PluginFunction): void {
this.plugins.set(name, execute);
}

async invokePlugin(pluginName: string, input: string): Promise<PluginResult> {
const startTime = Date.now();

// 1. Policy check
const approval = await this.axonflow.getPolicyApprovedContext({
userToken: this.userToken,
query: input,
context: {
plugin: pluginName,
framework: "semantic-kernel",
},
});

if (!approval.approved) {
return { success: false, blockReason: approval.blockReason };
}

// 2. Execute plugin
const plugin = this.plugins.get(pluginName);
if (!plugin) {
throw new Error(`Plugin not found: ${pluginName}`);
}

const result = await plugin(input);
const latencyMs = Date.now() - startTime;

// 3. Audit
await this.axonflow.auditLLMCall({
contextId: approval.contextId,
responseSummary: result.slice(0, 200),
provider: "semantic-kernel",
model: pluginName,
tokenUsage: { promptTokens: 50, completionTokens: 100, totalTokens: 150 },
latencyMs,
});

return { success: true, result };
}
}

// Usage
const kernel = new GovernedKernel("user-123");

kernel.registerPlugin("summarize", async (text) =>
`Summary: ${text.substring(0, 100)}...`
);

const result = await kernel.invokePlugin("summarize", "Long document...");

Pattern 3: Multi-Step Plans with Governance

Execute multi-step plans with policy checks at each step:

interface PlanStep {
plugin: string;
input: string;
}

class GovernedPlanner {
private kernel: GovernedKernel;

constructor(kernel: GovernedKernel) {
this.kernel = kernel;
}

async executePlan(steps: PlanStep[]): Promise<PluginResult[]> {
const results: PluginResult[] = [];

for (const step of steps) {
const result = await this.kernel.invokePlugin(step.plugin, step.input);
results.push(result);

if (!result.success) {
console.log(`Plan halted at step ${steps.indexOf(step)}: ${result.blockReason}`);
break;
}
}

return results;
}
}

// Execute a governed plan
const planner = new GovernedPlanner(kernel);
const results = await planner.executePlan([
{ plugin: "search", input: "Find renewable energy data" },
{ plugin: "analyze", input: "Analyze the data" },
{ plugin: "summarize", input: "Create a summary" },
]);

Example Implementations

LanguageSDKExample
Javaaxonflow-sdksemantic-kernel/java
TypeScript@axonflow/sdksemantic-kernel/typescript